@muten/shadcn 0.1.14 → 0.2.0

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 (182) hide show
  1. package/AGENTS.md +88 -1
  2. package/globals.css +1 -0
  3. package/kit.css +329 -124
  4. package/motion.css +3 -0
  5. package/package.json +1 -1
  6. package/registry/accordion.muten +3 -3
  7. package/registry/agenda.js +19 -16
  8. package/registry/agenda.muten +10 -10
  9. package/registry/alert-dialog.muten +2 -2
  10. package/registry/alert.muten +1 -1
  11. package/registry/app-shell.muten +1 -1
  12. package/registry/attachment.muten +1 -1
  13. package/registry/avatar.muten +4 -4
  14. package/registry/badge.muten +14 -3
  15. package/registry/band.muten +2 -2
  16. package/registry/bar-chart.muten +2 -2
  17. package/registry/bio-play.muten +2 -2
  18. package/registry/booking-play.muten +5 -5
  19. package/registry/booking.muten +2 -2
  20. package/registry/breadcrumb.muten +4 -4
  21. package/registry/button.muten +9 -7
  22. package/registry/calendar.js +15 -10
  23. package/registry/calendar.muten +1 -1
  24. package/registry/card.muten +1 -1
  25. package/registry/carousel.muten +1 -1
  26. package/registry/chat-play.muten +8 -8
  27. package/registry/chatbot.muten +5 -4
  28. package/registry/checklist.muten +1 -1
  29. package/registry/choice-list.muten +3 -3
  30. package/registry/code.js +10 -4
  31. package/registry/collapsible.muten +2 -2
  32. package/registry/color-picker.js +37 -36
  33. package/registry/color-picker.muten +1 -1
  34. package/registry/combobox.js +1 -1
  35. package/registry/combobox.muten +2 -2
  36. package/registry/command-palette.muten +2 -2
  37. package/registry/command.js +1 -1
  38. package/registry/command.muten +7 -7
  39. package/registry/context-menu.muten +3 -3
  40. package/registry/count-up.js +33 -31
  41. package/registry/count-up.muten +6 -4
  42. package/registry/date-field.js +1 -1
  43. package/registry/date-field.muten +3 -3
  44. package/registry/day-plan.muten +5 -5
  45. package/registry/device-frame.muten +3 -3
  46. package/registry/dialog.muten +1 -1
  47. package/registry/dirty-bar.js +2 -2
  48. package/registry/dirty-bar.muten +3 -3
  49. package/registry/drawer.muten +1 -1
  50. package/registry/dropdown-menu.muten +4 -4
  51. package/registry/empty.muten +1 -1
  52. package/registry/feature-grid.muten +1 -1
  53. package/registry/feature-rows.muten +2 -2
  54. package/registry/field-behavior.js +13 -3
  55. package/registry/field-behavior.muten +3 -3
  56. package/registry/field.muten +5 -5
  57. package/registry/file-input.js +8 -4
  58. package/registry/file-input.muten +3 -3
  59. package/registry/fixed-card.muten +4 -4
  60. package/registry/folder-picker.js +6 -2
  61. package/registry/folder-picker.muten +2 -2
  62. package/registry/heatmap.muten +5 -5
  63. package/registry/image-upload.js +1 -1
  64. package/registry/image-upload.muten +2 -2
  65. package/registry/inbox-play.muten +5 -5
  66. package/registry/indicator.muten +1 -1
  67. package/registry/info-table.muten +1 -1
  68. package/registry/input-group.muten +11 -11
  69. package/registry/input-otp.muten +2 -2
  70. package/registry/input.muten +1 -1
  71. package/registry/inspector.muten +2 -2
  72. package/registry/item.muten +2 -2
  73. package/registry/kit/board.js +36 -34
  74. package/registry/kit/collections.js +24 -24
  75. package/registry/kit/core.js +197 -114
  76. package/registry/kit/dates.js +29 -27
  77. package/registry/kit/i18n.js +172 -0
  78. package/registry/kit/menus.js +14 -13
  79. package/registry/kit/overlays.js +7 -2
  80. package/registry/kit/structure.js +66 -61
  81. package/registry/kit/table.js +19 -17
  82. package/registry/kit/values.js +36 -28
  83. package/registry/kit-skeleton.muten +1 -1
  84. package/registry/kit-widget.js +9 -4
  85. package/registry/lang-scope.js +8 -0
  86. package/registry/lang-scope.muten +5 -0
  87. package/registry/legal-doc.muten +5 -5
  88. package/registry/link-btn.muten +1 -1
  89. package/registry/list-motion.js +46 -0
  90. package/registry/list-motion.muten +4 -0
  91. package/registry/live-stack.muten +2 -2
  92. package/registry/logo-cloud.muten +2 -2
  93. package/registry/marketing.muten +7 -7
  94. package/registry/member-list.muten +2 -2
  95. package/registry/menubar.muten +3 -3
  96. package/registry/message-scroller.muten +3 -3
  97. package/registry/message.muten +9 -9
  98. package/registry/messaging.muten +1 -1
  99. package/registry/modal.muten +4 -4
  100. package/registry/money-field.js +1 -1
  101. package/registry/money-field.muten +2 -2
  102. package/registry/nav-button.muten +4 -4
  103. package/registry/navigation-menu.muten +4 -4
  104. package/registry/navpill.muten +6 -6
  105. package/registry/new-appointment.js +1 -1
  106. package/registry/new-appointment.muten +4 -4
  107. package/registry/number-field.js +6 -4
  108. package/registry/number-field.muten +1 -1
  109. package/registry/order-board.js +1 -1
  110. package/registry/order-board.muten +2 -2
  111. package/registry/order-play.muten +4 -4
  112. package/registry/pagination-nav.js +1 -1
  113. package/registry/pagination-nav.muten +3 -3
  114. package/registry/pagination.muten +4 -4
  115. package/registry/password-field.muten +2 -2
  116. package/registry/permission-matrix.muten +4 -4
  117. package/registry/phone-bar.muten +6 -6
  118. package/registry/phone-field.js +1 -1
  119. package/registry/phone-field.muten +1 -1
  120. package/registry/phone-shell.js +1 -1
  121. package/registry/pill.muten +2 -2
  122. package/registry/plans.muten +1 -1
  123. package/registry/popover.muten +2 -2
  124. package/registry/pricing.muten +4 -4
  125. package/registry/product-play.muten +3 -3
  126. package/registry/progress.muten +3 -3
  127. package/registry/queue.muten +1 -1
  128. package/registry/radio-group.muten +1 -1
  129. package/registry/rank-item.muten +4 -4
  130. package/registry/resizable.muten +1 -1
  131. package/registry/row-menu.js +1 -1
  132. package/registry/row-menu.muten +2 -2
  133. package/registry/save-button.muten +5 -5
  134. package/registry/scale-slider.js +1 -1
  135. package/registry/scale-slider.muten +2 -2
  136. package/registry/screen-tour.muten +1 -1
  137. package/registry/share-bar.muten +2 -2
  138. package/registry/share-button.muten +4 -4
  139. package/registry/sheet.muten +1 -1
  140. package/registry/sidebar.muten +12 -6
  141. package/registry/site-footer.muten +6 -6
  142. package/registry/sortable-behavior.js +67 -20
  143. package/registry/sortable-list.js +3 -1
  144. package/registry/sortable-list.muten +2 -2
  145. package/registry/sortable.muten +5 -4
  146. package/registry/spinner.muten +2 -2
  147. package/registry/split-button.muten +2 -2
  148. package/registry/stacked-bars.js +3 -3
  149. package/registry/stacked-bars.muten +2 -2
  150. package/registry/stamp-card.muten +2 -2
  151. package/registry/stat-band.muten +3 -3
  152. package/registry/stat.muten +3 -3
  153. package/registry/status-bar.muten +2 -2
  154. package/registry/stepper.muten +4 -4
  155. package/registry/store-qr.js +1 -1
  156. package/registry/store-qr.muten +2 -2
  157. package/registry/swatch.muten +1 -1
  158. package/registry/switch.muten +8 -7
  159. package/registry/table-view.muten +3 -3
  160. package/registry/tabs-behavior.js +9 -1
  161. package/registry/tabs.muten +2 -2
  162. package/registry/tag-input.js +1 -1
  163. package/registry/tag-input.muten +2 -2
  164. package/registry/text.muten +2 -2
  165. package/registry/time-field.js +1 -1
  166. package/registry/time-field.muten +3 -3
  167. package/registry/timeline.muten +4 -4
  168. package/registry/toaster.muten +2 -2
  169. package/registry/toggle-group.muten +1 -1
  170. package/registry/toggle.muten +3 -3
  171. package/registry/toolbar.muten +4 -4
  172. package/registry/tooltip.muten +2 -2
  173. package/registry/tree-view.js +38 -25
  174. package/registry/tree-view.muten +1 -1
  175. package/registry/veil.muten +1 -1
  176. package/registry/virtual-list.js +1 -1
  177. package/registry/virtual-list.muten +2 -2
  178. package/registry/virtual-window.js +4 -4
  179. package/registry/virtual-window.muten +2 -2
  180. package/registry/wordline.muten +1 -1
  181. package/registry/zoom-control.muten +1 -1
  182. package/registry.json +22 -2
package/kit.css CHANGED
@@ -35,7 +35,7 @@
35
35
  .cn-alert[data-tone="info"] > .mu-icon { color: var(--info); } .cn-alert[data-tone="ok"] > .mu-icon { color: var(--success); } .cn-alert[data-tone="wait"] > .mu-icon { color: var(--warning); }
36
36
  .cn-alert[data-tone="wait"] { background: color-mix(in srgb, var(--warning) 6%, var(--card)); }
37
37
  /* Progress: the value rides --w (muten's style(w: …)); Nova's flex-1 would stretch it to full. */
38
- .cn-progress > .cn-progress-indicator:not([style*="transform"]) { flex: none; width: var(--w, 0%); transition: width 260ms cubic-bezier(.22, 1, .36, 1); }
38
+ .cn-progress > .cn-progress-indicator:not([style*="transform"]) { flex: none; width: var(--w, 0%); transition: width var(--t-move) var(--ease-out); }
39
39
  /* Skeleton: the shimmer on Nova's block (the kit's own .cx-sk keeps working for Customs). */
40
40
  .cn-skeleton { background: linear-gradient(90deg, var(--sk-a) 0%, var(--sk-b) 50%, var(--sk-a) 100%); background-size: 200% 100%; animation: cx-sk 1.3s linear infinite; }
41
41
  .cn-skeleton[data-shape="circle"], .cn-skeleton[data-shape="pill"] { border-radius: 99px; }
@@ -58,6 +58,9 @@
58
58
  .cx-switch-row > .cx-switch-why { grid-column: 1; margin: 0; font-size: 13px; color: var(--muted-foreground); line-height: 1.45; }
59
59
  .cx-switch-row > .cx-switch-why:empty { display: none; }
60
60
  .cx-switch-row > .cn-switch { margin-top: 1px; }
61
+ /* Switch(disabled): the native attribute, dimmed like Nova's data-disabled; its row's words dim with it */
62
+ .cn-switch:disabled { cursor: not-allowed; opacity: .5; }
63
+ .cx-switch-row[data-disabled="true"] > :is(.cn-label, .cx-switch-why) { opacity: .55; }
61
64
  /* Tokens swatches from muten (the artifact paints them with <i style>): the color rides --c. */
62
65
  .cx-sw > .cx-sw-i { width: 22px; height: 22px; border-radius: 6px; box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--foreground) 15%, transparent); flex: none; background: var(--c); }
63
66
  .cx-z > .cx-z-code { font: 12px var(--font-mono); color: var(--muted-foreground); }
