tweakit 0.6.0 → 0.7.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 (132) hide show
  1. package/AGENTS.md +5 -5
  2. package/README.md +3 -3
  3. package/dist/tweaks/auto.js +1 -1
  4. package/dist/tweaks/auto.js.map +1 -1
  5. package/dist/tweaks/bezier-S2BOXQBM.js +2 -0
  6. package/dist/tweaks/bezier-S2BOXQBM.js.map +6 -0
  7. package/dist/tweaks/chunk-3MZAAMC2.js +2 -0
  8. package/dist/tweaks/chunk-3MZAAMC2.js.map +6 -0
  9. package/dist/tweaks/chunk-ETB35KFV.js +2 -0
  10. package/dist/tweaks/chunk-ETB35KFV.js.map +6 -0
  11. package/dist/tweaks/chunk-YB5GKWZI.js +2 -0
  12. package/dist/tweaks/chunk-YB5GKWZI.js.map +6 -0
  13. package/dist/tweaks/clip-A3VI53BK.js +2 -0
  14. package/dist/tweaks/clip-A3VI53BK.js.map +6 -0
  15. package/dist/tweaks/color-UGYMAF3W.js +2 -0
  16. package/dist/tweaks/core.js +1 -1
  17. package/dist/tweaks/core.js.map +3 -3
  18. package/dist/tweaks/fold-GY47HIQA.js +2 -0
  19. package/dist/tweaks/fold-GY47HIQA.js.map +6 -0
  20. package/dist/tweaks/gradient-BTX25WSH.js +2 -0
  21. package/dist/tweaks/gradient-BTX25WSH.js.map +6 -0
  22. package/dist/tweaks/{heavy-CLD7UAVI.js → heavy-NPFIGHRM.js} +2 -2
  23. package/dist/tweaks/image-HTQQ4WX6.js +4 -0
  24. package/dist/tweaks/image-HTQQ4WX6.js.map +6 -0
  25. package/dist/tweaks/{image-store-WRKY2NPU.js → image-store-QD5SMZXG.js} +1 -1
  26. package/dist/tweaks/image-store-QD5SMZXG.js.map +6 -0
  27. package/dist/tweaks/interval-BHPI4LRN.js +2 -0
  28. package/dist/tweaks/interval-BHPI4LRN.js.map +6 -0
  29. package/dist/tweaks/markup-QM53TQJZ.js +2 -0
  30. package/dist/tweaks/markup-QM53TQJZ.js.map +6 -0
  31. package/dist/tweaks/monitor-722WIYGW.js +3 -0
  32. package/dist/tweaks/monitor-722WIYGW.js.map +6 -0
  33. package/dist/tweaks/motion-HMCZK6HJ.js +2 -0
  34. package/dist/tweaks/motion-HMCZK6HJ.js.map +6 -0
  35. package/dist/tweaks/plot-D3NQ6XNN.js +3 -0
  36. package/dist/tweaks/plot-D3NQ6XNN.js.map +6 -0
  37. package/dist/tweaks/point-XDXKMDXH.js +2 -0
  38. package/dist/tweaks/point-XDXKMDXH.js.map +6 -0
  39. package/dist/tweaks/presets-EUWNSCUW.js +2 -0
  40. package/dist/tweaks/presets-EUWNSCUW.js.map +6 -0
  41. package/dist/tweaks/shadow-AM7VSRNS.js +2 -0
  42. package/dist/tweaks/shadow-AM7VSRNS.js.map +6 -0
  43. package/dist/tweaks/shadow-root-RYUV3AGS.js +3 -0
  44. package/dist/tweaks/shadow-root-RYUV3AGS.js.map +6 -0
  45. package/dist/tweaks/sides-JWIURDA4.js +2 -0
  46. package/dist/tweaks/sides-JWIURDA4.js.map +6 -0
  47. package/dist/tweaks/spring-BF5GBTPM.js +2 -0
  48. package/dist/tweaks/spring-BF5GBTPM.js.map +6 -0
  49. package/dist/tweaks/tabs-PRVUUZXZ.js +2 -0
  50. package/dist/tweaks/tabs-PRVUUZXZ.js.map +6 -0
  51. package/dist/tweaks.css +1 -1
  52. package/dist/tweaks.js +5 -4
  53. package/dist/tweaks.js.map +3 -3
  54. package/dist/types/tweaks/enhance.d.ts +3 -2
  55. package/dist/types/tweaks/types.d.ts +278 -72
  56. package/package.json +5 -2
  57. package/src/tweaks/auto.ts +9 -5
  58. package/src/tweaks/clip.ts +84 -22
  59. package/src/tweaks/controls/basic.ts +463 -139
  60. package/src/tweaks/controls/bezier.ts +152 -47
  61. package/src/tweaks/controls/color.ts +610 -161
  62. package/src/tweaks/controls/gradient.ts +256 -77
  63. package/src/tweaks/controls/image.ts +388 -132
  64. package/src/tweaks/controls/interval.ts +126 -41
  65. package/src/tweaks/controls/monitor.ts +142 -36
  66. package/src/tweaks/controls/motion.ts +231 -56
  67. package/src/tweaks/controls/plot.ts +321 -101
  68. package/src/tweaks/controls/point.ts +134 -45
  69. package/src/tweaks/controls/shadow.ts +103 -30
  70. package/src/tweaks/controls/sides.ts +150 -40
  71. package/src/tweaks/controls/spring.ts +220 -60
  72. package/src/tweaks/controls/tabs.ts +39 -18
  73. package/src/tweaks/core.ts +1 -1
  74. package/src/tweaks/easing.ts +45 -20
  75. package/src/tweaks/enhance.ts +21 -6
  76. package/src/tweaks/feedback.ts +133 -34
  77. package/src/tweaks/fold.ts +22 -10
  78. package/src/tweaks/heavy.ts +461 -137
  79. package/src/tweaks/icons.ts +12 -5
  80. package/src/tweaks/image-store.ts +131 -43
  81. package/src/tweaks/lazy.ts +36 -19
  82. package/src/tweaks/markup.ts +255 -92
  83. package/src/tweaks/panel.ts +1262 -438
  84. package/src/tweaks/presets.ts +85 -0
  85. package/src/tweaks/schema.ts +213 -51
  86. package/src/tweaks/shadow-root.ts +178 -0
  87. package/src/tweaks/shared.ts +802 -190
  88. package/src/tweaks/standalone.ts +120 -30
  89. package/src/tweaks/types.ts +644 -143
  90. package/src/tweaks/undo.ts +104 -0
  91. package/src/tweaks.css +556 -550
  92. package/src/wide-gamut.ts +473 -125
  93. package/dist/tweaks/bezier-JFEZMCLP.js +0 -2
  94. package/dist/tweaks/bezier-JFEZMCLP.js.map +0 -6
  95. package/dist/tweaks/chunk-6UO4DHBN.js +0 -2
  96. package/dist/tweaks/chunk-6UO4DHBN.js.map +0 -6
  97. package/dist/tweaks/chunk-DIQMLBAS.js +0 -2
  98. package/dist/tweaks/chunk-DIQMLBAS.js.map +0 -6
  99. package/dist/tweaks/chunk-XE6OUJBG.js +0 -2
  100. package/dist/tweaks/chunk-XE6OUJBG.js.map +0 -6
  101. package/dist/tweaks/clip-WW7JYO4X.js +0 -2
  102. package/dist/tweaks/clip-WW7JYO4X.js.map +0 -6
  103. package/dist/tweaks/color-YMMFURP6.js +0 -2
  104. package/dist/tweaks/fold-U6YRZLCJ.js +0 -2
  105. package/dist/tweaks/fold-U6YRZLCJ.js.map +0 -6
  106. package/dist/tweaks/gradient-JER7PW4D.js +0 -2
  107. package/dist/tweaks/gradient-JER7PW4D.js.map +0 -6
  108. package/dist/tweaks/image-H6QSLGO3.js +0 -4
  109. package/dist/tweaks/image-H6QSLGO3.js.map +0 -6
  110. package/dist/tweaks/image-store-WRKY2NPU.js.map +0 -6
  111. package/dist/tweaks/interval-TEUHQGZN.js +0 -2
  112. package/dist/tweaks/interval-TEUHQGZN.js.map +0 -6
  113. package/dist/tweaks/markup-NIBJD6E2.js +0 -2
  114. package/dist/tweaks/markup-NIBJD6E2.js.map +0 -6
  115. package/dist/tweaks/monitor-3BY7YCGX.js +0 -3
  116. package/dist/tweaks/monitor-3BY7YCGX.js.map +0 -6
  117. package/dist/tweaks/motion-FQPUBKKM.js +0 -2
  118. package/dist/tweaks/motion-FQPUBKKM.js.map +0 -6
  119. package/dist/tweaks/plot-2HBNBZSG.js +0 -3
  120. package/dist/tweaks/plot-2HBNBZSG.js.map +0 -6
  121. package/dist/tweaks/point-VV6XV3I6.js +0 -2
  122. package/dist/tweaks/point-VV6XV3I6.js.map +0 -6
  123. package/dist/tweaks/shadow-5E4E55I2.js +0 -2
  124. package/dist/tweaks/shadow-5E4E55I2.js.map +0 -6
  125. package/dist/tweaks/sides-P3STEVYY.js +0 -2
  126. package/dist/tweaks/sides-P3STEVYY.js.map +0 -6
  127. package/dist/tweaks/spring-HUVSXNVN.js +0 -2
  128. package/dist/tweaks/spring-HUVSXNVN.js.map +0 -6
  129. package/dist/tweaks/tabs-76FW7LQ7.js +0 -2
  130. package/dist/tweaks/tabs-76FW7LQ7.js.map +0 -6
  131. /package/dist/tweaks/{color-YMMFURP6.js.map → color-UGYMAF3W.js.map} +0 -0
  132. /package/dist/tweaks/{heavy-CLD7UAVI.js.map → heavy-NPFIGHRM.js.map} +0 -0