@@ -83,20 +86,13 @@ button.cn-item { width: 100%; font: inherit; text-align: left; cursor: pointer;
83
86
  .cx-row3 .cx-row3-s:empty { display: none; }
84
87
  .cx-empty-mini > .mu-icon { font-size: 22px; color: var(--success); }
85
88
  /* Text atom from a part: data-variant comes from data(), same rules as the kit's .cx-text below. */
86
- /* Button icon motion, for span.mu-icon (the kit's rules below key on svg). */
87
- @media (hover: hover) and (prefers-reduced-motion: no-preference) {
88
- .cn-button > .mu-icon { transition: transform 180ms cubic-bezier(.22, 1, .36, 1); }
89
- .cn-button:not(:disabled):hover > .mu-icon:last-child:not(:first-child) { transform: translateX(2px); }
90
- .cn-button:not(:disabled):hover > .mu-icon:first-child:not(:last-child) { transform: scale(1.12); }
91
- .cn-button:not(:disabled):hover > .mu-icon:only-child { transform: scale(1.12); }
92
- }
93
89
 
94
90
 
95
91
  /* ═════════════════════════ 1b. the artifact page's library rules (floating panels, drawer, svg size, checkbox, toasts) ═════════════════════════ */
96
92
  .pg-pop { position: fixed; top: 0; left: 0; z-index: 60; }
97
- .pg-drw-o { position: fixed; inset: 0; z-index: 50; background: rgb(0 0 0 / .3); backdrop-filter: blur(8px); opacity: 0; transition: opacity 450ms cubic-bezier(0.22, 1, 0.36, 1); }
93
+ .pg-drw-o { position: fixed; inset: 0; z-index: 50; background: rgb(0 0 0 / .3); backdrop-filter: blur(8px); opacity: 0; transition: opacity 450ms var(--ease-out); }
98
94
  .pg-drw-o[data-state="open"] { opacity: 1; }
99
- .pg-drw { position: fixed; z-index: 50; margin: 8px; display: flex; flex-direction: column; border-radius: 24px; border: 1px solid var(--popover); background: var(--popover); color: var(--popover-foreground); font-size: 14px; box-shadow: 0 20px 25px -5px rgb(0 0 0 / .1), 0 8px 10px -6px rgb(0 0 0 / .1); transition: transform 450ms cubic-bezier(0.22, 1, 0.36, 1); will-change: transform; outline: none; }
95
+ .pg-drw { position: fixed; z-index: 50; margin: 8px; display: flex; flex-direction: column; border-radius: 24px; border: 1px solid var(--popover); background: var(--popover); color: var(--popover-foreground); font-size: 14px; box-shadow: 0 20px 25px -5px rgb(0 0 0 / .1), 0 8px 10px -6px rgb(0 0 0 / .1); transition: transform 450ms var(--ease-out); will-change: transform; outline: none; }
100
96
  :root[data-theme="dark"] .pg-drw { border-color: var(--border); }
101
97
  .pg-drw[data-dir="right"] { top: 0; right: 0; bottom: 0; width: calc(75% - 16px); max-width: 24rem; transform: translateX(calc(100% + 8px)); }
102
98
  .pg-drw[data-dir="bottom"] { left: 0; right: 0; bottom: 0; max-height: calc(100dvh - 6rem); transform: translateY(calc(100% + 8px)); }
@@ -115,7 +111,7 @@ button.cn-item { width: 100%; font: inherit; text-align: left; cursor: pointer;
115
111
  .cb-mark { width: 14px; height: 14px; }
116
112
  [data-state="unchecked"] > .cn-checkbox-indicator { visibility: hidden; }
117
113
  .style-nova .cb-mark path { stroke-dasharray: 24; stroke-dashoffset: 24; }
118
- .style-nova [data-state="checked"] .cb-mark path { stroke-dashoffset: 0; transition: stroke-dashoffset 200ms cubic-bezier(0.22, 1, 0.36, 1) 40ms; }
114
+ .style-nova [data-state="checked"] .cb-mark path { stroke-dashoffset: 0; transition: stroke-dashoffset 200ms var(--ease-out) 40ms; }
119
115
  .style-nova [data-state="checked"] > .cn-checkbox-indicator { visibility: visible; }
120
116
  .style-nova .cn-checkbox { transition: background-color 150ms, border-color 150ms, color 150ms; }
121
117
  .style-nova .cn-checkbox:active { scale: .92; transition: scale 100ms; }
@@ -125,12 +121,12 @@ button.cn-item { width: 100%; font: inherit; text-align: left; cursor: pointer;
125
121
  --gap: .75rem; --peek: .75rem; --scale: max(0, 1 - var(--i) * 0.1); --shrink: calc(1 - var(--scale));
126
122
  height: var(--front-h); z-index: calc(1000 - var(--i));
127
123
  transform: translateY(calc(0px - var(--i) * var(--peek) - var(--shrink) * var(--front-h))) scale(var(--scale));
128
- transition: transform 500ms cubic-bezier(0.22, 1, 0.36, 1), opacity 500ms, height 150ms; will-change: transform; }
124
+ transition: transform 500ms var(--ease-out), opacity 500ms, height 150ms; will-change: transform; }
129
125
  .pg-toast::after { content: ""; position: absolute; top: 100%; left: 0; width: 100%; height: calc(var(--gap) + 1px); }
130
126
  .pg-toasts[data-expanded] .pg-toast { height: var(--h); transform: translateY(calc(0px - var(--off) - var(--i) * var(--gap))); }
131
127
  .pg-toast[data-start], .pg-toast[data-end] { transform: translateY(150%) !important; }
132
128
  .pg-toast[data-limited] { opacity: 0; }
133
- .pg-toast-c { display: flex; height: 100%; align-items: center; gap: 12px; overflow: hidden; padding: 16px; transition: opacity 250ms cubic-bezier(0.22, 1, 0.36, 1); }
129
+ .pg-toast-c { display: flex; height: 100%; align-items: center; gap: 12px; overflow: hidden; padding: 16px; transition: opacity 250ms var(--ease-out); }
134
130
  .pg-toast[data-behind] .pg-toast-c { opacity: 0; }
135
131
  .pg-toasts[data-expanded] .pg-toast-c { opacity: 1; }
136
132
  .pg-toast-t { font-size: 14px; font-weight: 500; } .pg-toast-d { font-size: 14px; color: var(--muted-foreground); }
@@ -151,7 +147,7 @@ button.cn-item { width: 100%; font: inherit; text-align: left; cursor: pointer;
151
147
  /* spinner (shadcn's animate-spin) */
152
148
  @keyframes pg-spin { to { transform: rotate(360deg); } }
153
149
  .animate-spin { animation: pg-spin 1s linear infinite; }
154
- .cn-progress-indicator { transition: transform 450ms ease; }
150
+ .cn-progress-indicator { transition: transform var(--t-move) var(--ease-out); }
155
151
  /* native select: options readable in both themes */
156
152
  .pg-opt-native { background: var(--popover); color: var(--popover-foreground); }
157
153
  /* textarea counter (our extra) */
@@ -195,7 +191,14 @@ button.cn-item { width: 100%; font: inherit; text-align: left; cursor: pointer;
195
191
  --overlay: rgb(0 0 0 / 0.1);
196
192
  --on-destructive: oklch(1 0 0);
197
193
  --z-content: 0; --z-sticky: 20; --z-fab: 30; --z-popover: 40; --z-sheet: 50; --z-modal: 60; --z-toast: 70;
194
+ /* MOTION: the library's one rhythm (docs: AGENTS.md «Motion», bench /motion). Three curves, six durations; a
195
+ component never writes its own. Entering or appearing: --ease-out. Moving on screen: --ease-in-out. A panel that
196
+ slides in: --ease-drawer. Leaving takes about 0.7 of entering. A hover never animates (0 ms). */
197
+ --ease-out: cubic-bezier(.23, 1, .32, 1); --ease-in-out: cubic-bezier(.77, 0, .175, 1); --ease-drawer: cubic-bezier(.32, .72, 0, 1);
198
+ --t-press: 100ms; --t-micro: 150ms; --t-pop: 180ms; --t-move: 240ms; --t-modal: 240ms; --t-sheet: 380ms;
198
199
  }
200
+ /* reduce motion, in one place: every duration token drops to nearly nothing and everything still works */
201
+ @media (prefers-reduced-motion: reduce) { :root { --t-press: 1ms; --t-micro: 1ms; --t-pop: 1ms; --t-move: 1ms; --t-modal: 1ms; --t-sheet: 1ms; } }
199
202
 
200
203
  /* ── pieles de ejemplo: lo único que cambia entre apps y tiendas ── */
201
204
  [data-skin="storio"] {
@@ -233,16 +236,25 @@ button.cn-item { width: 100%; font: inherit; text-align: left; cursor: pointer;
233
236
  .cx-create { background: var(--primary); color: var(--primary-foreground); }
234
237
  .cx-save { width: 9.25rem; justify-content: center; }
235
238
  .cx-save[data-status="saving"] { pointer-events: none; opacity: .85; }
236
- .cx-save[data-status="saved"] { background: var(--success); color: #fff; animation: cx-pop 260ms cubic-bezier(.22, 1, .36, 1); }
239
+ .cx-save[data-status="saved"] { background: var(--success); color: #fff; animation: cx-pop 260ms var(--ease-out); }
237
240
  @keyframes cx-pop { 50% { transform: scale(1.04); } }
238
241
  .cx-btn-label { display: inline-flex; align-items: center; gap: 6px; }
239
242
 
240
243
  /* ── Badge tones + dot + removable chip ── */
241
- .cx-tone-new { background: var(--info-soft); color: var(--info); }
242
- .cx-tone-ok { background: var(--success-soft); color: var(--success); }
243
- .cx-tone-wait { background: var(--warning-soft); color: var(--warning); }
244
- .cx-tone-bad { background: color-mix(in srgb, var(--destructive) 14%, transparent); color: var(--destructive); }
245
- .cx-tone-pro { background: var(--selected); color: var(--selected-foreground); letter-spacing: .04em; }
244
+ /* shadcn's custom colours: a light tint and the word pulled toward the ink, so it reads in light and dark alike */
245
+ .cn-badge[class*="cx-tone-"] { background: color-mix(in oklch, var(--tone, var(--muted-foreground)) 14%, transparent); color: color-mix(in oklch, var(--tone, var(--muted-foreground)) 72%, var(--foreground)); }
246
+ .cx-tone-new { --tone: var(--info); }
247
+ .cx-tone-ok { --tone: var(--success); }
248
+ .cx-tone-wait { --tone: var(--warning); }
249
+ .cx-tone-bad { --tone: var(--destructive); }
250
+ .cn-badge.cx-tone-pro { background: var(--selected); color: var(--selected-foreground); letter-spacing: .04em; }
251
+ .cn-badge[class*="cx-tone-"] .cx-dot { background: var(--tone, currentColor); }
252
+ /* IconBadge / LinkBadge: shadcn's data-icon padding (the side with the icon is tighter), icon 12 px */
253
+ .cn-badge.cx-badge-icon { padding-left: 6px; }
254
+ .cn-badge.cx-badge-icon.cx-badge-end { padding-left: 8px; padding-right: 6px; }
255
+ .cn-badge.cx-badge-icon > svg, .cn-badge.cx-badge-icon > .cn-spinner { width: 12px; height: 12px; flex: none; }
256
+ .cn-badge.cx-badge-end > :first-child { order: 2; }
257
+ .cn-badge.cx-badge-link { text-decoration: none; }
246
258
  .cx-dot { width: 6px; height: 6px; border-radius: 99px; background: currentColor; flex: none; }
247
259
 
248
260
 
@@ -260,8 +272,16 @@ button.cn-item { width: 100%; font: inherit; text-align: left; cursor: pointer;
260
272
  [aria-checked="true"] > .cx-radio { border-color: var(--selected); }
261
273
  [aria-checked="true"] > .cx-radio::after { content: ""; width: 8px; height: 8px; border-radius: 99px; background: var(--selected); }
262
274
 
263
- .cx-sortable .cx-item { transition: transform 180ms cubic-bezier(.22, 1, .36, 1); background: var(--background); }
264
- .cx-sortable .cx-item.lifting { transition: none; z-index: 2; box-shadow: 0 8px 24px -8px rgb(0 0 0 / .25); position: relative; }
275
+ /* Sortable (sortable-behavior.js): the others slide apart, the row taken lifts and follows the pointer, a ghost of it
276
+ glides to where it will land; on release it settles onto the ghost */
277
+ .cx-sortable { position: relative; }
278
+ .cx-sortable .cx-item { position: relative; transition: translate var(--t-move) var(--ease-out); background: var(--background); }
279
+ .cx-sortable .cx-item.lifting { z-index: 3; scale: 1.02; cursor: grabbing; border-color: color-mix(in srgb, var(--foreground) 22%, transparent); box-shadow: 0 1px 2px rgb(0 0 0 / .06), 0 14px 32px -12px rgb(0 0 0 / .32); transition: scale var(--t-micro) var(--ease-out), box-shadow var(--t-micro) var(--ease-out), border-color var(--t-micro) var(--ease-out); } /* the border reads where the shadow does not (dark) */
280
+ .cx-sortable .cx-item.settling { z-index: 3; transition: translate 200ms var(--ease-out), scale 200ms var(--ease-out), box-shadow 200ms var(--ease-out); }
281
+ .cx-sortable .cx-item.cx-sort-ghost { position: absolute; top: 0; margin: 0; z-index: 1; pointer-events: none; opacity: 0; border-style: dashed; border-color: color-mix(in srgb, var(--selected) 55%, transparent); background: color-mix(in srgb, var(--selected) 6%, var(--background)); box-shadow: none; scale: 1; transition: translate var(--t-move) var(--ease-out), opacity var(--t-micro) var(--ease-out); }
282
+ .cx-sortable .cx-item.cx-sort-ghost.on { opacity: 1; }
283
+ .cx-sortable .cx-item.cx-sort-ghost > * { opacity: .45; }
284
+ :root.cx-sorting, :root.cx-sorting * { cursor: grabbing !important; user-select: none; }
265
285
 
266
286
  /* ── Card de forma fija ── */
267
287
  .cx-fixed-card { width: 100%; max-width: 380px; }
@@ -285,7 +305,7 @@ button.cn-item { width: 100%; font: inherit; text-align: left; cursor: pointer;
285
305
 
286
306
 
287
307
 
288
- .cx-tab-ind { position: absolute; z-index: 0; top: 3px; height: 26px; border-radius: calc(var(--radius) * .8); background: var(--background); box-shadow: 0 1px 3px 0 rgb(0 0 0 / .1), 0 1px 2px -1px rgb(0 0 0 / .1); transition: left 260ms cubic-bezier(.22, 1, .36, 1), width 260ms cubic-bezier(.22, 1, .36, 1); }
308
+ .cx-tab-ind { position: absolute; z-index: 0; top: 3px; height: 26px; border-radius: calc(var(--radius) * .8); background: var(--background); box-shadow: 0 1px 3px 0 rgb(0 0 0 / .1), 0 1px 2px -1px rgb(0 0 0 / .1); transition: left var(--t-move) var(--ease-out), width var(--t-move) var(--ease-out); }
289
309
  .cx-tabs.line { background: transparent; border-bottom: 1px solid var(--border); border-radius: 0; padding: 0; }
290
310
 
291
311
  .cx-tabs.line .cx-tab-ind { top: auto; bottom: -1px; height: 2px; border-radius: 2px; background: var(--selected); box-shadow: none; }
@@ -320,7 +340,8 @@ button.cn-item { width: 100%; font: inherit; text-align: left; cursor: pointer;
320
340
  .cx-book-sum b { color: var(--foreground); font-weight: 600; }
321
341
 
322
342
  /* ── Modal adaptable: Dialog en escritorio, Drawer en el teléfono (o forzado con data-mode) ── */
323
- .cx-overlay { position: fixed; inset: 0; z-index: var(--z-modal); background: var(--overlay); backdrop-filter: blur(4px); opacity: 0; transition: opacity 160ms ease; }
343
+ .cx-overlay { position: fixed; inset: 0; z-index: var(--z-modal); background: var(--overlay); backdrop-filter: blur(4px); opacity: 0; transition: opacity calc(var(--t-modal) * .62) ease; }
344
+ .cx-modal-root[data-open="true"] > .cx-overlay { transition-duration: calc(var(--t-modal) * .84); }
324
345
  .cx-overlay[data-state="open"] { opacity: 1; }
325
346
  .cx-modal { position: fixed; z-index: var(--z-modal); display: flex; flex-direction: column; background: var(--popover); color: var(--popover-foreground); box-shadow: 0 0 0 1px color-mix(in srgb, var(--foreground) 10%, transparent), 0 20px 25px -5px rgb(0 0 0 / .1); outline: none; font-size: 14px;
326
347
  /* one height for every control in a modal's form: a select, a time and a text field side by side read as one form */
@@ -341,11 +362,13 @@ button.cn-item { width: 100%; font: inherit; text-align: left; cursor: pointer;
341
362
  .cx-handle { display: none; width: 96px; height: 4px; margin: 10px auto 0; border-radius: 99px; background: var(--muted); flex: none; }
342
363
  /* escritorio = Dialog */
343
364
  .cx-modal[data-mode="dialog"] { top: 50%; left: 50%; width: min(28rem, calc(100% - 2rem)); max-height: min(36rem, calc(100dvh - 4rem)); border-radius: calc(var(--radius) * 1.4); overflow: hidden;
344
- transform: translate(-50%, -50%) scale(.95); opacity: 0; transition: transform 160ms cubic-bezier(.22, 1, .36, 1), opacity 160ms ease; }
365
+ transform: translate(-50%, calc(-50% + 6px)) scale(.97); opacity: 0; transition: transform calc(var(--t-modal) * .62) ease, opacity calc(var(--t-modal) * .5) ease; }
366
+ /* entering is slower than leaving: it comes up from 6 px lower at 97 %, and goes in about 0.6 of the time */
367
+ .cx-modal[data-mode="dialog"][data-state="open"], .cx-modal-root[data-open="true"] > .cx-modal[data-mode="dialog"] { transition: transform var(--t-modal) var(--ease-out), opacity calc(var(--t-modal) * .75) var(--ease-out); }
345
368
  .cx-modal[data-mode="dialog"][data-state="open"] { transform: translate(-50%, -50%); opacity: 1; }
346
369
  /* teléfono = Drawer desde abajo, arrastrable */
347
370
  .cx-modal[data-mode="drawer"] { left: 0; right: 0; bottom: 0; margin-inline: auto; width: min(100%, 34rem); max-height: calc(100dvh - 3rem); border-radius: calc(var(--radius) * 1.8) calc(var(--radius) * 1.8) 0 0; overflow: hidden;
348
- transform: translateY(100%); transition: transform 450ms cubic-bezier(.32, .72, 0, 1); touch-action: none; }
371
+ transform: translateY(100%); transition: transform 450ms var(--ease-drawer); touch-action: none; }
349
372
  .cx-modal[data-mode="drawer"][data-state="open"] { transform: translateY(0); }
350
373
  .cx-modal[data-mode="drawer"] .cx-handle { display: block; }
351
374
  .cx-modal[data-mode="drawer"] .cx-modal-body { touch-action: pan-y; }
@@ -432,7 +455,7 @@ html.cx-locked { overflow: hidden; }
432
455
  .cx-who b { display: block; font-weight: 500; }
433
456
  .cx-who small { color: var(--muted-foreground); font-size: 13px; }
434
457
  .cx-online { background: var(--success) !important; box-shadow: 0 0 0 2px var(--background); }
435
- .cx-tip { position: fixed; z-index: var(--z-toast); padding: 6px 12px; border-radius: calc(var(--radius) - 2px); background: var(--foreground); color: var(--background); font-size: 12px; font-weight: 500; line-height: 1.35; white-space: nowrap; pointer-events: none; opacity: 0; transform: translateY(3px) scale(.96); transform-origin: var(--ax, 50%) 100%; transition: opacity 120ms ease-out, transform 160ms cubic-bezier(.23, 1, .32, 1); }
458
+ .cx-tip { position: fixed; z-index: var(--z-toast); padding: 6px 12px; border-radius: calc(var(--radius) - 2px); background: var(--foreground); color: var(--background); font-size: 12px; font-weight: 500; line-height: 1.35; white-space: nowrap; pointer-events: none; opacity: 0; transform: translateY(3px) scale(.96); transform-origin: var(--ax, 50%) 100%; transition: opacity calc(var(--t-micro) * .8) ease-out, transform var(--t-micro) var(--ease-out); }
436
459
  /* its arrow, toward the trigger (shadcn's TooltipArrow: the pill's own colour, a softly rounded square half out) */
437
460
  .cx-tip::after { content: ""; position: absolute; left: calc(var(--ax, 50%) - 5px); bottom: -4px; width: 10px; height: 10px; border-radius: 2px; background: inherit; transform: rotate(45deg); }
438
461
  .cx-tip[data-side="bottom"] { transform: translateY(-3px) scale(.96); transform-origin: var(--ax, 50%) 0; }
@@ -449,7 +472,13 @@ html.cx-locked { overflow: hidden; }
449
472
  /* ── Fields ── */
450
473
  .cx-form { display: flex; flex-direction: column; gap: 18px; width: 100%; max-width: 380px; }
451
474
  .cx-num { font-variant-numeric: tabular-nums; }
452
- .cx-clear { opacity: 1; transition: opacity 120ms ease; }
475
+ /* the browser's own clear X never shows: SearchInput draws its own (.cx-clear) in its end addon, where it keeps its
476
+ place empty or not, so nothing beside it moves */
477
+ /* on EVERY search field of an app with the kit, not only .cn-input: muten writes each SearchField as type=search, and
478
+ Chrome drew its own X behind whatever sits at the field's end (Storio's «Ask Storio» send button) */
479
+ input[type="search"]::-webkit-search-cancel-button, input[type="search"]::-webkit-search-decoration { -webkit-appearance: none; appearance: none; display: none; }
480
+ .cx-clear { opacity: 1; transition: opacity 120ms ease, visibility 120ms; }
481
+ .cx-clear.cx-off { opacity: 0; visibility: hidden; }
453
482
  .cx-clear[hidden] { display: inline-flex !important; opacity: 0; pointer-events: none; }
454
483
  .cx-count-line { display: flex; justify-content: space-between; gap: 12px; }
455
484
  .cx-count-line span:last-child { font-variant-numeric: tabular-nums; flex: none; }
@@ -461,8 +490,8 @@ html.cx-locked { overflow: hidden; }
461
490
 
462
491
  /* ── Combobox list: under the field, its width, Nova's popover look ── */
463
492
  .cx-combo-list { max-height: 248px; overflow-y: auto; padding: 4px; border-radius: calc(var(--radius) * 1); background: var(--popover); color: var(--popover-foreground); box-shadow: 0 0 0 1px color-mix(in srgb, var(--foreground) 10%, transparent), 0 4px 6px -1px rgb(0 0 0 / .1), 0 2px 4px -2px rgb(0 0 0 / .1); font-size: 14px; overscroll-behavior: contain;
464
- opacity: 0; transform: scale(.96); transition: opacity 100ms ease, transform 100ms ease !important; transition-property: opacity, transform !important; }
465
- .cx-combo-list[data-state="open"] { opacity: 1; transform: none; }
493
+ opacity: 0; transform: scale(.96); transition: opacity calc(var(--t-pop) * .67) ease, transform calc(var(--t-pop) * .67) ease !important; transition-property: opacity, transform !important; }
494
+ .cx-combo-list[data-state="open"] { opacity: 1; transform: none; transition-duration: var(--t-pop) !important; transition-timing-function: var(--ease-out) !important; }
466
495
  .cx-opt { display: flex; align-items: center; gap: 8px; min-height: 32px; padding: 6px 8px; border-radius: calc(var(--radius) * .6); cursor: default; user-select: none; }
467
496
  .cx-opt[data-hl] { background: var(--accent); color: var(--accent-foreground); }
468
497
  .cx-opt mark { background: none; color: inherit; font-weight: 600; }
@@ -490,7 +519,7 @@ html.cx-locked { overflow: hidden; }
490
519
  .cx-fake i { display: block; height: 34px; border-radius: calc(var(--radius) * .8); background: var(--muted); }
491
520
  .cx-fake i:nth-child(2n) { width: 80%; }
492
521
  /* Band: grows its own height in, pushes content (never covers it), one at a time */
493
- .cx-band-slot { display: grid; grid-template-rows: 0fr; transition: grid-template-rows 260ms cubic-bezier(.22, 1, .36, 1); flex: none; }
522
+ .cx-band-slot { display: grid; grid-template-rows: 0fr; transition: grid-template-rows 260ms var(--ease-out); flex: none; }
494
523
  .cx-band-slot[data-open] { grid-template-rows: 1fr; }
495
524
  .cx-band-slot > div { overflow: hidden; min-height: 0; }
496
525
  .cx-band { display: flex; align-items: center; gap: 10px; padding: 9px 14px 9px 18px; font-size: 13px; line-height: 1.35; border-bottom: 1px solid var(--border); }
@@ -506,16 +535,16 @@ html.cx-locked { overflow: hidden; }
506
535
  .cx-band-slot:not([data-open]) > div > .cx-band { opacity: 0; }
507
536
  /* Island: one pill that changes shape and text in place */
508
537
  .cx-island { position: absolute; top: 10px; left: 50%; z-index: 3; display: flex; align-items: center; gap: 8px; height: 34px; padding: 0 14px 0 10px; border-radius: 99px; background: oklch(0.16 0 0); color: #fff; font-size: 13px; font-weight: 500; white-space: nowrap; overflow: hidden;
509
- transform: translateX(-50%); transition: width 380ms cubic-bezier(.22, 1, .36, 1), opacity 200ms ease, transform 380ms cubic-bezier(.22, 1, .36, 1); }
538
+ transform: translateX(-50%); transition: width 380ms var(--ease-out), opacity 200ms ease, transform 380ms var(--ease-out); }
510
539
  .cx-island[data-state="hidden"] { opacity: 0; transform: translateX(-50%) scale(.7); }
511
540
  .cx-island-icon { width: 18px; height: 18px; display: grid; place-items: center; flex: none; }
512
541
  .cx-island-icon svg { width: 16px; height: 16px; }
513
542
  .cx-island[data-tone="done"] .cx-island-icon { color: oklch(0.8 0.17 150); }
514
543
  .cx-island[data-tone="error"] .cx-island-icon { color: oklch(0.72 0.19 25); }
515
- .cx-island[data-tone="error"] { animation: cx-shake 320ms ease; }
544
+ .cx-island[data-tone="error"] { animation: cx-shake calc(var(--t-move) * 1.35) var(--ease-out); }
516
545
  .cx-island-text { transition: opacity 140ms ease; }
517
546
  .cx-island-text.swap { opacity: 0; }
518
- @keyframes cx-shake { 25% { margin-left: -4px; } 75% { margin-left: 4px; } }
547
+ @keyframes cx-shake { 25% { translate: -4px 0; } 75% { translate: 4px 0; } } /* translate, not margin: a shake never moves the layout around it */
519
548
 
520
549
  /* ── Accordion chevron rotates ── */
521
550
  .cx-acc-chev { margin-left: auto; width: 16px; height: 16px; flex: none; color: var(--muted-foreground); transition: transform 200ms ease; pointer-events: none; }
@@ -565,7 +594,7 @@ html.cx-locked { overflow: hidden; }
565
594
  .cx-otp-sep span { width: 8px; height: 2px; border-radius: 2px; background: var(--muted-foreground); }
566
595
  .cx-otp .cn-input-otp { display: flex; align-items: center; }
567
596
  .cx-otp[data-state="bad"] .cn-input-otp-slot { border-color: var(--destructive); }
568
- .cx-otp[data-state="bad"] { animation: cx-shake 320ms ease; }
597
+ .cx-otp[data-state="bad"] { animation: cx-shake calc(var(--t-move) * 1.35) var(--ease-out); }
569
598
  .cx-otp[data-state="ok"] .cn-input-otp-slot { border-color: var(--success); color: var(--success); }
570
599
  #otp-msg[data-tone="bad"] { color: var(--destructive); }
571
600
  #otp-msg[data-tone="ok"] { color: var(--success); }
@@ -698,9 +727,9 @@ html.cx-locked { overflow: hidden; }
698
727
  .cx-check { width: 16px; }
699
728
 
700
729
  /* ── DataTable: a table while its box is wide, a list of Items when the box is narrow ── */
701
- .cx-dt-host { container-type: inline-size; width: 100%; max-width: 920px; margin: 0 auto; display: flex; flex-direction: column; transition: max-width 320ms cubic-bezier(.22, 1, .36, 1); }
730
+ .cx-dt-host { container-type: inline-size; width: 100%; max-width: 920px; margin: 0 auto; display: flex; flex-direction: column; transition: max-width 320ms var(--ease-out); }
702
731
  .cx-dt-host[data-narrow] { max-width: 380px; }
703
- .cx-dt-bulk { display: grid; grid-template-rows: 0fr; transition: grid-template-rows 220ms cubic-bezier(.22, 1, .36, 1); }
732
+ .cx-dt-bulk { display: grid; grid-template-rows: 0fr; transition: grid-template-rows 220ms var(--ease-out); }
704
733
  .cx-dt-bulk[data-open] { grid-template-rows: 1fr; }
705
734
  .cx-dt-bulk > div { overflow: hidden; min-height: 0; display: flex; align-items: center; justify-content: space-between; gap: 12px; font-size: 13px; font-weight: 500; }
706
735
  .cx-dt-bulk[data-open] > div { padding: 0 4px 10px; }
@@ -709,7 +738,6 @@ html.cx-locked { overflow: hidden; }
709
738
  .cx-dt th { position: sticky; top: 0; z-index: 1; background: var(--muted); text-align: left; font-weight: 500; color: var(--muted-foreground); height: 40px; padding: 0 12px; border-bottom: 1px solid var(--border); white-space: nowrap; }
710
739
  .cx-dt td { height: 52px; padding: 0 12px; border-bottom: 1px solid var(--border); vertical-align: middle; }
711
740
  .cx-dt tbody tr:last-child td { border-bottom: 0; }
712
- .cx-dt tbody tr { transition: background-color 120ms; }
713
741
  .cx-dt tbody tr:hover { background: color-mix(in srgb, var(--muted) 50%, transparent); }
714
742
  .cx-dt tbody tr[data-selected] { background: var(--selected-soft); }
715
743
  .cx-dt .cx-dt-check { width: 40px; padding-right: 0; }
@@ -1282,7 +1310,7 @@ html.cx-locked { overflow: hidden; }
1282
1310
  .cn-item-description .cx-ellipsis, .cn-item-description { overflow: hidden; text-overflow: ellipsis; }
1283
1311
 
1284
1312
  /* ═════ board behaviour only (the look is the Card atom) ═════ */
1285
- .cx-bcard { cursor: grab; touch-action: none; user-select: none; transition: transform 200ms cubic-bezier(.22, 1, .36, 1), box-shadow 150ms; }
1313
+ .cx-bcard { cursor: grab; touch-action: none; user-select: none; transition: transform 200ms var(--ease-out), box-shadow 150ms; }
1286
1314
  .cx-bcard-static { cursor: default; touch-action: auto; user-select: auto; }
1287
1315
  .cx-bcard:focus-visible { outline: 2px solid var(--ring); outline-offset: 2px; }
1288
1316
  .cx-bcard[data-lifted], .cx-bcard[data-held] { box-shadow: 0 0 0 2px var(--selected), 0 12px 28px -10px rgb(0 0 0 / .3); }
@@ -1310,6 +1338,11 @@ html.cx-locked { overflow: hidden; }
1310
1338
  .cx-tab { position: relative; z-index: 1; color: color-mix(in srgb, var(--foreground) 62%, transparent); }
1311
1339
  .cx-tab:hover { background: transparent; color: var(--foreground); }
1312
1340
  .cx-tab[aria-selected="true"] { color: var(--foreground); }
1341
+ /* only the pill moves: a tab never paints its own fill (hover, press, focus, chosen), or the clicked tab shows a second
1342
+ box under the pill while it slides; the word changes colour on the pill's own 260 ms curve */
1343
+ .cx-tabs > .cn-button.cx-tab, .cx-tabs > .cn-button.cx-tab:is(:hover, :active, :focus-visible, [aria-selected="true"], [aria-expanded="true"]) { background: transparent; border-color: transparent; box-shadow: none; transform: none; }
1344
+ .cx-tabs > .cn-button.cx-tab:focus-visible { outline: 2px solid var(--ring); outline-offset: -2px; }
1345
+ .cx-tabs > .cn-button.cx-tab { transition: color var(--t-move) var(--ease-out); }
1313
1346
  /* a segment picks with the sliding pill, not with a filled button: beats the generic «pressed = selected» fill above */
1314
1347
  .cx-tabs > .cn-button.cx-tab[aria-pressed="true"][aria-pressed][aria-pressed],
1315
1348
  .cx-tabs > .cn-button.cx-tab[aria-pressed="true"][aria-pressed][aria-pressed]:hover { background: transparent; border-color: transparent; color: var(--foreground); }
@@ -1338,7 +1371,7 @@ th[aria-sort="ascending"] .cx-th svg, th[aria-sort="descending"] .cx-th svg { op
1338
1371
  .cx-ph-tab[aria-current] { color: var(--selected); background: transparent; }
1339
1372
  .cx-ph-tab[aria-current]::after { content: ""; position: absolute; bottom: 4px; width: 4px; height: 4px; border-radius: 99px; background: var(--selected); }
1340
1373
  /* carousel dots: a real button (24 px) with a small mark */
1341
- .cx-dotb .cx-dot-mark { width: 8px; height: 8px; border-radius: 99px; background: color-mix(in srgb, var(--foreground) 22%, transparent); transition: width 240ms cubic-bezier(.22, 1, .36, 1), background-color 200ms; }
1374
+ .cx-dotb .cx-dot-mark { width: 8px; height: 8px; border-radius: 99px; background: color-mix(in srgb, var(--foreground) 22%, transparent); transition: width 240ms var(--ease-out), background-color 200ms; }
1342
1375
  .cx-dotb[aria-selected="true"] { width: 34px; }
1343
1376
  .cx-dotb[aria-selected="true"] .cx-dot-mark { width: 22px; background: var(--selected); }
1344
1377
  .cx-car-dots { display: flex; gap: 0; }
@@ -1361,6 +1394,9 @@ th[aria-sort="ascending"] .cx-th svg, th[aria-sort="descending"] .cx-th svg { op
1361
1394
  .cn-card[data-size="sm"] { gap: 0; padding-block: 0; }
1362
1395
  .cn-card[data-size="sm"] > .cn-card-header { padding: 12px 12px 4px; }
1363
1396
  .cn-card[data-size="sm"] > .cn-card-content { padding: 0 12px; }
1397
+ /* a content with nothing above or below it keeps the card's 12 px there too (it was glued to the edge) */
1398
+ .cn-card[data-size="sm"] > .cn-card-content:first-child { padding-top: 12px; }
1399
+ .cn-card[data-size="sm"] > .cn-card-content:last-child { padding-bottom: 12px; }
1364
1400
  .cn-card[data-size="sm"] > .cn-card-footer { margin-top: 8px; padding: 10px 12px; }
1365
1401
  .cn-card[data-size="sm"] .cn-item-group { gap: 0; }
1366
1402
  .cn-card[data-size="sm"] .cn-item-separator { margin-block: 0; }
@@ -1423,10 +1459,15 @@ p.cx-text { margin: 0; }
1423
1459
  .cx-car-track.dragging { scroll-snap-type: none; scroll-behavior: auto; cursor: grabbing; user-select: none; }
1424
1460
  .cx-car-track:focus-visible { outline: 2px solid var(--ring); outline-offset: 4px; }
1425
1461
  .cx-car-slide { scroll-snap-align: start; min-width: 0; }
1462
+ /* a scrolling track clips both axes: 6 px of room above and below keeps a card's ring and shadow whole (it was cut at
1463
+ the top), taken back with a negative margin so the carousel does not grow */
1464
+ .cx-car-track { padding-block: 6px; margin-block: -6px; }
1426
1465
  /* while the mouse drags the track it follows the hand; on release kit core settles it on the nearest page */
1427
1466
  .cx-car-track.dragging { scroll-snap-type: none; scroll-behavior: auto; cursor: grabbing; user-select: none; }
1428
- .cx-car[data-center] .cx-car-track { grid-auto-columns: 72%; padding-inline: 14%; }
1429
- .cx-car[data-center] .cx-car-slide { scroll-snap-align: center; transition: transform 260ms cubic-bezier(.22, 1, .36, 1), opacity 260ms; transform: scale(.92); opacity: .55; }
1467
+ /* centred like Embla's containScroll: the first slide sits at the start and the last at the end (no empty strip
1468
+ beside them), the ones between snap to the centre */
1469
+ .cx-car[data-center] .cx-car-track { grid-auto-columns: 72%; }
1470
+ .cx-car[data-center] .cx-car-slide { scroll-snap-align: center; transition: transform 260ms var(--ease-out), opacity 260ms; transform: scale(.92); opacity: .55; }
1430
1471
  .cx-car[data-center] .cx-car-slide[data-active] { transform: none; opacity: 1; }
1431
1472
  .cx-car-bar { display: flex; align-items: center; justify-content: space-between; gap: 12px; min-height: 28px; }
1432
1473
  .cx-car-ind { flex: 1; min-width: 0; }
@@ -1473,9 +1514,9 @@ p.cx-text { margin: 0; }
1473
1514
  .cn-sidebar-menu-badge { background: color-mix(in srgb, var(--foreground) 8%, transparent); }
1474
1515
 
1475
1516
  /* ═════ App frame (the shell is a molecule of Sidebar + Inset + Sheet) ═════ */
1476
- .cx-app { position: relative; display: flex; height: 600px; width: 100%; max-width: 1180px; margin: 0 auto; border-radius: calc(var(--radius) * 1.6); overflow: hidden; background: var(--background); box-shadow: 0 0 0 1px var(--border), 0 24px 60px -30px rgb(0 0 0 / .35); transition: max-width 320ms cubic-bezier(.22, 1, .36, 1); }
1517
+ .cx-app { position: relative; display: flex; height: 600px; width: 100%; max-width: 1180px; margin: 0 auto; border-radius: calc(var(--radius) * 1.6); overflow: hidden; background: var(--background); box-shadow: 0 0 0 1px var(--border), 0 24px 60px -30px rgb(0 0 0 / .35); transition: max-width 320ms var(--ease-out); }
1477
1518
  .cx-app[data-narrow] { max-width: 390px; }
1478
- .cx-app-sb { flex: none; width: var(--sidebar-width); border-right: 1px solid var(--sidebar-border); transition: width 220ms cubic-bezier(.22, 1, .36, 1); overflow: hidden; }
1519
+ .cx-app-sb { flex: none; width: var(--sidebar-width); border-right: 1px solid var(--sidebar-border); transition: width 220ms var(--ease-out); overflow: hidden; }
1479
1520
  .cx-app-sb[data-collapsible="icon"] { width: var(--sidebar-width-icon); }
1480
1521
  .cx-app-sb .cn-sidebar-inner { height: 100%; display: flex; flex-direction: column; padding: 8px; gap: 8px; }
1481
1522
  .cx-app-sb .cn-sidebar-content { gap: 6px; }
@@ -1492,12 +1533,12 @@ p.cx-text { margin: 0; }
1492
1533
  .cx-app-head { display: flex; flex-wrap: wrap; align-items: flex-end; justify-content: space-between; gap: 12px; }
1493
1534
  .cx-app-head h1.cx-text { margin: 0; font-size: 22px; }
1494
1535
  .cx-app-list { gap: 8px; }
1495
- .cx-app-row { width: 100%; text-align: left; font: inherit; color: inherit; background: var(--background); cursor: pointer; transition: background-color 150ms, border-color 150ms; }
1536
+ .cx-app-row { width: 100%; text-align: left; font: inherit; color: inherit; background: var(--background); cursor: pointer; }
1496
1537
  .cx-app-row:hover { background: color-mix(in srgb, var(--foreground) 3%, var(--background)); }
1497
1538
  .cx-app-row[data-active] { border-color: var(--selected); background: var(--selected-soft); }
1498
1539
  .cx-app-row .cn-item-actions { gap: 14px; }
1499
1540
  /* detail Sheet: inside the frame, slides from the right */
1500
- .cx-app .cn-sheet-content.cx-app-sheet[data-side] { position: absolute; top: 0; bottom: 0; right: 0; left: auto; height: auto; max-width: none; width: min(380px, 100%); border-left: 1px solid var(--border); box-shadow: -18px 0 40px -24px rgb(0 0 0 / .3); gap: 0; z-index: 3; transform: translateX(100%); transition: transform 240ms cubic-bezier(.22, 1, .36, 1); }
1541
+ .cx-app .cn-sheet-content.cx-app-sheet[data-side] { position: absolute; top: 0; bottom: 0; right: 0; left: auto; height: auto; max-width: none; width: min(380px, 100%); border-left: 1px solid var(--border); box-shadow: -18px 0 40px -24px rgb(0 0 0 / .3); gap: 0; z-index: 3; transform: translateX(100%); transition: transform 240ms var(--ease-out); }
1501
1542
  .cx-app .cn-sheet-content.cx-app-sheet[data-side][data-state="open"] { transform: none; }
1502
1543
  .cx-app-sheet .cn-sheet-header { padding: 16px 16px 8px; }
1503
1544
  .cx-app-sheet-body { flex: 1; overflow-y: auto; padding: 0 12px; scrollbar-width: thin; }
@@ -1514,7 +1555,7 @@ p.cx-text { margin: 0; }
1514
1555
  .cx-timeline li:not(:last-child)::after { content: ""; position: absolute; left: 3.5px; top: 18px; bottom: -10px; width: 1px; background: var(--border); }
1515
1556
  .cx-timeline li[data-now]::before { background: var(--selected); box-shadow: 0 0 0 4px var(--selected-soft); }
1516
1557
  /* the shell on a narrow box: sidebar becomes an off-canvas menu */
1517
- .cx-app[data-mobile] .cx-app-sb { position: absolute; z-index: 4; top: 0; bottom: 0; left: 0; width: min(270px, 85%); transform: translateX(-102%); transition: transform 240ms cubic-bezier(.22, 1, .36, 1); box-shadow: 18px 0 40px -24px rgb(0 0 0 / .35); }
1558
+ .cx-app[data-mobile] .cx-app-sb { position: absolute; z-index: 4; top: 0; bottom: 0; left: 0; width: min(270px, 85%); transform: translateX(-102%); transition: transform 240ms var(--ease-out); box-shadow: 18px 0 40px -24px rgb(0 0 0 / .35); }
1518
1559
  .cx-app[data-mobile][data-sb-open] .cx-app-sb { transform: none; }
1519
1560
  .cx-app[data-mobile] [data-app-scrim]:not([hidden]) { opacity: 1; z-index: 3; }
1520
1561
  .cx-app[data-mobile] .cx-app-search { width: 36px; padding: 0; justify-content: center; }
@@ -1533,21 +1574,6 @@ p.cx-text { margin: 0; }
1533
1574
 
1534
1575
 
1535
1576
  /* ═════════════════════════ 3. sys ═════════════════════════ */
1536
- /* ═════ Button · icon motion on hover (atom-level, every button with an icon) ═════ */
1537
- .cn-button svg { transition: transform 180ms cubic-bezier(.22, 1, .36, 1); }
1538
- @media (hover: hover) and (prefers-reduced-motion: no-preference) {
1539
- .cn-button:not(:disabled):hover > svg:last-child:not(:first-child) { transform: translateX(2px); } /* trailing arrow moves on */
1540
- .cn-button:not(:disabled):hover > svg:first-child:not(:last-child) { transform: scale(1.12); } /* leading icon lifts */
1541
- .cn-button:not(:disabled):hover > svg:first-child:not(:last-child):has(use[href="#plus"]) { transform: rotate(90deg); }
1542
- .cn-button:not(:disabled):hover > svg:only-child { transform: scale(1.12); }
1543
- .cn-button:not(:disabled):hover > svg:only-child:has(use[href="#left"]) { transform: translateX(-2px); }
1544
- .cn-button:not(:disabled):hover > svg:only-child:has(use[href="#right"]) { transform: translateX(2px); }
1545
- .cn-button:not(:disabled):hover > svg:only-child:has(use[href="#x"]) { transform: rotate(90deg); }
1546
- .cn-button:not(:disabled):hover > svg:only-child:has(use[href="#i-send"]) { transform: translate(2px, -2px); }
1547
- .cn-button:not(:disabled):hover > svg:only-child:has(use[href="#i-bell"]) { animation: cx-ring 600ms ease; }
1548
- .cn-button:not(:disabled):active > svg { transform: scale(.92); }
1549
- }
1550
- @keyframes cx-ring { 20% { transform: rotate(14deg); } 40% { transform: rotate(-12deg); } 60% { transform: rotate(8deg); } 80% { transform: rotate(-4deg); } }
1551
1577
 
1552
1578
  /* ═════ Calendar ═════ */
1553
1579
  .cx-cal { position: relative; display: inline-flex; flex-direction: column; gap: 8px; padding: 12px; border-radius: calc(var(--radius) * 1.2); box-shadow: 0 0 0 1px var(--border); background: var(--background); }
@@ -1577,22 +1603,24 @@ p.cx-text { margin: 0; }
1577
1603
  .cx-ag-colhead { display: grid; place-items: center; font-size: 13px; font-weight: 600; border-left: 1px solid var(--border); }
1578
1604
  .cx-ag-body { position: relative; display: grid; grid-template-columns: 56px repeat(var(--cols), 1fr); background-image: repeating-linear-gradient(to bottom, var(--border) 0 1px, transparent 1px var(--h)); }
1579
1605
  .cx-ag-times { position: relative; }
1580
- .cx-ag-times span { position: absolute; right: 8px; transform: translateY(-50%); white-space: nowrap; }
1581
- .cx-ag-times span:first-child { transform: none; }
1606
+ /* only the hour's own box (a direct child) sits on its line, centred on it; the text inside it is not a second
1607
+ absolute box (it was: the box collapsed to 0 high and the hour hung below its line) */
1608
+ .cx-ag-times > span { position: absolute; right: 8px; transform: translateY(-50%); white-space: nowrap; line-height: 1; }
1609
+ .cx-ag-times > span:first-child { transform: none; }
1582
1610
  .cx-ag-col { position: relative; border-left: 1px solid var(--border); }
1583
1611
  .cx-ag-slot { position: absolute; left: 0; right: 0; border: 0; background: transparent; cursor: pointer; }
1584
1612
  .cx-ag-slot:hover { background: color-mix(in srgb, var(--selected) 6%, transparent); }
1585
- .cx-ag-ev { --tone: var(--info); position: absolute; left: 4px; right: 4px; z-index: 1; display: flex; flex-direction: column; align-items: flex-start; gap: 0; overflow: hidden; padding: 4px 8px; border: 0; border-radius: calc(var(--radius) * .7); background: color-mix(in srgb, var(--tone) 20%, var(--background)); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--tone) 28%, transparent); color: var(--foreground); font: inherit; text-align: left; cursor: grab; touch-action: none; transition: box-shadow 150ms, top 160ms cubic-bezier(.22, 1, .36, 1); }
1613
+ .cx-ag-ev { --tone: var(--info); position: absolute; left: 4px; right: 4px; z-index: 1; display: flex; flex-direction: column; align-items: flex-start; gap: 0; overflow: hidden; padding: 4px 8px; border: 0; border-radius: calc(var(--radius) * .7); background: color-mix(in srgb, var(--tone) 20%, var(--background)); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--tone) 28%, transparent); color: var(--foreground); font: inherit; text-align: left; cursor: grab; touch-action: none; transition: box-shadow 150ms, top 160ms var(--ease-out); }
1586
1614
  .cx-ag-ev[data-tone="ok"] { --tone: var(--success); } .cx-ag-ev[data-tone="wait"] { --tone: var(--warning); } .cx-ag-ev[data-tone="info"] { --tone: var(--info); } .cx-ag-ev[data-tone="bad"] { --tone: var(--destructive); }
1587
1615
  .cx-ag-ev:hover { box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--tone) 40%, transparent), 0 4px 12px -6px rgb(0 0 0 / .35); }
1588
1616
  .cx-ag-ev[data-dragging] { transition: none; cursor: grabbing; z-index: 2; box-shadow: 0 10px 24px -10px rgb(0 0 0 / .4); }
1589
1617
  .cx-ag-ev:focus-visible { outline: 2px solid var(--ring); outline-offset: 1px; }
1590
1618
  .cx-ag-ev .cx-text { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%; }
1591
- .cx-ag-now { position: absolute; left: 56px; right: 0; height: 0; border-top: 2px solid var(--destructive); z-index: 2; pointer-events: none; }
1619
+ .cx-ag-now { position: absolute; left: 56px; right: 0; height: 0; margin-top: -1px; border-top: 2px solid var(--destructive); z-index: 2; pointer-events: none; } /* the 2px border centred ON the hour, like the pill and its tail */
1592
1620
  .cx-ag-now i { position: absolute; left: -5px; top: -6px; width: 10px; height: 10px; border-radius: 99px; background: var(--destructive); }
1593
1621
 
1594
1622
  /* ═════ Chat (inbox + chatbot) ═════ */
1595
- .cx-inbox { display: grid; grid-template-columns: 300px 1fr; height: 560px; width: 100%; max-width: 980px; margin: 0 auto; border-radius: calc(var(--radius) * 1.6); overflow: hidden; box-shadow: 0 0 0 1px var(--border); background: var(--background); transition: max-width 320ms cubic-bezier(.22, 1, .36, 1); }
1623
+ .cx-inbox { display: grid; grid-template-columns: 300px 1fr; height: 560px; width: 100%; max-width: 980px; margin: 0 auto; border-radius: calc(var(--radius) * 1.6); overflow: hidden; box-shadow: 0 0 0 1px var(--border); background: var(--background); transition: max-width 320ms var(--ease-out); }
1596
1624
  .cx-inbox[data-force-narrow] { max-width: 390px; }
1597
1625
  .cx-ib-list { display: flex; flex-direction: column; min-height: 0; border-right: 1px solid var(--border); }
1598
1626
  .cx-ib-items { flex: 1; overflow-y: auto; padding: 0 8px 8px; display: flex; flex-direction: column; gap: 2px; }
@@ -1633,6 +1661,7 @@ p.cx-text { margin: 0; }
1633
1661
  @keyframes cx-typing { 0%, 60%, 100% { opacity: .3; transform: none; } 30% { opacity: 1; transform: translateY(-3px); } }
1634
1662
  .cx-bot { display: flex; flex-direction: column; width: 100%; max-width: 440px; height: 520px; margin: 0 auto; border-radius: calc(var(--radius) * 1.6); overflow: hidden; box-shadow: 0 0 0 1px var(--border); background: var(--background); }
1635
1663
  .cx-bot-face { display: flex; align-items: center; justify-content: center; gap: 7px; width: 36px; height: 36px; flex: none; border-radius: 12px; background: var(--selected); }
1664
+ .cx-bot-logo { width: 36px; height: 36px; flex: none; border-radius: 12px; object-fit: contain; background: var(--muted); }
1636
1665
  .cx-bot-face i { display: block; width: 5px; height: 9px; border-radius: 99px; background: var(--selected-foreground); animation: cx-blink-eye 4s infinite; }
1637
1666
  @keyframes cx-blink-eye { 0%, 92%, 100% { transform: none; } 95% { transform: scaleY(.15); } }
1638
1667
  .cx-bot-sugs { padding: 8px 12px; gap: 6px; border-top: 1px solid var(--border); }
@@ -1657,7 +1686,7 @@ p.cx-text { margin: 0; }
1657
1686
  .cx-chart-x { display: grid; grid-template-columns: repeat(var(--n), 1fr); }
1658
1687
  .cx-chart-x span { overflow: visible; white-space: nowrap; }
1659
1688
  .cx-check-dot { display: grid; place-items: center; width: 24px; height: 24px; border-radius: 99px; box-shadow: inset 0 0 0 1.5px var(--border); font-size: 12px; font-weight: 600; color: var(--muted-foreground); }
1660
- .cx-check-dot[data-done] { background: var(--success); box-shadow: none; color: #fff; animation: cx-pop 300ms cubic-bezier(.22, 1, .36, 1); }
1689
+ .cx-check-dot[data-done] { background: var(--success); box-shadow: none; color: #fff; animation: cx-pop 300ms var(--ease-out); }
1661
1690
  .cx-check-dot svg { width: 13px; height: 13px; }
1662
1691
 
1663
1692
  /* ═════ Settings ═════ */
@@ -1669,8 +1698,8 @@ p.cx-text { margin: 0; }
1669
1698
  .cx-settings { container-type: inline-size; }
1670
1699
  /* floats over the form: it never takes or gives back space, it only fades and slides */
1671
1700
  .cx-dirty { position: sticky; bottom: 0; z-index: 5; height: 88px; box-sizing: border-box; padding: 24px 0 16px; display: flex; justify-content: flex-end; align-items: flex-end; pointer-events: none; }
1672
- .cx-dirty > .cx-dirty-in { position: relative; max-width: 100%; height: 48px; box-sizing: border-box; display: flex; align-items: center; gap: 16px; padding: 0 8px 0 18px; white-space: nowrap; border-radius: 99px; background: var(--foreground); color: var(--background); box-shadow: 0 16px 36px -14px rgb(0 0 0 / .45); opacity: 0; transform: translateY(10px) scale(.98); visibility: hidden; transition: opacity 180ms ease, transform 240ms cubic-bezier(.22, 1, .36, 1), visibility 0s linear 240ms; }
1673
- .cx-dirty[data-open] > .cx-dirty-in { opacity: 1; transform: none; visibility: visible; pointer-events: auto; transition: opacity 180ms ease, transform 240ms cubic-bezier(.22, 1, .36, 1), visibility 0s; }
1701
+ .cx-dirty > .cx-dirty-in { position: relative; max-width: 100%; height: 48px; box-sizing: border-box; display: flex; align-items: center; gap: 16px; padding: 0 8px 0 18px; white-space: nowrap; border-radius: 99px; background: var(--foreground); color: var(--background); box-shadow: 0 16px 36px -14px rgb(0 0 0 / .45); opacity: 0; transform: translateY(10px) scale(.98); visibility: hidden; transition: opacity 180ms ease, transform 240ms var(--ease-out), visibility 0s linear 240ms; }
1702
+ .cx-dirty[data-open] > .cx-dirty-in { opacity: 1; transform: none; visibility: visible; pointer-events: auto; transition: opacity 180ms ease, transform 240ms var(--ease-out), visibility 0s; }
1674
1703
  @media (prefers-reduced-motion: reduce) { .cx-dirty > .cx-dirty-in { transform: none; } }
1675
1704
  .cx-dirty .cx-text { color: inherit; }
1676
1705
  .cx-dirty .cn-button-variant-ghost { color: inherit; }
@@ -1688,6 +1717,8 @@ p.cx-text { margin: 0; }
1688
1717
  .cx-plans { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 240px), 1fr)); gap: 14px; width: 100%; max-width: 920px; }
1689
1718
  .cx-plan { display: flex; flex-direction: column; gap: 12px; padding: 0; cursor: pointer; transition: box-shadow 160ms, transform 160ms; }
1690
1719
  .cx-plan:hover { transform: translateY(-2px); }
1720
+ .style-nova .cn-card.cx-plan, .cn-card.cx-plan { overflow: visible; } /* nothing in a plan needs clipping, and a clipped card would also clip the strip below */
1721
+ .cx-plan:hover::after { content: ""; position: absolute; left: 0; right: 0; top: calc(100% - 2px); height: 8px; } /* fills the gap it lifts from: no blinking hover on its bottom edge */
1691
1722
  .cx-plan[aria-checked="true"] { box-shadow: 0 0 0 2px var(--selected); }
1692
1723
  .cx-plan:focus-visible { outline: 2px solid var(--ring); outline-offset: 3px; }
1693
1724
  .cx-plan-price { display: flex; align-items: baseline; gap: 6px; }
@@ -1737,7 +1768,7 @@ p.cx-text { margin: 0; }
1737
1768
  .cx-pw-meter { display: flex; flex-direction: column; gap: 8px; }
1738
1769
  .cx-pw-bars { display: grid; grid-template-columns: repeat(4, 1fr); gap: 4px; }
1739
1770
  .cx-pw-bars .cn-progress { height: 4px; }
1740
- .cx-pw-bars .cn-progress .cn-progress-indicator { transition: transform 260ms cubic-bezier(.22, 1, .36, 1), background-color 200ms; }
1771
+ .cx-pw-bars .cn-progress .cn-progress-indicator { transition: transform var(--t-move) var(--ease-out), background-color var(--t-move); }
1741
1772
  .cx-pw-bars .cn-progress[data-tone="bad"] .cn-progress-indicator { background: var(--destructive); }
1742
1773
  .cx-pw-bars .cn-progress[data-tone="wait"] .cn-progress-indicator { background: var(--warning); }
1743
1774
  .cx-pw-bars .cn-progress[data-tone="ok"] .cn-progress-indicator { background: var(--success); }
@@ -1788,6 +1819,8 @@ p.cx-text { margin: 0; }
1788
1819
  .cx-ag-strip { display: grid; grid-template-columns: repeat(7, 1fr); gap: 4px; }
1789
1820
  .cx-ag-day small { font-size: 11px; color: var(--muted-foreground); text-transform: uppercase; letter-spacing: .04em; }
1790
1821
  .cx-ag-day b { font-size: 16px; font-weight: 600; font-variant-numeric: tabular-nums; }
1822
+ /* a day with nothing booked has no count line: its name and number sit in the middle of the pill, not at its top */
1823
+ .cx-ag-day .cx-ag-count:empty { display: none; }
1791
1824
  .cx-ag-day .cx-ag-count { min-width: 18px; height: 16px; border-radius: 99px; font-size: 11px; font-weight: 600; color: var(--muted-foreground); font-variant-numeric: tabular-nums; }
1792
1825
  .cx-ag-day[data-today] b { color: var(--selected); }
1793
1826
  .cx-ag-day[aria-pressed="true"] small, .cx-ag-day[aria-pressed="true"] .cx-ag-count, .cx-ag-day[aria-pressed="true"] b { color: inherit; }
@@ -1851,7 +1884,7 @@ p.cx-text { margin: 0; }
1851
1884
  .cx-chat-sys svg { width: 14px; height: 14px; flex: none; }
1852
1885
 
1853
1886
  /* ── Messaging: 3 panes (wide) · thread + sheet (mid) · one pane (narrow) ── */
1854
- .cx-mx { display: grid; grid-template-columns: 320px minmax(0, 1fr) 288px; height: 640px; width: 100%; margin: 0 auto; border-radius: calc(var(--radius) * 1.6); border: 1px solid var(--border); background: var(--background); overflow: hidden; transition: max-width 260ms cubic-bezier(.22, 1, .36, 1); }
1887
+ .cx-mx { display: grid; grid-template-columns: 320px minmax(0, 1fr) 288px; height: 640px; width: 100%; margin: 0 auto; border-radius: calc(var(--radius) * 1.6); border: 1px solid var(--border); background: var(--background); overflow: hidden; transition: max-width 260ms var(--ease-out); }
1855
1888
  .cx-mx[data-layout="mid"] { grid-template-columns: 300px minmax(0, 1fr); }
1856
1889
  .cx-mx[data-layout="narrow"] { grid-template-columns: minmax(0, 1fr); }
1857
1890
  .cx-mx:not([data-layout="wide"]) .cx-mx-ctx:not(.cx-mx-ctx-sheet) { display: none; }
@@ -2013,7 +2046,7 @@ p.cx-text { margin: 0; }
2013
2046
  @container (max-width: 700px) {
2014
2047
  .cx-ag-scroll { scroll-snap-type: x mandatory; scroll-padding-left: 48px; }
2015
2048
  .cx-ag-col { scroll-snap-align: start; }
2016
- .cx-ag-times span { right: 5px; }
2049
+ .cx-ag-times > span { right: 5px; }
2017
2050
  .cx-ag-times .cx-text { font-size: 11px; letter-spacing: -.01em; } /* «09:00» fits its 48px column, never cut at the edge */
2018
2051
  }
2019
2052
 
@@ -2041,12 +2074,12 @@ p.cx-text { margin: 0; }
2041
2074
  .pk-scrim{ position:fixed; inset:0; z-index:var(--z-modal); background:transparent; }
2042
2075
  .pk-pop{ color:var(--popover-foreground); overflow-y:auto; overscroll-behavior:contain; scrollbar-width:thin; position:fixed; z-index:calc(var(--z-modal) + 1); width:292px; box-sizing:border-box; background:var(--popover); border-radius:20px;
2043
2076
  box-shadow:0 24px 60px -18px rgba(31,22,60,.38), 0 0 0 1px var(--border); padding:12px;
2044
- display:flex; flex-direction:column; gap:11px; animation:pk-in .18s cubic-bezier(.2,.8,.3,1); }
2077
+ display:flex; flex-direction:column; gap:11px; animation:pk-in .18s var(--ease-out); }
2045
2078
  @keyframes pk-in{ from{ opacity:0; transform:translateY(6px) scale(.98); } to{ opacity:1; transform:none; } }
2046
2079
  @media (max-width:35rem){
2047
2080
  .pk-scrim{ background:rgba(16,16,20,.28); }
2048
2081
  .pk-pop{ left:0 !important; right:0; top:auto !important; bottom:0 !important; max-height:88dvh !important; width:auto; border-radius:22px 22px 0 0;
2049
- padding:10px 16px calc(16px + env(safe-area-inset-bottom,0px)); animation:pk-up .24s cubic-bezier(.2,.8,.3,1); }
2082
+ padding:10px 16px calc(16px + env(safe-area-inset-bottom,0px)); animation:pk-up .24s var(--ease-out); }
2050
2083
  .pk-pop::before{ content:""; width:38px; height:5px; border-radius:5px; background:var(--muted); margin:0 auto 2px; }
2051
2084
  }
2052
2085
  @keyframes pk-up{ from{ transform:translateY(100%); } to{ transform:none; } }
@@ -2182,9 +2215,12 @@ p.cx-text { margin: 0; }
2182
2215
  .cx-band[data-tone="warn"] > .mu-icon { color: var(--warning); }
2183
2216
  .cx-band > .cx-band-text { flex: 1; }
2184
2217
  /* Accordion from muten: the panel folds with grid rows (exact height, no measuring) and its text fades. */
2185
- .cn-accordion-content.cx-acc-fold { display: grid; grid-template-rows: 1fr; transition: grid-template-rows 200ms ease; animation: none; }
2218
+ .cn-accordion-content.cx-acc-fold { display: grid; grid-template-rows: 1fr; transition: grid-template-rows var(--t-move) var(--ease-out); animation: none; }
2186
2219
  .cn-accordion-content.cx-acc-fold[data-open="false"] { grid-template-rows: 0fr; }
2187
- .cn-accordion-content.cx-acc-fold > .cn-accordion-content-inner { overflow: hidden; min-height: 0; transition: opacity 160ms ease; }
2220
+ /* the fold clips (it closes to 0 high) but 4 px past its box, so the first row's top edge and focus ring are not cut;
2221
+ folded, its opacity is 0, so those 4 px show nothing */
2222
+ .cn-accordion-content.cx-acc-fold > .cn-accordion-content-inner { overflow: clip; overflow-clip-margin: 4px; min-height: 0; transition: opacity var(--t-micro) ease, translate var(--t-move) var(--ease-out); }
2223
+ .cn-accordion-content.cx-acc-fold[data-open="false"] > .cn-accordion-content-inner { translate: 0 -4px; }
2188
2224
  .cn-accordion-content.cx-acc-fold[data-open="false"] > .cn-accordion-content-inner { opacity: 0; padding-bottom: 0; }