package/src/tweaks.css CHANGED
@@ -1,71 +1,77 @@
1
1
  /* Tweaks — a dependency-free real-time parameter panel. One stylesheet, namespaced
2
- * under `tw-` / `--tw-`, themed entirely through CSS custom properties. */
3
- /* Design tokens, for the panel and for every portaled node (all carry .tw-portal). Each
2
+ * under `twk-` / `--twk-`, themed entirely through CSS custom properties. */
3
+ /* Design tokens, for the panel and for every portaled node (all carry .twk-portal). Each
4
4
  * color is one light-dark(light, dark) pair, resolved by the color-scheme of the element that
5
5
  * paints it (see "Scheme resolution" below), so a token keeps one name and one meaning in both
6
- * schemes and a theme override wins in both. */
7
- .tw-panel, .tw-portal {
6
+ * schemes and a theme override wins in both. A shadow panel (opts.shadow) and its portals carry .twk-in-shadow and skip
7
+ * this rule: there the build copies it onto the kit's own :host, where the page's rules on that host override it. Keep
8
+ * the selector as written; build.mjs finds the rule by it. */
9
+ :where(.twk-panel, .twk-portal):not(.twk-in-shadow) {
8
10
  /* Surfaces — faint films over the base. Light: items a step brighter than the off-white
9
11
  * base; hover/active darken for contrast. */
10
- --tw-surface: light-dark(rgba(255, 255, 255, 0.82), rgba(255, 255, 255, 0.04));
11
- --tw-surface-hover: light-dark(rgba(0, 0, 0, 0.04), rgba(255, 255, 255, 0.075));
12
- --tw-surface-active: light-dark(rgba(0, 0, 0, 0.1), rgba(255, 255, 255, 0.13));
12
+ --twk-surface: light-dark(rgba(255, 255, 255, 0.82), rgba(255, 255, 255, 0.04));
13
+ --twk-surface-hover: light-dark(rgba(0, 0, 0, 0.04), rgba(255, 255, 255, 0.075));
14
+ --twk-surface-active: light-dark(rgba(0, 0, 0, 0.1), rgba(255, 255, 255, 0.13));
13
15
  /* Text — secondary/tertiary style 10–12px text, so they sit at WCAG 4.5:1 on the base. */
14
- --tw-text-root: light-dark(#1a1a1a, #fafafa);
15
- --tw-text-section: light-dark(rgba(0, 0, 0, 0.62), rgba(255, 255, 255, 0.64));
16
- --tw-text-label: light-dark(rgba(0, 0, 0, 0.58), rgba(255, 255, 255, 0.64));
17
- --tw-text-focus: light-dark(#000000, #ffffff);
18
- --tw-text-primary: light-dark(rgba(0, 0, 0, 0.88), rgba(255, 255, 255, 0.92));
19
- --tw-text-secondary: light-dark(rgba(0, 0, 0, 0.58), rgba(255, 255, 255, 0.56));
20
- --tw-text-tertiary: light-dark(rgba(0, 0, 0, 0.55), rgba(255, 255, 255, 0.5)); /* light .54 sat at 4.48:1 on the base */
16
+ --twk-text-root: light-dark(#1a1a1a, #fafafa);
17
+ --twk-text-section: light-dark(rgba(0, 0, 0, 0.62), rgba(255, 255, 255, 0.64));
18
+ --twk-text-label: light-dark(rgba(0, 0, 0, 0.58), rgba(255, 255, 255, 0.64));
19
+ --twk-text-focus: light-dark(#000000, #ffffff);
20
+ --twk-text-primary: light-dark(rgba(0, 0, 0, 0.88), rgba(255, 255, 255, 0.92));
21
+ --twk-text-secondary: light-dark(rgba(0, 0, 0, 0.58), rgba(255, 255, 255, 0.56));
22
+ --twk-text-tertiary: light-dark(rgba(0, 0, 0, 0.55), rgba(255, 255, 255, 0.5)); /* light .54 sat at 4.48:1 on the base */
21
23
  /* Text drawn on the accent; resolveTheme overrides it to black/white by the accent's
22
24
  * luminance when a theme sets `accent`. */
23
- --tw-on-accent: var(--tw-text-primary);
25
+ --twk-on-accent: var(--twk-text-primary);
24
26
  /* Borders — faint; the panel leans on its shadow for definition. */
25
- --tw-border: light-dark(rgba(0, 0, 0, 0.045), rgba(255, 255, 255, 0.045));
26
- --tw-border-hover: light-dark(rgba(0, 0, 0, 0.1), rgba(255, 255, 255, 0.11));
27
+ --twk-border: light-dark(rgba(0, 0, 0, 0.045), rgba(255, 255, 255, 0.045));
28
+ --twk-border-hover: light-dark(rgba(0, 0, 0, 0.1), rgba(255, 255, 255, 0.11));
27
29
  /* Text-selection wash, not the browser's blue. */
28
- --tw-selection: light-dark(rgba(0, 0, 0, 0.14), rgba(255, 255, 255, 0.22));
30
+ --twk-selection: light-dark(rgba(0, 0, 0, 0.14), rgba(255, 255, 255, 0.22));
29
31
  /* Base — the panel background and recessed insets; opaque, a step brighter than a dark page. */
30
- --tw-base: light-dark(#f3f3f3, #242424);
31
- --tw-checker: repeating-conic-gradient(#6b6b6b 0% 25%, #9a9a9a 0% 50%) 0 0 / 8px 8px; /* the alpha checker under swatches and strips */
32
- --tw-dropdown-bg: light-dark(#f6f6f6, #292929);
33
- --tw-radius: 8px;
34
- --tw-row-height: 32px;
35
- --tw-width: 256px; /* the panel's width (theme `width`) */
32
+ --twk-base: light-dark(#f3f3f3, #242424);
33
+ --twk-checker: repeating-conic-gradient(#6b6b6b 0% 25%, #9a9a9a 0% 50%) 0 0 / 8px 8px; /* the alpha checker under swatches and strips */
34
+ --twk-dropdown-bg: light-dark(#f6f6f6, #292929);
35
+ --twk-radius: 8px;
36
+ --twk-row-height: 32px;
37
+ --twk-width: 256px; /* the panel's width (theme `width`) */
36
38
  /* A shadow isn't a color, so light-dark() goes on each layer's color. */
37
- --tw-shadow-dropdown: 0 12px 32px -8px light-dark(rgba(0, 0, 0, 0.14), rgba(0, 0, 0, 0.6));
39
+ --twk-shadow-dropdown: 0 12px 32px -8px light-dark(rgba(0, 0, 0, 0.14), rgba(0, 0, 0, 0.6));
38
40
  /* Elevation — on dark a 1px rim-light, since a black shadow alone can't separate the
39
41
  * panel; on light a dark hairline edge, as a bright rim reads oddly there. -lifted deepens
40
42
  * it while floating or dragged. The two schemes' drop layers differ in geometry, not just
41
43
  * color, so each scheme's pair is listed and goes transparent in the other: a transparent
42
44
  * layer paints nothing, and both lists keep one length, so the lift still interpolates. */
43
- --tw-shadow-panel: 0 0 0 1px light-dark(var(--tw-border), rgba(255, 255, 255, 0.06)),
45
+ --twk-shadow-panel: 0 0 0 1px light-dark(var(--twk-border), rgba(255, 255, 255, 0.06)),
44
46
  0 8px 28px -8px light-dark(transparent, rgba(0, 0, 0, 0.5)), 0 18px 48px -16px light-dark(transparent, rgba(0, 0, 0, 0.38)),
45
47
  0 2px 8px -2px light-dark(rgba(0, 0, 0, 0.08), transparent), 0 18px 44px -12px light-dark(rgba(0, 0, 0, 0.16), transparent);
46
- --tw-shadow-panel-lifted: 0 0 0 1px light-dark(var(--tw-border-hover), rgba(255, 255, 255, 0.08)),
48
+ --twk-shadow-panel-lifted: 0 0 0 1px light-dark(var(--twk-border-hover), rgba(255, 255, 255, 0.08)),
47
49
  0 12px 32px -8px light-dark(transparent, rgba(0, 0, 0, 0.55)), 0 28px 64px -16px light-dark(transparent, rgba(0, 0, 0, 0.45)),
48
50
  0 6px 16px -3px light-dark(rgba(0, 0, 0, 0.12), transparent), 0 30px 70px -16px light-dark(rgba(0, 0, 0, 0.2), transparent);
49
51
  /* Success (the copy check) and danger (the plot's invalid cue). */
50
- --tw-success: light-dark(#15803d, #86efac);
51
- --tw-danger: light-dark(#c23232, #ff5f5f);
52
+ --twk-success: light-dark(#15803d, #86efac);
53
+ --twk-danger: light-dark(#c23232, #ff5f5f);
52
54
  /* Type — the system UI font, unbound from host tokens; mono is declared so the fontMono
53
55
  * theme lever has a token to land on. */
54
- --tw-font-sans: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
55
- --tw-font-mono: ui-monospace, "SF Mono", Menlo, monospace;
56
+ --twk-font-sans: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
57
+ --twk-font-mono: ui-monospace, "SF Mono", Menlo, monospace;
56
58
  /* Motion — one shared easing vocabulary: --out decelerates, --spring settles, --pop
57
59
  * overshoots on release, --inout is symmetric for longer mechanical moves. */
58
- --tw-ease-out: cubic-bezier(0.25, 1, 0.5, 1);
59
- --tw-ease-spring: cubic-bezier(0.22, 1, 0.36, 1);
60
- --tw-ease-pop: cubic-bezier(0.34, 1.56, 0.64, 1);
61
- --tw-ease-inout: cubic-bezier(0.4, 0, 0.2, 1);
60
+ --twk-ease-out: cubic-bezier(0.25, 1, 0.5, 1);
61
+ --twk-ease-spring: cubic-bezier(0.22, 1, 0.36, 1);
62
+ --twk-ease-pop: cubic-bezier(0.34, 1.56, 0.64, 1);
63
+ --twk-ease-inout: cubic-bezier(0.4, 0, 0.2, 1);
62
64
  /* --fold is a spring (visualDuration 0.35s, bounce 0.1) sampled into linear(); it starts
63
65
  * from rest, so folder children dissolve in step with the height. Pairs with 0.35s. */
64
- --tw-ease-fold: linear(0, 0.077, 0.237, 0.411, 0.570, 0.699, 0.797, 0.869, 0.918, 0.952, 0.973, 0.986, 0.994, 0.998, 1);
66
+ --twk-ease-fold: linear(0, 0.077, 0.237, 0.411, 0.570, 0.699, 0.797, 0.869, 0.918, 0.952, 0.973, 0.986, 0.994, 0.998, 1);
65
67
  /* Press: a dip in three tiers by size, so every control's edge moves about 1.5–2px. */
66
- --tw-press-icon: 0.88; --tw-press-chip: 0.95; --tw-press-row: 0.985;
67
-
68
- font-family: var(--tw-font-sans);
68
+ --twk-press-icon: 0.88; --twk-press-chip: 0.95; --twk-press-row: 0.985;
69
+ }
70
+ /* A shadow panel's first styles land at once (shadow-root.ts): held still meanwhile, nothing animates from the unstyled
71
+ * layout. */
72
+ :host([data-twk-settling]) *, :host([data-twk-settling]) *::before, :host([data-twk-settling]) *::after { transition: none !important; }
73
+ .twk-panel, .twk-portal {
74
+ font-family: var(--twk-font-sans);
69
75
  /* Buttons use `font: inherit`, so a host's global line-height would ride into every
70
76
  * control; pin it. */
71
77
  line-height: normal;
@@ -77,883 +83,883 @@
77
83
  color-scheme: light dark;
78
84
  box-sizing: border-box;
79
85
  }
80
- .tw-panel {
81
- width: var(--tw-width);
82
- background: var(--tw-base);
83
- box-shadow: var(--tw-shadow-panel);
86
+ .twk-panel {
87
+ width: var(--twk-width);
88
+ background: var(--twk-base);
89
+ box-shadow: var(--twk-shadow-panel);
84
90
  /* Concentric with the controls: control radius + padding, 8 + 10 = 18. */
85
- border-radius: calc(var(--tw-radius) + 10px);
91
+ border-radius: calc(var(--twk-radius) + 10px);
86
92
  /* No backdrop-filter: it would make the panel the containing block for the
87
93
  * popovers' position:fixed, detaching them from their triggers. */
88
94
  padding: 10px;
89
95
  }
90
- .tw-panel *,
91
- .tw-panel *::before,
92
- .tw-panel *::after { box-sizing: border-box; }
96
+ .twk-panel *,
97
+ .twk-panel *::before,
98
+ .twk-panel *::after { box-sizing: border-box; }
93
99
 
94
- :is(.tw-panel, .tw-portal) ::selection { background: var(--tw-selection); color: var(--tw-text-focus); }
100
+ :is(.twk-panel, .twk-portal) ::selection { background: var(--twk-selection); color: var(--twk-text-focus); }
95
101
 
96
102
  /* Floating mode. The z ladder (panel 99990 < popovers 99999 < tip 100000 < grab
97
103
  * guide 100001) sits above the 100–9999 band host sticky headers occupy. The placement
98
- * (panel.ts apply() writes the --tw-float-* insets and height cap), z-index and shadow sit at
99
- * plain .tw-panel specificity, so a host class loaded after this sheet overrides any of them. */
100
- .tw-panel[data-mode="floating"] { position: fixed; }
101
- .tw-panel:where([data-mode="floating"]) { left: var(--tw-float-x); top: var(--tw-float-y); max-height: var(--tw-float-max-h); z-index: 99990; box-shadow: var(--tw-shadow-panel-lifted); }
102
- .tw-panel:where(.is-raised) { z-index: 99991; } /* the floating panel touched last (panel.ts raise()) */
104
+ * (panel.ts apply() writes the --twk-float-* insets and height cap), z-index and shadow sit at
105
+ * plain .twk-panel specificity, so a host class loaded after this sheet overrides any of them. */
106
+ .twk-panel[data-mode="floating"] { position: fixed; }
107
+ .twk-panel:where([data-mode="floating"]) { left: var(--twk-float-x); top: var(--twk-float-y); max-height: var(--twk-float-max-h); z-index: 99990; box-shadow: var(--twk-shadow-panel-lifted); }
108
+ .twk-panel:where(.is-raised) { z-index: 99991; } /* the floating panel touched last (panel.ts raise()) */
103
109
  /* The lift into the floating shadow eases on a draggable panel only: a pinned one starts floated, with nothing to ease,
104
110
  * and an inline one keeps its shadow still. */
105
- .tw-panel[data-draggable="true"][data-mode="floating"] { transition: box-shadow 0.25s var(--tw-ease-out); }
106
- /* Registered as a <length>, so a host's --tw-float-gap in rem, vh or calc() computes to px for panel.ts to read; the
111
+ .twk-panel[data-draggable="true"][data-mode="floating"] { transition: box-shadow 0.25s var(--twk-ease-out); }
112
+ /* Registered as a <length>, so a host's --twk-float-gap in rem, vh or calc() computes to px for panel.ts to read; the
107
113
  * negative initial value reads as unset. */
108
- @property --tw-float-gap { syntax: "<length>"; inherits: true; initial-value: -1px; }
114
+ @property --twk-float-gap { syntax: "<length>"; inherits: true; initial-value: -1px; }
109
115
  /* The footprint a lifted panel leaves in its old slot, so the host layout holds still. */
110
- .tw-lift-slot { display: block; flex: none; pointer-events: none; }
116
+ .twk-lift-slot { display: block; flex: none; pointer-events: none; }
111
117
  /* The header is the drag handle. The negative margin (canceled by an equal padding-top)
112
118
  * stretches its hit area up through the panel's top padding to the edge. Draggable panels
113
119
  * only: a pinned floating one keeps a plain cursor, and a swipe on its header scrolls it. */
114
- .tw-panel[data-draggable="true"] > .tw-header,
115
- .tw-panel[data-draggable="true"] > .tw-header .tw-header-toggle { cursor: grab; }
116
- .tw-panel[data-draggable="true"] > .tw-header { touch-action: none; margin-top: -10px; padding-top: 10px; }
120
+ .twk-panel[data-draggable="true"] > .twk-header,
121
+ .twk-panel[data-draggable="true"] > .twk-header .twk-header-toggle { cursor: grab; }
122
+ .twk-panel[data-draggable="true"] > .twk-header { touch-action: none; margin-top: -10px; padding-top: 10px; }
117
123
  /* A drop near an edge eases the rest of the way to the margin (panel.ts endDrag()). */
118
- .tw-panel.is-snapping { transition: left 0.32s var(--tw-ease-spring), top 0.32s var(--tw-ease-spring), right 0.32s var(--tw-ease-spring), bottom 0.32s var(--tw-ease-spring); }
119
- .tw-panel.is-dragging { -webkit-user-select: none; user-select: none; box-shadow: var(--tw-shadow-panel-lifted); z-index: 99991; }
120
- /* A floating panel scrolls, header and all, inside the height cap its position sets (--tw-float-max-h). The scrollbar
124
+ .twk-panel.is-snapping { transition: left 0.32s var(--twk-ease-spring), top 0.32s var(--twk-ease-spring), right 0.32s var(--twk-ease-spring), bottom 0.32s var(--twk-ease-spring); }
125
+ .twk-panel.is-dragging { -webkit-user-select: none; user-select: none; box-shadow: var(--twk-shadow-panel-lifted); z-index: 99991; }
126
+ /* A floating panel scrolls, header and all, inside the height cap its position sets (--twk-float-max-h). The scrollbar
121
127
  * is hidden: wheel, trackpad, touch and keyboard focus still scroll it. A slider covers its row and takes every touch;
122
128
  * here a vertical swipe pans, a sideways one scrubs. */
123
- .tw-panel[data-mode="floating"] { overflow-y: auto; overscroll-behavior: contain; scrollbar-width: none; }
124
- .tw-panel[data-mode="floating"]::-webkit-scrollbar { display: none; } /* Safari before scrollbar-width */
125
- .tw-panel[data-mode="floating"] .tw-slider { touch-action: pan-y; }
126
- .tw-panel.is-dragging > .tw-header,
127
- .tw-panel.is-dragging > .tw-header .tw-header-toggle { cursor: grabbing; }
129
+ .twk-panel[data-mode="floating"] { overflow-y: auto; overscroll-behavior: contain; scrollbar-width: none; }
130
+ .twk-panel[data-mode="floating"]::-webkit-scrollbar { display: none; } /* Safari before scrollbar-width */
131
+ .twk-panel[data-mode="floating"] .twk-slider { touch-action: pan-y; }
132
+ .twk-panel.is-dragging > .twk-header,
133
+ .twk-panel.is-dragging > .twk-header .twk-header-toggle { cursor: grabbing; }
128
134
 
129
135
  /* Every :hover rule sits inside `@media (hover: hover) and (pointer: fine)`: a touch
130
136
  * screen keeps :hover on after a tap, so an unguarded hover style sticks. */
131
137
  /* A pane drags by its edge (popover() in shared.ts); its parts keep their own cursors. */
132
- :is(.tw-color-pop, .tw-presets-menu) { cursor: grab; }
133
- :is(.tw-color-pop, .tw-presets-menu) > * { cursor: auto; }
134
- :is(.tw-color-pop, .tw-presets-menu).is-moving, :is(.tw-color-pop, .tw-presets-menu).is-moving * { cursor: grabbing; }
138
+ :is(.twk-color-pop, .twk-presets-menu) { cursor: grab; }
139
+ :is(.twk-color-pop, .twk-presets-menu) > * { cursor: auto; }
140
+ :is(.twk-color-pop, .twk-presets-menu).is-moving, :is(.twk-color-pop, .twk-presets-menu).is-moving * { cursor: grabbing; }
135
141
 
136
142
  /* ── Header (root folder title) ── */
137
- .tw-header {
143
+ .twk-header {
138
144
  display: flex;
139
145
  align-items: center;
140
146
  justify-content: space-between;
141
147
  gap: 8px;
142
148
  padding-bottom: 9px;
143
149
  margin-bottom: 8px;
144
- border-bottom: 1px solid var(--tw-border); /* a surface film would read white on a light panel */
150
+ border-bottom: 1px solid var(--twk-border); /* a surface film would read white on a light panel */
145
151
  }
146
- .tw-title {
152
+ .twk-title {
147
153
  font-size: 14px;
148
154
  font-weight: 500;
149
155
  line-height: 17px; /* not normal: a toolbar-less header is 37px whatever the font (44 with the toolbar's 24px buttons) */
150
- color: var(--tw-text-root);
156
+ color: var(--twk-text-root);
151
157
  min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
152
158
  }
153
- .tw-header-toggle {
159
+ .twk-header-toggle {
154
160
  display: flex; align-items: center; gap: 6px; flex: 1; min-width: 0;
155
161
  background: none; border: none; padding: 0; margin: 0; cursor: pointer; color: inherit; font: inherit; text-align: left;
156
162
  }
157
- .tw-chevron { width: 15px; height: 15px; flex-shrink: 0; color: var(--tw-text-section); transition: transform 0.2s var(--tw-ease-out), color 0.15s; }
158
- .tw-panel.is-collapsed .tw-chevron { transform: rotate(-90deg); }
159
- .tw-toolbar { display: flex; align-items: center; gap: 4px; }
163
+ .twk-chevron { width: 15px; height: 15px; flex-shrink: 0; color: var(--twk-text-section); transition: transform 0.2s var(--twk-ease-out), color 0.15s; }
164
+ .twk-panel.is-collapsed .twk-chevron { transform: rotate(-90deg); }
165
+ .twk-toolbar { display: flex; align-items: center; gap: 4px; }
160
166
  /* Hint marker. pointer-events:auto overrides the slider label's pointer-events:none. */
161
- .tw-hint { display: inline-flex; align-items: center; justify-content: center; width: 14px; height: 14px; margin-left: 4px; padding: 0; vertical-align: -2px; flex: none; background: none; border: none; color: var(--tw-text-tertiary); cursor: help; pointer-events: auto; transition: color 0.15s, transform 0.18s var(--tw-ease-spring); }
162
- .tw-hint:focus-visible { color: var(--tw-text-secondary); transform: scale(1.2); }
167
+ .twk-hint { display: inline-flex; align-items: center; justify-content: center; width: 14px; height: 14px; margin-left: 4px; padding: 0; vertical-align: -2px; flex: none; background: none; border: none; color: var(--twk-text-tertiary); cursor: help; pointer-events: auto; transition: color 0.15s, transform 0.18s var(--twk-ease-spring); }
168
+ .twk-hint:focus-visible { color: var(--twk-text-secondary); transform: scale(1.2); }
163
169
  @media (hover: hover) and (pointer: fine) {
164
- .tw-hint:hover { color: var(--tw-text-secondary); transform: scale(1.2); }
170
+ .twk-hint:hover { color: var(--twk-text-secondary); transform: scale(1.2); }
165
171
  }
166
- .tw-hint:focus-visible { outline: 2px solid var(--tw-accent, var(--tw-text-focus)); outline-offset: 1px; border-radius: 999px; }
167
- .tw-hint svg { width: 13px; height: 13px; display: block; }
168
- .tw-tip { position: fixed; z-index: 100000; max-width: 220px; padding: 8px 12px; font-size: 12px; line-height: 1.4; color: var(--tw-text-primary); background: var(--tw-dropdown-bg); border: 1px solid var(--tw-border); border-radius: 8px; box-shadow: var(--tw-shadow-dropdown); pointer-events: none; opacity: 0; transform: translateY(2px); transition: opacity 0.12s, transform 0.12s; }
169
- .tw-tip.is-open { opacity: 1; transform: none; }
172
+ .twk-hint:focus-visible { outline: 2px solid var(--twk-accent, var(--twk-text-focus)); outline-offset: 1px; border-radius: 999px; }
173
+ .twk-hint svg { width: 13px; height: 13px; display: block; }
174
+ .twk-tip { position: fixed; z-index: 100000; max-width: 220px; padding: 8px 12px; font-size: 12px; line-height: 1.4; color: var(--twk-text-primary); background: var(--twk-dropdown-bg); border: 1px solid var(--twk-border); border-radius: 8px; box-shadow: var(--twk-shadow-dropdown); pointer-events: none; opacity: 0; transform: translateY(2px); transition: opacity 0.12s, transform 0.12s; }
175
+ .twk-tip.is-open { opacity: 1; transform: none; }
170
176
  /* The screen-reader status region (announce() in shared.ts): in the page, drawn as nothing. */
171
- .tw-sr, .tw-image-live { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; border: 0; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
177
+ .twk-sr, .twk-image-live { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; border: 0; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
172
178
 
173
179
  /* Collapse: grid-rows 1fr→0fr animates the body height. foldBody (shared.ts) animates only the visible part; this is the fallback. */
174
- .tw-body { display: grid; grid-template-rows: 1fr; transition: grid-template-rows 0.35s var(--tw-ease-fold); }
180
+ .twk-body { display: grid; grid-template-rows: 1fr; transition: grid-template-rows 0.35s var(--twk-ease-fold); }
175
181
  /* Clip y only, so a slider rubber-banding past its end still shows. Must be `clip`,
176
182
  * not `hidden`: `hidden` on one axis forces the other to compute to `auto`. */
177
183
  /* The 3px padding, offset by an equal negative margin so nothing moves, sets the clip edges out far enough not to cut a
178
184
  * first or last row's pick band (3px past the row). A collapsed panel drops the pair, or its 0fr row would show 3px of
179
185
  * the first row. */
180
- .tw-body > .tw-controls { overflow-x: visible; overflow-y: clip; min-height: 0; min-width: 0; padding-block: 3px; margin-block: -3px; }
181
- .tw-panel.is-collapsed .tw-body > .tw-controls { padding-block: 0; margin-block: 0; }
182
- .tw-panel.is-collapsed .tw-body { grid-template-rows: 0fr; }
183
- .tw-header { transition: margin-bottom 0.35s var(--tw-ease-fold), padding-bottom 0.35s var(--tw-ease-fold), border-color 0.35s var(--tw-ease-fold); }
186
+ .twk-body > .twk-controls { overflow-x: visible; overflow-y: clip; min-height: 0; min-width: 0; padding-block: 3px; margin-block: -3px; }
187
+ .twk-panel.is-collapsed .twk-body > .twk-controls { padding-block: 0; margin-block: 0; }
188
+ .twk-panel.is-collapsed .twk-body { grid-template-rows: 0fr; }
189
+ .twk-header { transition: margin-bottom 0.35s var(--twk-ease-fold), padding-bottom 0.35s var(--twk-ease-fold), border-color 0.35s var(--twk-ease-fold); }
184
190
  /* Collapsed, the header is the whole panel; drop its separator so the padding is even. */
185
- .tw-panel.is-collapsed .tw-header { margin-bottom: 0; padding-bottom: 0; border-color: transparent; }
191
+ .twk-panel.is-collapsed .twk-header { margin-bottom: 0; padding-bottom: 0; border-color: transparent; }
186
192
  /* A first folder brings its own spacing; the header's 8px gap would stack on it. */
187
- .tw-header:has(+ .tw-body > .tw-controls > .tw-folder:first-child) { margin-bottom: 0; }
188
- .tw-toolbar-btn {
193
+ .twk-header:has(+ .twk-body > .twk-controls > .twk-folder:first-child) { margin-bottom: 0; }
194
+ .twk-toolbar-btn {
189
195
  position: relative; display: flex; align-items: center; justify-content: center;
190
196
  width: 24px; height: 24px; padding: 0; flex-shrink: 0;
191
- background: var(--tw-surface); border: none; border-radius: calc(var(--tw-radius) - 2px);
192
- color: var(--tw-text-label); cursor: pointer;
197
+ background: var(--twk-surface); border: none; border-radius: calc(var(--twk-radius) - 2px);
198
+ color: var(--twk-text-label); cursor: pointer;
193
199
  will-change: transform; /* keep the layer standing (Safari pixel-snap) */
194
- transition: background 0.15s, color 0.15s, transform 0.24s var(--tw-ease-pop);
200
+ transition: background 0.15s, color 0.15s, transform 0.24s var(--twk-ease-pop);
195
201
  }
196
202
  @media (hover: hover) and (pointer: fine) {
197
- .tw-toolbar-btn:hover { background: var(--tw-surface-hover); color: var(--tw-text-primary); }
203
+ .twk-toolbar-btn:hover { background: var(--twk-surface-hover); color: var(--twk-text-primary); }
198
204
  }
199
- .tw-toolbar-btn:disabled { cursor: default; } /* the lazy window is "not yet", not "never" */
205
+ .twk-toolbar-btn:disabled { cursor: default; } /* the lazy window is "not yet", not "never" */
200
206
  /* The press dip shrinks the box, so a cursor near the edge would lose :hover mid-press;
201
207
  * ::after keeps the resting footprint hot ((24+4)·0.88 ≈ 24.6 ≥ 24). */
202
- .tw-toolbar-btn::after { content: ""; position: absolute; inset: -2px; }
203
- .tw-toolbar-btn:active { transform: scale(var(--tw-press-icon)); transition: transform 0.08s ease-out; }
204
- .tw-toolbar-btn svg { width: 14px; height: 14px; display: block; }
208
+ .twk-toolbar-btn::after { content: ""; position: absolute; inset: -2px; }
209
+ .twk-toolbar-btn:active { transform: scale(var(--twk-press-icon)); transition: transform 0.08s ease-out; }
210
+ .twk-toolbar-btn svg { width: 14px; height: 14px; display: block; }
205
211
  /* Copy ⇄ check cross-fade. Safari: will-change and a resting blur(0), never none —
206
212
  * WebKit demotes the layer when a transform/filter animation ends and re-snaps the
207
213
  * raster to device pixels, which read as a ~1px icon shift. */
208
- .tw-toolbar-btn__icons { position: relative; display: block; width: 14px; height: 14px; }
209
- .tw-toolbar-btn__icons svg { position: absolute; inset: 0; will-change: transform, filter; filter: blur(0); transition: opacity 0.25s var(--tw-ease-inout), transform 0.25s var(--tw-ease-inout), filter 0.25s var(--tw-ease-inout); }
210
- .tw-toolbar-btn__check { opacity: 0; transform: scale(0.25); filter: blur(2px); color: var(--tw-success); }
211
- .tw-toolbar-btn--swap.is-copied .tw-toolbar-btn__copy { opacity: 0; transform: scale(0.25); filter: blur(2px); }
212
- .tw-toolbar-btn--swap.is-copied .tw-toolbar-btn__check { opacity: 1; transform: none; filter: blur(0); }
213
- /* Reset: hover sets --tw-wind, each click adds −360deg to --tw-spin (JS spinReset). A
214
+ .twk-toolbar-btn__icons { position: relative; display: block; width: 14px; height: 14px; }
215
+ .twk-toolbar-btn__icons svg { position: absolute; inset: 0; will-change: transform, filter; filter: blur(0); transition: opacity 0.25s var(--twk-ease-inout), transform 0.25s var(--twk-ease-inout), filter 0.25s var(--twk-ease-inout); }
216
+ .twk-toolbar-btn__check { opacity: 0; transform: scale(0.25); filter: blur(2px); color: var(--twk-success); }
217
+ .twk-toolbar-btn--swap.is-copied .twk-toolbar-btn__copy { opacity: 0; transform: scale(0.25); filter: blur(2px); }
218
+ .twk-toolbar-btn--swap.is-copied .twk-toolbar-btn__check { opacity: 1; transform: none; filter: blur(0); }
219
+ /* Reset: hover sets --twk-wind, each click adds −360deg to --twk-spin (JS spinReset). A
214
220
  * transition, not keyframes, so a second click mid-spin retargets from the current angle. */
215
- .tw-toolbar-btn--reset svg { --tw-spin: 0deg; --tw-wind: 0deg; will-change: transform; transform: rotate(calc(var(--tw-spin) + var(--tw-wind))); transition: transform 0.34s cubic-bezier(0.34, 1.55, 0.5, 1); }
221
+ .twk-toolbar-btn--reset svg { --twk-spin: 0deg; --twk-wind: 0deg; will-change: transform; transform: rotate(calc(var(--twk-spin) + var(--twk-wind))); transition: transform 0.34s cubic-bezier(0.34, 1.55, 0.5, 1); }
216
222
  @media (hover: hover) and (pointer: fine) {
217
- .tw-toolbar-btn--reset:not(:disabled):hover svg { --tw-wind: 22deg; }
223
+ .twk-toolbar-btn--reset:not(:disabled):hover svg { --twk-wind: 22deg; }
218
224
  }
219
- .tw-toolbar-btn--reset.is-spinning svg { transition-duration: 0.5s; transition-timing-function: var(--tw-ease-inout); }
225
+ .twk-toolbar-btn--reset.is-spinning svg { transition-duration: 0.5s; transition-timing-function: var(--twk-ease-inout); }
220
226
  /* Icon parts move on hover (icons.ts): the button sets the distances, the parts read them. */
221
- .tw-copy-back { transform: translate(calc(-1 * var(--tw-fan, 0px)), calc(-1 * var(--tw-fan, 0px))); }
222
- .tw-copy-front { transform: translate(var(--tw-fan, 0px), var(--tw-fan, 0px)); }
223
- .tw-layer-top { transform: translateY(calc(-1 * var(--tw-lift, 0px))); }
224
- .tw-copy-back, .tw-copy-front, .tw-layer-top { transition: transform 0.3s var(--tw-ease-pop); }
227
+ .twk-copy-back { transform: translate(calc(-1 * var(--twk-fan, 0px)), calc(-1 * var(--twk-fan, 0px))); }
228
+ .twk-copy-front { transform: translate(var(--twk-fan, 0px), var(--twk-fan, 0px)); }
229
+ .twk-layer-top { transform: translateY(calc(-1 * var(--twk-lift, 0px))); }
230
+ .twk-copy-back, .twk-copy-front, .twk-layer-top { transition: transform 0.3s var(--twk-ease-pop); }
225
231
  @media (hover: hover) and (pointer: fine) {
226
- .tw-toolbar-btn:not(:disabled):hover { --tw-fan: 1px; --tw-lift: 1.75px; } /* a disabled button (the lazy window) holds still */
232
+ .twk-toolbar-btn:not(:disabled):hover { --twk-fan: 1px; --twk-lift: 1.75px; } /* a disabled button (the lazy window) holds still */
227
233
  }
228
- .tw-toolbar-btn[aria-expanded="true"] { --tw-lift: 1.75px; }
234
+ .twk-toolbar-btn[aria-expanded="true"] { --twk-lift: 1.75px; }
229
235
 
230
236
  /* Focus rings. Inset inside the controls stack, which clips for the collapse; outset
231
237
  * on the chips and handles. :focus-visible matches a clicked text field too, so
232
- * quietFocus (shared.ts) marks pointer-origin focus with .tw-focus-quiet. */
233
- .tw-trigger:focus-visible, .tw-select-trigger:focus-visible, .tw-button:focus-visible,
234
- .tw-seg-btn:focus-visible, .tw-seg[role="switch"]:focus-visible, .tw-header-toggle:focus-visible, .tw-folder-header:focus-visible,
235
- .tw-slider:focus-visible, .tw-pad:focus-visible, .tw-motion-stage:focus-visible, .tw-sides-link:focus-visible, .tw-radiogrid-btn:focus-visible, .tw-buttongroup-btn:focus-visible, .tw-tabs-tab:focus-visible,
236
- .tw-image-clear:focus-visible,
237
- .tw-text:focus-visible:not(.tw-focus-quiet), .tw-num:focus-visible:not(.tw-focus-quiet),
238
- .tw-color-chan-input:focus-visible:not(.tw-focus-quiet), .tw-color-mode:focus-visible,
239
- .tw-presets-load:focus-visible, .tw-presets-savebtn:focus-visible, .tw-presets-del:focus-visible, .tw-presets-input:focus-visible:not(.tw-focus-quiet) {
240
- outline: 2px solid var(--tw-accent, var(--tw-text-focus)); outline-offset: -2px;
241
- }
242
- .tw-toolbar-btn:focus-visible { outline: 2px solid var(--tw-accent, var(--tw-text-focus)); outline-offset: 1px; }
243
- .tw-gradient-stop:focus-visible, .tw-gradient-add:focus-visible, .tw-bezier-handle:focus-visible { outline: 2px solid var(--tw-accent, var(--tw-text-focus)); outline-offset: 2px; }
238
+ * quietFocus (shared.ts) marks pointer-origin focus with .twk-focus-quiet. */
239
+ .twk-trigger:focus-visible, .twk-select-trigger:focus-visible, .twk-button:focus-visible,
240
+ .twk-seg-btn:focus-visible, .twk-seg[role="switch"]:focus-visible, .twk-header-toggle:focus-visible, .twk-folder-header:focus-visible,
241
+ .twk-slider:focus-visible, .twk-pad:focus-visible, .twk-motion-stage:focus-visible, .twk-sides-link:focus-visible, .twk-radiogrid-btn:focus-visible, .twk-buttongroup-btn:focus-visible, .twk-tabs-tab:focus-visible,
242
+ .twk-image-clear:focus-visible,
243
+ .twk-text:focus-visible:not(.twk-focus-quiet), .twk-num:focus-visible:not(.twk-focus-quiet),
244
+ .twk-color-chan-input:focus-visible:not(.twk-focus-quiet), .twk-color-mode:focus-visible,
245
+ .twk-presets-load:focus-visible, .twk-presets-savebtn:focus-visible, .twk-presets-del:focus-visible, .twk-presets-input:focus-visible:not(.twk-focus-quiet) {
246
+ outline: 2px solid var(--twk-accent, var(--twk-text-focus)); outline-offset: -2px;
247
+ }
248
+ .twk-toolbar-btn:focus-visible { outline: 2px solid var(--twk-accent, var(--twk-text-focus)); outline-offset: 1px; }
249
+ .twk-gradient-stop:focus-visible, .twk-gradient-add:focus-visible, .twk-bezier-handle:focus-visible { outline: 2px solid var(--twk-accent, var(--twk-text-focus)); outline-offset: 2px; }
244
250
 
245
251
  /* ── Controls stack ── */
246
- .tw-controls { display: flex; flex-direction: column; gap: 6px; }
252
+ .twk-controls { display: flex; flex-direction: column; gap: 6px; }
247
253
  /* A flex item's floor is its min-content width; one nowrap child would widen every row. */
248
- .tw-controls > * { min-width: 0; }
249
- .tw-cond-hidden { display: none !important; }
254
+ .twk-controls > * { min-width: 0; }
255
+ .twk-cond-hidden { display: none !important; }
250
256
  .is-disabled { opacity: 0.4; cursor: not-allowed; }
251
257
  .is-disabled * { pointer-events: none; } /* the row stays a target for the not-allowed cursor */
252
258
  /* Reset target (double-click). touch-action: manipulation drops double-tap-to-zoom. */
253
- .tw-resettable { touch-action: manipulation; -webkit-user-select: none; user-select: none; -webkit-touch-callout: none; }
259
+ .twk-resettable { touch-action: manipulation; -webkit-user-select: none; user-select: none; -webkit-touch-callout: none; }
254
260
  /* Hold-to-reset fill. A transition, so an early release drains it. Doubled class: targets
255
- * like .tw-slider-value declare their own `transition: color`, which would win on order. */
256
- .tw-resettable.tw-resettable { transition: color 0.15s, background-color 0.3s ease-out; }
257
- .tw-resettable.tw-reset-holding { border-radius: calc(var(--tw-radius) - 4px); background-color: var(--tw-surface-active); transition: color 0.15s, background-color 0.5s ease-out; }
261
+ * like .twk-slider-value declare their own `transition: color`, which would win on order. */
262
+ .twk-resettable.twk-resettable { transition: color 0.15s, background-color 0.3s ease-out; }
263
+ .twk-resettable.twk-reset-holding { border-radius: calc(var(--twk-radius) - 4px); background-color: var(--twk-surface-active); transition: color 0.15s, background-color 0.5s ease-out; }
258
264
 
259
265
  /* ── Slider ── */
260
- .tw-slider-wrap { position: relative; height: var(--tw-row-height); }
261
- .tw-slider {
266
+ .twk-slider-wrap { position: relative; height: var(--twk-row-height); }
267
+ .twk-slider {
262
268
  position: absolute; inset: 0;
263
269
  cursor: pointer; user-select: none; -webkit-user-select: none; overflow: hidden;
264
- background: var(--tw-surface); border-radius: var(--tw-radius);
270
+ background: var(--twk-surface); border-radius: var(--twk-radius);
265
271
  touch-action: none;
266
272
  }
267
- .tw-slider-hashmarks { position: absolute; inset: 0; pointer-events: none; }
268
- .tw-slider-hashmark {
273
+ .twk-slider-hashmarks { position: absolute; inset: 0; pointer-events: none; }
274
+ .twk-slider-hashmark {
269
275
  position: absolute; top: 50%; width: 1px; height: 8px; border-radius: 999px;
270
276
  transform: translateX(-50%) translateY(-50%);
271
277
  background: rgba(255, 255, 255, 0); transition: background 200ms;
272
278
  }
273
- .tw-slider.is-active .tw-slider-hashmark { background: var(--tw-border-hover); }
274
- .tw-slider.is-active .tw-slider-value { color: var(--tw-text-focus); }
275
- .tw-slider-fill {
279
+ .twk-slider.is-active .twk-slider-hashmark { background: var(--twk-border-hover); }
280
+ .twk-slider.is-active .twk-slider-value { color: var(--twk-text-focus); }
281
+ .twk-slider-fill {
276
282
  position: absolute; top: 0; bottom: 0; left: 0; width: 0;
277
- background: var(--tw-surface-active); transition: background 0.15s; pointer-events: none;
283
+ background: var(--twk-surface-active); transition: background 0.15s; pointer-events: none;
278
284
  }
279
- .tw-slider.is-active .tw-slider-fill { background: var(--tw-accent, var(--tw-border-hover)); }
280
- .tw-slider-handle {
285
+ .twk-slider.is-active .twk-slider-fill { background: var(--twk-accent, var(--twk-border-hover)); }
286
+ .twk-slider-handle {
281
287
  position: absolute; top: 50%; left: 0; width: 3px; height: 20px;
282
- border-radius: 999px; background: var(--tw-text-primary);
288
+ border-radius: 999px; background: var(--twk-text-primary);
283
289
  transform: translateY(-50%) scaleX(0.25); opacity: 0; pointer-events: none;
284
- transition: opacity 0.15s, transform 0.2s var(--tw-ease-spring);
290
+ transition: opacity 0.15s, transform 0.2s var(--twk-ease-spring);
285
291
  }
286
292
  /* .is-hover is set in JS (pointerenter/leave) as a companion to :hover. */
287
- .tw-slider.is-hover .tw-slider-handle,
288
- .tw-slider:focus-within .tw-slider-handle { opacity: 0.5; transform: translateY(-50%) scaleX(1); }
293
+ .twk-slider.is-hover .twk-slider-handle,
294
+ .twk-slider:focus-within .twk-slider-handle { opacity: 0.5; transform: translateY(-50%) scaleX(1); }
289
295
  @media (hover: hover) and (pointer: fine) {
290
- .tw-slider:hover .tw-slider-handle { opacity: 0.5; transform: translateY(-50%) scaleX(1); }
296
+ .twk-slider:hover .twk-slider-handle { opacity: 0.5; transform: translateY(-50%) scaleX(1); }
291
297
  }
292
- .tw-slider.is-active .tw-slider-handle { opacity: 0.9; transform: translateY(-50%) scaleX(1); }
298
+ .twk-slider.is-active .twk-slider-handle { opacity: 0.9; transform: translateY(-50%) scaleX(1); }
293
299
  /* Interval handles are focusable; the single slider's are decorative. */
294
- .tw-slider-handle:focus-visible { opacity: 0.9; transform: translateY(-50%) scaleX(1); outline: 2px solid var(--tw-accent, var(--tw-text-focus)); outline-offset: 2px; }
300
+ .twk-slider-handle:focus-visible { opacity: 0.9; transform: translateY(-50%) scaleX(1); outline: 2px solid var(--twk-accent, var(--twk-text-focus)); outline-offset: 2px; }
295
301
  /* The dodge: the handle yields under the label or value text. */
296
- .tw-slider.is-active.is-dodge .tw-slider-handle,
297
- .tw-slider.is-hover.is-dodge .tw-slider-handle { opacity: 0.1; transform: translateY(-50%) scaleX(1) scaleY(0.75); }
302
+ .twk-slider.is-active.is-dodge .twk-slider-handle,
303
+ .twk-slider.is-hover.is-dodge .twk-slider-handle { opacity: 0.1; transform: translateY(-50%) scaleX(1) scaleY(0.75); }
298
304
  @media (hover: hover) and (pointer: fine) {
299
- .tw-slider:hover.is-dodge .tw-slider-handle { opacity: 0.1; transform: translateY(-50%) scaleX(1) scaleY(0.75); }
305
+ .twk-slider:hover.is-dodge .twk-slider-handle { opacity: 0.1; transform: translateY(-50%) scaleX(1) scaleY(0.75); }
300
306
  }
301
307
  /* Interval has two handles, so the dodge is per-handle (the one under the text). */
302
- .tw-interval.is-active .tw-slider-handle.is-dodge { opacity: 0.1; transform: translateY(-50%) scaleX(1) scaleY(0.75); }
303
- .tw-slider-label {
308
+ .twk-interval.is-active .twk-slider-handle.is-dodge { opacity: 0.1; transform: translateY(-50%) scaleX(1) scaleY(0.75); }
309
+ .twk-slider-label {
304
310
  position: absolute; left: 12px; top: 50%; transform: translateY(-50%); /* 12px = the row's left padding */
305
311
  max-width: calc(100% - 64px);
306
- font-size: 12px; font-weight: 500; color: var(--tw-text-label); transition: color 0.15s;
312
+ font-size: 12px; font-weight: 500; color: var(--twk-text-label); transition: color 0.15s;
307
313
  pointer-events: none; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
308
314
  }
309
- .tw-interval .tw-slider-label { max-width: calc(100% - 112px); } /* the control tightens this inline on render */
310
- .tw-slider-value {
315
+ .twk-interval .twk-slider-label { max-width: calc(100% - 112px); } /* the control tightens this inline on render */
316
+ .twk-slider-value {
311
317
  position: absolute; right: 10px; top: 50%; transform: translateY(-50%);
312
318
  font-size: 12px; font-weight: 500;
313
- color: var(--tw-text-label); pointer-events: auto;
319
+ color: var(--twk-text-label); pointer-events: auto;
314
320
  transition: color 0.15s;
315
321
  }
316
322
  /* The 800ms hover arms editing; the underline makes click-to-type discoverable. */
317
- .tw-slider-value.is-editable { cursor: text; color: var(--tw-text-focus); text-decoration: underline; text-decoration-color: var(--tw-text-tertiary); text-underline-offset: 3px; }
318
- .tw-slider-input {
323
+ .twk-slider-value.is-editable { cursor: text; color: var(--twk-text-focus); text-decoration: underline; text-decoration-color: var(--twk-text-tertiary); text-underline-offset: 3px; }
324
+ .twk-slider-input {
319
325
  position: absolute; right: 10px; top: 50%; transform: translateY(-50%);
320
326
  min-width: 4ch; max-width: 60%; /* the editor sizes its width to the text it opens on */
321
- font-size: 12px; font-weight: 500; font-family: var(--tw-font-sans);
322
- color: var(--tw-text-label); background: transparent; border: none;
327
+ font-size: 12px; font-weight: 500; font-family: var(--twk-font-sans);
328
+ color: var(--twk-text-label); background: transparent; border: none;
323
329
  padding: 0; outline: none; text-align: right;
324
330
  }
325
- .tw-slider-input:focus { color: var(--tw-text-focus); }
331
+ .twk-slider-input:focus { color: var(--twk-text-focus); }
326
332
 
327
333
  /* ── Labeled row (label + control side by side) ── */
328
- .tw-row {
334
+ .twk-row {
329
335
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
330
- height: var(--tw-row-height); padding: 2px 10px 2px 12px;
331
- background: var(--tw-surface); border-radius: var(--tw-radius);
336
+ height: var(--twk-row-height); padding: 2px 10px 2px 12px;
337
+ background: var(--twk-surface); border-radius: var(--twk-radius);
332
338
  }
333
- .tw-row-label {
334
- font-size: 12px; font-weight: 500; line-height: 17px; color: var(--tw-text-label); transition: color 0.15s;
339
+ .twk-row-label {
340
+ font-size: 12px; font-weight: 500; line-height: 17px; color: var(--twk-text-label); transition: color 0.15s;
335
341
  min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
336
342
  }
337
343
  /* Every row lights its label on hover, never its value. */
338
344
  @media (hover: hover) and (pointer: fine) {
339
- .tw-slider-wrap:not(.is-disabled):hover .tw-slider-label,
340
- :is(.tw-row, .tw-sides, .tw-radiogrid):not(.is-disabled):hover :is(.tw-row-label, .tw-radiogrid-label),
341
- :is(.tw-spring, .tw-bezier, .tw-plot):not(.is-disabled):hover :is(.tw-spring-label, .tw-bezier-label, .tw-plot-label) { color: var(--tw-text-primary); }
345
+ .twk-slider-wrap:not(.is-disabled):hover .twk-slider-label,
346
+ :is(.twk-row, .twk-sides, .twk-radiogrid):not(.is-disabled):hover :is(.twk-row-label, .twk-radiogrid-label),
347
+ :is(.twk-spring, .twk-bezier, .twk-plot):not(.is-disabled):hover :is(.twk-spring-label, .twk-bezier-label, .twk-plot-label) { color: var(--twk-text-primary); }
342
348
  }
343
349
  /* An explicit `label: ""` hides the element so it takes no gap. */
344
- .tw-slider-label:empty, .tw-row-label:empty, .tw-select-label:empty, .tw-radiogrid-label:empty, .tw-trigger-label:empty, .tw-fps-label:empty { display: none; }
345
- .tw-row-label:empty + *, .tw-select-label:empty + *, .tw-trigger-label:empty + * { margin-left: auto; }
350
+ .twk-slider-label:empty, .twk-row-label:empty, .twk-select-label:empty, .twk-radiogrid-label:empty, .twk-trigger-label:empty, .twk-fps-label:empty { display: none; }
351
+ .twk-row-label:empty + *, .twk-select-label:empty + *, .twk-trigger-label:empty + * { margin-left: auto; }
346
352
 
347
353
  /* ── Segmented control (also the Off/On toggle) ── */
348
- .tw-seg { position: relative; display: flex; gap: 2px; padding: 2px; border-radius: var(--tw-radius); }
354
+ .twk-seg { position: relative; display: flex; gap: 2px; padding: 2px; border-radius: var(--twk-radius); }
349
355
  /* The pill sits 4px off the top and bottom, so -8px (the row's 10px right padding, less
350
356
  * the 4px gap, less the seg's 2px pad) puts it 4px off the right too. */
351
- .tw-row .tw-seg { flex-shrink: 0; margin-right: -8px; }
352
- .tw-seg-pill {
357
+ .twk-row .twk-seg { flex-shrink: 0; margin-right: -8px; }
358
+ .twk-seg-pill {
353
359
  position: absolute; top: 2px; bottom: 2px; left: 0; width: 0;
354
- background: var(--tw-accent, var(--tw-surface-active)); border-radius: calc(var(--tw-radius) - 2px); z-index: 0; pointer-events: none;
360
+ background: var(--twk-accent, var(--twk-surface-active)); border-radius: calc(var(--twk-radius) - 2px); z-index: 0; pointer-events: none;
355
361
  }
356
- .tw-seg.is-ready .tw-seg-pill { transition: left 0.24s var(--tw-ease-out), width 0.24s var(--tw-ease-out); }
357
- .tw-seg-btn {
362
+ .twk-seg.is-ready .twk-seg-pill { transition: left 0.24s var(--twk-ease-out), width 0.24s var(--twk-ease-out); }
363
+ .twk-seg-btn {
358
364
  position: relative; z-index: 1; flex: 0 0 auto; min-width: 40px; padding: 6px 8px; text-align: center;
359
365
  font: inherit; font-size: 12px; line-height: 14px; font-weight: 500; /* 14px, not normal: the same height whatever the font */
360
366
  background: transparent; border: none; cursor: pointer;
361
- color: var(--tw-text-label); transition: color 0.15s, transform 0.24s var(--tw-ease-pop);
367
+ color: var(--twk-text-label); transition: color 0.15s, transform 0.24s var(--twk-ease-pop);
362
368
  }
363
- .tw-seg-btn[data-active="true"] { color: var(--tw-on-accent); }
369
+ .twk-seg-btn[data-active="true"] { color: var(--twk-on-accent); }
364
370
  @media (hover: hover) and (pointer: fine) {
365
- .tw-seg-btn:not([data-active="true"]):hover, .tw-tabs-tab:not([data-active="true"]):hover { color: var(--tw-text-primary); } /* a tab's label lights like a segment's */
371
+ .twk-seg-btn:not([data-active="true"]):hover, .twk-tabs-tab:not([data-active="true"]):hover { color: var(--twk-text-primary); } /* a tab's label lights like a segment's */
366
372
  }
367
373
  /* A press squashes the label; the pill itself does the liquid stretch (JS, WAAPI). */
368
- .tw-seg-btn:active { transform: scale(var(--tw-press-chip)); transition: transform 0.08s ease-out; }
374
+ .twk-seg-btn:active { transform: scale(var(--twk-press-chip)); transition: transform 0.08s ease-out; }
369
375
  /* In a row the buttons hug their text; 5px padding matches the tabs' pill height. */
370
- .tw-row .tw-seg .tw-seg-btn { min-width: 0; padding: 5px; }
376
+ .twk-row .twk-seg .twk-seg-btn { min-width: 0; padding: 5px; }
371
377
 
372
378
  /* ── Radio grid — a segmented control wrapped into a 2- or 3-column grid ── */
373
379
  /* A 4px frame round the cells: one row is 24px cell + 8px frame = the 32px row height. */
374
- .tw-radiogrid {
380
+ .twk-radiogrid {
375
381
  display: flex; align-items: flex-start; gap: 10px; /* a two-line grid keeps its label on the first line, as every tall row does */
376
- min-height: var(--tw-row-height); padding: 4px 4px 4px 12px;
377
- background: var(--tw-surface); border-radius: var(--tw-radius);
382
+ min-height: var(--twk-row-height); padding: 4px 4px 4px 12px;
383
+ background: var(--twk-surface); border-radius: var(--twk-radius);
378
384
  }
379
- .tw-radiogrid-label {
385
+ .twk-radiogrid-label {
380
386
  flex: 1 1 5ch; min-width: 5ch; line-height: 24px; /* a cell's height: its text lands 9px down */
381
- font-size: 12px; font-weight: 500; color: var(--tw-text-label); transition: color 0.15s;
387
+ font-size: 12px; font-weight: 500; color: var(--twk-text-label); transition: color 0.15s;
382
388
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
383
389
  }
384
390
  /* One row of choices: each cell hugs its word like the Off/On toggle's segments, and a tight row
385
391
  * shortens the label to 5ch before it cuts a cell. */
386
- .tw-radiogrid-grid {
392
+ .twk-radiogrid-grid {
387
393
  flex: 0 1 auto; min-width: 0; margin-left: auto;
388
- display: grid; grid-template-columns: repeat(var(--tw-rg-cols, 3), minmax(0, auto)); gap: 4px;
394
+ display: grid; grid-template-columns: repeat(var(--twk-rg-cols, 3), minmax(0, auto)); gap: 4px;
389
395
  }
390
396
  /* Several rows: equal columns beside a label that keeps its width; a grid too wide for the row
391
397
  * shrinks its cells, since a choice that long belongs in a list. */
392
- .tw-radiogrid.is-grid .tw-radiogrid-label { flex: 0 1 auto; min-width: 0; }
393
- .tw-radiogrid.is-grid .tw-radiogrid-grid { flex: 1 1 0; max-width: max-content; grid-template-columns: repeat(var(--tw-rg-cols, 3), minmax(0, 1fr)); }
394
- .tw-radiogrid-btn {
398
+ .twk-radiogrid.is-grid .twk-radiogrid-label { flex: 0 1 auto; min-width: 0; }
399
+ .twk-radiogrid.is-grid .twk-radiogrid-grid { flex: 1 1 0; max-width: max-content; grid-template-columns: repeat(var(--twk-rg-cols, 3), minmax(0, 1fr)); }
400
+ .twk-radiogrid-btn {
395
401
  font: inherit; font-size: 12px; line-height: 14px; font-weight: 500; font-variant-numeric: tabular-nums; /* 14px, not normal: the pill is 24px whatever the font */
396
402
  min-width: 24px; padding: 5px; text-align: center; /* 5px, the 24px pill height shared with the toggle; at least as wide, so a glyph cell is square */
397
- color: var(--tw-text-label); background: transparent;
398
- border: none; border-radius: calc(var(--tw-radius) - 2px);
399
- cursor: pointer; transition: background 0.15s, color 0.15s, transform 0.24s var(--tw-ease-pop);
403
+ color: var(--twk-text-label); background: transparent;
404
+ border: none; border-radius: calc(var(--twk-radius) - 2px);
405
+ cursor: pointer; transition: background 0.15s, color 0.15s, transform 0.24s var(--twk-ease-pop);
400
406
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
401
407
  }
402
408
  @media (hover: hover) and (pointer: fine) {
403
- .tw-radiogrid-btn:hover { background: var(--tw-surface-hover); color: var(--tw-text-primary); }
409
+ .twk-radiogrid-btn:hover { background: var(--twk-surface-hover); color: var(--twk-text-primary); }
404
410
  }
405
- .tw-radiogrid-btn:active { transform: scale(var(--tw-press-chip)); transition: background 0.15s, color 0.15s, transform 0.08s ease-out; }
406
- .tw-radiogrid-btn[data-active="true"] { background: var(--tw-accent, var(--tw-surface-active)); color: var(--tw-on-accent); }
411
+ .twk-radiogrid-btn:active { transform: scale(var(--twk-press-chip)); transition: background 0.15s, color 0.15s, transform 0.08s ease-out; }
412
+ .twk-radiogrid-btn[data-active="true"] { background: var(--twk-accent, var(--twk-surface-active)); color: var(--twk-on-accent); }
407
413
 
408
414
  /* ── Button (action) ── */
409
- .tw-button {
415
+ .twk-button {
410
416
  display: flex; align-items: center; justify-content: center;
411
- width: 100%; height: var(--tw-row-height); padding: 0 16px; font: inherit; font-size: 12px; font-weight: 500;
412
- color: var(--tw-text-secondary); background: var(--tw-surface);
413
- border: none; border-radius: var(--tw-radius); cursor: pointer;
414
- transition: background 0.15s, color 0.15s, transform 0.24s var(--tw-ease-pop);
417
+ width: 100%; height: var(--twk-row-height); padding: 0 16px; font: inherit; font-size: 12px; font-weight: 500;
418
+ color: var(--twk-text-secondary); background: var(--twk-surface);
419
+ border: none; border-radius: var(--twk-radius); cursor: pointer;
420
+ transition: background 0.15s, color 0.15s, transform 0.24s var(--twk-ease-pop);
415
421
  }
416
422
  @media (hover: hover) and (pointer: fine) {
417
- .tw-button:hover { background: var(--tw-surface-hover); color: var(--tw-text-primary); } /* a whole-row button also lights its fill */
423
+ .twk-button:hover { background: var(--twk-surface-hover); color: var(--twk-text-primary); } /* a whole-row button also lights its fill */
418
424
  }
419
- .tw-button:active { background: var(--tw-surface-active); transform: scale(var(--tw-press-row)); transition: background 0.15s, color 0.15s, transform 0.08s ease-out; }
425
+ .twk-button:active { background: var(--twk-surface-active); transform: scale(var(--twk-press-row)); transition: background 0.15s, color 0.15s, transform 0.08s ease-out; }
420
426
 
421
427
  /* ── Button group — compact actions under one label ── */
422
428
  /* -6px (the row's 10px right padding, less the 4px gap) sits the buttons 4px off the right
423
429
  * like the top and bottom; a button is never narrower than its label, so many buttons wrap
424
430
  * rather than cut their words. */
425
- .tw-buttongroup { height: auto; min-height: var(--tw-row-height); padding-top: 4px; padding-bottom: 4px; } /* 4px above and below a wrapped group too, not only a one-line one */
426
- .tw-buttongroup-btns { display: flex; flex-wrap: wrap; gap: 4px; flex: 1; min-width: 0; margin-right: -6px; }
427
- .tw-buttongroup:not(:has(> .tw-row-label)) { padding-left: 4px; } /* with no label, the left matches the other three sides */
428
- .tw-buttongroup-btn { flex: 1 1 0; min-width: min-content; padding: 5px 8px; font: inherit; font-size: 12px; line-height: 14px; font-weight: 500; color: var(--tw-text-label); background: var(--tw-surface-active); border: none; border-radius: calc(var(--tw-radius) - 2px); cursor: pointer; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; transition: background 0.15s, color 0.15s, transform 0.24s var(--tw-ease-pop); }
431
+ .twk-buttongroup { height: auto; min-height: var(--twk-row-height); padding-top: 4px; padding-bottom: 4px; } /* 4px above and below a wrapped group too, not only a one-line one */
432
+ .twk-buttongroup-btns { display: flex; flex-wrap: wrap; gap: 4px; flex: 1; min-width: 0; margin-right: -6px; }
433
+ .twk-buttongroup:not(:has(> .twk-row-label)) { padding-left: 4px; } /* with no label, the left matches the other three sides */
434
+ .twk-buttongroup-btn { flex: 1 1 0; min-width: min-content; padding: 5px 8px; font: inherit; font-size: 12px; line-height: 14px; font-weight: 500; color: var(--twk-text-label); background: var(--twk-surface-active); border: none; border-radius: calc(var(--twk-radius) - 2px); cursor: pointer; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; transition: background 0.15s, color 0.15s, transform 0.24s var(--twk-ease-pop); }
429
435
  @media (hover: hover) and (pointer: fine) {
430
- .tw-buttongroup-btn:hover { background: var(--tw-surface-hover); color: var(--tw-text-primary); }
436
+ .twk-buttongroup-btn:hover { background: var(--twk-surface-hover); color: var(--twk-text-primary); }
431
437
  }
432
- .tw-buttongroup-btn:active { transform: scale(var(--tw-press-chip)); transition: background 0.15s, color 0.15s, transform 0.08s ease-out; }
438
+ .twk-buttongroup-btn:active { transform: scale(var(--twk-press-chip)); transition: background 0.15s, color 0.15s, transform 0.08s ease-out; }
433
439
 
434
440
  /* ── Separator — a thin divider between sections ── */
435
- .tw-separator { height: 1px; margin: 3px 2px; background: var(--tw-border); }
441
+ .twk-separator { height: 1px; margin: 3px 2px; background: var(--twk-border); }
436
442
 
437
443
  /* ── Tabs — paged controls with a sliding pill ── */
438
- .tw-tabs { display: flex; flex-direction: column; gap: 8px; }
439
- .tw-tabs-bar { position: relative; display: flex; gap: 4px; padding: 4px; min-width: 0; /* the rows' 4px frame round 24px pills: a 32px row */ background: var(--tw-surface); border-radius: var(--tw-radius); }
440
- .tw-tabs-pill { position: absolute; top: 4px; bottom: 4px; left: 0; width: 0; background: var(--tw-surface-active); border-radius: calc(var(--tw-radius) - 2px); z-index: 0; transition: left 0.24s var(--tw-ease-out), width 0.24s var(--tw-ease-out); }
441
- .tw-tabs-tab { position: relative; z-index: 1; flex: 1; min-width: 0; padding: 5px 8px; font: inherit; font-size: 12px; line-height: 14px; font-weight: 500; color: var(--tw-text-label); background: transparent; border: none; border-radius: calc(var(--tw-radius) - 2px); cursor: pointer; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; transition: color 0.15s, transform 0.24s var(--tw-ease-pop); }
442
- .tw-tabs-tab[data-active="true"] { color: var(--tw-text-primary); }
443
- .tw-tabs-tab:active { transform: scale(var(--tw-press-chip)); transition: color 0.15s, transform 0.08s ease-out; }
444
- .tw-tabs-page { display: none; flex-direction: column; gap: 6px; }
444
+ .twk-tabs { display: flex; flex-direction: column; gap: 8px; }
445
+ .twk-tabs-bar { position: relative; display: flex; gap: 4px; padding: 4px; min-width: 0; /* the rows' 4px frame round 24px pills: a 32px row */ background: var(--twk-surface); border-radius: var(--twk-radius); }
446
+ .twk-tabs-pill { position: absolute; top: 4px; bottom: 4px; left: 0; width: 0; background: var(--twk-surface-active); border-radius: calc(var(--twk-radius) - 2px); z-index: 0; transition: left 0.24s var(--twk-ease-out), width 0.24s var(--twk-ease-out); }
447
+ .twk-tabs-tab { position: relative; z-index: 1; flex: 1; min-width: 0; padding: 5px 8px; font: inherit; font-size: 12px; line-height: 14px; font-weight: 500; color: var(--twk-text-label); background: transparent; border: none; border-radius: calc(var(--twk-radius) - 2px); cursor: pointer; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; transition: color 0.15s, transform 0.24s var(--twk-ease-pop); }
448
+ .twk-tabs-tab[data-active="true"] { color: var(--twk-text-primary); }
449
+ .twk-tabs-tab:active { transform: scale(var(--twk-press-chip)); transition: color 0.15s, transform 0.08s ease-out; }
450
+ .twk-tabs-page { display: none; flex-direction: column; gap: 6px; }
445
451
  /* The page's entrance replays on every activation (display flips). */
446
- .tw-tabs-page[data-active="true"] { display: flex; animation: tw-page-in 0.3s var(--tw-ease-out); }
447
- @keyframes tw-page-in { from { opacity: 0; transform: translateY(5px); } to { opacity: 1; transform: none; } }
452
+ .twk-tabs-page[data-active="true"] { display: flex; animation: twk-page-in 0.3s var(--twk-ease-out); }
453
+ @keyframes twk-page-in { from { opacity: 0; transform: translateY(5px); } to { opacity: 1; transform: none; } }
448
454
 
449
455
  /* ── Select ── */
450
- .tw-select { position: relative; }
451
- .tw-select-trigger {
456
+ .twk-select { position: relative; }
457
+ .twk-select-trigger {
452
458
  display: flex; align-items: center; justify-content: space-between; gap: 10px; width: 100%; /* the gap holds once the value can shrink to the label */
453
- height: var(--tw-row-height); padding: 0 10px 0 12px; /* the row idiom: 12 left, 10 right */
454
- font: inherit; font-size: 12px; font-weight: 500; color: var(--tw-text-label);
455
- background: var(--tw-surface); border: none; border-radius: var(--tw-radius);
456
- cursor: pointer; transition: background 0.15s, transform 0.24s var(--tw-ease-pop);
459
+ height: var(--twk-row-height); padding: 0 10px 0 12px; /* the row idiom: 12 left, 10 right */
460
+ font: inherit; font-size: 12px; font-weight: 500; color: var(--twk-text-label);
461
+ background: var(--twk-surface); border: none; border-radius: var(--twk-radius);
462
+ cursor: pointer; transition: background 0.15s, transform 0.24s var(--twk-ease-pop);
457
463
  }
458
464
  @media (hover: hover) and (pointer: fine) {
459
- .tw-select-trigger:hover { background: var(--tw-surface-hover); } /* the whole row is the button */
460
- .tw-select-trigger:hover .tw-select-label, .tw-select-trigger:hover .tw-select-chevron { color: var(--tw-text-primary); } /* the chevron lights like a folder's */
461
- }
462
- .tw-select-trigger:active { transform: scale(var(--tw-press-row)); transition: background 0.15s, transform 0.08s ease-out; }
463
- .tw-select.is-open .tw-select-trigger { background: var(--tw-surface-active); }
464
- .tw-select-label { transition: color 0.15s; font-weight: 500; flex: 1 1 0; min-width: 5ch; text-align: left; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } /* a rigid label took the chevron out of the panel */
465
- .tw-select-right { display: flex; align-items: center; gap: 8px; min-width: 0; flex: 0 1 auto; max-width: calc(100% - 5ch - 10px); }
466
- .tw-select-value { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
465
+ .twk-select-trigger:hover { background: var(--twk-surface-hover); } /* the whole row is the button */
466
+ .twk-select-trigger:hover .twk-select-label, .twk-select-trigger:hover .twk-select-chevron { color: var(--twk-text-primary); } /* the chevron lights like a folder's */
467
+ }
468
+ .twk-select-trigger:active { transform: scale(var(--twk-press-row)); transition: background 0.15s, transform 0.08s ease-out; }
469
+ .twk-select.is-open .twk-select-trigger { background: var(--twk-surface-active); }
470
+ .twk-select-label { transition: color 0.15s; font-weight: 500; flex: 1 1 0; min-width: 5ch; text-align: left; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } /* a rigid label took the chevron out of the panel */
471
+ .twk-select-right { display: flex; align-items: center; gap: 8px; min-width: 0; flex: 0 1 auto; max-width: calc(100% - 5ch - 10px); }
472
+ .twk-select-value { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
467
473
  /* The folder's 15px chevron; the glyph sits 3.75px inside its box, so pull it out by that side-bearing. */
468
- .tw-select-chevron { width: 15px; height: 15px; flex-shrink: 0; color: var(--tw-text-section); opacity: 0.6; margin-right: -3.75px; transition: transform 0.2s var(--tw-ease-out), color 0.15s; }
469
- .tw-select.is-open .tw-select-chevron { transform: rotate(180deg); }
474
+ .twk-select-chevron { width: 15px; height: 15px; flex-shrink: 0; color: var(--twk-text-section); opacity: 0.6; margin-right: -3.75px; transition: transform 0.2s var(--twk-ease-out), color 0.15s; }
475
+ .twk-select.is-open .twk-select-chevron { transform: rotate(180deg); }
470
476
  /* The three portaled panes' shell (list dropdown, presets menu, color pop): fixed on <body> and placed by JS, the dropdown
471
477
  * surface, and the entrance. Each one's own rule below sets its size, radius and origin. */
472
- :is(.tw-select-dropdown, .tw-presets-menu, .tw-color-pop) {
478
+ :is(.twk-select-dropdown, .twk-presets-menu, .twk-color-pop) {
473
479
  position: fixed; z-index: 99999;
474
- background: var(--tw-dropdown-bg); border: 1px solid var(--tw-border); box-shadow: var(--tw-shadow-dropdown);
475
- opacity: 0; transform: translateY(-6px) scale(var(--tw-pop-s)); pointer-events: none;
476
- transition: opacity 0.16s var(--tw-ease-out), transform 0.24s var(--tw-ease-spring);
480
+ background: var(--twk-dropdown-bg); border: 1px solid var(--twk-border); box-shadow: var(--twk-shadow-dropdown);
481
+ opacity: 0; transform: translateY(-6px) scale(var(--twk-pop-s)); pointer-events: none;
482
+ transition: opacity 0.16s var(--twk-ease-out), transform 0.24s var(--twk-ease-spring);
477
483
  }
478
- .tw-select-dropdown {
479
- padding: 4px; border-radius: calc(var(--tw-radius) + 2px); /* concentric: option (radius − 2) + padding 4 */
484
+ .twk-select-dropdown {
485
+ padding: 4px; border-radius: calc(var(--twk-radius) + 2px); /* concentric: option (radius − 2) + padding 4 */
480
486
  max-height: 60vh; overflow-y: auto;
481
487
  transform-origin: top; transition-duration: 0.16s, 0.22s;
482
488
  }
483
489
  /* A pane matches its row, but never runs past the window's 8px margins. */
484
- :is(.tw-select-dropdown, .tw-color-pop) { max-width: calc(100vw - 16px); }
490
+ :is(.twk-select-dropdown, .twk-color-pop) { max-width: calc(100vw - 16px); }
485
491
  /* Opened above the trigger: grow up from it. */
486
- :is(.tw-select-dropdown, .tw-color-pop)[data-side="above"] { transform-origin: bottom; transform: translateY(6px) scale(var(--tw-pop-s)); }
487
- .tw-select-option {
492
+ :is(.twk-select-dropdown, .twk-color-pop)[data-side="above"] { transform-origin: bottom; transform: translateY(6px) scale(var(--twk-pop-s)); }
493
+ .twk-select-option {
488
494
  display: flex; align-items: center; gap: 8px; width: 100%; padding: 8px 10px; font: inherit; font-size: 12px; font-weight: 500;
489
495
  line-height: 14px; /* not normal: a 30px option whatever the font, the chips' 14px */
490
- color: var(--tw-text-label); background: transparent; border: none; border-radius: calc(var(--tw-radius) - 2px);
496
+ color: var(--twk-text-label); background: transparent; border: none; border-radius: calc(var(--twk-radius) - 2px);
491
497
  cursor: pointer; text-align: left; transition: background 0.15s, color 0.15s;
492
498
  }
493
499
  @media (hover: hover) and (pointer: fine) {
494
- .tw-select-option:hover { background: var(--tw-surface-hover); }
500
+ .twk-select-option:hover { background: var(--twk-surface-hover); }
495
501
  }
496
- .tw-select-option:focus-visible { background: var(--tw-surface-hover); color: var(--tw-text-primary); outline: none; } /* roving focus reads as the hover highlight */
497
- .tw-select-option[data-selected="true"] { color: var(--tw-on-accent); background: var(--tw-accent, var(--tw-surface-active)); }
498
- .tw-option-swatch { flex-shrink: 0; width: 12px; height: 12px; border-radius: 4px; box-shadow: inset 0 0 0 1px var(--tw-border-hover); }
499
- .tw-option-label { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
502
+ .twk-select-option:focus-visible { background: var(--twk-surface-hover); color: var(--twk-text-primary); outline: none; } /* roving focus reads as the hover highlight */
503
+ .twk-select-option[data-selected="true"] { color: var(--twk-on-accent); background: var(--twk-accent, var(--twk-surface-active)); }
504
+ .twk-option-swatch { flex-shrink: 0; width: 12px; height: 12px; border-radius: 4px; box-shadow: inset 0 0 0 1px var(--twk-border-hover); }
505
+ .twk-option-label { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
500
506
 
501
507
  /* A row or folder picked for ⌘C / ⌘V (a press beside it): a filled band behind it, edge to edge across the panel at any
502
508
  * depth (the panel clips it to its rounded edge while something is picked). A row's band reaches 3px past it, halfway
503
509
  * across the 6px gap to its neighbors, the same on both sides wherever it sits; nothing moves. A folder's band runs
504
510
  * between its own rules. */
505
- .tw-panel:not([data-mode="floating"]):has(.is-picked) { overflow: clip; } /* both axes, so it follows the rounded corners; a floating panel already clips, scrolling */
506
- .tw-panel .is-picked { position: relative; isolation: isolate; }
507
- .tw-panel .is-picked::before { content: ""; position: absolute; inset: -3px -100vmax; z-index: -1; pointer-events: none; background: color-mix(in srgb, var(--tw-accent, var(--tw-text-focus)) 5%, transparent); }
508
- .tw-panel .tw-folder.is-picked::before { inset-block: 0; }
511
+ .twk-panel:not([data-mode="floating"]):has(.is-picked) { overflow: clip; } /* both axes, so it follows the rounded corners; a floating panel already clips, scrolling */
512
+ .twk-panel .is-picked { position: relative; isolation: isolate; }
513
+ .twk-panel .is-picked::before { content: ""; position: absolute; inset: -3px -100vmax; z-index: -1; pointer-events: none; background: color-mix(in srgb, var(--twk-accent, var(--twk-text-focus)) 5%, transparent); }
514
+ .twk-panel .twk-folder.is-picked::before { inset-block: 0; }
509
515
  /* A paste onto the pick answers on the band: a brief brightening when it lands, a brief danger tint when nothing fits. */
510
- .tw-panel .is-picked::before { transition: background 0.35s var(--tw-ease-out); }
511
- .tw-panel .is-picked.is-pasted::before { background: color-mix(in srgb, var(--tw-accent, var(--tw-text-focus)) 20%, transparent); transition-duration: 0.08s; }
512
- .tw-panel .is-picked.is-refused::before { background: color-mix(in srgb, var(--tw-danger) 18%, transparent); transition-duration: 0.08s; }
516
+ .twk-panel .is-picked::before { transition: background 0.35s var(--twk-ease-out); }
517
+ .twk-panel .is-picked.is-pasted::before { background: color-mix(in srgb, var(--twk-accent, var(--twk-text-focus)) 20%, transparent); transition-duration: 0.08s; }
518
+ .twk-panel .is-picked.is-refused::before { background: color-mix(in srgb, var(--twk-danger) 18%, transparent); transition-duration: 0.08s; }
513
519
 
514
520
  /* ── Presets menu — portaled; save / load / delete named snapshots ── */
515
- .tw-presets-menu {
521
+ .twk-presets-menu {
516
522
  width: 208px;
517
523
  display: flex; flex-direction: column; gap: 6px; padding: 8px;
518
- border-radius: calc(var(--tw-radius) + 4px); /* concentric: inner picker surface (radius) + padding 4 */
524
+ border-radius: calc(var(--twk-radius) + 4px); /* concentric: inner picker surface (radius) + padding 4 */
519
525
  transform-origin: top right;
520
526
  }
521
- .tw-presets-menu[data-side="above"] { transform-origin: bottom right; transform: translateY(6px) scale(var(--tw-pop-s)); }
527
+ .twk-presets-menu[data-side="above"] { transform-origin: bottom right; transform: translateY(6px) scale(var(--twk-pop-s)); }
522
528
  /* Open: after every pane's [data-side] origin, which it must beat at the same specificity. */
523
- :is(.tw-select-dropdown, .tw-presets-menu, .tw-color-pop).is-open { opacity: 1; transform: none; pointer-events: auto; }
524
- .tw-presets-save { display: flex; gap: 6px; }
525
- .tw-presets-input { flex: 1; min-width: 0; font: inherit; font-size: 12px; font-weight: 500; color: var(--tw-text-primary); background: var(--tw-surface); border: 1px solid var(--tw-border); border-radius: calc(var(--tw-radius) - 2px); padding: 6px 8px; outline: none; }
526
- .tw-presets-input:focus { border-color: var(--tw-border-hover); }
527
- .tw-presets-input::placeholder { color: var(--tw-text-tertiary); }
528
- .tw-presets-savebtn { flex-shrink: 0; font: inherit; font-size: 12px; font-weight: 500; color: var(--tw-text-primary); background: var(--tw-surface); border: none; border-radius: calc(var(--tw-radius) - 2px); padding: 6px 10px; cursor: pointer; transition: background 0.15s, transform 0.24s var(--tw-ease-pop); }
529
+ :is(.twk-select-dropdown, .twk-presets-menu, .twk-color-pop).is-open { opacity: 1; transform: none; pointer-events: auto; }
530
+ .twk-presets-save { display: flex; gap: 6px; }
531
+ .twk-presets-input { flex: 1; min-width: 0; font: inherit; font-size: 12px; font-weight: 500; color: var(--twk-text-primary); background: var(--twk-surface); border: 1px solid var(--twk-border); border-radius: calc(var(--twk-radius) - 2px); padding: 6px 8px; outline: none; }
532
+ .twk-presets-input:focus { border-color: var(--twk-border-hover); }
533
+ .twk-presets-input::placeholder { color: var(--twk-text-tertiary); }
534
+ .twk-presets-savebtn { flex-shrink: 0; font: inherit; font-size: 12px; font-weight: 500; color: var(--twk-text-primary); background: var(--twk-surface); border: none; border-radius: calc(var(--twk-radius) - 2px); padding: 6px 10px; cursor: pointer; transition: background 0.15s, transform 0.24s var(--twk-ease-pop); }
529
535
  @media (hover: hover) and (pointer: fine) {
530
- .tw-presets-savebtn:hover { background: var(--tw-surface-hover); }
536
+ .twk-presets-savebtn:hover { background: var(--twk-surface-hover); }
531
537
  }
532
- .tw-presets-list { display: flex; flex-direction: column; gap: 2px; max-height: 200px; overflow-y: auto; }
533
- .tw-presets-empty { padding: 8px; font-size: 12px; font-weight: 500; color: var(--tw-text-tertiary); text-align: center; }
534
- .tw-presets-row { display: flex; align-items: center; gap: 2px; }
535
- .tw-presets-load { flex: 1; min-width: 0; text-align: left; font: inherit; font-size: 12px; font-weight: 500; color: var(--tw-text-label); background: transparent; border: none; border-radius: calc(var(--tw-radius) - 2px); padding: 6px 8px; cursor: pointer; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; transition: background 0.15s, color 0.15s; }
538
+ .twk-presets-list { display: flex; flex-direction: column; gap: 2px; max-height: 200px; overflow-y: auto; }
539
+ .twk-presets-empty { padding: 8px; font-size: 12px; font-weight: 500; color: var(--twk-text-tertiary); text-align: center; }
540
+ .twk-presets-row { display: flex; align-items: center; gap: 2px; }
541
+ .twk-presets-load { flex: 1; min-width: 0; text-align: left; font: inherit; font-size: 12px; font-weight: 500; color: var(--twk-text-label); background: transparent; border: none; border-radius: calc(var(--twk-radius) - 2px); padding: 6px 8px; cursor: pointer; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; transition: background 0.15s, color 0.15s; }
536
542
  @media (hover: hover) and (pointer: fine) {
537
- .tw-presets-load:hover { background: var(--tw-surface-hover); color: var(--tw-text-primary); }
543
+ .twk-presets-load:hover { background: var(--twk-surface-hover); color: var(--twk-text-primary); }
538
544
  }
539
- .tw-presets-del { flex-shrink: 0; display: flex; align-items: center; justify-content: center; width: 24px; height: 24px; color: var(--tw-text-tertiary); background: transparent; border: none; border-radius: calc(var(--tw-radius) - 2px); cursor: pointer; transition: background 0.15s, color 0.15s, transform 0.24s var(--tw-ease-pop); }
545
+ .twk-presets-del { flex-shrink: 0; display: flex; align-items: center; justify-content: center; width: 24px; height: 24px; color: var(--twk-text-tertiary); background: transparent; border: none; border-radius: calc(var(--twk-radius) - 2px); cursor: pointer; transition: background 0.15s, color 0.15s, transform 0.24s var(--twk-ease-pop); }
540
546
  @media (hover: hover) and (pointer: fine) {
541
- .tw-presets-del:hover { background: var(--tw-surface-hover); color: var(--tw-text-primary); }
547
+ .twk-presets-del:hover { background: var(--twk-surface-hover); color: var(--twk-text-primary); }
542
548
  }
543
- .tw-presets-del:active { transform: scale(var(--tw-press-icon)); transition: background 0.15s, color 0.15s, transform 0.08s ease-out; }
544
- .tw-presets-savebtn:active { transform: scale(var(--tw-press-chip)); transition: background 0.15s, transform 0.08s ease-out; }
545
- .tw-presets-del svg { width: 12px; height: 12px; display: block; }
549
+ .twk-presets-del:active { transform: scale(var(--twk-press-icon)); transition: background 0.15s, color 0.15s, transform 0.08s ease-out; }
550
+ .twk-presets-savebtn:active { transform: scale(var(--twk-press-chip)); transition: background 0.15s, transform 0.08s ease-out; }
551
+ .twk-presets-del svg { width: 12px; height: 12px; display: block; }
546
552
  /* Clear, in the gallery's head: a small text button the label's size, quiet until hovered; its text ends 10px in, as a row's value. */
547
- .tw-image-clear { flex-shrink: 0; height: 24px; padding: 0 6px; font: inherit; font-size: 12px; font-weight: 500; color: var(--tw-text-tertiary); background: transparent; border: none; border-radius: calc(var(--tw-radius) - 2px); cursor: pointer; transition: background 0.15s, color 0.15s, transform 0.24s var(--tw-ease-pop); }
553
+ .twk-image-clear { flex-shrink: 0; height: 24px; padding: 0 6px; font: inherit; font-size: 12px; font-weight: 500; color: var(--twk-text-tertiary); background: transparent; border: none; border-radius: calc(var(--twk-radius) - 2px); cursor: pointer; transition: background 0.15s, color 0.15s, transform 0.24s var(--twk-ease-pop); }
548
554
  @media (hover: hover) and (pointer: fine) {
549
- .tw-image-clear:hover { background: var(--tw-surface-hover); color: var(--tw-text-primary); }
555
+ .twk-image-clear:hover { background: var(--twk-surface-hover); color: var(--twk-text-primary); }
550
556
  }
551
- .tw-image-clear:active { transform: scale(var(--tw-press-chip)); transition: background 0.15s, color 0.15s, transform 0.08s ease-out; }
557
+ .twk-image-clear:active { transform: scale(var(--twk-press-chip)); transition: background 0.15s, color 0.15s, transform 0.08s ease-out; }
552
558
 
553
559
  /* ── Trigger rows — the row button behind the color, gradient and point controls ── */
554
- .tw-trigger {
560
+ .twk-trigger {
555
561
  display: flex; align-items: center; justify-content: space-between; gap: 10px; width: 100%;
556
- height: var(--tw-row-height); padding: 0 10px 0 12px; /* the row idiom: 12 left, 10 right */
557
- font: inherit; font-size: 12px; font-weight: 500; color: var(--tw-text-label); cursor: pointer;
558
- background: var(--tw-surface); border: none; border-radius: var(--tw-radius); transition: background 0.15s, transform 0.24s var(--tw-ease-pop);
562
+ height: var(--twk-row-height); padding: 0 10px 0 12px; /* the row idiom: 12 left, 10 right */
563
+ font: inherit; font-size: 12px; font-weight: 500; color: var(--twk-text-label); cursor: pointer;
564
+ background: var(--twk-surface); border: none; border-radius: var(--twk-radius); transition: background 0.15s, transform 0.24s var(--twk-ease-pop);
559
565
  }
560
566
  @media (hover: hover) and (pointer: fine) {
561
- .tw-trigger:hover { background: var(--tw-surface-hover); } /* the whole row is the button */
562
- .tw-trigger:hover .tw-trigger-label { color: var(--tw-text-primary); }
567
+ .twk-trigger:hover { background: var(--twk-surface-hover); } /* the whole row is the button */
568
+ .twk-trigger:hover .twk-trigger-label { color: var(--twk-text-primary); }
563
569
  }
564
- .tw-trigger:active { transform: scale(var(--tw-press-row)); transition: background 0.15s, transform 0.08s ease-out; }
565
- .is-open > .tw-trigger { background: var(--tw-surface-active); }
570
+ .twk-trigger:active { transform: scale(var(--twk-press-row)); transition: background 0.15s, transform 0.08s ease-out; }
571
+ .is-open > .twk-trigger { background: var(--twk-surface-active); }
566
572
  /* The label takes what the right cluster leaves, down to 5ch; shrink weights instead
567
573
  * shaved a fraction of a pixel off the readout and drew an ellipsis over a value that fit. */
568
- .tw-trigger-label { transition: color 0.15s; flex: 1 1 0; min-width: 5ch; text-align: left; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } /* a <button> centers its text by default */
569
- .tw-trigger-right { display: flex; align-items: center; gap: 8px; min-width: 0; flex: 0 1 auto; max-width: calc(100% - 5ch - 10px); }
574
+ .twk-trigger-label { transition: color 0.15s; flex: 1 1 0; min-width: 5ch; text-align: left; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } /* a <button> centers its text by default */
575
+ .twk-trigger-right { display: flex; align-items: center; gap: 8px; min-width: 0; flex: 0 1 auto; max-width: calc(100% - 5ch - 10px); }
570
576
  /* word-spacing separates a multi-part readout ("60% 0.23 286") into columns; visual only. */
571
- .tw-trigger-value { word-spacing: 0.1em; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; max-width: 20ch; }
577
+ .twk-trigger-value { word-spacing: 0.1em; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; max-width: 20ch; }
572
578
  /* The 18px preview chip — swatch, gradient strip or mini pad. */
573
- .tw-trigger-chip { height: 18px; flex-shrink: 0; border-radius: calc(var(--tw-radius) - 4px); box-shadow: inset 0 0 0 1px var(--tw-border-hover); transition: transform 0.2s var(--tw-ease-spring); }
574
- .is-open > .tw-trigger .tw-trigger-chip { transform: scale(1.07); } /* 1.14 read as a jump on the wider chips */
579
+ .twk-trigger-chip { height: 18px; flex-shrink: 0; border-radius: calc(var(--twk-radius) - 4px); box-shadow: inset 0 0 0 1px var(--twk-border-hover); transition: transform 0.2s var(--twk-ease-spring); }
580
+ .is-open > .twk-trigger .twk-trigger-chip { transform: scale(1.07); } /* 1.14 read as a jump on the wider chips */
575
581
 
576
582
  /* ── Color — a trigger row that opens the picker ── */
577
- .tw-color { position: relative; }
578
- .tw-color-swatch { width: 18px; }
583
+ .twk-color { position: relative; }
584
+ .twk-color-swatch { width: 18px; }
579
585
 
580
586
  /* Portaled surfaces open on <body>, escaping any ancestor that becomes a containing block
581
- * for position:fixed (a transform or filter) and the panel's overflow. Each carries .tw-portal,
587
+ * for position:fixed (a transform or filter) and the panel's overflow. Each carries .twk-portal,
582
588
  * which hands it the tokens, font and line-height above. */
583
- .tw-portal *, .tw-portal *::before, .tw-portal *::after { box-sizing: border-box; }
589
+ .twk-portal *, .twk-portal *::before, .twk-portal *::after { box-sizing: border-box; }
584
590
  /* Entrance scale from the height placeBelow() records: the far edge moves ~5px at any height, so a tall pop doesn't zoom. */
585
- .tw-portal { --tw-pop-s: max(0.96, calc(1 - 5 / var(--tw-pop-h, 125))); }
591
+ .twk-portal { --twk-pop-s: max(0.96, calc(1 - 5 / var(--twk-pop-h, 125))); }
586
592
 
587
593
  /* the color dropdown */
588
- .tw-color-pop {
594
+ .twk-color-pop {
589
595
  min-width: 236px; /* the default panel's row width */
590
596
  display: flex; flex-direction: column; gap: 8px; padding: 10px;
591
- border-radius: calc(var(--tw-radius) + 10px); /* concentric: inner radius + padding 10 */
597
+ border-radius: calc(var(--twk-radius) + 10px); /* concentric: inner radius + padding 10 */
592
598
  max-height: calc(100dvh - 16px); overflow-y: auto; overflow-x: clip; /* a short viewport scrolls the picker */
593
599
  transform-origin: top;
594
600
  }
595
601
  /* The picker body as one column, so the gradient pop can mount it under the stop bar. */
596
- .tw-color-body { display: flex; flex-direction: column; gap: 8px; min-width: 0; }
597
- .tw-color-mode-row { display: flex; align-items: center; gap: 8px; }
598
- .tw-color-mode { appearance: none; font: inherit; font-size: 12px; font-weight: 500; color: var(--tw-text-primary); background-color: var(--tw-surface); background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='15' height='15' viewBox='0 0 24 24' fill='none' stroke='%23888888' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E"); background-repeat: no-repeat; background-position: right 6.25px center; border: 1px solid var(--tw-border); border-radius: calc(var(--tw-radius) - 2px); padding: 4px 26px 4px 10px; line-height: 14px; cursor: pointer; outline: none; } /* 14px, not normal: 24px tall whatever the font, the chips' 14px. The folder's 15px chevron: right 6.25px + its 3.75px side-bearing = 10 */
602
+ .twk-color-body { display: flex; flex-direction: column; gap: 8px; min-width: 0; }
603
+ .twk-color-mode-row { display: flex; align-items: center; gap: 8px; }
604
+ .twk-color-mode { appearance: none; font: inherit; font-size: 12px; font-weight: 500; color: var(--twk-text-primary); background-color: var(--twk-surface); background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='15' height='15' viewBox='0 0 24 24' fill='none' stroke='%23888888' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E"); background-repeat: no-repeat; background-position: right 6.25px center; border: 1px solid var(--twk-border); border-radius: calc(var(--twk-radius) - 2px); padding: 4px 26px 4px 10px; line-height: 14px; cursor: pointer; outline: none; } /* 14px, not normal: 24px tall whatever the font, the chips' 14px. The folder's 15px chevron: right 6.25px + its 3.75px side-bearing = 10 */
599
605
  @media (hover: hover) and (pointer: fine) {
600
- .tw-color-mode:hover { background-color: var(--tw-surface-hover); }
606
+ .twk-color-mode:hover { background-color: var(--twk-surface-hover); }
601
607
  }
602
- .tw-color-mode:focus-visible { border-color: var(--tw-border-hover); } /* match the channel inputs' focus cue */
603
- .tw-color-gamut { margin-left: auto; font-size: 12px; font-weight: 500; color: var(--tw-text-tertiary); }
604
- .tw-color-channels { display: grid; grid-template-columns: repeat(3, 1fr); gap: 6px; }
605
- .tw-color-channels--hex { grid-template-columns: 1fr; }
606
- .tw-color-chan { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
607
- .tw-color-chan-label { font-size: 10px; font-weight: 600; letter-spacing: 0.03em; color: var(--tw-text-tertiary); text-transform: uppercase; }
608
- .tw-color-chan-input { width: 100%; min-width: 0; font: inherit; font-size: 12px; font-weight: 500; color: var(--tw-text-label); background: var(--tw-surface); border: 1px solid var(--tw-border); border-radius: calc(var(--tw-radius) - 2px); padding: 5px 6px; outline: none; text-transform: uppercase; }
609
- .tw-color-chan-input:focus { color: var(--tw-text-focus); border-color: var(--tw-border-hover); }
608
+ .twk-color-mode:focus-visible { border-color: var(--twk-border-hover); } /* match the channel inputs' focus cue */
609
+ .twk-color-gamut { margin-left: auto; font-size: 12px; font-weight: 500; color: var(--twk-text-tertiary); }
610
+ .twk-color-channels { display: grid; grid-template-columns: repeat(3, 1fr); gap: 6px; }
611
+ .twk-color-channels--hex { grid-template-columns: 1fr; }
612
+ .twk-color-chan { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
613
+ .twk-color-chan-label { font-size: 10px; font-weight: 600; letter-spacing: 0.03em; color: var(--twk-text-tertiary); text-transform: uppercase; }
614
+ .twk-color-chan-input { width: 100%; min-width: 0; font: inherit; font-size: 12px; font-weight: 500; color: var(--twk-text-label); background: var(--twk-surface); border: 1px solid var(--twk-border); border-radius: calc(var(--twk-radius) - 2px); padding: 5px 6px; outline: none; text-transform: uppercase; }
615
+ .twk-color-chan-input:focus { color: var(--twk-text-focus); border-color: var(--twk-border-hover); }
610
616
 
611
617
  /* The plane and strips: no overflow:hidden, since the thumbs ride the edges. */
612
- .tw-wg-area { position: relative; width: 100%; height: 140px; border-radius: var(--tw-radius); cursor: crosshair; touch-action: none; box-shadow: inset 0 0 0 1px var(--tw-border); }
613
- .tw-wg-canvas { position: absolute; inset: 0; width: 100%; height: 100%; display: block; border-radius: inherit; }
618
+ .twk-wg-area { position: relative; width: 100%; height: 140px; border-radius: var(--twk-radius); cursor: crosshair; touch-action: none; box-shadow: inset 0 0 0 1px var(--twk-border); }
619
+ .twk-wg-canvas { position: absolute; inset: 0; width: 100%; height: 100%; display: block; border-radius: inherit; }
614
620
  /* Thumbs are positioned by left/top, not transform, so the grab scale doesn't lag the drag. */
615
- .tw-wg-thumb { position: absolute; width: 14px; height: 14px; border-radius: 999px; border: 2px solid #fff; box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.45); transform: translate(-50%, -50%); pointer-events: none; transition: transform 0.18s var(--tw-ease-spring), box-shadow 0.18s; }
616
- .tw-wg-hue, .tw-wg-alpha { position: relative; width: 100%; height: 14px; border-radius: 999px; cursor: ew-resize; touch-action: none; box-shadow: inset 0 0 0 1px var(--tw-border); }
617
- .tw-wg-alpha { background: var(--tw-checker); }
618
- .tw-wg-hue-canvas { position: absolute; inset: 0; width: 100%; height: 100%; display: block; border-radius: inherit; }
619
- .tw-wg-hue-thumb { position: absolute; top: 50%; width: 16px; height: 16px; border-radius: 50%; border: 2px solid #fff; box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.45), 0 1px 3px rgba(0, 0, 0, 0.4); transform: translate(-50%, -50%); pointer-events: none; transition: transform 0.18s var(--tw-ease-spring), box-shadow 0.18s; }
620
- .tw-wg-area.is-grabbing .tw-wg-thumb, .tw-wg-hue.is-grabbing .tw-wg-hue-thumb, .tw-wg-alpha.is-grabbing .tw-wg-hue-thumb { transform: translate(-50%, -50%) scale(1.3); box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.45), 0 2px 8px rgba(0, 0, 0, 0.45); }
621
- .tw-wg-alpha-grad { position: absolute; inset: 0; border-radius: inherit; }
621
+ .twk-wg-thumb { position: absolute; width: 14px; height: 14px; border-radius: 999px; border: 2px solid #fff; box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.45); transform: translate(-50%, -50%); pointer-events: none; transition: transform 0.18s var(--twk-ease-spring), box-shadow 0.18s; }
622
+ .twk-wg-hue, .twk-wg-alpha { position: relative; width: 100%; height: 14px; border-radius: 999px; cursor: ew-resize; touch-action: none; box-shadow: inset 0 0 0 1px var(--twk-border); }
623
+ .twk-wg-alpha { background: var(--twk-checker); }
624
+ .twk-wg-hue-canvas { position: absolute; inset: 0; width: 100%; height: 100%; display: block; border-radius: inherit; }
625
+ .twk-wg-hue-thumb { position: absolute; top: 50%; width: 16px; height: 16px; border-radius: 50%; border: 2px solid #fff; box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.45), 0 1px 3px rgba(0, 0, 0, 0.4); transform: translate(-50%, -50%); pointer-events: none; transition: transform 0.18s var(--twk-ease-spring), box-shadow 0.18s; }
626
+ .twk-wg-area.is-grabbing .twk-wg-thumb, .twk-wg-hue.is-grabbing .twk-wg-hue-thumb, .twk-wg-alpha.is-grabbing .twk-wg-hue-thumb { transform: translate(-50%, -50%) scale(1.3); box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.45), 0 2px 8px rgba(0, 0, 0, 0.45); }
627
+ .twk-wg-alpha-grad { position: absolute; inset: 0; border-radius: inherit; }
622
628
 
623
629
  /* ── Gradient — a trigger row that opens a stop bar + picker body ── */
624
- .tw-gradient { position: relative; }
625
- .tw-gradient-preview { width: 72px; background: var(--tw-checker); }
630
+ .twk-gradient { position: relative; }
631
+ .twk-gradient-preview { width: 72px; background: var(--twk-checker); }
626
632
 
627
633
  /* Native selects size to their widest option, so two in one row need tight padding to fit the 236px floor. */
628
- .tw-gradient-pop .tw-color-mode-row { gap: 4px; }
629
- .tw-gradient-pop .tw-color-mode { padding-left: 6px; padding-right: 20px; }
630
- .tw-gradient-bar-row { display: flex; align-items: stretch; gap: 6px; }
631
- .tw-gradient-bar-row > .tw-gradient-bar { flex: 1; min-width: 0; }
632
- .tw-gradient-bar { position: relative; width: 100%; height: 28px; border-radius: var(--tw-radius); box-shadow: inset 0 0 0 1px var(--tw-border); background: var(--tw-checker); }
633
- .tw-gradient-grad { position: absolute; inset: 0; border-radius: inherit; pointer-events: none; }
634
- .tw-gradient-rail { position: absolute; left: 0; right: 0; bottom: -5px; height: 14px; pointer-events: none; }
635
- .tw-gradient-stop { position: absolute; top: 0; width: 13px; height: 13px; padding: 0; transform: translateX(-50%); border-radius: calc(var(--tw-radius) - 4px); background: var(--stop); border: 2px solid var(--tw-dropdown-bg); box-shadow: 0 0 0 1px var(--tw-border-hover), 0 1px 3px rgba(0, 0, 0, 0.4); cursor: ew-resize; touch-action: none; pointer-events: auto; transition: opacity 0.12s ease, transform 0.16s var(--tw-ease-spring), box-shadow 0.16s; }
636
- .tw-gradient-stop[data-sel="true"] { box-shadow: 0 0 0 2px var(--tw-accent, var(--tw-text-focus)), 0 1px 3px rgba(0, 0, 0, 0.4); z-index: 1; }
634
+ .twk-gradient-pop .twk-color-mode-row { gap: 4px; }
635
+ .twk-gradient-pop .twk-color-mode { padding-left: 6px; padding-right: 20px; }
636
+ .twk-gradient-bar-row { display: flex; align-items: stretch; gap: 6px; }
637
+ .twk-gradient-bar-row > .twk-gradient-bar { flex: 1; min-width: 0; }
638
+ .twk-gradient-bar { position: relative; width: 100%; height: 28px; border-radius: var(--twk-radius); box-shadow: inset 0 0 0 1px var(--twk-border); background: var(--twk-checker); }
639
+ .twk-gradient-grad { position: absolute; inset: 0; border-radius: inherit; pointer-events: none; }
640
+ .twk-gradient-rail { position: absolute; left: 0; right: 0; bottom: -5px; height: 14px; pointer-events: none; }
641
+ .twk-gradient-stop { position: absolute; top: 0; width: 13px; height: 13px; padding: 0; transform: translateX(-50%); border-radius: calc(var(--twk-radius) - 4px); background: var(--stop); border: 2px solid var(--twk-dropdown-bg); box-shadow: 0 0 0 1px var(--twk-border-hover), 0 1px 3px rgba(0, 0, 0, 0.4); cursor: ew-resize; touch-action: none; pointer-events: auto; transition: opacity 0.12s ease, transform 0.16s var(--twk-ease-spring), box-shadow 0.16s; }
642
+ .twk-gradient-stop[data-sel="true"] { box-shadow: 0 0 0 2px var(--twk-accent, var(--twk-text-focus)), 0 1px 3px rgba(0, 0, 0, 0.4); z-index: 1; }
637
643
  /* Hit targets: an invisible padded pseudo-element takes the press from ~21px around. */
638
- .tw-gradient-stop::before, .tw-bezier-handle::before { content: ""; position: absolute; inset: -4px; border-radius: inherit; }
644
+ .twk-gradient-stop::before, .twk-bezier-handle::before { content: ""; position: absolute; inset: -4px; border-radius: inherit; }
639
645
  @media (hover: hover) and (pointer: fine) {
640
- .tw-gradient-stop:hover { transform: translateX(-50%) scale(1.2); }
646
+ .twk-gradient-stop:hover { transform: translateX(-50%) scale(1.2); }
641
647
  }
642
648
  /* A stop dragged clear of the bar: release to remove. */
643
- .tw-gradient-stop[data-removing="true"] { opacity: 0.3; transform: translateX(-50%) scale(0.7); }
644
- .tw-gradient-add { display: flex; align-items: center; justify-content: center; flex: none; width: 28px; padding: 0; border: none; border-radius: var(--tw-radius); background: var(--tw-surface); color: var(--tw-text-primary); cursor: pointer; will-change: transform; transition: background 0.15s, transform 0.24s var(--tw-ease-pop); } /* will-change: the Safari pixel-snap fix */
645
- .tw-gradient-add:active { transform: scale(var(--tw-press-icon)); transition: background 0.15s, transform 0.08s ease-out; }
646
- .tw-gradient-add svg { width: 16px; height: 16px; display: block; will-change: transform; transition: transform 0.2s var(--tw-ease-pop); }
649
+ .twk-gradient-stop[data-removing="true"] { opacity: 0.3; transform: translateX(-50%) scale(0.7); }
650
+ .twk-gradient-add { display: flex; align-items: center; justify-content: center; flex: none; width: 28px; padding: 0; border: none; border-radius: var(--twk-radius); background: var(--twk-surface); color: var(--twk-text-primary); cursor: pointer; will-change: transform; transition: background 0.15s, transform 0.24s var(--twk-ease-pop); } /* will-change: the Safari pixel-snap fix */
651
+ .twk-gradient-add:active { transform: scale(var(--twk-press-icon)); transition: background 0.15s, transform 0.08s ease-out; }
652
+ .twk-gradient-add svg { width: 16px; height: 16px; display: block; will-change: transform; transition: transform 0.2s var(--twk-ease-pop); }
647
653
  @media (hover: hover) and (pointer: fine) {
648
- .tw-gradient-add:hover { background: var(--tw-surface-hover); }
649
- .tw-gradient-add:hover svg { transform: scale(1.2); }
654
+ .twk-gradient-add:hover { background: var(--twk-surface-hover); }
655
+ .twk-gradient-add:hover svg { transform: scale(1.2); }
650
656
  }
651
657
 
652
658
  /* ── Text + number rows (string / number blades) ── */
653
- .tw-text, .tw-num {
659
+ .twk-text, .twk-num {
654
660
  flex: 1; min-width: 0; font: inherit; font-size: 12px; font-weight: 500;
655
- color: var(--tw-text-label); background: transparent; border: none; outline: none;
661
+ color: var(--twk-text-label); background: transparent; border: none; outline: none;
656
662
  text-align: right; cursor: text; padding: 0;
657
- border-radius: calc(var(--tw-radius) - 4px); /* rounds the focus ring (outline follows radius) */
663
+ border-radius: calc(var(--twk-radius) - 4px); /* rounds the focus ring (outline follows radius) */
658
664
  }
659
- .tw-text:focus, .tw-num:focus { color: var(--tw-text-focus); }
660
- .tw-text::placeholder { color: var(--tw-text-tertiary); }
661
- .tw-row-multiline { position: relative; flex-direction: column; align-items: stretch; height: auto; gap: 4px; padding: 7.5px 10px 10px 12px; } /* top: the label's text 9px down, as a 32px row's; bottom 10 > the 8px radius keeps the grip clear */
662
- .tw-textarea { flex: none; width: 100%; resize: vertical; min-height: 3em; padding: 0; line-height: 1.45; text-align: left; }
665
+ .twk-text:focus, .twk-num:focus { color: var(--twk-text-focus); }
666
+ .twk-text::placeholder { color: var(--twk-text-tertiary); }
667
+ .twk-row-multiline { position: relative; flex-direction: column; align-items: stretch; height: auto; gap: 4px; padding: 7.5px 10px 10px 12px; } /* top: the label's text 9px down, as a 32px row's; bottom 10 > the 8px radius keeps the grip clear */
668
+ .twk-textarea { flex: none; width: 100%; resize: vertical; min-height: 3em; padding: 0; line-height: 1.45; text-align: left; }
663
669
  /* The native resizer grip is invisible on mobile Safari, so draw one; a mask lets it
664
670
  * take the token color in both schemes. */
665
- .tw-textarea::-webkit-resizer { background: transparent; }
671
+ .twk-textarea::-webkit-resizer { background: transparent; }
666
672
  /* Text past the field's edges fades rather than cuts (basic.ts clip()). */
667
- .tw-textarea:is(.is-clip-top, .is-clip-bottom) { --tw-clip-top: 0px; --tw-clip-bottom: 0px; -webkit-mask-image: linear-gradient(to bottom, transparent, #000 var(--tw-clip-top), #000 calc(100% - var(--tw-clip-bottom)), transparent); mask-image: linear-gradient(to bottom, transparent, #000 var(--tw-clip-top), #000 calc(100% - var(--tw-clip-bottom)), transparent); }
668
- .tw-textarea.is-clip-top { --tw-clip-top: 14px; }
669
- .tw-textarea.is-clip-bottom { --tw-clip-bottom: 14px; }
670
- .tw-row-multiline::after {
673
+ .twk-textarea:is(.is-clip-top, .is-clip-bottom) { --twk-clip-top: 0px; --twk-clip-bottom: 0px; -webkit-mask-image: linear-gradient(to bottom, transparent, #000 var(--twk-clip-top), #000 calc(100% - var(--twk-clip-bottom)), transparent); mask-image: linear-gradient(to bottom, transparent, #000 var(--twk-clip-top), #000 calc(100% - var(--twk-clip-bottom)), transparent); }
674
+ .twk-textarea.is-clip-top { --twk-clip-top: 14px; }
675
+ .twk-textarea.is-clip-bottom { --twk-clip-bottom: 14px; }
676
+ .twk-row-multiline::after {
671
677
  content: ""; position: absolute; right: 8px; bottom: 8px; width: 11px; height: 11px;
672
- pointer-events: none; background-color: var(--tw-text-tertiary);
673
- --tw-grip: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath d='M11 3 3 11M11 7 7 11' stroke='%23000' stroke-width='1.5' stroke-linecap='round'/%3E%3C/svg%3E");
674
- -webkit-mask: var(--tw-grip) right bottom / contain no-repeat; mask: var(--tw-grip) right bottom / contain no-repeat;
678
+ pointer-events: none; background-color: var(--twk-text-tertiary);
679
+ --twk-grip: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath d='M11 3 3 11M11 7 7 11' stroke='%23000' stroke-width='1.5' stroke-linecap='round'/%3E%3C/svg%3E");
680
+ -webkit-mask: var(--twk-grip) right bottom / contain no-repeat; mask: var(--twk-grip) right bottom / contain no-repeat;
675
681
  }
676
682
 
677
683
  /* ── Image — a trigger row that is also the drop zone; with options, a gallery: the plot's stack of a flush label, the tiles and the kit's button ── */
678
- .tw-image { position: relative; }
679
- .tw-image-input { display: none; }
680
- .tw-image-text, .tw-image-note { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
681
- .tw-image-note:empty { display: none; }
682
- .tw-image-note:not(:empty) + :is(.tw-image-text, .tw-trigger-value) { display: none; } /* a refused file's reason takes the text's place for a moment */
683
- .tw-image-thumb { width: 24px; background: var(--tw-base) center / cover no-repeat; } /* 24 × 18: the tiles' 4:3 */
684
- .tw-image-thumb[data-set="false"] { background: var(--tw-checker); }
684
+ .twk-image { position: relative; }
685
+ .twk-image-input { display: none; }
686
+ .twk-image-text, .twk-image-note { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
687
+ .twk-image-note:empty { display: none; }
688
+ .twk-image-note:not(:empty) + :is(.twk-image-text, .twk-trigger-value) { display: none; } /* a refused file's reason takes the text's place for a moment */
689
+ .twk-image-thumb { width: 24px; background: var(--twk-base) center / cover no-repeat; } /* 24 × 18: the tiles' 4:3 */
690
+ .twk-image-thumb[data-set="false"] { background: var(--twk-checker); }
685
691
  /* JS toggles [data-over] on the trigger, or on the gallery root, while a file is dragged over it. */
686
- .tw-image .tw-trigger[data-over="true"], .tw-image-gallery[data-over="true"] .tw-image-upload { background: var(--tw-surface-hover); outline: 1.5px dashed var(--tw-border-hover); outline-offset: -2px; }
687
- .tw-image .tw-trigger[data-over="true"] .tw-image-thumb { transform: scale(1.14); }
688
- .tw-image-gallery { display: flex; flex-direction: column; gap: 6px; }
692
+ .twk-image .twk-trigger[data-over="true"], .twk-image-gallery[data-over="true"] .twk-image-upload { background: var(--twk-surface-hover); outline: 1.5px dashed var(--twk-border-hover); outline-offset: -2px; }
693
+ .twk-image .twk-trigger[data-over="true"] .twk-image-thumb { transform: scale(1.14); }
694
+ .twk-image-gallery { display: flex; flex-direction: column; gap: 6px; }
689
695
  /* Under the tiles: the upload button, then Clear, as tall as the button. They fold the way a folder does, on its curve:
690
696
  * Clear folds sideways when there's nothing to clear and the upload button eases into its room; a foot with nothing in
691
697
  * it folds up, and the pane eases shorter. The foot's own space is part of the pane's drag edge; its buttons still take
692
698
  * their own presses. The focus rings are inset, so the clipping leaves them whole. */
693
- .tw-image-foot { display: grid; grid-template-rows: 1fr; pointer-events: none; transition: grid-template-rows 0.35s var(--tw-ease-fold), margin-top 0.35s var(--tw-ease-fold); }
694
- .tw-image-foot[data-open="false"] { grid-template-rows: 0fr; margin-top: -6px; } /* -6px takes back the pane's gap above it */
695
- .tw-image-foot-row { display: flex; align-items: stretch; justify-content: flex-end; min-height: 0; overflow: hidden; }
696
- .tw-image-foot-row > * { pointer-events: auto; }
697
- .tw-image-foot .tw-image-upload { flex: 1; min-width: 0; }
698
- .tw-image-clear-slot { display: grid; grid-template-columns: 1fr; margin-left: 6px; overflow: hidden; transition: grid-template-columns 0.35s var(--tw-ease-fold), margin-left 0.35s var(--tw-ease-fold), opacity 0.35s var(--tw-ease-fold); } /* the fade too, as a folder's content */
699
- .tw-image-clear-slot[data-open="false"] { grid-template-columns: 0fr; margin-left: 0; opacity: 0; }
700
- .tw-image-clear-slot .tw-image-clear { height: auto; min-height: 24px; min-width: 0; padding: 0 10px; white-space: nowrap; transition: padding 0.35s var(--tw-ease-fold), background 0.15s, color 0.15s, transform 0.24s var(--tw-ease-pop); }
701
- .tw-image-clear-slot[data-open="false"] .tw-image-clear { padding: 0; } /* its padding folds too, or 20px of it would stay */
702
- .tw-image-grid { display: grid; grid-template-columns: repeat(var(--tw-img-cols, 2), minmax(0, 1fr)); gap: 6px; }
699
+ .twk-image-foot { display: grid; grid-template-rows: 1fr; pointer-events: none; transition: grid-template-rows 0.35s var(--twk-ease-fold), margin-top 0.35s var(--twk-ease-fold); }
700
+ .twk-image-foot[data-open="false"] { grid-template-rows: 0fr; margin-top: -6px; } /* -6px takes back the pane's gap above it */
701
+ .twk-image-foot-row { display: flex; align-items: stretch; justify-content: flex-end; min-height: 0; overflow: hidden; }
702
+ .twk-image-foot-row > * { pointer-events: auto; }
703
+ .twk-image-foot .twk-image-upload { flex: 1; min-width: 0; }
704
+ .twk-image-clear-slot { display: grid; grid-template-columns: 1fr; margin-left: 6px; overflow: hidden; transition: grid-template-columns 0.35s var(--twk-ease-fold), margin-left 0.35s var(--twk-ease-fold), opacity 0.35s var(--twk-ease-fold); } /* the fade too, as a folder's content */
705
+ .twk-image-clear-slot[data-open="false"] { grid-template-columns: 0fr; margin-left: 0; opacity: 0; }
706
+ .twk-image-clear-slot .twk-image-clear { height: auto; min-height: 24px; min-width: 0; padding: 0 10px; white-space: nowrap; transition: padding 0.35s var(--twk-ease-fold), background 0.15s, color 0.15s, transform 0.24s var(--twk-ease-pop); }
707
+ .twk-image-clear-slot[data-open="false"] .twk-image-clear { padding: 0; } /* its padding folds too, or 20px of it would stay */
708
+ .twk-image-grid { display: grid; grid-template-columns: repeat(var(--twk-img-cols, 2), minmax(0, 1fr)); gap: 6px; }
703
709
  /* A tile is its image; a hairline ring set off by a gap marks the chosen one, so the picture stays readable.
704
710
  * The gap is outline-offset, which shows the panel behind it. */
705
- .tw-image-tile {
706
- aspect-ratio: 4 / 3; padding: 0; background: var(--tw-surface) center / cover no-repeat;
707
- border: none; border-radius: calc(var(--tw-radius) - 2px); box-shadow: inset 0 0 0 1px var(--tw-border-hover);
708
- cursor: pointer; transition: transform 0.24s var(--tw-ease-pop), box-shadow 0.15s;
711
+ .twk-image-tile {
712
+ aspect-ratio: 4 / 3; padding: 0; background: var(--twk-surface) center / cover no-repeat;
713
+ border: none; border-radius: calc(var(--twk-radius) - 2px); box-shadow: inset 0 0 0 1px var(--twk-border-hover);
714
+ cursor: pointer; transition: transform 0.24s var(--twk-ease-pop), box-shadow 0.15s;
709
715
  }
710
- .tw-image-tile:active { transform: scale(var(--tw-press-chip)); transition: transform 0.08s ease-out; }
711
- .tw-image-tile[data-active="true"] { outline: 1.5px solid var(--tw-accent, var(--tw-text-focus)); outline-offset: 1px; }
716
+ .twk-image-tile:active { transform: scale(var(--twk-press-chip)); transition: transform 0.08s ease-out; }
717
+ .twk-image-tile[data-active="true"] { outline: 1.5px solid var(--twk-accent, var(--twk-text-focus)); outline-offset: 1px; }
712
718
  /* The kit's inner focus ring, after the chosen tile's rule so it wins there too. An outline, not a shadow: the hover
713
719
  * hairline is a shadow and would replace it, a host's own button:focus-visible outline would add a second ring, and
714
720
  * forced colors drop shadows. */
715
- .tw-image-tile:focus-visible { outline: 2px solid var(--tw-accent, var(--tw-text-focus)); outline-offset: -2px; }
716
- .tw-image-upload { gap: 6px; } /* a .tw-button, plus the icon's gap */
717
- .tw-image-upload svg { width: 14px; height: 14px; flex: none; }
721
+ .twk-image-tile:focus-visible { outline: 2px solid var(--twk-accent, var(--twk-text-focus)); outline-offset: -2px; }
722
+ .twk-image-upload { gap: 6px; } /* a .twk-button, plus the icon's gap */
723
+ .twk-image-upload svg { width: 14px; height: 14px; flex: none; }
718
724
  @media (hover: hover) and (pointer: fine) {
719
- .tw-image-tile:hover { box-shadow: inset 0 0 0 1px var(--tw-text-tertiary); } /* the hairline lights, nothing moves; the chosen tile too: on, and about to be pressed, are two states */
725
+ .twk-image-tile:hover { box-shadow: inset 0 0 0 1px var(--twk-text-tertiary); } /* the hairline lights, nothing moves; the chosen tile too: on, and about to be pressed, are two states */
720
726
  }
721
727
 
722
728
  /* ── FPS graph (monitor blade) ── */
723
- .tw-fps {
729
+ .twk-fps {
724
730
  display: grid; grid-template-columns: 1fr auto; align-items: center; gap: 0 8px;
725
- background: var(--tw-surface); border-radius: var(--tw-radius); padding: 9px 10px 10px 12px; /* the row idiom: 12 left / 10 right; the label's text 9px down */
731
+ background: var(--twk-surface); border-radius: var(--twk-radius); padding: 9px 10px 10px 12px; /* the row idiom: 12 left / 10 right; the label's text 9px down */
726
732
  line-height: 14px; /* not normal: a graphless monitor is 33px whatever the font, the chips' 14px */
727
733
  }
728
- .tw-fps-label { font-size: 12px; font-weight: 500; color: var(--tw-text-label); }
729
- .tw-fps-val { font-size: 12px; color: var(--tw-text-primary); text-align: right; }
730
- .tw-fps-canvas { grid-column: 1 / -1; width: 100%; height: 40px; display: block; margin-top: 6px; }
734
+ .twk-fps-label { font-size: 12px; font-weight: 500; color: var(--twk-text-label); }
735
+ .twk-fps-val { font-size: 12px; color: var(--twk-text-primary); text-align: right; }
736
+ .twk-fps-canvas { grid-column: 1 / -1; width: 100%; height: 40px; display: block; margin-top: 6px; }
731
737
  /* Monitor string buffer — the last N values, newest at the bottom. */
732
- .tw-monitor-buffer { grid-column: 1 / -1; margin: 6px 0 0; height: calc(var(--tw-monitor-rows, 4) * 1.45em); overflow: hidden; font-family: var(--tw-font-sans); font-variant-numeric: tabular-nums; font-size: 11px; line-height: 1.45; color: var(--tw-text-secondary); white-space: pre; word-break: break-all; }
738
+ .twk-monitor-buffer { grid-column: 1 / -1; margin: 6px 0 0; height: calc(var(--twk-monitor-rows, 4) * 1.45em); overflow: hidden; font-family: var(--twk-font-sans); font-variant-numeric: tabular-nums; font-size: 11px; line-height: 1.45; color: var(--twk-text-secondary); white-space: pre; word-break: break-all; }
733
739
 
734
740
  /* ── Number grab handle (drag to scrub) ── */
735
- .tw-num-wrap { display: flex; align-items: center; gap: 4px; }
736
- .tw-slider-unit { color: var(--tw-text-tertiary); } /* the slider readout's unit ("24 px") */
741
+ .twk-num-wrap { display: flex; align-items: center; gap: 4px; }
742
+ .twk-slider-unit { color: var(--twk-text-tertiary); } /* the slider readout's unit ("24 px") */
737
743
  /* The unit shares the input's size and line box, so centering lands both on one baseline. */
738
- .tw-num-unit { flex-shrink: 0; font-size: 12px; line-height: normal; color: var(--tw-text-tertiary); padding-right: 2px; user-select: none; -webkit-user-select: none; }
739
- .tw-num-wrap .tw-num { flex: 0 0 auto; width: 6ch; text-overflow: ellipsis; } /* a wide value shows it is cut */
740
- .tw-row > .tw-num-wrap .tw-num { width: 9ch; } /* the Number row has room a field grid lacks */
741
- .tw-num-grab { display: flex; align-items: center; justify-content: center; width: 12px; height: 18px; flex-shrink: 0; color: var(--tw-text-tertiary); cursor: ew-resize; touch-action: none; transition: color 0.15s, transform 0.18s var(--tw-ease-spring); }
742
- .tw-num-grab svg { width: 12px; height: 12px; display: block; }
743
- .tw-num-grab.is-dragging { color: var(--tw-text-primary); transform: scale(1.2); }
744
+ .twk-num-unit { flex-shrink: 0; font-size: 12px; line-height: normal; color: var(--twk-text-tertiary); padding-right: 2px; user-select: none; -webkit-user-select: none; }
745
+ .twk-num-wrap .twk-num { flex: 0 0 auto; width: 6ch; text-overflow: ellipsis; } /* a wide value shows it is cut */
746
+ .twk-row > .twk-num-wrap .twk-num { width: 9ch; } /* the Number row has room a field grid lacks */
747
+ .twk-num-grab { display: flex; align-items: center; justify-content: center; width: 12px; height: 18px; flex-shrink: 0; color: var(--twk-text-tertiary); cursor: ew-resize; touch-action: none; transition: color 0.15s, transform 0.18s var(--twk-ease-spring); }
748
+ .twk-num-grab svg { width: 12px; height: 12px; display: block; }
749
+ .twk-num-grab.is-dragging { color: var(--twk-text-primary); transform: scale(1.2); }
744
750
  @media (hover: hover) and (pointer: fine) {
745
- .tw-num-grab:hover { color: var(--tw-text-primary); transform: scale(1.2); }
751
+ .twk-num-grab:hover { color: var(--twk-text-primary); transform: scale(1.2); }
746
752
  }
747
753
  /* Grab guide — portaled to <body> while scrubbing a number field. */
748
- .tw-grab-guide { position: fixed; inset: 0; z-index: 100001; pointer-events: none; } /* tops the kit's z ladder */
754
+ .twk-grab-guide { position: fixed; inset: 0; z-index: 100001; pointer-events: none; } /* tops the kit's z ladder */
749
755
  /* round dots with a controllable gap (`dotted` packs them too tightly) */
750
- .tw-grab-line { position: fixed; height: 2px; border: 0; transform: translateY(-50%); background: radial-gradient(circle closest-side, var(--tw-text-label) 99%, transparent) 0 50% / var(--tw-grab-gap, 7px) 2px repeat-x; }
751
- .tw-grab-dot { position: fixed; width: 5px; height: 5px; border-radius: 50%; background: var(--tw-text-label); transform: translate(-50%, -50%); }
752
- .tw-grab-bubble { position: fixed; transform: translate(-50%, -100%); padding: 2px 7px; border-radius: calc(var(--tw-radius) - 2px); background: var(--tw-dropdown-bg); border: 1px solid var(--tw-border); box-shadow: var(--tw-shadow-dropdown); color: var(--tw-text-primary); font-size: 11px; font-weight: 500; white-space: nowrap; transform-origin: bottom center; animation: tw-bubble-in 0.18s var(--tw-ease-pop); }
753
- @keyframes tw-bubble-in { from { opacity: 0; transform: translate(-50%, -100%) scale(0.8); } to { opacity: 1; transform: translate(-50%, -100%) scale(1); } }
754
- .tw-grab-arrow { position: fixed; width: 0; height: 0; border-top: 4px solid transparent; border-bottom: 4px solid transparent; border-left: 7px solid var(--tw-text-label); }
756
+ .twk-grab-line { position: fixed; height: 2px; border: 0; transform: translateY(-50%); background: radial-gradient(circle closest-side, var(--twk-text-label) 99%, transparent) 0 50% / var(--twk-grab-gap, 7px) 2px repeat-x; }
757
+ .twk-grab-dot { position: fixed; width: 5px; height: 5px; border-radius: 50%; background: var(--twk-text-label); transform: translate(-50%, -50%); }
758
+ .twk-grab-bubble { position: fixed; transform: translate(-50%, -100%); padding: 2px 7px; border-radius: calc(var(--twk-radius) - 2px); background: var(--twk-dropdown-bg); border: 1px solid var(--twk-border); box-shadow: var(--twk-shadow-dropdown); color: var(--twk-text-primary); font-size: 11px; font-weight: 500; white-space: nowrap; transform-origin: bottom center; animation: twk-bubble-in 0.18s var(--twk-ease-pop); }
759
+ @keyframes twk-bubble-in { from { opacity: 0; transform: translate(-50%, -100%) scale(0.8); } to { opacity: 1; transform: translate(-50%, -100%) scale(1); } }
760
+ .twk-grab-arrow { position: fixed; width: 0; height: 0; border-top: 4px solid transparent; border-bottom: 4px solid transparent; border-left: 7px solid var(--twk-text-label); }
755
761
 
756
762
  /* ── Folder — collapsible titled group ── */
757
763
  /* Consecutive folders share one rule; the first and last drop their outer rule. */
758
- .tw-folder { display: flex; flex-direction: column; border-top: 1px solid var(--tw-border); border-bottom: 1px solid var(--tw-border); }
759
- .tw-folder + .tw-folder { border-top: none; margin-top: -6px; } /* cancel the 6px stack gap so the rules merge */
760
- .tw-folder .tw-folder { margin-left: 10px; }
761
- .tw-controls > .tw-folder:first-child { border-top: none; }
762
- .tw-folder:last-child { border-bottom: none; }
763
- .tw-folder-header { display: flex; align-items: center; justify-content: space-between; gap: 6px; width: 100%; height: var(--tw-row-height); padding: 0; background: none; border: none; cursor: pointer; color: inherit; font: inherit; text-align: left; }
764
- .tw-folder-title { font-size: 12px; font-weight: 600; color: var(--tw-text-section); letter-spacing: -0.01em; transition: color 0.15s; }
764
+ .twk-folder { display: flex; flex-direction: column; border-top: 1px solid var(--twk-border); border-bottom: 1px solid var(--twk-border); }
765
+ .twk-folder + .twk-folder { border-top: none; margin-top: -6px; } /* cancel the 6px stack gap so the rules merge */
766
+ .twk-folder .twk-folder { margin-left: 10px; }
767
+ .twk-controls > .twk-folder:first-child { border-top: none; }
768
+ .twk-folder:last-child { border-bottom: none; }
769
+ .twk-folder-header { display: flex; align-items: center; justify-content: space-between; gap: 6px; width: 100%; height: var(--twk-row-height); padding: 0; background: none; border: none; cursor: pointer; color: inherit; font: inherit; text-align: left; }
770
+ .twk-folder-title { font-size: 12px; font-weight: 600; color: var(--twk-text-section); letter-spacing: -0.01em; transition: color 0.15s; }
765
771
  @media (hover: hover) and (pointer: fine) {
766
- .tw-folder-header:hover .tw-folder-title { color: var(--tw-text-primary); }
767
- .tw-folder-header:hover .tw-chevron { color: var(--tw-text-primary); }
772
+ .twk-folder-header:hover .twk-folder-title { color: var(--twk-text-primary); }
773
+ .twk-folder-header:hover .twk-chevron { color: var(--twk-text-primary); }
768
774
  }
769
775
  /* The 15px box carries 3.75px of side-bearing; pull it out so the glyph lands 2px in. The
770
776
  * caret rides the body's 0.35s --fold clock so it turns in step with the fade. */
771
- .tw-folder-header .tw-chevron { color: var(--tw-text-section); opacity: 0.6; margin-right: -1.75px; transition: transform 0.35s var(--tw-ease-fold), color 0.15s; }
772
- .tw-folder.is-collapsed .tw-folder-header .tw-chevron { transform: rotate(180deg); }
777
+ .twk-folder-header .twk-chevron { color: var(--twk-text-section); opacity: 0.6; margin-right: -1.75px; transition: transform 0.35s var(--twk-ease-fold), color 0.15s; }
778
+ .twk-folder.is-collapsed .twk-folder-header .twk-chevron { transform: rotate(180deg); }
773
779
  /* contain: layout isolates the fold's per-frame re-layout from the rest of the panel. */
774
- .tw-folder-body { display: grid; grid-template-rows: 1fr; transition: grid-template-rows 0.35s var(--tw-ease-fold); contain: layout; }
775
- .tw-folder.is-collapsed .tw-folder-body { grid-template-rows: 0fr; }
776
- /* Height and opacity share the --fold clock; y-clip only, as on .tw-body > .tw-controls. */
777
- .tw-folder-body > .tw-controls { overflow-x: visible; overflow-y: clip; min-height: 0; min-width: 0; padding: 3px 0 8px; margin-top: -3px; opacity: 1; /* top: the pick band's clip edge, as on .tw-body */ transition: opacity 0.35s var(--tw-ease-fold), padding-bottom 0.35s var(--tw-ease-fold), margin-bottom 0.35s var(--tw-ease-fold); }
780
+ .twk-folder-body { display: grid; grid-template-rows: 1fr; transition: grid-template-rows 0.35s var(--twk-ease-fold); contain: layout; }
781
+ .twk-folder.is-collapsed .twk-folder-body { grid-template-rows: 0fr; }
782
+ /* Height and opacity share the --fold clock; y-clip only, as on .twk-body > .twk-controls. */
783
+ .twk-folder-body > .twk-controls { overflow-x: visible; overflow-y: clip; min-height: 0; min-width: 0; padding: 3px 0 8px; margin-top: -3px; opacity: 1; /* top: the pick band's clip edge, as on .twk-body */ transition: opacity 0.35s var(--twk-ease-fold), padding-bottom 0.35s var(--twk-ease-fold), margin-bottom 0.35s var(--twk-ease-fold); }
778
784
  /* Collapse the padding too, so a collapsed group is exactly its header. */
779
- .tw-folder.is-collapsed .tw-folder-body > .tw-controls { opacity: 0; padding-bottom: 0; }
785
+ .twk-folder.is-collapsed .twk-folder-body > .twk-controls { opacity: 0; padding-bottom: 0; }
780
786
  /* The 8px only clears the bottom hairline, which the last folder drops. */
781
- .tw-folder:last-child .tw-folder-body > .tw-controls { padding-bottom: 0; }
782
- .tw-folder:last-child:not(.is-collapsed) .tw-folder-body > .tw-controls { padding-bottom: 3px; margin-bottom: -3px; } /* the pick band's clip edge; both fold away together */
787
+ .twk-folder:last-child .twk-folder-body > .twk-controls { padding-bottom: 0; }
788
+ .twk-folder:last-child:not(.is-collapsed) .twk-folder-body > .twk-controls { padding-bottom: 3px; margin-bottom: -3px; } /* the pick band's clip edge; both fold away together */
783
789
  /* In a flex column the rows would shrink to fit the folding track; keep their height. */
784
- .tw-body > .tw-controls > *, .tw-folder-body > .tw-controls > * { flex-shrink: 0; }
790
+ .twk-body > .twk-controls > *, .twk-folder-body > .twk-controls > * { flex-shrink: 0; }
785
791
 
786
792
  /* ── Shadow, sides, motion and spring ── */
787
793
  /* The shadow control (shadow.ts). */
788
- .tw-shadow-chip, .tw-motion-chip { width: 26px; background: var(--tw-base); overflow: hidden; } /* the two drawn chips: a tile with the shadow, a curve */
789
- .tw-shadow-chip { display: flex; align-items: center; justify-content: center; }
790
- .tw-shadow-chip-tile { width: 12px; height: 8px; border-radius: 2px; background: var(--tw-dropdown-bg); }
791
- .tw-shadow-stage { display: flex; align-items: center; justify-content: center; height: 84px; border-radius: var(--tw-radius); background: var(--tw-base); overflow: hidden; }
792
- .tw-shadow-card { width: 64px; height: 40px; border-radius: 9px; background: var(--tw-dropdown-bg); }
793
- .tw-shadow-color { width: 100%; } /* the color row in the pop discloses the picker body */
794
- .tw-shadow-color .tw-trigger-right, .tw-shadow-color .tw-trigger-value { max-width: none; } /* the row has the pop's full width */
795
- .tw-shadow-pop .tw-color-body[hidden] { display: none; } /* display: flex would beat the hidden attribute */
796
- .tw-shadow-chevron { flex-shrink: 0; width: 12px; height: 12px; color: var(--tw-text-tertiary); transition: transform 0.2s var(--tw-ease-pop); }
797
- .tw-shadow-color[aria-expanded="true"] .tw-shadow-chevron { transform: rotate(180deg); }
798
- .tw-shadow-pop .tw-color-body { margin-top: 2px; }
794
+ .twk-shadow-chip, .twk-motion-chip { width: 26px; background: var(--twk-base); overflow: hidden; } /* the two drawn chips: a tile with the shadow, a curve */
795
+ .twk-shadow-chip { display: flex; align-items: center; justify-content: center; }
796
+ .twk-shadow-chip-tile { width: 12px; height: 8px; border-radius: 2px; background: var(--twk-dropdown-bg); }
797
+ .twk-shadow-stage { display: flex; align-items: center; justify-content: center; height: 84px; border-radius: var(--twk-radius); background: var(--twk-base); overflow: hidden; }
798
+ .twk-shadow-card { width: 64px; height: 40px; border-radius: 9px; background: var(--twk-dropdown-bg); }
799
+ .twk-shadow-color { width: 100%; } /* the color row in the pop discloses the picker body */
800
+ .twk-shadow-color .twk-trigger-right, .twk-shadow-color .twk-trigger-value { max-width: none; } /* the row has the pop's full width */
801
+ .twk-shadow-pop .twk-color-body[hidden] { display: none; } /* display: flex would beat the hidden attribute */
802
+ .twk-shadow-chevron { flex-shrink: 0; width: 12px; height: 12px; color: var(--twk-text-tertiary); transition: transform 0.2s var(--twk-ease-pop); }
803
+ .twk-shadow-color[aria-expanded="true"] .twk-shadow-chevron { transform: rotate(180deg); }
804
+ .twk-shadow-pop .twk-color-body { margin-top: 2px; }
799
805
  /* The sides control (sides.ts). */
800
- .tw-sides-link { flex-shrink: 0; display: flex; align-items: center; justify-content: center; width: 22px; height: 22px; padding: 0; color: var(--tw-text-tertiary); background: transparent; border: none; border-radius: calc(var(--tw-radius) - 3px); cursor: pointer; transition: color 0.15s, background 0.15s, transform 0.24s var(--tw-ease-pop); }
801
- .tw-sides-link svg { width: 14px; height: 14px; }
802
- .tw-sides-link[aria-pressed="true"] { color: var(--tw-accent, var(--tw-text-primary)); }
806
+ .twk-sides-link { flex-shrink: 0; display: flex; align-items: center; justify-content: center; width: 22px; height: 22px; padding: 0; color: var(--twk-text-tertiary); background: transparent; border: none; border-radius: calc(var(--twk-radius) - 3px); cursor: pointer; transition: color 0.15s, background 0.15s, transform 0.24s var(--twk-ease-pop); }
807
+ .twk-sides-link svg { width: 14px; height: 14px; }
808
+ .twk-sides-link[aria-pressed="true"] { color: var(--twk-accent, var(--twk-text-primary)); }
803
809
  @media (hover: hover) and (pointer: fine) {
804
- .tw-sides-link:hover { background: var(--tw-surface-hover); color: var(--tw-text-primary); }
810
+ .twk-sides-link:hover { background: var(--twk-surface-hover); color: var(--twk-text-primary); }
805
811
  }
806
- .tw-sides-link:active { transform: scale(var(--tw-press-icon)); transition: color 0.15s, background 0.15s, transform 0.08s ease-out; }
807
- .tw-sides > .tw-row .tw-num-wrap { margin-left: auto; } /* the link is last in the row too, at the pixels the head's link takes */
808
- .tw-sides.is-linked .tw-sides-multi, .tw-sides:not(.is-linked) > .tw-row { display: none; }
812
+ .twk-sides-link:active { transform: scale(var(--twk-press-icon)); transition: color 0.15s, background 0.15s, transform 0.08s ease-out; }
813
+ .twk-sides > .twk-row .twk-num-wrap { margin-left: auto; } /* the link is last in the row too, at the pixels the head's link takes */
814
+ .twk-sides.is-linked .twk-sides-multi, .twk-sides:not(.is-linked) > .twk-row { display: none; }
809
815
  /* The open block: a head with the row's exact geometry (so label and link stay put on toggle), then four fields in a
810
816
  * line. Four across leaves ~38px inside a field — room for the grab and three digits, not the unit, which the head
811
817
  * shows once after the readout, ending 14px from the link like the row's "px" does. */
812
- .tw-sides-multi { display: flex; flex-direction: column; gap: 6px; padding: 0 10px 10px 12px; background: var(--tw-surface); border-radius: var(--tw-radius); }
813
- .tw-sides-head { display: flex; align-items: center; gap: 12px; height: var(--tw-row-height); }
814
- .tw-fields.tw-shadow-fields { grid-auto-flow: row; grid-template-columns: 1fr 1fr; } /* 2×2: four across left one digit showing */
815
- .tw-sides-multi .tw-fields { grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 4px; direction: ltr; } /* the places are physical; an rtl host must not mirror them */
816
- .tw-sides-multi .tw-field .tw-num-wrap { padding: 3px 5px; }
817
- .tw-sides-multi .tw-field .tw-num-unit { display: none; }
818
- .tw-sides-glyph { display: flex; align-items: center; height: 12px; } /* the glyph sits in a text label's line */
819
- .tw-sides-glyph svg { width: 12px; height: 12px; }
820
- .tw-sides-glyph[data-at="top-right"] svg, .tw-sides-glyph[data-at="right"] svg { transform: rotate(90deg); }
821
- .tw-sides-glyph[data-at="bottom-right"] svg, .tw-sides-glyph[data-at="bottom"] svg { transform: rotate(180deg); }
822
- .tw-sides-glyph[data-at="bottom-left"] svg, .tw-sides-glyph[data-at="left"] svg { transform: rotate(270deg); }
818
+ .twk-sides-multi { display: flex; flex-direction: column; gap: 6px; padding: 0 10px 10px 12px; background: var(--twk-surface); border-radius: var(--twk-radius); }
819
+ .twk-sides-head { display: flex; align-items: center; gap: 12px; height: var(--twk-row-height); }
820
+ .twk-fields.twk-shadow-fields { grid-auto-flow: row; grid-template-columns: 1fr 1fr; } /* 2×2: four across left one digit showing */
821
+ .twk-sides-multi .twk-fields { grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 4px; direction: ltr; } /* the places are physical; an rtl host must not mirror them */
822
+ .twk-sides-multi .twk-field .twk-num-wrap { padding: 3px 5px; }
823
+ .twk-sides-multi .twk-field .twk-num-unit { display: none; }
824
+ .twk-sides-glyph { display: flex; align-items: center; height: 12px; } /* the glyph sits in a text label's line */
825
+ .twk-sides-glyph svg { width: 12px; height: 12px; }
826
+ .twk-sides-glyph[data-at="top-right"] svg, .twk-sides-glyph[data-at="right"] svg { transform: rotate(90deg); }
827
+ .twk-sides-glyph[data-at="bottom-right"] svg, .twk-sides-glyph[data-at="bottom"] svg { transform: rotate(180deg); }
828
+ .twk-sides-glyph[data-at="bottom-left"] svg, .twk-sides-glyph[data-at="left"] svg { transform: rotate(270deg); }
823
829
  /* The readout is set like the row's field — the digits' tone and weight, the unit's 4px gap, weight and inset — so a
824
830
  * toggle swaps one for the other without a glyph moving or changing tone. */
825
- .tw-sides-sum { margin-left: auto; font-size: 12px; font-weight: 500; color: var(--tw-text-label); word-spacing: 0.1em; }
826
- .tw-sides-unit { margin-left: 4px; padding-right: 2px; font-weight: 400; color: var(--tw-text-tertiary); }
831
+ .twk-sides-sum { margin-left: auto; font-size: 12px; font-weight: 500; color: var(--twk-text-label); word-spacing: 0.1em; }
832
+ .twk-sides-unit { margin-left: 4px; padding-right: 2px; font-weight: 400; color: var(--twk-text-tertiary); }
827
833
  /* The motion control (motion.ts). */
828
- .tw-motion-chip { position: relative; }
829
- .tw-motion-chip svg { display: block; width: 100%; height: 100%; }
830
- .tw-motion-chip path { fill: none; stroke: var(--tw-accent, var(--tw-text-primary)); stroke-width: 1.25; stroke-linecap: round; stroke-linejoin: round; vector-effect: non-scaling-stroke; } /* non-scaling: 1.25 CSS px whatever the viewBox */
831
- .tw-motion-body, .tw-motion-pane { display: flex; flex-direction: column; gap: 8px; }
832
- .tw-motion-pop .tw-bezier-graph, .tw-motion-pop .tw-spring-viz { height: 120px; } /* the popover has the room the inline editors don't */
833
- .tw-motion-pane .tw-spring > .tw-row { display: none; } /* duration + bounce only (motion.ts springTime) */
834
- .tw-motion-stage { display: flex; align-items: center; height: 56px; padding: 0 16px; border-radius: var(--tw-radius); background: var(--tw-surface); overflow: hidden; cursor: pointer; } /* padding: STAGE_PAD in motion.ts, room for overshoot */
835
- .tw-motion-box { width: 24px; height: 24px; border-radius: 7px; background: var(--tw-accent, var(--tw-text-primary)); will-change: transform; }
836
- .tw-spring { display: flex; flex-direction: column; gap: 6px; }
837
- .tw-spring-viz { width: 100%; height: 96px; border-radius: var(--tw-radius); background: var(--tw-surface); overflow: hidden; cursor: crosshair; touch-action: none; transition: background 0.15s; }
834
+ .twk-motion-chip { position: relative; }
835
+ .twk-motion-chip svg { display: block; width: 100%; height: 100%; }
836
+ .twk-motion-chip path { fill: none; stroke: var(--twk-accent, var(--twk-text-primary)); stroke-width: 1.25; stroke-linecap: round; stroke-linejoin: round; vector-effect: non-scaling-stroke; } /* non-scaling: 1.25 CSS px whatever the viewBox */
837
+ .twk-motion-body, .twk-motion-pane { display: flex; flex-direction: column; gap: 8px; }
838
+ .twk-motion-pop .twk-bezier-graph, .twk-motion-pop .twk-spring-viz { height: 120px; } /* the popover has the room the inline editors don't */
839
+ .twk-motion-pane .twk-spring > .twk-row { display: none; } /* duration + bounce only (motion.ts springTime) */
840
+ .twk-motion-stage { display: flex; align-items: center; height: 56px; padding: 0 16px; border-radius: var(--twk-radius); background: var(--twk-surface); overflow: hidden; cursor: pointer; } /* padding: STAGE_PAD in motion.ts, room for overshoot */
841
+ .twk-motion-box { width: 24px; height: 24px; border-radius: 7px; background: var(--twk-accent, var(--twk-text-primary)); will-change: transform; }
842
+ .twk-spring { display: flex; flex-direction: column; gap: 6px; }
843
+ .twk-spring-viz { width: 100%; height: 96px; border-radius: var(--twk-radius); background: var(--twk-surface); overflow: hidden; cursor: crosshair; touch-action: none; transition: background 0.15s; }
838
844
  @media (hover: hover) and (pointer: fine) {
839
- .tw-spring-viz:hover { background: var(--tw-surface-hover); }
845
+ .twk-spring-viz:hover { background: var(--twk-surface-hover); }
840
846
  }
841
847
  /* No resting border, so the grab cue is an inset ring. */
842
- .tw-spring-viz.is-dragging { background: var(--tw-surface-hover); box-shadow: inset 0 0 0 1px var(--tw-border-hover); }
843
- .tw-spring-canvas { width: 100%; height: 100%; display: block; }
848
+ .twk-spring-viz.is-dragging { background: var(--twk-surface-hover); box-shadow: inset 0 0 0 1px var(--twk-border-hover); }
849
+ .twk-spring-canvas { width: 100%; height: 100%; display: block; }
844
850
  /* Labeled numeric fields — shared by Spring, Point and Bezier */
845
- .tw-fields { display: grid; grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr); gap: 6px; }
851
+ .twk-fields { display: grid; grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr); gap: 6px; }
846
852
  /* ── Point — a trigger row that opens the 2D pad ── */
847
- .tw-point { position: relative; }
853
+ .twk-point { position: relative; }
848
854
  /* The mini pad's radius equals the dot's (2.5px), so a cornered dot sits concentric. */
849
- .tw-point-preview { position: relative; width: 18px; border-radius: 2.5px; background: var(--tw-base); overflow: hidden; }
850
- .tw-point-preview-dot { position: absolute; width: 5px; height: 5px; border-radius: 999px; background: var(--tw-accent, var(--tw-text-primary)); transform: translate(-50%, -50%); }
851
- .tw-point-body { display: flex; flex-direction: column; gap: 8px; min-width: 0; }
852
- .tw-point-pop .tw-pad { max-width: 240px; margin: 0 auto; } /* fill the pane, square, up to 240px */
855
+ .twk-point-preview { position: relative; width: 18px; border-radius: 2.5px; background: var(--twk-base); overflow: hidden; }
856
+ .twk-point-preview-dot { position: absolute; width: 5px; height: 5px; border-radius: 999px; background: var(--twk-accent, var(--twk-text-primary)); transform: translate(-50%, -50%); }
857
+ .twk-point-body { display: flex; flex-direction: column; gap: 8px; min-width: 0; }
858
+ .twk-point-pop .twk-pad { max-width: 240px; margin: 0 auto; } /* fill the pane, square, up to 240px */
853
859
  /* ── 2D point pad — overflow:visible so the thumb may overhang at the extremes ── */
854
- .tw-pad { position: relative; width: 100%; max-width: 176px; aspect-ratio: 1; margin: 0 auto 8px; border-radius: var(--tw-radius); background: var(--tw-base); box-shadow: inset 0 0 0 1px var(--tw-border); cursor: crosshair; touch-action: none; overflow: visible; }
855
- .tw-pad-line { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; }
856
- .tw-pad-line line { stroke: var(--tw-accent, var(--tw-text-tertiary)); stroke-width: 1.5; vector-effect: non-scaling-stroke; stroke-linecap: round; }
857
- .tw-pad-axis { position: absolute; background: var(--tw-border); pointer-events: none; }
858
- .tw-pad-axis-x { left: 0; right: 0; top: 50%; height: 1px; transform: translateY(-50%); }
859
- .tw-pad-axis-y { top: 0; bottom: 0; left: 50%; width: 1px; transform: translateX(-50%); }
860
- .tw-pad-thumb { position: absolute; width: 14px; height: 14px; border-radius: 999px; background: var(--tw-accent, var(--tw-text-primary)); border: 2px solid var(--tw-base); box-shadow: 0 0 0 1px var(--tw-border-hover), 0 1px 3px rgba(0, 0, 0, 0.3); transform: translate(-50%, -50%); pointer-events: none; transition: transform 0.18s var(--tw-ease-spring), box-shadow 0.18s; }
861
- .tw-pad.is-grabbing .tw-pad-thumb { transform: translate(-50%, -50%) scale(1.3); box-shadow: 0 0 0 1px var(--tw-border-hover), 0 3px 9px rgba(0, 0, 0, 0.4); }
862
- .tw-field { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
863
- .tw-field-label { font-size: 10px; line-height: 12px; font-weight: 600; letter-spacing: 0.02em; color: var(--tw-text-tertiary); text-transform: uppercase; }
864
- .tw-field .tw-num-wrap { background: var(--tw-base); border: 1px solid var(--tw-border); border-radius: calc(var(--tw-radius) - 2px); padding: 3px 4px; gap: 2px; line-height: 15px; transition: border-color 0.15s; }
865
- .tw-field .tw-num-unit { line-height: inherit; } /* the input's 15px line box; with the label's 12px, a field is 42px whatever the font */
866
- .tw-field .tw-num-wrap:focus-within { border-color: var(--tw-border-hover); }
860
+ .twk-pad { position: relative; width: 100%; max-width: 176px; aspect-ratio: 1; margin: 0 auto 8px; border-radius: var(--twk-radius); background: var(--twk-base); box-shadow: inset 0 0 0 1px var(--twk-border); cursor: crosshair; touch-action: none; overflow: visible; }
861
+ .twk-pad-line { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; }
862
+ .twk-pad-line line { stroke: var(--twk-accent, var(--twk-text-tertiary)); stroke-width: 1.5; vector-effect: non-scaling-stroke; stroke-linecap: round; }
863
+ .twk-pad-axis { position: absolute; background: var(--twk-border); pointer-events: none; }
864
+ .twk-pad-axis-x { left: 0; right: 0; top: 50%; height: 1px; transform: translateY(-50%); }
865
+ .twk-pad-axis-y { top: 0; bottom: 0; left: 50%; width: 1px; transform: translateX(-50%); }
866
+ .twk-pad-thumb { position: absolute; width: 14px; height: 14px; border-radius: 999px; background: var(--twk-accent, var(--twk-text-primary)); border: 2px solid var(--twk-base); box-shadow: 0 0 0 1px var(--twk-border-hover), 0 1px 3px rgba(0, 0, 0, 0.3); transform: translate(-50%, -50%); pointer-events: none; transition: transform 0.18s var(--twk-ease-spring), box-shadow 0.18s; }
867
+ .twk-pad.is-grabbing .twk-pad-thumb { transform: translate(-50%, -50%) scale(1.3); box-shadow: 0 0 0 1px var(--twk-border-hover), 0 3px 9px rgba(0, 0, 0, 0.4); }
868
+ .twk-field { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
869
+ .twk-field-label { font-size: 10px; line-height: 12px; font-weight: 600; letter-spacing: 0.02em; color: var(--twk-text-tertiary); text-transform: uppercase; }
870
+ .twk-field .twk-num-wrap { background: var(--twk-base); border: 1px solid var(--twk-border); border-radius: calc(var(--twk-radius) - 2px); padding: 3px 4px; gap: 2px; line-height: 15px; transition: border-color 0.15s; }
871
+ .twk-field .twk-num-unit { line-height: inherit; } /* the input's 15px line box; with the label's 12px, a field is 42px whatever the font */
872
+ .twk-field .twk-num-wrap:focus-within { border-color: var(--twk-border-hover); }
867
873
  /* Keyboard focus rings the whole boxed field, not the input alone inside it. */
868
- .tw-field .tw-num-wrap:has(> .tw-num:focus-visible:not(.tw-focus-quiet)), .tw-field .tw-num-wrap:has(> .tw-color-chan-input:focus-visible:not(.tw-focus-quiet)) { outline: 2px solid var(--tw-accent, var(--tw-text-focus)); outline-offset: -1px; }
869
- .tw-field .tw-num-wrap > .tw-num:focus-visible, .tw-field .tw-num-wrap > .tw-color-chan-input:focus-visible { outline: none; }
874
+ .twk-field .twk-num-wrap:has(> .twk-num:focus-visible:not(.twk-focus-quiet)), .twk-field .twk-num-wrap:has(> .twk-color-chan-input:focus-visible:not(.twk-focus-quiet)) { outline: 2px solid var(--twk-accent, var(--twk-text-focus)); outline-offset: -1px; }
875
+ .twk-field .twk-num-wrap > .twk-num:focus-visible, .twk-field .twk-num-wrap > .twk-color-chan-input:focus-visible { outline: none; }
870
876
  /* The same for the plot's expression field and a slider's inline number field. */
871
- .tw-plot-field:has(> .tw-plot-input:focus-visible:not(.tw-focus-quiet)) { outline: 2px solid var(--tw-accent, var(--tw-text-focus)); outline-offset: -2px; }
872
- .tw-slider:has(.tw-slider-input:focus-visible:not(.tw-focus-quiet)) { outline: 2px solid var(--tw-accent, var(--tw-text-focus)); outline-offset: -2px; }
873
- .tw-wg-area:focus-visible, .tw-wg-hue:focus-visible, .tw-wg-alpha:focus-visible { outline: 2px solid var(--tw-accent, var(--tw-text-focus)); outline-offset: 2px; }
874
- .tw-field .tw-num { flex: 1; width: auto; min-width: 0; }
875
- .tw-field .tw-num-grab { width: 10px; }
877
+ .twk-plot-field:has(> .twk-plot-input:focus-visible:not(.twk-focus-quiet)) { outline: 2px solid var(--twk-accent, var(--twk-text-focus)); outline-offset: -2px; }
878
+ .twk-slider:has(.twk-slider-input:focus-visible:not(.twk-focus-quiet)) { outline: 2px solid var(--twk-accent, var(--twk-text-focus)); outline-offset: -2px; }
879
+ .twk-wg-area:focus-visible, .twk-wg-hue:focus-visible, .twk-wg-alpha:focus-visible { outline: 2px solid var(--twk-accent, var(--twk-text-focus)); outline-offset: 2px; }
880
+ .twk-field .twk-num { flex: 1; width: auto; min-width: 0; }
881
+ .twk-field .twk-num-grab { width: 10px; }
876
882
 
877
883
  /* ── Cubic bezier — interactive curve editor ── */
878
- .tw-bezier { display: flex; flex-direction: column; gap: 6px; }
879
- .tw-bezier-graph { position: relative; width: 100%; height: 96px; background: var(--tw-surface); border-radius: var(--tw-radius); overflow: hidden; touch-action: none; }
880
- .tw-bezier-svg { position: absolute; inset: 0; width: 100%; height: 100%; display: block; }
881
- .tw-bezier-guide { stroke: var(--tw-border); stroke-width: 1; }
882
- .tw-bezier-diag { stroke: var(--tw-surface-active); stroke-width: 1; stroke-dasharray: 2 3; }
883
- .tw-bezier-ctl { stroke: var(--tw-text-tertiary); stroke-width: 1.5; }
884
- .tw-bezier-curve { fill: none; stroke: var(--tw-accent, var(--tw-text-primary)); stroke-width: 2; stroke-linecap: round; }
885
- .tw-bezier-handle {
884
+ .twk-bezier { display: flex; flex-direction: column; gap: 6px; }
885
+ .twk-bezier-graph { position: relative; width: 100%; height: 96px; background: var(--twk-surface); border-radius: var(--twk-radius); overflow: hidden; touch-action: none; }
886
+ .twk-bezier-svg { position: absolute; inset: 0; width: 100%; height: 100%; display: block; }
887
+ .twk-bezier-guide { stroke: var(--twk-border); stroke-width: 1; }
888
+ .twk-bezier-diag { stroke: var(--twk-surface-active); stroke-width: 1; stroke-dasharray: 2 3; }
889
+ .twk-bezier-ctl { stroke: var(--twk-text-tertiary); stroke-width: 1.5; }
890
+ .twk-bezier-curve { fill: none; stroke: var(--twk-accent, var(--twk-text-primary)); stroke-width: 2; stroke-linecap: round; }
891
+ .twk-bezier-handle {
886
892
  position: absolute; width: 14px; height: 14px; margin: 0; padding: 0; /* 14px, as the color-area and point-pad thumbs */
887
893
  transform: translate(-50%, -50%); border-radius: 999px;
888
- background: var(--tw-text-primary); border: 2px solid var(--tw-base);
894
+ background: var(--twk-text-primary); border: 2px solid var(--twk-base);
889
895
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.4); cursor: grab; touch-action: none;
890
- transition: transform 0.18s var(--tw-ease-spring), box-shadow 0.18s;
896
+ transition: transform 0.18s var(--twk-ease-spring), box-shadow 0.18s;
891
897
  }
892
898
  @media (hover: hover) and (pointer: fine) {
893
- .tw-bezier-handle:hover { transform: translate(-50%, -50%) scale(1.2); }
899
+ .twk-bezier-handle:hover { transform: translate(-50%, -50%) scale(1.2); }
894
900
  }
895
- .tw-bezier-handle.is-dragging { cursor: grabbing; transform: translate(-50%, -50%) scale(1.3); box-shadow: 0 3px 9px rgba(0, 0, 0, 0.45); }
896
- .tw-bezier-fields { gap: 4px; }
901
+ .twk-bezier-handle.is-dragging { cursor: grabbing; transform: translate(-50%, -50%) scale(1.3); box-shadow: 0 3px 9px rgba(0, 0, 0, 0.45); }
902
+ .twk-bezier-fields { gap: 4px; }
897
903
 
898
904
  /* Plot — a function graph with an editable, safely-parsed expression. */
899
- .tw-plot { display: flex; flex-direction: column; gap: 6px; }
900
- .tw-plot-label, .tw-spring-label, .tw-bezier-label { font-size: 12px; font-weight: 500; color: var(--tw-text-label); padding: 0; transition: color 0.15s; } /* flush with the full-width graph below */
905
+ .twk-plot { display: flex; flex-direction: column; gap: 6px; }
906
+ .twk-plot-label, .twk-spring-label, .twk-bezier-label { font-size: 12px; font-weight: 500; color: var(--twk-text-label); padding: 0; transition: color 0.15s; } /* flush with the full-width graph below */
901
907
  /* As a panel row, a graph sits in a row's frame, as a radio grid does: 4px round the graph, the label
902
908
  * 12px in like every row's. (The motion pane's editors aren't rows, and stay flush.) */
903
- :is(.tw-controls, .tw-controls > [data-tw]) > :is(.tw-spring, .tw-bezier, .tw-plot) { padding: 4px; background: var(--tw-surface); border-radius: var(--tw-radius); } /* a markup panel's control sits in its host */
909
+ :is(.twk-controls, .twk-controls > [data-twk]) > :is(.twk-spring, .twk-bezier, .twk-plot) { padding: 4px; background: var(--twk-surface); border-radius: var(--twk-radius); } /* a markup panel's control sits in its host */
904
910
  /* A 24px line for the label: in the 4px frame its text lands 9px down, where a
905
911
  * 32px row's centered label does. */
906
- :is(.tw-controls, .tw-controls > [data-tw]) > :is(.tw-spring, .tw-bezier, .tw-plot) > :is(.tw-spring-label, .tw-bezier-label, .tw-plot-label) { padding-left: 8px; line-height: 24px; }
907
- .tw-plot-graph { position: relative; width: 100%; height: 140px; background: var(--tw-surface); border-radius: var(--tw-radius); overflow: hidden; box-shadow: inset 0 0 0 1px var(--tw-border); }
908
- .tw-plot-svg { position: absolute; inset: 0; width: 100%; height: 100%; display: block; }
909
- .tw-plot-axis { stroke: var(--tw-border); stroke-width: 1; }
910
- .tw-plot-curve { fill: none; stroke: var(--tw-accent, var(--tw-text-primary)); stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
911
- .tw-plot-field { display: flex; align-items: center; gap: 7px; height: var(--tw-row-height); padding: 0 10px 0 12px; background: var(--tw-surface); border-radius: var(--tw-radius); box-shadow: inset 0 0 0 1px transparent; transition: box-shadow 0.15s; } /* the row idiom: 12 left, 10 right */
912
- .tw-plot-field:focus-within { box-shadow: inset 0 0 0 1px var(--tw-border-hover); }
913
- .tw-plot-fx { flex: none; font-size: 12px; color: var(--tw-text-tertiary); font-variant-numeric: tabular-nums; -webkit-user-select: none; user-select: none; }
914
- .tw-plot-input { flex: 1; min-width: 0; padding: 0; background: none; border: none; outline: none; color: var(--tw-text-primary); font-family: var(--tw-font-mono); font-size: 12px; }
915
- .tw-plot-input::placeholder { color: var(--tw-text-tertiary); }
916
- .tw-plot.is-invalid .tw-plot-curve { opacity: 0.25; }
917
- .tw-plot.is-invalid .tw-plot-field { box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--tw-danger) 60%, transparent); }
918
- .tw-plot.is-invalid .tw-plot-fx { color: color-mix(in srgb, var(--tw-danger) 80%, transparent); }
912
+ :is(.twk-controls, .twk-controls > [data-twk]) > :is(.twk-spring, .twk-bezier, .twk-plot) > :is(.twk-spring-label, .twk-bezier-label, .twk-plot-label) { padding-left: 8px; line-height: 24px; }
913
+ .twk-plot-graph { position: relative; width: 100%; height: 140px; background: var(--twk-surface); border-radius: var(--twk-radius); overflow: hidden; box-shadow: inset 0 0 0 1px var(--twk-border); }
914
+ .twk-plot-svg { position: absolute; inset: 0; width: 100%; height: 100%; display: block; }
915
+ .twk-plot-axis { stroke: var(--twk-border); stroke-width: 1; }
916
+ .twk-plot-curve { fill: none; stroke: var(--twk-accent, var(--twk-text-primary)); stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
917
+ .twk-plot-field { display: flex; align-items: center; gap: 7px; height: var(--twk-row-height); padding: 0 10px 0 12px; background: var(--twk-surface); border-radius: var(--twk-radius); box-shadow: inset 0 0 0 1px transparent; transition: box-shadow 0.15s; } /* the row idiom: 12 left, 10 right */
918
+ .twk-plot-field:focus-within { box-shadow: inset 0 0 0 1px var(--twk-border-hover); }
919
+ .twk-plot-fx { flex: none; font-size: 12px; color: var(--twk-text-tertiary); font-variant-numeric: tabular-nums; -webkit-user-select: none; user-select: none; }
920
+ .twk-plot-input { flex: 1; min-width: 0; padding: 0; background: none; border: none; outline: none; color: var(--twk-text-primary); font-family: var(--twk-font-mono); font-size: 12px; }
921
+ .twk-plot-input::placeholder { color: var(--twk-text-tertiary); }
922
+ .twk-plot.is-invalid .twk-plot-curve { opacity: 0.25; }
923
+ .twk-plot.is-invalid .twk-plot-field { box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--twk-danger) 60%, transparent); }
924
+ .twk-plot.is-invalid .twk-plot-fx { color: color-mix(in srgb, var(--twk-danger) 80%, transparent); }
919
925
 
920
926
  /* ── Scheme resolution ──────────────────────────────────────────────────────────
921
927
  * Panels and portals follow the OS (color-scheme: light dark, above), dark when it asks for
922
- * dark. A [data-tw-scheme="dark"] or "light" pin on the node or any ancestor sets the scheme
928
+ * dark. A [data-twk-scheme="dark"] or "light" pin on the node or any ancestor sets the scheme
923
929
  * instead, and a light pin outranks a dark one wherever they nest: it comes later at the same
924
930
  * specificity, so a dark pin inside a light scope cannot re-darken its subtree. The pins set
925
931
  * color-scheme on the kit's own nodes only, never on the host element that carries the
926
932
  * attribute. The :where() keeps both rules at base class specificity. */
927
- .tw-panel:where([data-tw-scheme="dark"], [data-tw-scheme="dark"] *), .tw-portal:where([data-tw-scheme="dark"], [data-tw-scheme="dark"] *) { color-scheme: dark; }
928
- .tw-panel:where([data-tw-scheme="light"], [data-tw-scheme="light"] *), .tw-portal:where([data-tw-scheme="light"], [data-tw-scheme="light"] *) { color-scheme: light; }
933
+ .twk-panel:where([data-twk-scheme="dark"], [data-twk-scheme="dark"] *), .twk-portal:where([data-twk-scheme="dark"], [data-twk-scheme="dark"] *) { color-scheme: dark; }
934
+ .twk-panel:where([data-twk-scheme="light"], [data-twk-scheme="light"] *), .twk-portal:where([data-twk-scheme="light"], [data-twk-scheme="light"] *) { color-scheme: light; }
929
935
  /* The one rule light-dark() can't carry: on light the slider handle takes an edge for definition, and on dark it has no
930
936
  * shadow at all (a transparent one still moves the antialiasing of its rounded ends). So it picks the scheme the way the
931
937
  * pins above do: light under the OS unless a dark pin covers it, or under a light pin anywhere. */
932
- @media (prefers-color-scheme: light) { .tw-slider-handle:where(:not([data-tw-scheme="dark"] *)) { box-shadow: 0 0 0 0.5px rgba(0, 0, 0, 0.08); } }
933
- .tw-slider-handle:where([data-tw-scheme="light"] *) { box-shadow: 0 0 0 0.5px rgba(0, 0, 0, 0.08); }
938
+ @media (prefers-color-scheme: light) { .twk-slider-handle:where(:not([data-twk-scheme="dark"] *)) { box-shadow: 0 0 0 0.5px rgba(0, 0, 0, 0.08); } }
939
+ .twk-slider-handle:where([data-twk-scheme="light"] *) { box-shadow: 0 0 0 0.5px rgba(0, 0, 0, 0.08); }
934
940
  /* Tabular figures on the readouts. After the control rules: `font: inherit` also clears
935
941
  * font-variant-numeric, so this has to win on source order. */
936
- .tw-slider-value, .tw-slider-input, .tw-fps-val, .tw-num, .tw-grab-bubble,
937
- .tw-trigger-value, .tw-color-gamut, .tw-color-chan-input, .tw-sides-sum {
942
+ .twk-slider-value, .twk-slider-input, .twk-fps-val, .twk-num, .twk-grab-bubble,
943
+ .twk-trigger-value, .twk-color-gamut, .twk-color-chan-input, .twk-sides-sum {
938
944
  font-variant-numeric: tabular-nums;
939
945
  }
940
946
 
941
947
  @media (prefers-reduced-motion: reduce) {
942
- .tw-panel, .tw-panel *, .tw-portal, .tw-portal * { transition: none !important; }
948
+ .twk-panel, .twk-panel *, .twk-portal, .twk-portal * { transition: none !important; }
943
949
  /* the transition kill doesn't touch animations */
944
- .tw-tabs-page[data-active="true"], .tw-grab-bubble { animation: none; }
950
+ .twk-tabs-page[data-active="true"], .twk-grab-bubble { animation: none; }
945
951
  }
946
952
 
947
953
  /* Forced colors flatten every background, so state shown only by background needs an
948
954
  * outline or border in the system Highlight, which survives. */
949
955
  @media (forced-colors: active) {
950
- .tw-seg-pill, .tw-tabs-pill, .tw-slider-fill { outline: 2px solid Highlight; outline-offset: -2px; }
951
- .tw-radiogrid-btn[data-active="true"] { outline: 2px solid Highlight; outline-offset: -2px; }
952
- .tw-image-tile[data-active="true"] { outline-color: Highlight; }
953
- .tw-gradient-stop[data-sel="true"] { border-color: Highlight; }
954
- .tw-select-option:focus-visible { outline: 2px solid Highlight; outline-offset: -2px; } /* the roving focus is a background, flattened here */
956
+ .twk-seg-pill, .twk-tabs-pill, .twk-slider-fill { outline: 2px solid Highlight; outline-offset: -2px; }
957
+ .twk-radiogrid-btn[data-active="true"] { outline: 2px solid Highlight; outline-offset: -2px; }
958
+ .twk-image-tile[data-active="true"] { outline-color: Highlight; }
959
+ .twk-gradient-stop[data-sel="true"] { border-color: Highlight; }
960
+ .twk-select-option:focus-visible { outline: 2px solid Highlight; outline-offset: -2px; } /* the roving focus is a background, flattened here */
955
961
  /* Where the color is the content, keep it: a swatch, strip, bar or thumb flattened to Canvas shows nothing. */
956
- .tw-color-swatch, .tw-option-swatch, .tw-image-thumb, .tw-gradient-preview, .tw-gradient-bar, .tw-gradient-stop, .tw-wg-area, .tw-wg-hue, .tw-wg-alpha, .tw-pad-thumb { forced-color-adjust: none; }
962
+ .twk-color-swatch, .twk-option-swatch, .twk-image-thumb, .twk-gradient-preview, .twk-gradient-bar, .twk-gradient-stop, .twk-wg-area, .twk-wg-hue, .twk-wg-alpha, .twk-pad-thumb { forced-color-adjust: none; }
957
963
  /* …but their focus ring is still the system's: opting out would leave it in the accent, which may match the flattened background. */
958
- .tw-gradient-stop:focus-visible, .tw-wg-area:focus-visible, .tw-wg-hue:focus-visible, .tw-wg-alpha:focus-visible { outline-color: Highlight; }
964
+ .twk-gradient-stop:focus-visible, .twk-wg-area:focus-visible, .twk-wg-hue:focus-visible, .twk-wg-alpha:focus-visible { outline-color: Highlight; }
959
965
  }