2189
2225
  .cn-accordion-trigger > .cx-acc-chev { margin-left: auto; font-size: 16px; color: var(--muted-foreground); transition: transform 200ms ease; }
2190
2226
  .cn-accordion-trigger[aria-expanded="true"] > .cx-acc-chev { transform: rotate(180deg); }
@@ -2205,9 +2241,9 @@ p.cx-text { margin: 0; }
2205
2241
 
2206
2242
  /* ── Loading: the content fades in where its skeleton was (kit-skeleton.muten) ── */
2207
2243
  :where(.style-nova) .cx-loading, :where(.style-nova) .cx-loaded, :where(.style-nova) .cx-sk-host { display: block; }
2208
- .cx-loaded { animation: cx-loaded-in 180ms ease-out; }
2209
- @keyframes cx-loaded-in { from { opacity: 0.001; } to { opacity: 1; } }
2210
- @media (prefers-reduced-motion: reduce) { .cx-loaded { animation: none; } }
2244
+ /* the content lands where its skeleton was, out of a short blur: the shape does not change, only the pixels */
2245
+ .cx-loaded { animation: cx-loaded-in var(--t-move) var(--ease-out); }
2246
+ @keyframes cx-loaded-in { from { opacity: 0.001; filter: blur(3px); } to { opacity: 1; filter: none; } }
2211
2247
  /* a skeleton line inside an Item title takes the title's width (the title shrinks to its text otherwise) */
2212
2248
  .cn-item-title:has(> .cx-sk) { width: 100%; }
2213
2249
  .cx-sk-tr > .cx-sk { flex: 0 1 auto; min-width: 0; }
@@ -2237,7 +2273,7 @@ p.cx-text { margin: 0; }
2237
2273
  .cx-rg-row:focus-visible .cx-rg-dot { border-color: var(--ring); box-shadow: 0 0 0 3px color-mix(in srgb, var(--ring) 50%, transparent); }
2238
2274
  .cx-rg-dot { position: relative; display: block; width: 16px; height: 16px; margin-top: 2px; border-radius: 99px; border: 1px solid var(--input); background: transparent; transition: background-color 150ms, border-color 150ms; }
2239
2275
  .cx-rg-dot[data-state="true"] { background: var(--primary); border-color: var(--primary); }
2240
- .cx-rg-dot::after { content: ""; position: absolute; top: 50%; left: 50%; width: 8px; height: 8px; border-radius: 99px; background: var(--primary-foreground); transform: translate(-50%, -50%) scale(0); transition: transform 150ms cubic-bezier(.22, 1, .36, 1); }
2276
+ .cx-rg-dot::after { content: ""; position: absolute; top: 50%; left: 50%; width: 8px; height: 8px; border-radius: 99px; background: var(--primary-foreground); transform: translate(-50%, -50%) scale(0); transition: transform 150ms var(--ease-out); }
2241
2277
  .cx-rg-dot[data-state="true"]::after { transform: translate(-50%, -50%) scale(1); }
2242
2278
  .cx-rg-row:active .cx-rg-dot { scale: .92; }
2243
2279
  .cx-rg-text { display: flex; flex-direction: column; gap: 2px; }
@@ -2259,16 +2295,15 @@ p.cx-text { margin: 0; }
2259
2295
 
2260
2296
  /* ── NumberField / VectorField (number-field.js, vector-field.muten) ── */
2261
2297
  .cx-nf-host { display: block; }
2262
- .cx-nf { width: fit-content; max-width: 100%; }
2263
- .cx-nf-input { text-align: left; font-variant-numeric: tabular-nums; field-sizing: content; min-width: 3ch; width: auto; flex: 0 1 auto; padding-right: 2px; }
2298
+ .cx-nf { width: 100%; }
2299
+ .cx-nf-input { text-align: left; font-variant-numeric: tabular-nums; min-width: 3ch; flex: 1 1 auto; padding-right: 2px; }
2264
2300
  .cx-nf-prefix { font-size: 12px; font-weight: 600; color: var(--muted-foreground); min-width: 14px; }
2265
2301
  .cx-nf-unit { font-size: 13px; color: var(--muted-foreground); padding-right: 2px; }
2266
2302
  .cx-nf-steps { gap: 0; padding-right: 4px; }
2267
2303
  .cx-nf-btn:disabled { opacity: .35; }
2268
- .cx-vector { display: flex; flex-wrap: wrap; gap: 8px; width: fit-content; max-width: 100%; }
2269
- .cx-vector .cx-nf { max-width: none; }
2304
+ .cx-vector { display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: 8px; width: 100%; }
2270
2305
  /* ── FileInput (file-input.js) ── */
2271
- .cx-file-host { display: flex; flex-direction: column; gap: 8px; width: 100%; max-width: 360px; }
2306
+ .cx-file-host { display: flex; flex-direction: column; gap: 8px; width: 100%; }
2272
2307
  .cx-file-drop { aspect-ratio: auto; min-height: 132px; }
2273
2308
  .cx-file-drop[data-over] { border-color: var(--primary); background: color-mix(in srgb, var(--primary) 6%, transparent); }
2274
2309
  .cx-file-msg { text-align: left; margin: 0; color: var(--destructive); }
@@ -2326,7 +2361,7 @@ p.cx-text { margin: 0; }
2326
2361
  .cx-stp { display: grid; grid-template-columns: repeat(var(--n, 3), minmax(0, 1fr)); gap: 6px; width: 100%; max-width: 480px; }
2327
2362
  .cx-stp-step { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
2328
2363
  .cx-stp-bar { height: 4px; }
2329
- .cx-stp-bar > .cn-progress-indicator { width: 0; transition: width 260ms cubic-bezier(.22, 1, .36, 1); }
2364
+ .cx-stp-bar > .cn-progress-indicator { width: 0; transition: width var(--t-move) var(--ease-out); }
2330
2365
  .cx-stp-step[data-state="done"] .cx-stp-bar > .cn-progress-indicator, .cx-stp-step[data-state="now"] .cx-stp-bar > .cn-progress-indicator { width: 100%; }
2331
2366
  .cx-stp-dot, .cx-stp-desc { display: none; }
2332
2367
  .cx-stp-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
@@ -2425,6 +2460,9 @@ p.cx-text { margin: 0; }
2425
2460
  .cx-ov-trigger { display: inline-flex; }
2426
2461
  .cx-ov-zone > .cx-ov-trigger { display: flex; width: 100%; }
2427
2462
  .cx-ov-behavior { display: none; }
2463
+ /* a panel place() lifted to the top layer: the browser's popover look goes (zero specificity, so the panel's own
2464
+ padding, border and background still win) */
2465
+ :where(.cx-floated[popover]) { position: fixed; inset: auto; margin: 0; border: 0; padding: 0; overflow: visible; background: transparent; color: inherit; }
2428
2466
  .cx-ov-content[popover] { position: fixed; inset: auto; margin: 0; border: 0; overflow: visible; width: var(--w, auto); max-width: calc(100vw - 16px); color: var(--popover-foreground); }
2429
2467
  .cx-ov-content:not([popover]) { display: none; }
2430
2468
  .cx-ov-content[popover]:popover-open { display: flex; flex-direction: column; }
@@ -2460,21 +2498,22 @@ p.cx-text { margin: 0; }
2460
2498
  .cx-navmenu-trigger .cn-navigation-menu-trigger-icon { width: 12px; height: 12px; transition: rotate 200ms ease; }
2461
2499
  .cx-ov-trigger[data-open] .cn-navigation-menu-trigger-icon { rotate: 180deg; }
2462
2500
  .cx-navmenu-panel[popover]:popover-open { display: grid; grid-template-columns: repeat(var(--cols, 2), minmax(0, 1fr)); gap: 4px; padding: 8px; border-radius: calc(var(--radius) * 1.2); background: var(--popover); box-shadow: 0 0 0 1px color-mix(in srgb, var(--foreground) 10%, transparent), 0 10px 30px -10px rgb(0 0 0 / .25); }
2463
- .cx-navmenu-link { display: flex; flex-direction: column; align-items: flex-start; gap: 2px; padding: 10px; border-radius: calc(var(--radius) * .8); color: inherit; text-decoration: none; }
2501
+ .cx-navmenu-link.cn-navigation-menu-link { display: flex; flex-direction: column; align-items: flex-start; text-align: start; gap: 2px; padding: 10px; border-radius: calc(var(--radius) * .8); color: inherit; text-decoration: none; }
2464
2502
  .cx-navmenu-link:hover, .cx-navmenu-link:focus-visible { background: var(--muted); outline: none; }
2465
2503
  .cx-navmenu-title { font-size: 14px; font-weight: 500; }
2466
2504
  .cx-navmenu-desc { font-size: 13px; line-height: 1.4; color: var(--muted-foreground); }
2467
2505
  .cx-navmenu-desc:empty { display: none; }
2468
2506
  /* the panels' enter / exit (Nova's animate-in reads data-open / data-closed, which the kit sets) */
2469
- .cx-ov-content[data-open] { animation: cx-ov-in 120ms cubic-bezier(.22, 1, .36, 1); }
2470
- .cx-ov-content[data-closed] { animation: cx-ov-out 100ms ease forwards; }
2507
+ .cx-ov-content[data-open] { animation: cx-ov-in var(--t-pop) var(--ease-out); }
2508
+ .cx-ov-content[data-closed] { animation: cx-ov-out calc(var(--t-pop) * .67) ease forwards; }
2471
2509
  @keyframes cx-ov-in { from { opacity: 0; transform: scale(.96) translateY(var(--ov-dy, -4px)); } }
2472
2510
  @keyframes cx-ov-out { to { opacity: 0; transform: scale(.97); } }
2473
2511
  .cx-ov-content[data-side="top"] { --ov-dy: 4px; }
2474
2512
  @media (prefers-reduced-motion: reduce) { .cx-ov-content[data-open], .cx-ov-content[data-closed] { animation-duration: 1ms; } }
2475
2513
 
2476
2514
  /* ── Modal mode sheet (sheet.muten): a full-height panel from a side, or full-width from the top/bottom ── */
2477
- .cx-modal[data-mode="sheet"] { top: 0; bottom: 0; right: 0; width: min(26rem, calc(100% - 2rem)); max-height: none; border-radius: 0; overflow: hidden; transition: transform 300ms cubic-bezier(.32, .72, 0, 1); transform: translateX(100%); }
2515
+ .cx-modal[data-mode="sheet"] { top: 0; bottom: 0; right: 0; width: min(26rem, calc(100% - 2rem)); max-height: none; border-radius: 0; overflow: hidden; transition: transform calc(var(--t-sheet) * .68) var(--ease-in-out); transform: translateX(100%); }
2516
+ .cx-modal-root[data-open="true"] > .cx-modal[data-mode="sheet"] { transition: transform var(--t-sheet) var(--ease-drawer); }
2478
2517
  .cx-modal[data-mode="sheet"][data-side="left"] { right: auto; left: 0; transform: translateX(-100%); }
2479
2518
  .cx-modal[data-mode="sheet"]:is([data-side="top"], [data-side="bottom"]) { left: 0; right: 0; width: 100%; height: auto; max-height: min(80dvh, 40rem); }
2480
2519
  .cx-modal[data-mode="sheet"][data-side="top"] { bottom: auto; transform: translateY(-100%); }
@@ -2520,7 +2559,7 @@ a.cx-bc-item:hover { color: var(--foreground); }
2520
2559
  .cx-pl-row { display: flex; flex-direction: row; align-items: center; gap: 4px; }
2521
2560
  .cx-pl-more { display: inline-grid; place-items: center; width: 32px; height: 32px; color: var(--muted-foreground); }
2522
2561
  /* ── AppSidebar ── */
2523
- .cx-sidebar { display: flex; width: 248px; flex: none; border-right: 1px solid var(--border); background: color-mix(in srgb, var(--muted) 35%, var(--background)); transition: width 200ms cubic-bezier(.22, 1, .36, 1); overflow: hidden; }
2562
+ .cx-sidebar { display: flex; width: 248px; flex: none; border-right: 1px solid var(--border); background: color-mix(in srgb, var(--muted) 35%, var(--background)); transition: width 200ms var(--ease-out); overflow: hidden; }
2524
2563
  .cx-sidebar[data-collapsed="true"] { width: 56px; }
2525
2564
  .cx-sb-inner { display: flex; flex-direction: column; width: 100%; gap: 4px; padding: 8px; }
2526
2565
  .cx-sb-head, .cx-sb-foot { display: flex; flex-direction: column; gap: 4px; }
@@ -2531,7 +2570,7 @@ a.cx-bc-item:hover { color: var(--foreground); }
2531
2570
  .cx-sb-menu { display: flex; flex-direction: column; gap: 2px; }
2532
2571
  .cx-sb-item { display: flex; align-items: center; gap: 8px; width: 100%; height: 32px; padding: 0 8px; border: 0; border-radius: calc(var(--radius) * .7); background: none; color: inherit; font: inherit; font-size: 14px; text-align: left; cursor: pointer; white-space: nowrap; }
2533
2572
  .cx-sb-item:hover { background: var(--accent); }
2534
- .cx-sb-item[data-active="true"], .cx-sb-item[aria-current="page"] { background: var(--selected-soft); color: var(--selected); font-weight: 500; }
2573
+ .cx-sb-item[data-active="true"], .cx-sb-item[aria-current="page"], .cx-sb-item[aria-current="true"], .cx-sb-item.is-active { background: var(--selected-soft); color: var(--selected); font-weight: 500; }
2535
2574
  .cx-sb-item { position: relative; text-decoration: none; } /* the count sits inside its own row, never past its edge */
2536
2575
  .cx-sb-item > .mu-icon { width: 16px; height: 16px; flex: none; }
2537
2576
  .cx-sb-label { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
@@ -2591,12 +2630,19 @@ a.cx-bc-item:hover { color: var(--foreground); }
2591
2630
  .cx-insp-head:hover { background: color-mix(in srgb, var(--muted) 50%, transparent); }
2592
2631
  .cx-insp-head .cx-acc-chev { width: 14px; height: 14px; color: var(--muted-foreground); transition: rotate 200ms ease; }
2593
2632
  .cx-insp-head[aria-expanded="false"] .cx-acc-chev { rotate: -90deg; }
2594
- .cx-insp-body { display: flex; flex-direction: column; gap: 8px; padding: 2px 12px 12px; }
2633
+ .cx-insp-body { display: flex; flex-direction: column; gap: 8px; padding: 4px 12px 12px; }
2595
2634
  .cx-insp-row { display: grid; grid-template-columns: 84px minmax(0, 1fr); align-items: center; gap: 10px; }
2596
2635
  .cx-insp-label { font-size: 13px; color: var(--muted-foreground); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
2597
2636
  .cx-insp-ctl { display: flex; flex-direction: row; align-items: center; min-width: 0; }
2598
- .cx-insp-ctl .cx-nf, .cx-insp-ctl .cx-vector { max-width: 100%; }
2599
- .cx-tree-host { display: block; width: 100%; max-width: 320px; }
2637
+ .cx-tree-host { display: block; width: 100%; max-width: 320px; min-width: 220px; }
2638
+ /* a folder's group folds like the accordion (no rebuild, no jump): rows 0fr ↔ 1fr, its rows fade with it */
2639
+ .cx-tree-fold { display: grid; grid-template-rows: 0fr; transition: grid-template-rows 200ms var(--ease-out); }
2640
+ .cx-tree-row[aria-expanded="true"] + .cx-tree-fold { grid-template-rows: 1fr; }
2641
+ .cx-tree-inner { display: flex; flex-direction: column; min-height: 0; overflow: clip; overflow-clip-margin: 2px; opacity: 0; transition: opacity 160ms ease; }
2642
+ .cx-tree-row[aria-expanded="true"] + .cx-tree-fold > .cx-tree-inner { opacity: 1; }
2643
+ .cx-tree-chev svg { transition: rotate 200ms var(--ease-out); }
2644
+ .cx-tree-row[aria-expanded="true"] > .cx-tree-chev svg { rotate: 90deg; }
2645
+ @media (prefers-reduced-motion: reduce) { .cx-tree-fold, .cx-tree-inner, .cx-tree-chev svg { transition: none; } }
2600
2646
  .cx-tree { display: flex; flex-direction: column; padding: 4px; border: 1px solid var(--border); border-radius: calc(var(--radius) * 1.2); }
2601
2647
  .cx-tree-row { display: flex; align-items: center; gap: 6px; width: 100%; height: 30px; padding-right: 8px; border: 0; border-radius: calc(var(--radius) * .6); background: none; color: inherit; font: inherit; font-size: 13px; text-align: left; cursor: pointer; outline: none; }
2602
2648
  .cx-tree-row:hover { background: var(--accent); }
@@ -2665,7 +2711,7 @@ a.cx-bc-item:hover { color: var(--foreground); }
2665
2711
  .cx-hero-blinds { position: relative; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 14px; min-height: 280px; width: 100%; padding: 40px 20px; border-radius: calc(var(--radius) * 1.6); overflow: hidden; text-align: center; }
2666
2712
  .cx-hero-blinds > :not(.cx-blinds) { position: relative; z-index: 1; }
2667
2713
  /* NavPill: sticky glass; it firms up as the page scrolls (scroll-driven animation; plain glass where unsupported) */
2668
- .cx-navpill-wrap { position: sticky; top: 12px; z-index: var(--z-sticky, 20); display: flex; justify-content: center; width: 100%; padding: 0 12px; }
2714
+ .cx-navpill-wrap { position: sticky; top: 12px; z-index: var(--z-sticky, 20); display: flex; flex-direction: row; justify-content: center; width: 100%; padding: 0 12px; }
2669
2715
  .cx-navpill { display: flex; flex-direction: row; align-items: center; justify-content: space-between; gap: 16px; width: 100%; max-width: 64rem; padding: 8px 8px 8px 18px; border: 1px solid color-mix(in srgb, var(--border) 70%, transparent); border-radius: 99px; background: color-mix(in srgb, var(--background) 70%, transparent); -webkit-backdrop-filter: blur(14px) saturate(1.4); backdrop-filter: blur(14px) saturate(1.4); }
2670
2716
  .cx-navpill a:not(.cn-button) { color: var(--muted-foreground); text-decoration: none; font-size: 14px; }
2671
2717
  .cx-navpill a:not(.cn-button):hover { color: var(--foreground); }
@@ -2808,7 +2854,7 @@ a.cx-bc-item:hover { color: var(--foreground); }
2808
2854
 
2809
2855
  /* ── LiveStack: news that keeps arriving; the newest in front, the older ones step back behind it ── */
2810
2856
  .cx-live { position: relative; display: block; width: 100%; max-width: 380px; height: var(--live-h, 200px); }
2811
- .cx-live-card { position: absolute; left: 0; right: 0; top: 0; z-index: calc(10 - var(--i, 0)); display: flex; align-items: center; gap: 12px; padding: 14px 16px; border-radius: calc(var(--radius) * 1.6); border: 1px solid var(--border); background: var(--card); color: var(--card-foreground); box-shadow: 0 18px 36px -24px rgb(11 11 13 / .45); transform-origin: top center; transform: translateY(calc(var(--i, 0) * 14px)) scale(calc(1 - var(--i, 0) * 0.05)); opacity: calc(1 - var(--i, 0) * 0.28); transition: transform 560ms cubic-bezier(.23, 1, .32, 1), opacity 420ms ease, filter 420ms ease; }
2857
+ .cx-live-card { position: absolute; left: 0; right: 0; top: 0; z-index: calc(10 - var(--i, 0)); display: flex; align-items: center; gap: 12px; padding: 14px 16px; border-radius: calc(var(--radius) * 1.6); border: 1px solid var(--border); background: var(--card); color: var(--card-foreground); box-shadow: 0 18px 36px -24px rgb(11 11 13 / .45); transform-origin: top center; transform: translateY(calc(var(--i, 0) * 14px)) scale(calc(1 - var(--i, 0) * 0.05)); opacity: calc(1 - var(--i, 0) * 0.28); transition: transform 560ms var(--ease-out), opacity 420ms ease, filter 420ms ease; }
2812
2858
  .cx-live-card[data-enter] { transform: translateY(-28px) scale(.96); opacity: 0; filter: blur(4px); }
2813
2859
  .cx-live-card + .cx-live-card + .cx-live-card + .cx-live-card { opacity: 0; }
2814
2860
  .cx-live-ic { display: grid; place-items: center; flex: none; width: 36px; height: 36px; border-radius: 10px; background: var(--muted); color: var(--foreground); }
@@ -2823,8 +2869,13 @@ a.cx-bc-item:hover { color: var(--foreground); }
2823
2869
  @media (prefers-reduced-motion: reduce) { .cx-live-card { transition: none; } }
2824
2870
 
2825
2871
  /* ── Press: anything you press gives way a little (0.97), so the page feels it heard you ── */
2826
- .style-nova .cn-button { transition-property: color, background-color, border-color, box-shadow, transform; transition-duration: 160ms; transition-timing-function: cubic-bezier(.23, 1, .32, 1); }
2827
- .style-nova .cn-button:active:not(:disabled) { transform: scale(0.97); }
2872
+ /* HOVER IS INSTANT (shadcn's menus and Emil's rule for what is seen tens of times a day): a row, a menu item or a
2873
+ button changes colour the moment the pointer is on it and the moment it leaves. A fade there leaves trails when the
2874
+ pointer sweeps a list (the last row still fading out, the next fading in, all of them dimming in the gap between
2875
+ two rows), which reads as the whole menu blinking. Only what moves keeps its motion: the press, the focus ring. */
2876
+ .style-nova .cn-button { transition-property: box-shadow, transform; transition-duration: 160ms; transition-timing-function: var(--ease-out); }
2877
+ .style-nova :is(.cx-sb-item, .cx-tree-row, .cx-app-row, .cx-navmenu-link, .cn-sidebar-menu-button, .cn-dropdown-menu-item, .cn-context-menu-item, .cn-menubar-item, .cn-menubar-trigger, .cn-navigation-menu-trigger, .cn-navigation-menu-link, .cn-command-item, .cn-select-item, .cn-item, .cn-toggle, .cn-tabs-trigger), :is(.cx-sb-item, .cx-tree-row, .cx-app-row, .cx-navmenu-link, .cn-sidebar-menu-button, .cn-dropdown-menu-item, .cn-context-menu-item, .cn-menubar-item, .cn-menubar-trigger, .cn-navigation-menu-trigger, .cn-navigation-menu-link, .cn-command-item, .cn-select-item, .cn-item, .cn-toggle, .cn-tabs-trigger) { transition-property: none; } /* .style-nova: beats Nova's own transition-all */
2878
+ /* pressing a button: Nova's own 1px drop (active:translate-y-px), as shadcn; no scale (it changed the button's size) */
2828
2879
 
2829
2880
  /* contrast: the label of the hour groups (Mañana, Tarde) and an appointment's second line read at 4.5:1 */
2830
2881
  .cx-hours-h { margin: 0; font-size: 12px; font-weight: 600; color: var(--muted-foreground); }
@@ -2876,13 +2927,13 @@ a.cx-bc-item:hover { color: var(--foreground); }
2876
2927
  .cx-play[data-in] .cx-bkp-ok { animation: cx-bkp-ok 9s infinite; }
2877
2928
  }
2878
2929
  @media (prefers-reduced-motion: reduce) { .cx-bkp-on { opacity: 1; transform: none; } }
2879
- @keyframes cx-bkp-day { 0%, 12% { opacity: 0; transform: scale(.92); } 16%, 86% { opacity: 1; transform: none; animation-timing-function: cubic-bezier(.23, 1, .32, 1); } 92%, 100% { opacity: 0; transform: scale(.92); } }
2930
+ @keyframes cx-bkp-day { 0%, 12% { opacity: 0; transform: scale(.92); } 16%, 86% { opacity: 1; transform: none; animation-timing-function: var(--ease-out); } 92%, 100% { opacity: 0; transform: scale(.92); } }
2880
2931
  @keyframes cx-bkp-hour { 0%, 26% { opacity: 0; transform: scale(.92); } 30%, 86% { opacity: 1; transform: none; } 92%, 100% { opacity: 0; transform: scale(.92); } }
2881
2932
  @keyframes cx-bkp-tap1 { 0%, 12% { opacity: 0; transform: scale(.4); } 14% { opacity: 1; transform: scale(.8); } 22%, 100% { opacity: 0; transform: scale(2); } }
2882
2933
  @keyframes cx-bkp-tap2 { 0%, 26% { opacity: 0; transform: scale(.4); } 28% { opacity: 1; transform: scale(.8); } 36%, 100% { opacity: 0; transform: scale(2); } }
2883
2934
  @keyframes cx-bkp-tap3 { 0%, 39% { opacity: 0; transform: scale(.4); } 41% { opacity: 1; transform: scale(.8); } 49%, 100% { opacity: 0; transform: scale(2.4); } }
2884
2935
  @keyframes cx-bkp-press { 0%, 39% { transform: none; } 41% { transform: scale(.97); } 44%, 100% { transform: none; } }
2885
- @keyframes cx-bkp-done { 0%, 45% { opacity: 0; transform: translateY(16px); } 51%, 84% { opacity: 1; transform: none; animation-timing-function: cubic-bezier(.23, 1, .32, 1); } 90%, 100% { opacity: 0; transform: translateY(16px); } }
2936
+ @keyframes cx-bkp-done { 0%, 45% { opacity: 0; transform: translateY(16px); } 51%, 84% { opacity: 1; transform: none; animation-timing-function: var(--ease-out); } 90%, 100% { opacity: 0; transform: translateY(16px); } }
2886
2937
  @keyframes cx-bkp-ok { 0%, 48% { transform: scale(.4); } 56%, 100% { transform: none; } }
2887
2938
 
2888
2939
  /* DayPlan (10s) */
@@ -2926,7 +2977,7 @@ a.cx-bc-item:hover { color: var(--foreground); }
2926
2977
  }
2927
2978
  @media (prefers-reduced-motion: reduce) { .cx-dp-free { display: none; } }
2928
2979
  @keyframes cx-dp-free { 0%, 28% { opacity: 1; } 34%, 88% { opacity: 0; } 94%, 100% { opacity: 1; } }
2929
- @keyframes cx-dp-new { 0%, 30% { opacity: 0; transform: translateY(-8px) scale(.98); } 36%, 88% { opacity: 1; transform: none; animation-timing-function: cubic-bezier(.23, 1, .32, 1); } 94%, 100% { opacity: 0; transform: translateY(-8px) scale(.98); } }
2980
+ @keyframes cx-dp-new { 0%, 30% { opacity: 0; transform: translateY(-8px) scale(.98); } 36%, 88% { opacity: 1; transform: none; animation-timing-function: var(--ease-out); } 94%, 100% { opacity: 0; transform: translateY(-8px) scale(.98); } }
2930
2981
  @keyframes cx-dp-ring { 0%, 34% { opacity: 0; } 38% { opacity: 1; } 60%, 100% { opacity: 0; } }
2931
2982
  @keyframes cx-dp-dot { 0%, 34% { opacity: 0; } 38%, 88% { opacity: 1; } 94%, 100% { opacity: 0; } }
2932
2983
  @keyframes cx-dp-out { 0%, 36% { opacity: 1; transform: none; } 40%, 88% { opacity: 0; transform: translateY(-100%); } 94%, 100% { opacity: 1; transform: none; } }
@@ -2999,8 +3050,8 @@ a.cx-bc-item:hover { color: var(--foreground); }
2999
3050
  it comes near the viewport. */
3000
3051
  .cx-device-link { position: absolute; inset: 0; z-index: 4; border-radius: inherit; }
3001
3052
  .cx-device-link:focus-visible { outline: 3px solid var(--ring, #6B46F2); outline-offset: 4px; }
3002
- .cx-device[data-link="true"] { transition: transform .25s cubic-bezier(.2, .8, .2, 1); }
3003
- @media (hover: hover) { .cx-device[data-link="true"]:has(> .cx-device-link:hover) { transform: translateY(-4px); } }
3053
+ .cx-device[data-link="true"] { transition: transform .25s var(--ease-out); }
3054
+ @media (hover: hover) { .cx-device[data-link="true"]:has(> .cx-device-link:hover) { transform: translateY(-4px); } .cx-device-link:hover::after { content: ""; position: absolute; left: 0; right: 0; top: calc(100% - 2px); height: 10px; } } /* the strip fills the gap it lifts from: no blinking hover on its bottom edge */
3004
3055
  @media (prefers-reduced-motion: reduce) { .cx-device[data-link="true"] { transition: none; transform: none !important; } }
3005
3056
  .cx-device[data-shot="true"] > .cx-device-screen { background: #111; content-visibility: auto; }
3006
3057
  .cx-device-shot { position: absolute; inset: 0; background: var(--shot) top center / cover no-repeat; }
@@ -3211,7 +3262,7 @@ a.cx-bc-item:hover { color: var(--foreground); }
3211
3262
  .cx-play[data-in] .cx-odp-go[data-next] .cx-odp-g2, .cx-play[data-in] .cx-odp-badge[data-next] .cx-odp-b2 { animation: cx-odp-in 9s infinite; }
3212
3263
  }
3213
3264
  @keyframes cx-odp-out { 0%, 42% { opacity: 1; transform: none; } 47%, 88% { opacity: 0; transform: translateY(-100%); } 94%, 100% { opacity: 1; transform: none; } }
3214
- @keyframes cx-odp-in { 0%, 42% { opacity: 0; transform: translateY(100%); } 47%, 88% { opacity: 1; transform: none; animation-timing-function: cubic-bezier(.23, 1, .32, 1); } 94%, 100% { opacity: 0; transform: translateY(100%); } }
3265
+ @keyframes cx-odp-in { 0%, 42% { opacity: 0; transform: translateY(100%); } 47%, 88% { opacity: 1; transform: none; animation-timing-function: var(--ease-out); } 94%, 100% { opacity: 0; transform: translateY(100%); } }
3215
3266
 
3216
3267
  /* a play fills its bento cell: the two twins end at the same line */
3217
3268
  .cx-bento-cell .cx-play { flex: 1; }
@@ -3260,7 +3311,7 @@ a.cx-bc-item:hover { color: var(--foreground); }
3260
3311
  /* The native accordion opens and closes to its content's height (browsers with ::details-content; the rest snap). */
3261
3312
  @media (prefers-reduced-motion: no-preference) {
3262
3313
  .cn-accordion > details.cn-accordion-item { interpolate-size: allow-keywords; }
3263
- .cn-accordion > details.cn-accordion-item::details-content { height: 0; overflow: clip; transition: height 200ms cubic-bezier(.23, 1, .32, 1), content-visibility 200ms allow-discrete; }
3314
+ .cn-accordion > details.cn-accordion-item::details-content { height: 0; overflow: clip; transition: height 200ms var(--ease-out), content-visibility 200ms allow-discrete; }
3264
3315
  .cn-accordion > details.cn-accordion-item[open]::details-content { height: auto; }
3265
3316
  }
3266
3317
 
@@ -4157,12 +4208,12 @@ a.cx-bc-item:hover { color: var(--foreground); }
4157
4208
  .cx-nav-fixed .cx-navpill-wrap { position: relative; top: auto; }
4158
4209
 
4159
4210
  .cx-nav-sheet { position: fixed; inset: 0; z-index: var(--z-sheet, 50); display: flex; flex-direction: column; padding: 12px 16px calc(20px + env(safe-area-inset-bottom, 0px)); box-sizing: border-box; background: var(--background); color: var(--foreground); font-family: var(--font-sans); visibility: hidden; opacity: 0; transform: translateY(-8px); transition: opacity 200ms ease, transform 200ms ease, visibility 0s linear 200ms; }
4160
- .cx-nav-sheet[data-open] { visibility: visible; opacity: 1; transform: none; transition: opacity 250ms ease, transform 300ms cubic-bezier(.23, 1, .32, 1); }
4211
+ .cx-nav-sheet[data-open] { visibility: visible; opacity: 1; transform: none; transition: opacity 250ms ease, transform 300ms var(--ease-out); }
4161
4212
  @media (min-width: 641px) { .cx-nav-sheet { display: none; } }
4162
4213
  .cx-nav-sheet-top { display: flex; flex-direction: row; align-items: center; justify-content: space-between; height: 58px; padding: 0 8px 0 16px; border: 1px solid var(--border); border-radius: 18px; box-sizing: border-box; }
4163
4214
  .cx-nav-sheet-top img { height: 14px; width: auto; display: block; }
4164
4215
  .cx-nav-sheet-list { display: flex; flex-direction: column; margin-top: 22px; }
4165
- .cx-nav-sheet-item { display: flex; flex-direction: row; align-items: center; justify-content: space-between; gap: 16px; padding: 18px 6px; border-bottom: 1px solid var(--border); color: var(--foreground); text-decoration: none; opacity: 0; transform: translateY(6px); transition: opacity 250ms ease, transform 300ms cubic-bezier(.23, 1, .32, 1); }
4216
+ .cx-nav-sheet-item { display: flex; flex-direction: row; align-items: center; justify-content: space-between; gap: 16px; padding: 18px 6px; border-bottom: 1px solid var(--border); color: var(--foreground); text-decoration: none; opacity: 0; transform: translateY(6px); transition: opacity 250ms ease, transform 300ms var(--ease-out); }
4166
4217
  .cx-nav-sheet[data-open] .cx-nav-sheet-item { opacity: 1; transform: none; }
4167
4218
  .cx-nav-sheet[data-open] .cx-nav-sheet-item:nth-child(2) { transition-delay: 50ms; }
4168
4219
  .cx-nav-sheet[data-open] .cx-nav-sheet-item:nth-child(3) { transition-delay: 100ms; }
@@ -4223,10 +4274,10 @@ a.cx-bc-item:hover { color: var(--foreground); }
4223
4274
  .cx-tour-hit { position: absolute; inset: 0; z-index: 1; margin: 0; padding: 0; border: 0; border-radius: inherit; background: transparent; cursor: pointer; }
4224
4275
  .cx-tour-hit:focus-visible { outline: 2px solid var(--ring); outline-offset: 2px; }
4225
4276
  /* the open step's line unfolds to its height and the others fold, so the list glides instead of jumping */
4226
- .cx-tour-desc { display: grid; grid-template-rows: 0fr; opacity: 0; transition: grid-template-rows 420ms cubic-bezier(.23, 1, .32, 1), opacity 260ms ease; }
4277
+ .cx-tour-desc { display: grid; grid-template-rows: 0fr; opacity: 0; transition: grid-template-rows 420ms var(--ease-out), opacity 260ms ease; }
4227
4278
  .cx-tour-desc > .cx-stp-desc { overflow: hidden; min-height: 0; margin: 0; }
4228
4279
  .cx-tour-steps > li[data-state="now"] .cx-tour-desc { grid-template-rows: 1fr; opacity: 1; transition-delay: 0ms, 120ms; }
4229
- .cx-tour-steps .cx-stp-label, .cx-tour-steps .cx-stp-dot { transition: color 260ms ease, background-color 260ms ease, transform 260ms cubic-bezier(.23, 1, .32, 1); }
4280
+ .cx-tour-steps .cx-stp-label, .cx-tour-steps .cx-stp-dot { transition: color 260ms ease, background-color 260ms ease, transform 260ms var(--ease-out); }
4230
4281
  .cx-tour-steps > li[data-state="now"] .cx-stp-dot { transform: scale(1.06); }
4231
4282
  .cx-tour-steps > li { position: relative; }
4232
4283
  .cx-tour-steps > li > .cx-stp-bar { display: none; position: absolute; left: 40px; right: 0; bottom: 6px; height: 3px; overflow: hidden; border-radius: 3px; background: var(--muted); }
@@ -4243,15 +4294,15 @@ a.cx-bc-item:hover { color: var(--foreground); }
4243
4294
  .cx-device[data-kind="phone"] > .cx-device-screen.cx-tour-screen > .cx-tour-tap { width: 34px; height: 34px; }
4244
4295
  .cx-device[data-kind="phone"] > .cx-device-screen.cx-tour-screen > img { width: 100%; }
4245
4296
  .cx-tour-screen { position: relative; aspect-ratio: 390 / 844; overflow: hidden; background: var(--muted); }
4246
- .cx-tour-screen > img { position: absolute; left: 0; top: 0; width: 100%; height: auto; display: block; opacity: 0; transform: scale(1.015); transition: opacity 480ms cubic-bezier(.23, 1, .32, 1), transform 620ms cubic-bezier(.23, 1, .32, 1); will-change: opacity; }
4297
+ .cx-tour-screen > img { position: absolute; left: 0; top: 0; width: 100%; height: auto; display: block; opacity: 0; transform: scale(1.015); transition: opacity 480ms var(--ease-out), transform 620ms var(--ease-out); will-change: opacity; }
4247
4298
  .cx-tour-screen > img:not([data-long]) { height: 100%; object-fit: cover; object-position: top; }
4248
4299
  .cx-tour-screen > img[data-on="1"] { opacity: 1; transform: none; }
4249
4300
  .cx-tour-tap { position: absolute; z-index: 3; width: 34px; height: 34px; margin: -17px 0 0 -17px; border-radius: 50%; background: rgb(255 255 255 / .55); box-shadow: 0 0 0 2px rgb(255 255 255 / .9), 0 6px 16px rgb(0 0 0 / .25); opacity: 0; pointer-events: none; }
4250
- .cx-tour-tap[data-go] { animation: cx-tour-tap 1.1s cubic-bezier(.23, 1, .32, 1) 1.2s both; }
4301
+ .cx-tour-tap[data-go] { animation: cx-tour-tap 1.1s var(--ease-out) 1.2s both; }
4251
4302
  @keyframes cx-tour-tap { 0% { opacity: 0; transform: scale(1.4); } 30% { opacity: 1; transform: scale(1); } 45% { transform: scale(.82); } 60% { transform: scale(1); } 100% { opacity: 0; transform: scale(1); } }
4252
4303
  .cx-tour-done { position: absolute; inset: 0; z-index: 4; display: flex; align-items: flex-end; background: rgb(11 11 13 / 0); pointer-events: none; transition: background 300ms ease; }
4253
4304
  .cx-tour-done[data-on="1"] { background: rgb(11 11 13 / .35); }
4254
- .cx-tour-sheet { width: 100%; padding: 10px 18px 22px; border-radius: 22px 22px 0 0; background: var(--card, #fff); color: var(--foreground); display: flex; flex-direction: column; align-items: center; gap: 10px; text-align: center; transform: translateY(100%); transition: transform 420ms cubic-bezier(.23, 1, .32, 1); font-family: var(--font-sans); }
4305
+ .cx-tour-sheet { width: 100%; padding: 10px 18px 22px; border-radius: 22px 22px 0 0; background: var(--card, #fff); color: var(--foreground); display: flex; flex-direction: column; align-items: center; gap: 10px; text-align: center; transform: translateY(100%); transition: transform 420ms var(--ease-out); font-family: var(--font-sans); }
4255
4306
  .cx-tour-done[data-on="1"] .cx-tour-sheet { transform: none; }
4256
4307
  .cx-tour-grab { width: 36px; height: 4px; border-radius: 4px; background: var(--border); }
4257
4308
  .cx-tour-ok { width: 44px; height: 44px; border-radius: 50%; background: var(--success); color: #fff; display: grid; place-items: center; }
@@ -4566,7 +4617,7 @@ a.cx-bc-item:hover { color: var(--foreground); }
4566
4617
  .cx-cp[data-skin] .cx-cp-t, .cx-cp[data-skin] .cx-cp-s { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
4567
4618
  /* never blank: no hide-and-show loop with a skin, only a rise when it comes into view */
4568
4619
  @media (prefers-reduced-motion: no-preference) {
4569
- .cx-cp[data-skin][data-in] :is(.cx-cp-msg, .cx-cp-card, .cx-cp-btns, .cx-cp-note) { opacity: 1; animation: cx-cp-rise 420ms cubic-bezier(.23, 1, .32, 1) both; }
4620
+ .cx-cp[data-skin][data-in] :is(.cx-cp-msg, .cx-cp-card, .cx-cp-btns, .cx-cp-note) { opacity: 1; animation: cx-cp-rise 420ms var(--ease-out) both; }
4570
4621
  .cx-cp[data-skin][data-in] [data-step="2"] { animation-delay: 120ms; } .cx-cp[data-skin][data-in] [data-step="3"] { animation-delay: 240ms; }
4571
4622
  .cx-cp[data-skin][data-in] [data-step="4"] { animation-delay: 360ms; } .cx-cp[data-skin][data-in] [data-step="5"] { animation-delay: 480ms; }
4572
4623
  }
@@ -4880,6 +4931,9 @@ a.cx-bc-item:hover { color: var(--foreground); }
4880
4931
  /* the time now: the line runs from each today column's edge, its dot on the first, and the time sits in the hours gutter */
4881
4932
  .cx-ag-col > .cx-ag-now { left: 0; right: 0; }
4882
4933
  .cx-ag-col > .cx-ag-now i { left: -5px; }
4934
+ .cx-ag-col > .cx-ag-now-past { opacity: .35; } /* the days already gone: the same hour, faint */
4935
+ /* the pill and the line are one: a short tail from the pill to the hour column's edge, where the first day's line starts */
4936
+ .cx-ag-nowtime::after { content: ""; position: absolute; left: 100%; top: 50%; width: 6px; border-top: 2px solid var(--destructive); transform: translateY(-1px); }
4883
4937
  .cx-ag-nowtime { position: absolute; right: 4px; z-index: 3; transform: translateY(-50%); padding: 1px 5px; border-radius: 6px; background: var(--destructive); color: var(--destructive-foreground, #fff); font-size: 10px; font-weight: 600; line-height: 1.4; white-space: nowrap; font-variant-numeric: tabular-nums; }
4884
4938
  /* the crossed-citas alert sits under the strip, full width, with its button */
4885
4939
  .cx-ag-top > [data-ag-alert] { margin: 0; }
@@ -4926,7 +4980,7 @@ a.cx-bc-item:hover { color: var(--foreground); }
4926
4980
  .cx-pbar-cell[data-side="center"]::-webkit-scrollbar { display: none; }
4927
4981
  .cx-pbar-cell[data-side="right"] { justify-self: end; }
4928
4982
  .cx-pbar-cell:empty { visibility: hidden; }
4929
- .cx-pbar-btn { position: relative; flex: none; display: grid; place-items: center; width: 2.75rem; height: 2.75rem; padding: 0; border: 0; border-radius: .9rem; background: transparent; color: var(--muted-foreground); cursor: pointer; font: inherit; transition: transform .12s cubic-bezier(.22,1,.36,1), background-color .15s; }
4983
+ .cx-pbar-btn { position: relative; flex: none; display: grid; place-items: center; width: 2.75rem; height: 2.75rem; padding: 0; border: 0; border-radius: .9rem; background: transparent; color: var(--muted-foreground); cursor: pointer; font: inherit; transition: transform .12s var(--ease-out), background-color .15s; }
4930
4984
  .cx-pbar-btn .mu-icon, .cx-pbar-btn svg { font-size: 1.3rem; width: 1.3rem; height: 1.3rem; }
4931
4985
  .cx-pbar-btn:hover { background: color-mix(in oklab, var(--foreground) 6%, transparent); color: var(--foreground); }
4932
4986
  .cx-pbar-btn:active { transform: scale(.92); }
@@ -4946,25 +5000,43 @@ a.cx-bc-item:hover { color: var(--foreground); }
4946
5000
  /* THE FOLD GLIDES. Nothing pops and nothing moves sideways: the width shrinks (200 ms) to exactly the icon column
4947
5001
  (the inner padding, the row's padding, the 16 px icon, and back), and the words fade where they are. */
4948
5002
  .cx-sidebar { --sb-pad: 8px; }
4949
- .cx-sidebar[data-collapsed="true"] { width: 64px; }
4950
- .cx-sidebar .cx-sb-item, .cx-sidebar[data-collapsed="true"] .cx-sb-item { justify-content: flex-start; padding: 0 var(--sb-pad); transition: padding .2s cubic-bezier(.22, 1, .36, 1), background-color .15s ease; }
5003
+ /* exactly the icon column: 8 inner + 8 group + 8 row + 16 icon + 8 + 8 + 8, plus the 1 px border on the right, so the
5004
+ icon and its hover/current square sit on the rail's centre */
5005
+ .cx-sidebar[data-collapsed="true"] { width: 65px; }
5006
+ /* ANY PIECE GOES IN. Whatever sits in a row where the icon goes (an Avatar, a dot, an image, a Kbd) keeps its own size and
5007
+ never squeezes; an avatar there takes 20 px so the rows stay 32 high. Folded, what the header or footer holds keeps
5008
+ its width too (a Who, a button with words, a select) and the rail clips it at its edge, instead of crushing it into
5009
+ the 16 px column. */
5010
+ .cx-sb-item > :not(.cx-sb-label):not(.cx-sb-count) { flex: none; }
5011
+ .cx-sb-item > .cn-avatar { width: 20px; height: 20px; margin-inline: -2px; font-size: 10px; }
5012
+ .cx-sb-item > img { width: 16px; height: 16px; border-radius: 4px; object-fit: cover; }
5013
+ /* the header and footer keep the OPEN width in both states (248 less the inner and group padding and the border), so
5014
+ folding or unfolding never rewraps their words: the rail only shows more or less of them, and the words fade */
5015
+ .cx-sidebar :is(.cx-sb-head, .cx-sb-foot) > .cx-who { flex: none; width: 215px; max-width: none; } /* only the name block: a button there (the fold toggle) keeps its own size and stays in the icon column */
5016
+ .cx-sidebar .cx-who-t { min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; transition: opacity .16s ease; }
5017
+ .cx-sidebar[data-collapsed="true"] .cx-who-t { opacity: 0; }
5018
+ .cx-sidebar .cx-sb-item, .cx-sidebar[data-collapsed="true"] .cx-sb-item { justify-content: flex-start; padding: 0 var(--sb-pad); transition: padding .2s var(--ease-out); }
4951
5019
  .cx-sb-label, .cx-sb-glabel, .cx-sb-count { transition: opacity .16s ease; }
5020
+ /* unfolding, the words wait for the rail (it grows in 200 ms) and then fade in whole, instead of being uncovered a few
5021
+ letters at a time; folding, they go at once */
5022
+ .cx-sidebar:not([data-collapsed="true"]) :is(.cx-sb-label, .cx-sb-count, .cx-who-t) { transition: opacity .14s ease .14s; }
5023
+ .cx-sidebar:not([data-collapsed="true"]) .cx-sb-glabel { transition: color .14s ease .14s, height .2s var(--ease-out), padding .2s var(--ease-out); }
4952
5024
  .cx-sb-label { white-space: nowrap; overflow: hidden; }
4953
5025
  .cx-sidebar[data-collapsed="true"] .cx-sb-label, .cx-sidebar[data-collapsed="true"] .cx-sb-glabel { display: block; opacity: 0; pointer-events: none; }
4954
5026
  .cx-sidebar[data-collapsed="true"] .cx-sb-count:not(:empty) { display: inline-grid; opacity: 0; }
4955
5027
  /* FOLDED, A SECTION'S TITLE BECOMES A SHORT LINE between its icons and the ones above: its words fade where they are and
4956
5028
  its height closes with the fold (200 ms), so the icon column has no holes where the titles were. A group with no
4957
5029
  title (the first) takes no room at all. */
4958
- .cx-sidebar .cx-sb-glabel { position: relative; transition: opacity .16s ease, height .2s cubic-bezier(.22, 1, .36, 1), padding .2s cubic-bezier(.22, 1, .36, 1); }
5030
+ .cx-sidebar .cx-sb-glabel { position: relative; transition: opacity .16s ease, height .2s var(--ease-out), padding .2s var(--ease-out); }
4959
5031
  .cx-sidebar[data-collapsed="true"] .cx-sb-glabel { height: 17px; padding-top: 0; padding-bottom: 0; overflow: hidden; color: transparent; opacity: 1; }
4960
5032
  .cx-sidebar[data-collapsed="true"] .cx-sb-glabel::after { content: ""; position: absolute; left: 50%; top: 8px; width: 20px; height: 1px; margin-left: -10px; background: var(--border); }
4961
5033
  .cx-sidebar[data-collapsed="true"] .cx-sb-glabel:empty { display: none; }
4962
- .cx-sidebar .cx-sb-group { transition: padding .2s cubic-bezier(.22, 1, .36, 1); }
5034
+ .cx-sidebar .cx-sb-group { transition: padding .2s var(--ease-out); }
4963
5035
  .cx-sidebar[data-collapsed="true"] .cx-sb-group { padding-top: 2px; padding-bottom: 2px; }
4964
5036
  /* A SECTION THAT LOADS: while its page is on its way (the router marks the link with data-loading), the row's icon
4965
5037
  turns into a small ring that spins, and turns back when the page is there. */
4966
5038
  @keyframes cx-sb-spin { to { transform: rotate(360deg); } }
4967
- .cx-sb-item > .mu-icon { transition: opacity .15s ease, transform .2s cubic-bezier(.22, 1, .36, 1); }
5039
+ .cx-sb-item > .mu-icon { transition: opacity .15s ease, transform .2s var(--ease-out); }
4968
5040
  .cx-sb-item[data-loading] > .mu-icon { opacity: 0; transform: scale(.4) rotate(-90deg); }
4969
5041
  .cx-sb-item[data-loading]::before { content: ""; position: absolute; left: var(--sb-pad, 8px); top: 50%; width: 16px; height: 16px; margin-top: -8px; box-sizing: border-box; border: 2px solid currentColor; border-right-color: transparent; border-radius: 50%; animation: cx-sb-spin .7s linear infinite; }
4970
5042
  @media (prefers-reduced-motion: reduce) {
@@ -5059,18 +5131,24 @@ a.cx-bc-item:hover { color: var(--foreground); }
5059
5131
  .mu-chart-s6 { --series: oklch(0.62 0.05 260); }
5060
5132
  .mu-chart-s7 { --series: oklch(0.8 0.1 120); }
5061
5133
  [data-tone="brand"] { --series: var(--selected, var(--primary)); }
5134
+ /* the status tones are colours too (a Progress, the password's strength bars): without these a tone left no colour */
5135
+ [data-tone="ok"] { --series: var(--success); }
5136
+ [data-tone="wait"] { --series: var(--warning); }
5137
+ [data-tone="bad"] { --series: var(--destructive); }
5138
+ [data-tone="info"] { --series: var(--info, var(--primary)); }
5139
+ [data-tone="note"] { --series: var(--selected, var(--primary)); }
5062
5140
  :is(.mu-chart-bar, .mu-chart-dot, .mu-chart-slice)[class*="mu-chart-s"] { fill: var(--series); }
5063
5141
  .mu-chart-legend { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: row; flex-wrap: wrap; justify-content: center; gap: 6px 18px; font-size: 13px; color: var(--muted-foreground); }
5064
5142
  .mu-chart-legend-item { display: flex; flex-direction: row; align-items: center; gap: 7px; }
5065
5143
  .mu-chart-swatch { width: 10px; height: 10px; border-radius: 3px; background: var(--series); flex: none; }
5066
5144
 
5067
5145
  /* Progress with a tone: the bar takes the series colour */
5068
- .cn-progress[data-tone]:not([data-tone=""]) > .cn-progress-indicator { background: var(--series); }
5146
+ .cn-progress[data-tone]:not([data-tone=""]) > .cn-progress-indicator { background: var(--series, var(--primary)); }
5069
5147
 
5070
5148
  /* ShareBar (share-bar.muten): one whole cut into its parts, each part's value and name under it */
5071
5149
  .cx-split { display: flex; flex-direction: column; gap: 14px; width: 100%; min-width: 0; }
5072
5150
  .cx-split-bar { display: flex; flex-direction: row; gap: 3px; height: 12px; border-radius: 999px; overflow: hidden; background: var(--muted); }
5073
- .cx-split-seg { flex: var(--f, 0) 1 0; min-width: 0; background: var(--series, var(--primary)); transition: flex-grow .5s cubic-bezier(.22, 1, .36, 1); }
5151
+ .cx-split-seg { flex: var(--f, 0) 1 0; min-width: 0; background: var(--series, var(--primary)); transition: flex-grow calc(var(--t-move) * 2) var(--ease-out); }
5074
5152
  .cx-split-seg[style*="--f: 0;"], .cx-split-seg[style*="--f:0;"] { display: none; }
5075
5153
  .cx-split-keys { display: flex; flex-direction: row; flex-wrap: wrap; gap: 10px 32px; }
5076
5154
  .cx-split-key { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
@@ -5112,7 +5190,7 @@ a.cx-bc-item:hover { color: var(--foreground); }
5112
5190
  .cx-stb-bar { height: 100%; display: flex; flex-direction: column; justify-content: flex-end; padding: 0; border: 0; background: none; cursor: pointer; border-radius: 6px; }
5113
5191
  .cx-stb-bar:disabled { cursor: default; }
5114
5192
  .cx-stb-bar:focus-visible { outline: 2px solid var(--ring); outline-offset: 3px; }
5115
- .cx-stb-stack { display: flex; flex-direction: column-reverse; gap: 2px; border-radius: 6px 6px 3px 3px; overflow: hidden; transform-origin: bottom; opacity: .55; transition: height .5s cubic-bezier(.22, 1, .36, 1), opacity .2s; }
5193
+ .cx-stb-stack { display: flex; flex-direction: column-reverse; gap: 2px; border-radius: 6px 6px 3px 3px; overflow: hidden; transform-origin: bottom; opacity: .55; transition: height .5s var(--ease-out), opacity .2s; }
5116
5194
  .cx-stb-bars:not(:has([aria-pressed="true"])) .cx-stb-stack, .cx-stb-bar[aria-pressed="true"] .cx-stb-stack, .cx-stb-bar:not(:disabled):hover .cx-stb-stack { opacity: 1; }
5117
5195
  .cx-stb-bar:disabled { pointer-events: none; }
5118
5196
  .cx-stb-seg { display: block; flex: 1 1 0; min-height: 2px; background: var(--series); }
@@ -5297,3 +5375,130 @@ a.cx-bc-item:hover { color: var(--foreground); }
5297
5375
  /* ChatPlay(face: "host"): an empty face slot the page fills with its own face (a brand mascot, say). The kit sends
5298
5376
  cx-cp:think / cx-cp:answer / cx-cp:rest on it on the play's clock, so that face can act along with the chat. */
5299
5377
  .cx-cp-face.cx-cp-face-host { position: relative; background: none; overflow: visible; }
5378
+
5379
+ /* ═════ Button & ButtonGroup · RTL, menus and selects inside a group ═════ */
5380
+ /* right to left: a block (or the page, with dir="rtl") flows the other way; flex rows, icon sides and groups follow.
5381
+ An icon that points somewhere (an arrow) takes cx-flip and mirrors itself. */
5382
+ .cx-rtl { direction: rtl; }
5383
+ :is([dir="rtl"], .cx-rtl) .cx-flip { scale: -1 1; }
5384
+ /* a horizontal group in RTL: Nova cuts the left/right corners by side; here they are cut by order instead */
5385
+ :is([dir="rtl"], .cx-rtl) .cn-button-group-orientation-horizontal.cn-button-group-orientation-horizontal > * { border-radius: var(--radius-lg, .5rem); border-left-width: 1px; border-right-width: 1px; }
5386
+ :is([dir="rtl"], .cx-rtl) .cn-button-group-orientation-horizontal.cn-button-group-orientation-horizontal > *:not(:first-child) { border-top-right-radius: 0; border-bottom-right-radius: 0; border-right-width: 0; }
5387
+ :is([dir="rtl"], .cx-rtl) .cn-button-group-orientation-horizontal.cn-button-group-orientation-horizontal > *:not(:last-child) { border-top-left-radius: 0; border-bottom-left-radius: 0; }
5388
+ /* Nova rounds the last segment's right side with !important (its outer edge left to right); right to left the outer
5389
+ edge of the last one is its left */
5390
+ :is([dir="rtl"], .cx-rtl) .cn-button-group-orientation-horizontal > [data-slot]:not(:has(~ [data-slot])):not(:first-child) { border-top-right-radius: 0 !important; border-bottom-right-radius: 0 !important; border-top-left-radius: var(--radius-lg, .5rem) !important; border-bottom-left-radius: var(--radius-lg, .5rem) !important; }
5391
+ /* a DropdownMenu in a vertical group: its trigger joins the button above (the horizontal case is in the 2026-10-10 audit block) */
5392
+ .cn-button-group-orientation-vertical > .cx-ov:not(:first-child) .cx-ov-trigger > .cn-button { border-top-left-radius: 0; border-top-right-radius: 0; border-top-width: 0; }
5393
+ .cn-button-group-orientation-vertical > .cx-ov:not(:last-child) .cx-ov-trigger > .cn-button { border-bottom-left-radius: 0; border-bottom-right-radius: 0; }
5394
+ /* a native select as a group member (muten's Select with class cx-select): an outline button with its chevron */
5395
+ .cx-select { appearance: none; height: 32px; padding: 0 30px 0 10px; border: 1px solid var(--border); border-radius: var(--radius-lg, .5rem); background: var(--background) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 24 24' fill='none' stroke='%23888' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E") no-repeat right 9px center; color: var(--foreground); font: 500 14px var(--font-sans); cursor: pointer; outline: none; }
5396
+ .cx-select:hover { background-color: var(--muted); }
5397
+ .cx-select:focus-visible { border-color: var(--ring); box-shadow: 0 0 0 3px color-mix(in srgb, var(--ring) 50%, transparent); position: relative; z-index: 1; }
5398
+ :is([dir="rtl"], .cx-rtl) .cx-select { padding: 0 10px 0 30px; background-position: left 9px center; }
5399
+
5400
+
5401
+ /* ═════ Motion · the pieces that did not move (tokens in :root, docs in AGENTS.md «Motion») ═════ */
5402
+ /* Switch: the track's colour and the thumb travel together; pressed, the thumb stretches toward where it will go */
5403
+ .style-nova .cn-switch { transition: background-color var(--t-move) var(--ease-out); }
5404
+ /* its colour changes with the track, not ahead of it (in dark it flipped at once and blinked) */
5405
+ .style-nova .cn-switch-thumb { transition: translate var(--t-move) var(--ease-out), transform var(--t-move) var(--ease-out), width var(--t-micro) var(--ease-out), margin var(--t-micro) var(--ease-out), background-color var(--t-move) var(--ease-out); }
5406
+ /* PRESSED, THE THUMB WIDENS 4 px TOWARD WHERE IT WILL GO (as the reference does), with its width and margin: never a
5407
+ scale with a transform-origin that flips with the state, which on release threw the thumb 2.5 px outside the
5408
+ track for a frame. Checked, its position is calc(100% - 2px) of its own width, so the 4 px it gains move it right
5409
+ by 4 more: a -8 px margin keeps its right edge where it was. */
5410
+ .style-nova .cn-switch:active:not(:disabled) .cn-switch-thumb { width: calc(1rem + 4px); }
5411
+ .style-nova .cn-switch[data-size="sm"]:active:not(:disabled) .cn-switch-thumb { width: calc(.75rem + 4px); }
5412
+ .style-nova .cn-switch[data-checked]:not([data-checked="false"]):active:not(:disabled) .cn-switch-thumb { margin-left: -8px; }
5413
+ /* Checkbox: the box fills and the check grows in from its middle; it gives a little under the press */
5414
+ /* a transition, not an animation: it runs only when the box is ticked, never on the first paint nor when its row moves */
5415
+ .cx-checkbox > input[type="checkbox"] { background-size: 4px; transition: background-color var(--t-micro) var(--ease-out), border-color var(--t-micro) var(--ease-out), background-size var(--t-micro) var(--ease-out), scale var(--t-press) var(--ease-out); }
5416
+ .cx-checkbox > input[type="checkbox"]:checked { background-size: 12px; }
5417
+ /* Tab panels: once the person has switched tabs (not on the first paint), the new panel comes in from the side they
5418
+ went toward, with a short blur that joins the two states. TabsBehavior sets data-moved and data-dir on the root. */
5419
+ [data-tabs-moved] .cx-tab-panel:not(.hidden) { animation: cx-panel-in var(--t-move) var(--ease-out); }
5420
+ [data-tabs-moved][data-tabs-dir="back"] .cx-tab-panel:not(.hidden) { --panel-dx: -8px; }
5421
+ @keyframes cx-panel-in { from { opacity: 0; translate: var(--panel-dx, 8px) 0; filter: blur(2px); } }
5422
+
5423
+ /* Btn loading: the button keeps its width (nothing beside it moves); its content fades where it is and the spinner
5424
+ comes in centred on top of it */
5425
+ /* only while loading, and only if the button is not placed already: every Btn carries data-loading ("false"), and a
5426
+ blanket position: relative pulled the dialogs' absolute close button into the flow */
5427
+ .cn-button[data-loading="true"]:not(.absolute):not([class*="close"]) { position: relative; }
5428
+ .cn-button[data-loading="true"] > :not(.cn-spinner) { transition: opacity var(--t-micro) var(--ease-out); }
5429
+ .cn-button[data-loading="true"] > :not(.cn-spinner) { opacity: 0; }
5430
+ .cn-button[data-loading="true"] > .cn-spinner { position: absolute; left: 50%; top: 50%; translate: -50% -50%; margin: 0; animation: pg-spin 1s linear infinite, cx-fade var(--t-micro) var(--ease-out); }
5431
+ /* SaveButton: «Guardando…» and «Guardado» come in with a short blur (the width is fixed already) */
5432
+ .cx-save:is([data-status="saving"], [data-status="saved"]) .cx-btn-label { animation: cx-label-in var(--t-micro) var(--ease-out); }
5433
+ @keyframes cx-label-in { from { opacity: 0; filter: blur(2px); translate: 0 4px; } }
5434
+
5435
+ /* A label or icon that changes to say what happened (Copiar → Copiado, the copy icon → its check) swaps in: the new
5436
+ one grows from its middle out of a short blur, so the change reads as one thing turning into another */
5437
+ .cx-swap-in { display: inline-flex; align-items: center; gap: inherit; animation: cx-swap-in var(--t-micro) var(--ease-out); }
5438
+ @keyframes cx-swap-in { from { opacity: 0; scale: .6; filter: blur(2px); } }
5439
+
5440
+ /* CountUp: digits that roll. Every character is a 1em box on one line; a digit is a 0-9 column that slides to its
5441
+ value (only the ones that change move). The plain text is for screen readers. */
5442
+ .cx-countup { display: inline-flex; }
5443
+ .cx-roll { display: inline-flex; align-items: flex-start; font-variant-numeric: tabular-nums; line-height: 1; height: 1em; }
5444
+ .cx-roll-d { display: inline-block; height: 1em; overflow: hidden; }
5445
+ .cx-roll-d > span { display: flex; flex-direction: column; transition: translate calc(var(--t-move) * 2) var(--ease-out); }
5446
+ .cx-roll-d em, .cx-roll-s { font-style: normal; display: block; height: 1em; line-height: 1; white-space: pre; }
5447
+ .cx-roll-sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
5448
+ .cx-countup { position: relative; }
5449
+
5450
+ /* List rows (ListMotion, inside every ItemGroup): a new row opens its place and fades in; a leaving row closes its
5451
+ gap. The height goes from/to its own size (interpolate-size; where a browser lacks it, the row only fades). */
5452
+ :root { interpolate-size: allow-keywords; }
5453
+ .cx-list-motion { display: none; }
5454
+ .cx-row-enter { animation: cx-row-in var(--t-move) var(--ease-out); overflow: clip; }
5455
+ @keyframes cx-row-in { from { opacity: 0; height: 0; padding-block: 0; margin-block: 0; translate: 0 -4px; } }
5456
+ .cx-row-leave { animation: cx-row-out calc(var(--t-move) * .75) var(--ease-out) forwards; overflow: clip; pointer-events: none; }
5457
+ @keyframes cx-row-out { from { height: var(--mu-h, auto); } to { opacity: 0; height: 0; padding-block: 0; margin-block: 0; border-width: 0; } }
5458
+
5459
+ /* Entrances after the first paint (core.js sets data-kit-live once the page has painted): an Alert that appears
5460
+ opens to its height and fades in; one taken away by a `when` closes its gap (muten's .mu-leave, --mu-h). */
5461
+ .cn-alert { transition: opacity var(--t-move) var(--ease-out), height var(--t-move) var(--ease-out), translate var(--t-move) var(--ease-out); }
5462
+ :root[data-kit-live] .cn-alert { @starting-style { opacity: 0; height: 0; translate: 0 -4px; } }
5463
+ .cn-alert.mu-leave { animation: cx-row-out calc(var(--t-move) * .75) var(--ease-out) forwards; overflow: clip; }
5464
+
5465
+ /* Calendar: a month (or a year) changed by its arrow slides in from that side; the title stays put */
5466
+ .cx-cal-months[data-slide] .cx-cal-month > :not(.cx-cal-head), .cx-cal-picker[data-slide] > :not(.cx-cal-head) { animation: cx-cal-in var(--t-move) var(--ease-out); }
5467
+ .cx-cal-months[data-slide="prev"], .cx-cal-picker[data-slide="prev"] { --cal-dx: -12px; }
5468
+ @keyframes cx-cal-in { from { opacity: 0; translate: var(--cal-dx, 12px) 0; } }
5469
+ .cx-row-swap { animation: cx-fade var(--t-move) var(--ease-out); }
5470
+ .cx-app-page[data-in] { animation: cx-fade var(--t-micro) var(--ease-out); } /* a section opened from the menu: a short fade, no slide */
5471
+
5472
+ /* Toggle: the hover stays instant (no trails across a toggle group), but turning it ON fades in; turning it off is
5473
+ instant like the hover. Same for a pressed ToggleGroup item. */
5474
+ .style-nova .cn-toggle[aria-pressed="true"], .style-nova .cn-toggle[data-state="on"] { transition-property: background-color, color, box-shadow; transition-duration: var(--t-micro); transition-timing-function: var(--ease-out); }
5475
+
5476
+ /* SidebarTrigger: in the icon column (its 32 px square sits where the rows' icons sit), folded or open */
5477
+ .cx-sb-trigger { align-self: flex-start; flex: none; margin-left: 0; }
5478
+ .cx-sidebar[data-collapsed="true"] .cx-sb-trigger { align-self: flex-start; }
5479
+
5480
+ /* Sheet: its content arrives a moment after the panel, one row after another; leaving, it goes with the panel */
5481
+ .cx-modal[data-mode="sheet"] .cx-modal-body > * { transition: opacity var(--t-pop) var(--ease-out), translate calc(var(--t-sheet) * .8) var(--ease-out); }
5482
+ .cx-modal-root:not([data-open="true"]) > .cx-modal[data-mode="sheet"] .cx-modal-body > * { opacity: 0; translate: 12px 0; transition-duration: 0s; transition-delay: calc(var(--t-sheet) * .7); } /* reset only once the panel is gone */
5483
+ .cx-modal-root[data-open="true"] > .cx-modal[data-mode="sheet"] .cx-modal-body > :nth-child(1) { transition-delay: 60ms; }
5484
+ .cx-modal-root[data-open="true"] > .cx-modal[data-mode="sheet"] .cx-modal-body > :nth-child(2) { transition-delay: 90ms; }
5485
+ .cx-modal-root[data-open="true"] > .cx-modal[data-mode="sheet"] .cx-modal-body > :nth-child(3) { transition-delay: 120ms; }
5486
+ .cx-modal-root[data-open="true"] > .cx-modal[data-mode="sheet"] .cx-modal-body > :nth-child(n+4) { transition-delay: 150ms; }
5487
+ /* Command palette: opened from the keyboard (⌘K) a hundred times a day, so it opens and closes with no animation */
5488
+ .cx-modal-root.cx-palette > :is(.cx-modal, .cx-overlay), .cx-modal-root:has(.cx-cmd) > :is(.cx-modal, .cx-overlay) { transition: none !important; }
5489
+ /* Timeline and the vertical Stepper: a step that becomes the current one changes its dot on the move token */
5490
+ .cx-tl-item::before, .cx-tl-item { transition: background-color var(--t-move) var(--ease-out), box-shadow var(--t-move) var(--ease-out), opacity var(--t-move) var(--ease-out); }
5491
+ .cx-stp-dot, .cx-stp[data-orientation="vertical"] .cx-stp-step::after { transition: background-color var(--t-move) var(--ease-out), border-color var(--t-move) var(--ease-out), color var(--t-move) var(--ease-out); }
5492
+ /* Heatmap: a cell takes its new value's colour on the move token */
5493
+ .cx-heat-c { transition: background-color var(--t-move) var(--ease-out); }
5494
+ /* A field that turns invalid shakes once, where it is (FieldBehavior sets data-invalid when the check fails) */
5495
+ .cn-field[data-invalid] :is(.cn-input-group, .cn-input) { animation: cx-shake calc(var(--t-move) * 1.35) var(--ease-out); }
5496
+
5497
+ /* NavigationMenu / Menubar: switching menus in the same bar swaps the panel in place; the old one goes at once and the
5498
+ new one only fades in (no scale-in, no close animation crossing it) */
5499
+ .cx-ov-content[data-open][data-swap] { animation: cx-fade calc(var(--t-pop) * .8) var(--ease-out); }
5500
+ .cx-ov-content[data-closed][data-swap] { animation: none; }
5501
+
5502
+ /* LangScope: a plain block; its host draws nothing */
5503
+ .cx-lang-scope { display: contents; }
5504
+ .cx-lang-host { display: none; }