twiddle-panel 0.7.0 → 0.8.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.
package/dist/twiddle.mjs CHANGED
@@ -1,148 +1,148 @@
1
- const pt = /* @__PURE__ */ new Set();
2
- let T = Xn(), X = [], ve = [], Ie = null, Xt = 0;
3
- function Xn() {
4
- const t = new URLSearchParams(location.search).get("twd");
5
- if (!t) return {};
1
+ const gt = /* @__PURE__ */ new Set();
2
+ let M = sa(), Q = [], ke = [], qe = null, an = 0;
3
+ function sa() {
4
+ const e = new URLSearchParams(location.search).get("twd");
5
+ if (!e) return {};
6
6
  try {
7
- return JSON.parse(t);
7
+ return JSON.parse(e);
8
8
  } catch {
9
9
  return {};
10
10
  }
11
11
  }
12
- function Jn() {
13
- const t = new URL(location.href);
14
- Object.keys(T).length ? t.searchParams.set("twd", JSON.stringify(T)) : t.searchParams.delete("twd"), history.replaceState(history.state, "", t);
12
+ function ia() {
13
+ const e = new URL(location.href);
14
+ Object.keys(M).length ? e.searchParams.set("twd", JSON.stringify(M)) : e.searchParams.delete("twd"), history.replaceState(history.state, "", e);
15
15
  }
16
- function yt() {
17
- Jn(), pt.forEach((t) => t(T));
16
+ function Et() {
17
+ ia(), gt.forEach((e) => e(M));
18
18
  }
19
- function se(t, e) {
20
- if (JSON.stringify(t) === JSON.stringify(T)) return;
19
+ function pe(e, t) {
20
+ if (JSON.stringify(e) === JSON.stringify(M)) return;
21
21
  const n = Date.now();
22
- e && e === Ie && n - Xt < 400 || (X.push(T), X.length > 100 && X.shift()), ve = [], Ie = e, Xt = n, T = t, yt();
22
+ t && t === qe && n - an < 400 || (Q.push(M), Q.length > 100 && Q.shift()), ke = [], qe = t, an = n, M = e, Et();
23
23
  }
24
- function Be(t, e) {
25
- const n = { ...t, ...e };
24
+ function Ve(e, t) {
25
+ const n = { ...e, ...t };
26
26
  return Object.keys(n).forEach((a) => {
27
27
  n[a] == null && delete n[a];
28
28
  }), Object.keys(n).length ? n : null;
29
29
  }
30
- function U(t, e, n) {
31
- const a = { ...t };
32
- return n == null ? delete a[e] : a[e] = n, a;
30
+ function B(e, t, n) {
31
+ const a = { ...e };
32
+ return n == null ? delete a[t] : a[t] = n, a;
33
33
  }
34
- function ut() {
35
- return JSON.parse(JSON.stringify(T));
34
+ function mt() {
35
+ return JSON.parse(JSON.stringify(M));
36
36
  }
37
- function Qn(t, e) {
38
- se(U(T, t, Be(T[t], e)), `${t}:${Object.keys(e)}`);
37
+ function la(e, t) {
38
+ pe(B(M, e, Ve(M[e], t)), `${e}:${Object.keys(t)}`);
39
39
  }
40
- function ea(t, e = `multi:${Object.keys(t)}`) {
41
- const n = Object.entries(t).reduce((a, [o, r]) => U(a, o, Be(a[o], r)), T);
42
- se(n, e);
40
+ function da(e, t = `multi:${Object.keys(e)}`) {
41
+ const n = Object.entries(e).reduce((a, [o, s]) => B(a, o, Ve(a[o], s)), M);
42
+ pe(n, t);
43
43
  }
44
- function vn(t, e, n) {
45
- const a = U(T.scopes?.[t], e, Be(T.scopes?.[t]?.[e], n)), o = U(T.scopes, t, Object.keys(a).length ? a : null);
46
- se(U(T, "scopes", Object.keys(o).length ? o : null), `${t}|${e}:${Object.keys(n)}`);
44
+ function Mn(e, t, n) {
45
+ const a = B(M.scopes?.[e], t, Ve(M.scopes?.[e]?.[t], n)), o = B(M.scopes, e, Object.keys(a).length ? a : null);
46
+ pe(B(M, "scopes", Object.keys(o).length ? o : null), `${e}|${t}:${Object.keys(n)}`);
47
47
  }
48
- function ta(t, e) {
49
- if (e) return vn(t, e, Object.fromEntries(
50
- Object.keys(T.scopes?.[t]?.[e] || {}).map((a) => [a, null])
48
+ function ca(e, t) {
49
+ if (t) return Mn(e, t, Object.fromEntries(
50
+ Object.keys(M.scopes?.[e]?.[t] || {}).map((a) => [a, null])
51
51
  ));
52
- const n = U(T.scopes, t, null);
53
- se(U(T, "scopes", Object.keys(n).length ? n : null));
54
- }
55
- function na(t, e) {
56
- const n = T.scopes?.[t];
57
- if (!n || t === e) return;
58
- let a = { ...T.scopes[e] };
59
- Object.entries(n).forEach(([r, s]) => {
60
- a = U(a, r, Be(a[r], s));
52
+ const n = B(M.scopes, e, null);
53
+ pe(B(M, "scopes", Object.keys(n).length ? n : null));
54
+ }
55
+ function pa(e, t) {
56
+ const n = M.scopes?.[e];
57
+ if (!n || e === t) return;
58
+ let a = { ...M.scopes[t] };
59
+ Object.entries(n).forEach(([s, r]) => {
60
+ a = B(a, s, Ve(a[s], r));
61
61
  });
62
- const o = U(U(T.scopes, t, null), e, a);
63
- se(U(T, "scopes", o));
62
+ const o = B(B(M.scopes, e, null), t, a);
63
+ pe(B(M, "scopes", o));
64
64
  }
65
- function yn() {
66
- se({});
65
+ function Tn() {
66
+ pe({});
67
67
  }
68
- function kn() {
69
- X.length && (ve.push(T), T = X.pop(), Ie = null, yt());
68
+ function An() {
69
+ Q.length && (ke.push(M), M = Q.pop(), qe = null, Et());
70
70
  }
71
- function Sn() {
72
- ve.length && (X.push(T), T = ve.pop(), Ie = null, yt());
71
+ function Ln() {
72
+ ke.length && (Q.push(M), M = ke.pop(), qe = null, Et());
73
73
  }
74
- const aa = () => X.length > 0, oa = () => ve.length > 0;
75
- function $n(t) {
76
- return pt.add(t), () => pt.delete(t);
74
+ const ua = () => Q.length > 0, ha = () => ke.length > 0;
75
+ function Pn(e) {
76
+ return gt.add(e), () => gt.delete(e);
77
77
  }
78
- const ra = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
78
+ const fa = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
79
79
  __proto__: null,
80
- canRedo: oa,
81
- canUndo: aa,
82
- getState: ut,
83
- moveScope: na,
84
- onChange: $n,
85
- redo: Sn,
86
- removeScope: ta,
87
- resetState: yn,
88
- setScope: vn,
89
- setSection: Qn,
90
- setSections: ea,
91
- undo: kn
92
- }, Symbol.toStringTag, { value: "Module" })), En = [];
93
- let ht = [], kt = {}, R = null, oe = !1, Cn = {};
94
- function sa(t) {
95
- typeof t == "function" && En.push(t);
96
- }
97
- const Jt = (t) => Object.assign({}, ...En.map((e) => e(t) || {})), ft = (t) => /^(\.[\w-]+|\[data-twd="[^"]+"\])$/.test(t), ia = (t) => {
80
+ canRedo: ha,
81
+ canUndo: ua,
82
+ getState: mt,
83
+ moveScope: pa,
84
+ onChange: Pn,
85
+ redo: Ln,
86
+ removeScope: ca,
87
+ resetState: Tn,
88
+ setScope: Mn,
89
+ setSection: la,
90
+ setSections: da,
91
+ undo: An
92
+ }, Symbol.toStringTag, { value: "Module" })), On = [];
93
+ let bt = [], Ct = {}, R = null, de = !1, Fn = {};
94
+ function ga(e) {
95
+ typeof e == "function" && On.push(e);
96
+ }
97
+ const on = (e) => Object.assign({}, ...On.map((t) => t(e) || {})), xt = (e) => /^(\.[\w-]+|\[data-twd="[^"]+"\])$/.test(e), ma = (e) => {
98
98
  try {
99
- return document.querySelector(t), !0;
99
+ return document.querySelector(e), !0;
100
100
  } catch {
101
101
  return !1;
102
102
  }
103
103
  };
104
- function la() {
104
+ function ba() {
105
105
  return (!R || !R.isConnected) && (R = document.createElement("style"), R.id = "twiddle-scoped", document.head.appendChild(R)), R;
106
106
  }
107
- function Tn() {
108
- const t = document.documentElement;
109
- ht.forEach((e) => t.style.removeProperty(e)), ht = [], kt = {};
110
- }
111
- function gt(t) {
112
- if (Cn = t, oe) return;
107
+ function Nn() {
113
108
  const e = document.documentElement;
114
- Tn();
115
- const n = Jt(t), a = Object.entries(t.scopes || {}).filter(([r]) => ia(r)).sort(([r], [s]) => ft(s) - ft(r)).map(([r, s]) => [r, Jt(s)]);
116
- Object.entries(n).forEach(([r, s]) => e.style.setProperty(r, s)), ht = Object.keys(n), kt = n;
117
- const o = a.filter(([, r]) => Object.keys(r).length).map(([r, s]) => `${r} {
118
- ${Object.entries(s).map(([i, l]) => ` ${i}: ${l} !important;`).join(`
109
+ bt.forEach((t) => e.style.removeProperty(t)), bt = [], Ct = {};
110
+ }
111
+ function wt(e) {
112
+ if (Fn = e, de) return;
113
+ const t = document.documentElement;
114
+ Nn();
115
+ const n = on(e), a = Object.entries(e.scopes || {}).filter(([s]) => ma(s)).sort(([s], [r]) => xt(r) - xt(s)).map(([s, r]) => [s, on(r)]);
116
+ Object.entries(n).forEach(([s, r]) => t.style.setProperty(s, r)), bt = Object.keys(n), Ct = n;
117
+ const o = a.filter(([, s]) => Object.keys(s).length).map(([s, r]) => `${s} {
118
+ ${Object.entries(r).map(([i, l]) => ` ${i}: ${l} !important;`).join(`
119
119
  `)}
120
120
  }`).join(`
121
121
  `);
122
- (o || R) && (la().textContent = o);
122
+ (o || R) && (ba().textContent = o);
123
123
  }
124
- function Mn(t) {
125
- const e = document.documentElement, n = oe, a = { ...kt };
126
- Object.keys(a).forEach((o) => e.style.removeProperty(o)), R && (R.disabled = !0);
124
+ function jn(e) {
125
+ const t = document.documentElement, n = de, a = { ...Ct };
126
+ Object.keys(a).forEach((o) => t.style.removeProperty(o)), R && (R.disabled = !0);
127
127
  try {
128
- return t();
128
+ return e();
129
129
  } finally {
130
- n || (Object.entries(a).forEach(([o, r]) => e.style.setProperty(o, r)), R && (R.disabled = !1));
130
+ n || (Object.entries(a).forEach(([o, s]) => t.style.setProperty(o, s)), R && (R.disabled = !1));
131
131
  }
132
132
  }
133
- function da(t, e = document.documentElement) {
134
- return Mn(() => {
135
- const n = getComputedStyle(e);
136
- return Object.fromEntries(t.map((a) => [a, n.getPropertyValue(a).trim()]));
133
+ function xa(e, t = document.documentElement) {
134
+ return jn(() => {
135
+ const n = getComputedStyle(t);
136
+ return Object.fromEntries(e.map((a) => [a, n.getPropertyValue(a).trim()]));
137
137
  });
138
138
  }
139
- function ca() {
140
- oe || (Tn(), R && (R.disabled = !0), oe = !0);
139
+ function wa() {
140
+ de || (Nn(), R && (R.disabled = !0), de = !0);
141
141
  }
142
- function pa() {
143
- oe && (oe = !1, R && (R.disabled = !1), gt(Cn));
142
+ function ya() {
143
+ de && (de = !1, R && (R.disabled = !1), wt(Fn));
144
144
  }
145
- const ua = `
145
+ const va = `
146
146
  :host { all: initial; } /* block fonts/colours inherited from the page */
147
147
  * { box-sizing: border-box; }
148
148
 
@@ -160,11 +160,17 @@ const ua = `
160
160
  position: fixed;
161
161
  right: 16px;
162
162
  bottom: 16px;
163
+ /* Where it sits is set inline by position.js (drag / nudge, per site);
164
+ --off-x / --off-y = distance from the two nearest edges. */
165
+ --off-x: 16px;
166
+ --off-y: 16px;
163
167
  z-index: 2147483647;
164
168
  font: 13px/1.4 ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
165
169
  color: var(--p-fg);
170
+ /* The panel opens to the left of the launcher, bottoms aligned,
171
+ so it can use the full window height. */
166
172
  display: flex;
167
- flex-direction: column;
173
+ flex-direction: row;
168
174
  align-items: flex-end;
169
175
  gap: 10px;
170
176
  }
@@ -196,6 +202,18 @@ const ua = `
196
202
  cursor: pointer;
197
203
  padding: 0;
198
204
  }
205
+ /* Open or minimised: centred on the tabs. 10px = panel border 1 + tab bar
206
+ padding 6 + half a tab (~49px) − half the launcher (22). */
207
+ .launcher { transition: margin-bottom .2s cubic-bezier(0.2, 0.8, 0.2, 1); }
208
+ .wrap.is-open .launcher { margin-bottom: 10px; }
209
+ /* Left corners: the panel opens to the right of the launcher. Top corners: tops aligned. */
210
+ .wrap.at-left { flex-direction: row-reverse; }
211
+ .wrap.at-top { align-items: flex-start; }
212
+ .wrap.at-top.is-open .launcher { margin-bottom: 0; }
213
+ .launcher { touch-action: none; }
214
+ .wrap.dragging .launcher { cursor: grabbing; }
215
+ .wrap.dragging .panel { pointer-events: none; }
216
+ @media (prefers-reduced-motion: reduce) { .launcher { transition: none; } }
199
217
  .mark { display: block; overflow: visible; }
200
218
  .mark-disc { fill: #16161a; }
201
219
  .mark-knob { fill: #4f46e5; }
@@ -212,11 +230,12 @@ const ua = `
212
230
  /* Panel */
213
231
  .panel {
214
232
  width: 360px; /* tabbed layout (Figma 254:323); was 300 */
215
- max-width: calc(100vw - 32px);
233
+ /* 70px = 16 room on the far side + 44 launcher + 10 gap */
234
+ max-width: calc(100vw - var(--off-x) - 70px);
216
235
  /* As tall as its content; scrolls only when the window is shorter.
217
- 90px = 16 margin + 44 launcher + 10 gap + 20 room at the top. */
218
- max-height: calc(100vh - 90px);
219
- max-height: calc(100dvh - 90px);
236
+ 32px = 16 margin below + 16 room at the top. */
237
+ max-height: calc(100vh - var(--off-y) - 16px);
238
+ max-height: calc(100dvh - var(--off-y) - 16px);
220
239
  background: var(--p-bg);
221
240
  border: 1px solid var(--p-line);
222
241
  border-radius: 14px;
@@ -443,8 +462,7 @@ const ua = `
443
462
  padding: 1px 4px;
444
463
  }
445
464
 
446
- /* Beside the launcher (left of it, centred on it). The panel always sits
447
- above the launcher, so a toast here never covers the footer's Copy prompt. */
465
+ /* Closed: beside the launcher (on the panel's side of it, centred on it). */
448
466
  .toast {
449
467
  position: absolute; bottom: 22px; right: 54px; transform: translateY(50%);
450
468
  background: var(--p-accent); color: var(--p-on-accent);
@@ -453,6 +471,13 @@ const ua = `
453
471
  opacity: 0; transition: opacity .15s ease; pointer-events: none;
454
472
  }
455
473
  .toast.show { opacity: 1; }
474
+ .wrap.at-left .toast { right: auto; left: 54px; }
475
+ .wrap.at-top .toast { bottom: auto; top: 22px; transform: translateY(-50%); }
476
+ /* Open or minimised: just above the panel, right-aligned with it, so it
477
+ never covers a row (Ravi, 2026-10-16; Figma 269:1037). Top corners: below it. */
478
+ .wrap.is-open .toast { bottom: calc(100% + 8px); right: 54px; left: auto; top: auto; transform: none; z-index: 3; }
479
+ .wrap.is-open.at-left .toast { right: auto; left: 54px; }
480
+ .wrap.is-open.at-top .toast { bottom: auto; top: calc(100% + 8px); }
456
481
 
457
482
  /* ---------- Proposals below: not yet designed in Figma ---------- */
458
483
 
@@ -763,6 +788,11 @@ const ua = `
763
788
  .tab:hover { background: var(--p-hover); color: var(--p-fg); }
764
789
  .tab[aria-pressed="true"] { background: var(--p-hover); color: var(--p-fg); font-weight: 600; }
765
790
  .tab span { max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
791
+
792
+ /* Minimised (proposal, not yet in Figma): only the tab bar (and the Compare
793
+ strip while it shows); clicking a tab opens the panel on that tab. */
794
+ .panel.minimised > :not(.tabbar):not(.original-pill) { display: none; }
795
+ .panel.minimised .tabbar { border-top: 0; }
766
796
  .page-fields { gap: 16px; }
767
797
  .page-heading { display: flex; align-items: baseline; justify-content: space-between; font-size: 13px; font-weight: 500; color: var(--p-fg); margin-bottom: -6px; }
768
798
  .page-heading .muted { font-size: 12px; font-weight: 400; }
@@ -871,7 +901,7 @@ const ua = `
871
901
  .summary-swatch { width: 14px; height: 14px; border-radius: 4px; flex: none; }
872
902
  .summary-swatch[hidden] { display: none; }
873
903
  .summary-row .chev { display: grid; color: var(--p-muted); }
874
- `, P = (t, e, n) => `<svg width="${t}" height="${t}" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="${e}" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">${n}</svg>`, A = {
904
+ `, P = (e, t, n) => `<svg width="${e}" height="${e}" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="${t}" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">${n}</svg>`, A = {
875
905
  dial: P(20, 1.8, '<circle cx="12" cy="12" r="8"/><path d="M12 12l4-4"/><path d="M12 2v2M12 20v2M2 12h2M20 12h2"/>'),
876
906
  link: P(15, 2, '<path d="M10 13a5 5 0 0 0 7.07 0l3-3a5 5 0 0 0-7.07-7.07l-1 1"/><path d="M14 11a5 5 0 0 0-7.07 0l-3 3a5 5 0 0 0 7.07 7.07l1-1"/>'),
877
907
  reset: P(15, 2, '<path d="M3 12a9 9 0 1 0 3-6.7"/><path d="M3 3v6h6"/>'),
@@ -884,6 +914,7 @@ const ua = `
884
914
  redo: P(15, 2, '<path d="M15 14l5-5-5-5"/><path d="M20 9H9a5 5 0 0 0 0 10h3"/>'),
885
915
  back: P(15, 2, '<path d="M15 18l-6-6 6-6"/>'),
886
916
  down: P(12, 2.4, '<path d="M6 9l6 6 6-6"/>'),
917
+ minimise: P(15, 2, '<path d="M5 12h14"/>'),
887
918
  copy: P(13, 2, '<rect x="9" y="9" width="11" height="11" rx="2"/><path d="M5 15V5a2 2 0 0 1 2-2h10"/>'),
888
919
  warn: P(13, 2, '<path d="M12 3l9.5 17h-19z"/><path d="M12 10v4M12 17.5v.01"/>'),
889
920
  // Launcher: the Panel product mark (Twiddle brand; Figma Logo page › Panel mark; also site/favicon.svg).
@@ -898,211 +929,280 @@ const ua = `
898
929
  tabShadows: P(18, 2, '<rect x="4" y="4" width="12" height="12" rx="2"/><path d="M20 8v10a2 2 0 0 1-2 2H8"/>'),
899
930
  sliders: P(15, 2, '<path d="M4 7h10M18 7h2M4 17h4M12 17h8"/><circle cx="16" cy="7" r="2"/><circle cx="10" cy="17" r="2"/>')
900
931
  // Figma: Icon/Sliders (proposal)
901
- }, An = "twiddle:";
902
- function fe(t) {
932
+ }, Rn = "twiddle:";
933
+ function Z(e) {
903
934
  try {
904
- return localStorage.getItem(An + t);
935
+ return localStorage.getItem(Rn + e);
905
936
  } catch {
906
937
  return null;
907
938
  }
908
939
  }
909
- function ge(t, e) {
940
+ function oe(e, t) {
910
941
  try {
911
- localStorage.setItem(An + t, e);
942
+ localStorage.setItem(Rn + e, t);
912
943
  } catch {
913
944
  }
914
945
  }
915
- function re({ label: t, options: e, onSelect: n }) {
946
+ const re = 44, ae = 8, ka = 4, zn = { corner: "br", x: 16, y: 16 };
947
+ function Sa() {
948
+ try {
949
+ const e = JSON.parse(Z("position"));
950
+ if (e && /^[tb][lr]$/.test(e.corner) && isFinite(e.x) && isFinite(e.y)) return e;
951
+ } catch {
952
+ }
953
+ return { ...zn };
954
+ }
955
+ function rn(e) {
956
+ const t = Math.max(ae, innerWidth - re - ae), n = Math.max(ae, innerHeight - re - ae);
957
+ return { corner: e.corner, x: Math.round(Math.min(Math.max(e.x, ae), t)), y: Math.round(Math.min(Math.max(e.y, ae), n)) };
958
+ }
959
+ function $a({ wrapEl: e, launcherEl: t }) {
960
+ let n = Sa();
961
+ function a() {
962
+ const d = rn(n), p = d.corner[1] === "l", u = d.corner[0] === "t";
963
+ Object.assign(e.style, {
964
+ left: p ? `${d.x}px` : "auto",
965
+ right: p ? "auto" : `${d.x}px`,
966
+ top: u ? `${d.y}px` : "auto",
967
+ bottom: u ? "auto" : `${d.y}px`
968
+ }), e.style.setProperty("--off-x", `${d.x}px`), e.style.setProperty("--off-y", `${d.y}px`), e.classList.toggle("at-left", p), e.classList.toggle("at-top", u);
969
+ }
970
+ function o(d, p = !0) {
971
+ n = rn(d), a(), p && oe("position", JSON.stringify(n));
972
+ }
973
+ function s(d, p) {
974
+ const u = d + re / 2 < innerWidth / 2, v = p + re / 2 < innerHeight / 2, f = !v && e.classList.contains("is-open") ? 10 : 0;
975
+ return {
976
+ corner: (v ? "t" : "b") + (u ? "l" : "r"),
977
+ x: u ? d : innerWidth - d - re,
978
+ y: v ? p : innerHeight - p - re - f
979
+ };
980
+ }
981
+ let r = null, i = !1;
982
+ t.addEventListener("pointerdown", (d) => {
983
+ if (d.button !== 0) return;
984
+ const p = t.getBoundingClientRect();
985
+ r = { id: d.pointerId, sx: d.clientX, sy: d.clientY, dx: d.clientX - p.left, dy: d.clientY - p.top, moving: !1 };
986
+ }), t.addEventListener("pointermove", (d) => {
987
+ if (!(!r || d.pointerId !== r.id)) {
988
+ if (!r.moving) {
989
+ if (Math.hypot(d.clientX - r.sx, d.clientY - r.sy) < ka) return;
990
+ r.moving = !0, t.setPointerCapture(d.pointerId), e.classList.add("dragging");
991
+ }
992
+ o(s(d.clientX - r.dx, d.clientY - r.dy), !1);
993
+ }
994
+ });
995
+ const l = (d) => {
996
+ !r || d.pointerId !== r.id || (r.moving && (i = !0, e.classList.remove("dragging"), o(n)), r = null);
997
+ };
998
+ return t.addEventListener("pointerup", l), t.addEventListener("pointercancel", l), t.addEventListener("keydown", (d) => {
999
+ const p = { ArrowLeft: [-1, 0], ArrowRight: [1, 0], ArrowUp: [0, -1], ArrowDown: [0, 1] }[d.key];
1000
+ if (!p || d.altKey || d.metaKey || d.ctrlKey) return;
1001
+ d.preventDefault();
1002
+ const u = d.shiftKey ? 10 : 1, v = n.corner[1] === "l" ? 1 : -1, f = n.corner[0] === "t" ? 1 : -1;
1003
+ o({ corner: n.corner, x: n.x + p[0] * u * v, y: n.y + p[1] * u * f });
1004
+ }), window.addEventListener("resize", a), a(), {
1005
+ /** Call from the launcher's click handler: true = ignore this click (it ended a drag). */
1006
+ consumeClick() {
1007
+ const d = i;
1008
+ return i = !1, d;
1009
+ },
1010
+ reset() {
1011
+ o({ ...zn });
1012
+ }
1013
+ };
1014
+ }
1015
+ function ce({ label: e, options: t, onSelect: n }) {
916
1016
  const a = document.createElement("div");
917
- a.className = "seg", a.setAttribute("role", "radiogroup"), a.setAttribute("aria-label", t);
918
- const o = e.map((l) => {
919
- const p = document.createElement("button");
920
- return p.type = "button", p.setAttribute("role", "radio"), p.dataset.value = l.value, p.textContent = l.label, l.title && (p.title = l.title), p.addEventListener("click", () => i(l.value)), p;
1017
+ a.className = "seg", a.setAttribute("role", "radiogroup"), a.setAttribute("aria-label", e);
1018
+ const o = t.map((l) => {
1019
+ const d = document.createElement("button");
1020
+ return d.type = "button", d.setAttribute("role", "radio"), d.dataset.value = l.value, d.textContent = l.label, l.title && (d.title = l.title), d.addEventListener("click", () => i(l.value)), d;
921
1021
  });
922
1022
  a.append(...o);
923
- let r = null;
924
- function s(l) {
925
- r = l, o.forEach((c) => c.setAttribute("aria-checked", String(c.dataset.value === l)));
926
- const p = Math.max(0, o.findIndex((c) => c.dataset.value === l));
927
- o.forEach((c, h) => {
928
- c.tabIndex = h === p ? 0 : -1;
1023
+ let s = null;
1024
+ function r(l) {
1025
+ s = l, o.forEach((p) => p.setAttribute("aria-checked", String(p.dataset.value === l)));
1026
+ const d = Math.max(0, o.findIndex((p) => p.dataset.value === l));
1027
+ o.forEach((p, u) => {
1028
+ p.tabIndex = u === d ? 0 : -1;
929
1029
  });
930
1030
  }
931
- function i(l, p) {
932
- l !== r && (s(l), n(l)), p && o.find((c) => c.dataset.value === l).focus();
1031
+ function i(l, d) {
1032
+ l !== s && (r(l), n(l)), d && o.find((p) => p.dataset.value === l).focus();
933
1033
  }
934
1034
  return a.addEventListener("keydown", (l) => {
935
- const p = o.indexOf(l.target);
936
- if (p < 0) return;
937
- const c = { ArrowRight: 1, ArrowDown: 1, ArrowLeft: -1, ArrowUp: -1 }[l.key];
938
- let h;
939
- if (c) h = (p + c + o.length) % o.length;
940
- else if (l.key === "Home") h = 0;
941
- else if (l.key === "End") h = o.length - 1;
1035
+ const d = o.indexOf(l.target);
1036
+ if (d < 0) return;
1037
+ const p = { ArrowRight: 1, ArrowDown: 1, ArrowLeft: -1, ArrowUp: -1 }[l.key];
1038
+ let u;
1039
+ if (p) u = (d + p + o.length) % o.length;
1040
+ else if (l.key === "Home") u = 0;
1041
+ else if (l.key === "End") u = o.length - 1;
942
1042
  else return;
943
- l.preventDefault(), i(o[h].dataset.value, !0);
944
- }), s(null), { el: a, setValue: s };
1043
+ l.preventDefault(), i(o[u].dataset.value, !0);
1044
+ }), r(null), { el: a, setValue: r };
945
1045
  }
946
- const Ln = /var\(\s*(--twd-[\w-]+)/g, ha = /::?[\w-]+(\([^)]*\))?/g;
947
- function* Ue(t) {
948
- for (const e of t)
949
- e.selectorText != null && (yield e), e.cssRules && (yield* Ue(e.cssRules));
1046
+ const In = /var\(\s*(--twd-[\w-]+)/g, Ea = /::?[\w-]+(\([^)]*\))?/g;
1047
+ function* _e(e) {
1048
+ for (const t of e)
1049
+ t.selectorText != null && (yield t), t.cssRules && (yield* _e(t.cssRules));
950
1050
  }
951
- function fa() {
952
- const t = [];
953
- for (const e of document.styleSheets) {
954
- if (e.ownerNode?.id === "twiddle-scoped") continue;
1051
+ function Ca() {
1052
+ const e = [];
1053
+ for (const t of document.styleSheets) {
1054
+ if (t.ownerNode?.id === "twiddle-scoped") continue;
955
1055
  let n;
956
1056
  try {
957
- n = e.cssRules;
1057
+ n = t.cssRules;
958
1058
  } catch {
959
1059
  continue;
960
1060
  }
961
- for (const a of Ue(n)) {
962
- const o = [...a.style.cssText.matchAll(Ln)].map((r) => r[1]);
963
- o.length && t.push({ selectors: a.selectorText.split(","), vars: o });
1061
+ for (const a of _e(n)) {
1062
+ const o = [...a.style.cssText.matchAll(In)].map((s) => s[1]);
1063
+ o.length && e.push({ selectors: a.selectorText.split(","), vars: o });
964
1064
  }
965
1065
  }
966
- return t;
1066
+ return e;
967
1067
  }
968
- function ga(t, e) {
969
- const n = e.replace(ha, "").trim() || "*";
1068
+ function Ma(e, t) {
1069
+ const n = t.replace(Ea, "").trim() || "*";
970
1070
  try {
971
- return t.matches(n);
1071
+ return e.matches(n);
972
1072
  } catch {
973
1073
  return !1;
974
1074
  }
975
1075
  }
976
- function Pn(t) {
977
- const e = /* @__PURE__ */ new Set(), n = [];
978
- return fa().forEach(({ selectors: a, vars: o }) => {
979
- const r = a.find((s) => ga(t, s));
980
- r && (o.forEach((s) => e.add(s)), n.push(r.trim()));
981
- }), [...(t.getAttribute("style") || "").matchAll(Ln)].forEach((a) => e.add(a[1])), { names: e, selectors: n };
1076
+ function Dn(e) {
1077
+ const t = /* @__PURE__ */ new Set(), n = [];
1078
+ return Ca().forEach(({ selectors: a, vars: o }) => {
1079
+ const s = a.find((r) => Ma(e, r));
1080
+ s && (o.forEach((r) => t.add(r)), n.push(s.trim()));
1081
+ }), [...(e.getAttribute("style") || "").matchAll(In)].forEach((a) => t.add(a[1])), { names: t, selectors: n };
982
1082
  }
983
- const mt = (t) => window.CSS && CSS.escape ? CSS.escape(t) : t;
984
- function ma(t) {
985
- const e = [];
986
- let n = t;
1083
+ const yt = (e) => window.CSS && CSS.escape ? CSS.escape(e) : e;
1084
+ function Ta(e) {
1085
+ const t = [];
1086
+ let n = e;
987
1087
  for (; n && n.nodeType === 1 && n !== document.body && n !== document.documentElement; ) {
988
- if (n.id && document.querySelectorAll(`#${mt(n.id)}`).length === 1)
989
- return e.unshift(`#${mt(n.id)}`), e.join(" > ");
990
- const a = n.localName, o = [...n.parentElement.children].filter((r) => r.localName === a);
991
- e.unshift(o.length > 1 ? `${a}:nth-of-type(${o.indexOf(n) + 1})` : a), n = n.parentElement;
1088
+ if (n.id && document.querySelectorAll(`#${yt(n.id)}`).length === 1)
1089
+ return t.unshift(`#${yt(n.id)}`), t.join(" > ");
1090
+ const a = n.localName, o = [...n.parentElement.children].filter((s) => s.localName === a);
1091
+ t.unshift(o.length > 1 ? `${a}:nth-of-type(${o.indexOf(n) + 1})` : a), n = n.parentElement;
992
1092
  }
993
- return ["body", ...e].join(" > ");
1093
+ return ["body", ...t].join(" > ");
994
1094
  }
995
- function ba(t, e = Pn(t).selectors) {
996
- const n = t.getAttribute("data-twd");
1095
+ function Aa(e, t = Dn(e).selectors) {
1096
+ const n = e.getAttribute("data-twd");
997
1097
  if (n) return `[data-twd="${n.replace(/"/g, "")}"]`;
998
- const a = [...t.classList], r = a.find((s) => e.some((i) => new RegExp(`\\.${s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(?![\\w-])`).test(i))) || a[0];
999
- return r ? `.${mt(r)}` : null;
1098
+ const a = [...e.classList], s = a.find((r) => t.some((i) => new RegExp(`\\.${r.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(?![\\w-])`).test(i))) || a[0];
1099
+ return s ? `.${yt(s)}` : null;
1000
1100
  }
1001
- function xa(t) {
1002
- const e = Pn(t), n = ba(t, e.selectors), a = (t.getAttribute("aria-label") || t.placeholder || t.textContent || "").trim().replace(/\s+/g, " ");
1101
+ function La(e) {
1102
+ const t = Dn(e), n = Aa(e, t.selectors), a = (e.getAttribute("aria-label") || e.placeholder || e.textContent || "").trim().replace(/\s+/g, " ");
1003
1103
  return {
1004
- el: t,
1005
- tag: t.localName,
1006
- classes: [...t.classList],
1104
+ el: e,
1105
+ tag: e.localName,
1106
+ classes: [...e.classList],
1007
1107
  text: a.length > 40 ? `${a.slice(0, 40)}…` : a,
1008
- tokens: e.names,
1009
- elementSelector: ma(t),
1108
+ tokens: t.names,
1109
+ elementSelector: Ta(e),
1010
1110
  classSelector: n,
1011
1111
  classCount: n ? document.querySelectorAll(n).length : 0
1012
1112
  };
1013
1113
  }
1014
- const bt = (t) => t.localName + [...t.classList].map((e) => `.${e}`).join(""), wa = { radius: "px", intensity: "%", base: "px" }, On = (t) => t.charAt(0).toUpperCase() + t.slice(1), va = (t, e) => typeof e == "number" || /^\d+$/.test(e) ? `${e}${wa[t] || ""}` : On(String(e));
1015
- function Qt(t, e, n) {
1016
- const a = Mn(() => t.tokens({ [t.id]: e }) || {}), o = Object.keys(a);
1017
- let r = {};
1114
+ const vt = (e) => e.localName + [...e.classList].map((t) => `.${t}`).join(""), Pa = { radius: "px", intensity: "%", base: "px" }, Un = (e) => e.charAt(0).toUpperCase() + e.slice(1), Oa = (e, t) => typeof t == "number" || /^\d+$/.test(t) ? `${t}${Pa[e] || ""}` : Un(String(t));
1115
+ function sn(e, t, n) {
1116
+ const a = jn(() => e.tokens({ [e.id]: t }) || {}), o = Object.keys(a);
1117
+ let s = {};
1018
1118
  if (o.length) {
1019
- const s = n.selector ? document.querySelector(n.selector) : void 0;
1020
- r = da(o, s || void 0);
1119
+ const r = n.selector ? document.querySelector(n.selector) : void 0;
1120
+ s = xa(o, r || void 0);
1021
1121
  }
1022
1122
  return {
1023
1123
  ...n,
1024
- sectionId: t.id,
1025
- title: t.title,
1026
- settings: Object.entries(e).map(([s, i]) => ({ key: s, label: On(s), value: va(s, i), ...t.describe?.(s, i) || {} })),
1027
- tokens: o.map((s) => ({ name: s, from: r[s] || "", to: a[s] }))
1124
+ sectionId: e.id,
1125
+ title: e.title,
1126
+ settings: Object.entries(t).map(([r, i]) => ({ key: r, label: Un(r), value: Oa(r, i), ...e.describe?.(r, i) || {} })),
1127
+ tokens: o.map((r) => ({ name: r, from: s[r] || "", to: a[r] }))
1028
1128
  };
1029
1129
  }
1030
- function en(t, e) {
1130
+ function ln(e, t) {
1031
1131
  const n = [];
1032
- return e.forEach((a) => {
1033
- t[a.id] && n.push(Qt(a, t[a.id], { kind: "global", selector: null }));
1034
- }), Object.entries(t.scopes || {}).forEach(([a, o]) => {
1035
- const r = ft(a) ? "class" : "element";
1036
- e.forEach((s) => {
1037
- o[s.id] && n.push(Qt(s, o[s.id], { kind: r, selector: a }));
1132
+ return t.forEach((a) => {
1133
+ e[a.id] && n.push(sn(a, e[a.id], { kind: "global", selector: null }));
1134
+ }), Object.entries(e.scopes || {}).forEach(([a, o]) => {
1135
+ const s = xt(a) ? "class" : "element";
1136
+ t.forEach((r) => {
1137
+ o[r.id] && n.push(sn(r, o[r.id], { kind: s, selector: a }));
1038
1138
  });
1039
1139
  }), n;
1040
1140
  }
1041
- function ya(t) {
1042
- const e = (o) => Object.values(o || {}).reduce((r, s) => r + Object.keys(s || {}).length, 0), { scopes: n, ...a } = t;
1043
- return e(a) + Object.values(n || {}).reduce((o, r) => o + e(r), 0);
1141
+ function Fa(e) {
1142
+ const t = (o) => Object.values(o || {}).reduce((s, r) => s + Object.keys(r || {}).length, 0), { scopes: n, ...a } = e;
1143
+ return t(a) + Object.values(n || {}).reduce((o, s) => o + t(s), 0);
1044
1144
  }
1045
- const ae = (t) => `\`${t}\``, Ze = (t) => t.map((e) => ` - ${ae(e.name)}: ${e.from ? `${ae(e.from)} → ` : ""}${ae(e.to)}`), Xe = (t) => `${t.title} › ${t.settings.map((e) => `${e.label}: ${e.value}`).join(", ")}`;
1046
- function ka(t) {
1047
- let e = null;
1145
+ const le = (e) => `\`${e}\``, et = (e) => e.map((t) => ` - ${le(t.name)}: ${t.from ? `${le(t.from)} → ` : ""}${le(t.to)}`), tt = (e) => `${e.title} › ${e.settings.map((t) => `${t.label}: ${t.value}`).join(", ")}`;
1146
+ function Na(e) {
1147
+ let t = null;
1048
1148
  try {
1049
- e = document.querySelector(t);
1149
+ t = document.querySelector(e);
1050
1150
  } catch {
1051
1151
  }
1052
- if (!e) return "not found on the current page";
1053
- const n = (e.getAttribute("aria-label") || e.placeholder || e.textContent || "").trim().replace(/\s+/g, " ").slice(0, 60);
1054
- return `${ae(bt(e))}${n ? ` with text “${n}”` : ""}`;
1152
+ if (!t) return "not found on the current page";
1153
+ const n = (t.getAttribute("aria-label") || t.placeholder || t.textContent || "").trim().replace(/\s+/g, " ").slice(0, 60);
1154
+ return `${le(vt(t))}${n ? ` with text “${n}”` : ""}`;
1055
1155
  }
1056
- function tn(t, e) {
1156
+ function dn(e, t) {
1057
1157
  const n = /* @__PURE__ */ new Map();
1058
- return t.forEach((a) => {
1059
- const o = a[e];
1158
+ return e.forEach((a) => {
1159
+ const o = a[t];
1060
1160
  n.has(o) || n.set(o, []), n.get(o).push(a);
1061
1161
  }), [...n];
1062
1162
  }
1063
- function Sa(t, { link: e = location.href, warnings: n = [] } = {}) {
1064
- if (!t.length) return "";
1065
- const a = t.filter((i) => i.kind === "global"), o = t.filter((i) => i.kind === "class"), r = t.filter((i) => i.kind === "element"), s = [];
1066
- if (s.push(
1163
+ function ja(e, { link: t = location.href, warnings: n = [] } = {}) {
1164
+ if (!e.length) return "";
1165
+ const a = e.filter((i) => i.kind === "global"), o = e.filter((i) => i.kind === "class"), s = e.filter((i) => i.kind === "element"), r = [];
1166
+ if (r.push(
1067
1167
  "# Apply design tweaks from Panel",
1068
1168
  "",
1069
1169
  "I explored design changes to this prototype with Panel (by Twiddle), a tweak panel that overrides the prototype's `--twd-*` CSS custom properties (design tokens) at runtime. Make these changes permanent in the source code.",
1070
1170
  "",
1071
- `Preview of the tweaked version: ${e}`
1072
- ), a.length && (s.push(
1171
+ `Preview of the tweaked version: ${t}`
1172
+ ), a.length && (r.push(
1073
1173
  "",
1074
1174
  "## 1. Global token changes",
1075
1175
  "",
1076
1176
  "Update the default value of each token where it is defined (usually a `:root` rule or a `tokens.css` / `twiddle-tokens.css` file). Change the definition, not the places that use it.",
1077
1177
  ""
1078
- ), a.forEach((i) => s.push(`- **${Xe(i)}**`, ...Ze(i.tokens)))), o.length && (s.push(
1178
+ ), a.forEach((i) => r.push(`- **${tt(i)}**`, ...et(i.tokens)))), o.length && (r.push(
1079
1179
  "",
1080
1180
  `## ${a.length ? 2 : 1}. Component-wide changes`,
1081
1181
  "",
1082
1182
  "These apply to every element matching the selector. In the CSS rule for that component, redeclare the token, e.g. `.input { --twd-radius-control: 999px; }`. Don't replace the `var(--twd-…)` usages with fixed values.",
1083
1183
  ""
1084
- ), tn(o, "selector").forEach(([i, l]) => {
1085
- s.push(`- ${ae(i)} (${document.querySelectorAll(i).length} on this page)`), l.forEach((p) => s.push(` - **${Xe(p)}**`, ...Ze(p.tokens).map((c) => ` ${c}`)));
1086
- })), r.length) {
1184
+ ), dn(o, "selector").forEach(([i, l]) => {
1185
+ r.push(`- ${le(i)} (${document.querySelectorAll(i).length} on this page)`), l.forEach((d) => r.push(` - **${tt(d)}**`, ...et(d.tokens).map((p) => ` ${p}`)));
1186
+ })), s.length) {
1087
1187
  const i = 1 + (a.length ? 1 : 0) + (o.length ? 1 : 0);
1088
- s.push(
1188
+ r.push(
1089
1189
  "",
1090
1190
  `## ${i}. Single-element changes`,
1091
1191
  "",
1092
1192
  "These apply to one element only. Add a modifier class to that element (e.g. `input--pill`) and redeclare the token in a rule for that class. The selectors below are runtime paths to help you find the element; don't copy them into the stylesheet.",
1093
1193
  ""
1094
- ), tn(r, "selector").forEach(([l, p]) => {
1095
- s.push(`- ${ka(l)}, found at ${ae(l)}`), p.forEach((c) => s.push(` - **${Xe(c)}**`, ...Ze(c.tokens).map((h) => ` ${h}`)));
1194
+ ), dn(s, "selector").forEach(([l, d]) => {
1195
+ r.push(`- ${Na(l)}, found at ${le(l)}`), d.forEach((p) => r.push(` - **${tt(p)}**`, ...et(p.tokens).map((u) => ` ${u}`)));
1096
1196
  });
1097
1197
  }
1098
- return n.length && s.push(
1198
+ return n.length && r.push(
1099
1199
  "",
1100
1200
  "## Contrast to check",
1101
1201
  "",
1102
1202
  "Panel flagged these colour pairs as below WCAG AA with the new values:",
1103
1203
  "",
1104
1204
  ...n.map((i) => `- ${i.text}`)
1105
- ), s.push(
1205
+ ), r.push(
1106
1206
  "",
1107
1207
  "## Constraints",
1108
1208
  "",
@@ -1110,88 +1210,88 @@ function Sa(t, { link: e = location.href, warnings: n = [] } = {}) {
1110
1210
  "- Change only what is listed above.",
1111
1211
  "- If a value uses `color-mix()` and the project targets older browsers, ask before converting it.",
1112
1212
  "- When done, list the files you changed."
1113
- ), s.join(`
1213
+ ), r.join(`
1114
1214
  `);
1115
1215
  }
1116
- const Re = 4.5, $a = 3, Ea = [
1216
+ const Ue = 4.5, Ra = 3, za = [
1117
1217
  ["--twd-text", "--twd-bg", "Text on background"],
1118
1218
  ["--twd-text", "--twd-surface", "Text on surface"],
1119
1219
  ["--twd-text-muted", "--twd-surface", "Muted text on surface"],
1120
1220
  ["--twd-on-primary", "--twd-primary", "Text on brand"]
1121
1221
  ];
1122
- let W;
1123
- function St(t) {
1124
- if (!t || (W ||= Object.assign(document.createElement("canvas"), { width: 1, height: 1 }).getContext("2d", { willReadFrequently: !0 }), !W)) return null;
1125
- W.clearRect(0, 0, 1, 1), W.fillStyle = "#000", W.fillStyle = t, W.fillRect(0, 0, 1, 1);
1126
- const [e, n, a, o] = W.getImageData(0, 0, 1, 1).data;
1127
- return [e, n, a, o / 255];
1128
- }
1129
- function nn(t) {
1130
- const e = document.createElement("i");
1131
- e.style.cssText = `position:absolute;visibility:hidden;color:var(${t})`, document.documentElement.appendChild(e);
1132
- const n = getComputedStyle(e).color;
1133
- return e.remove(), St(n);
1134
- }
1135
- const an = ([t, e, n]) => {
1222
+ let Y;
1223
+ function Mt(e) {
1224
+ if (!e || (Y ||= Object.assign(document.createElement("canvas"), { width: 1, height: 1 }).getContext("2d", { willReadFrequently: !0 }), !Y)) return null;
1225
+ Y.clearRect(0, 0, 1, 1), Y.fillStyle = "#000", Y.fillStyle = e, Y.fillRect(0, 0, 1, 1);
1226
+ const [t, n, a, o] = Y.getImageData(0, 0, 1, 1).data;
1227
+ return [t, n, a, o / 255];
1228
+ }
1229
+ function cn(e) {
1230
+ const t = document.createElement("i");
1231
+ t.style.cssText = `position:absolute;visibility:hidden;color:var(${e})`, document.documentElement.appendChild(t);
1232
+ const n = getComputedStyle(t).color;
1233
+ return t.remove(), Mt(n);
1234
+ }
1235
+ const pn = ([e, t, n]) => {
1136
1236
  const a = (o) => (o /= 255, o <= 0.03928 ? o / 12.92 : ((o + 0.055) / 1.055) ** 2.4);
1137
- return 0.2126 * a(t) + 0.7152 * a(e) + 0.0722 * a(n);
1138
- }, Fn = (t, e) => t.slice(0, 3).map((n, a) => n * t[3] + e[a] * (1 - t[3])).concat(1);
1139
- function Nn(t, e) {
1140
- const n = an(t[3] < 1 ? Fn(t, e) : t), a = an(e);
1237
+ return 0.2126 * a(e) + 0.7152 * a(t) + 0.0722 * a(n);
1238
+ }, Bn = (e, t) => e.slice(0, 3).map((n, a) => n * e[3] + t[a] * (1 - e[3])).concat(1);
1239
+ function qn(e, t) {
1240
+ const n = pn(e[3] < 1 ? Bn(e, t) : e), a = pn(t);
1141
1241
  return (Math.max(n, a) + 0.05) / (Math.min(n, a) + 0.05);
1142
1242
  }
1143
- const jn = (t) => `${t.toFixed(1)}:1`;
1144
- function Je() {
1145
- const t = [];
1146
- return Ea.forEach(([e, n, a]) => {
1147
- const o = nn(e), r = nn(n);
1148
- if (!o || !r || r[3] === 0) return;
1149
- const s = Nn(o, r);
1150
- s < Re && t.push({ label: a, ratio: s, needed: Re, text: `${a}: ${jn(s)} · AA needs ${Re}` });
1151
- }), t;
1152
- }
1153
- function Ca(t) {
1243
+ const Hn = (e) => `${e.toFixed(1)}:1`;
1244
+ function nt() {
1154
1245
  const e = [];
1155
- for (let n = t; n && n.nodeType === 1; n = n.parentElement) {
1156
- const a = St(getComputedStyle(n).backgroundColor);
1157
- if (a && a[3] > 0 && (e.push(a), a[3] >= 1))
1246
+ return za.forEach(([t, n, a]) => {
1247
+ const o = cn(t), s = cn(n);
1248
+ if (!o || !s || s[3] === 0) return;
1249
+ const r = qn(o, s);
1250
+ r < Ue && e.push({ label: a, ratio: r, needed: Ue, text: `${a}: ${Hn(r)} · AA needs ${Ue}` });
1251
+ }), e;
1252
+ }
1253
+ function Ia(e) {
1254
+ const t = [];
1255
+ for (let n = e; n && n.nodeType === 1; n = n.parentElement) {
1256
+ const a = Mt(getComputedStyle(n).backgroundColor);
1257
+ if (a && a[3] > 0 && (t.push(a), a[3] >= 1))
1158
1258
  break;
1159
1259
  }
1160
- return e.reduceRight((n, a) => Fn(a, n), [255, 255, 255, 1]);
1260
+ return t.reduceRight((n, a) => Bn(a, n), [255, 255, 255, 1]);
1161
1261
  }
1162
- function Ta(t) {
1163
- if (!t.textContent.trim() && !t.placeholder) return null;
1164
- const e = getComputedStyle(t), n = St(e.color);
1262
+ function Da(e) {
1263
+ if (!e.textContent.trim() && !e.placeholder) return null;
1264
+ const t = getComputedStyle(e), n = Mt(t.color);
1165
1265
  if (!n) return null;
1166
- const a = parseFloat(e.fontSize), r = a >= 24 || a >= 18.66 && parseInt(e.fontWeight, 10) >= 700 ? $a : Re, s = Nn(n, Ca(t));
1167
- return s < r ? { label: "This element", ratio: s, needed: r, text: `This element’s text: ${jn(s)} · AA needs ${r}` } : null;
1168
- }
1169
- const on = "twiddle-panel", Ma = "0.7.0", rn = "https://panel.twiddle.in", Aa = "--twd-radius-control", La = /var\(\s*--twd-/;
1170
- function Pa(t) {
1171
- return t.style.cssText.split(";").some((e) => {
1172
- const n = e.slice(0, e.indexOf(":")).trim();
1173
- return n && !n.startsWith("--") && La.test(e);
1266
+ const a = parseFloat(t.fontSize), s = a >= 24 || a >= 18.66 && parseInt(t.fontWeight, 10) >= 700 ? Ra : Ue, r = qn(n, Ia(e));
1267
+ return r < s ? { label: "This element", ratio: r, needed: s, text: `This element’s text: ${Hn(r)} · AA needs ${s}` } : null;
1268
+ }
1269
+ const un = "twiddle-panel", Ua = "0.8.0", hn = "https://panel.twiddle.in", Ba = "--twd-radius-control", qa = /var\(\s*--twd-/;
1270
+ function Ha(e) {
1271
+ return e.style.cssText.split(";").some((t) => {
1272
+ const n = t.slice(0, t.indexOf(":")).trim();
1273
+ return n && !n.startsWith("--") && qa.test(t);
1174
1274
  });
1175
1275
  }
1176
- function Oa() {
1177
- if (!getComputedStyle(document.documentElement).getPropertyValue(Aa).trim()) return "no-tokens";
1178
- let t = !1;
1179
- for (const e of document.styleSheets) {
1180
- if (e.ownerNode?.id === "twiddle-scoped") continue;
1276
+ function Va() {
1277
+ if (!getComputedStyle(document.documentElement).getPropertyValue(Ba).trim()) return "no-tokens";
1278
+ let e = !1;
1279
+ for (const t of document.styleSheets) {
1280
+ if (t.ownerNode?.id === "twiddle-scoped") continue;
1181
1281
  let n;
1182
1282
  try {
1183
- n = e.cssRules;
1283
+ n = t.cssRules;
1184
1284
  } catch {
1185
- t = !0;
1285
+ e = !0;
1186
1286
  continue;
1187
1287
  }
1188
- for (const a of Ue(n)) if (Pa(a)) return "ok";
1288
+ for (const a of _e(n)) if (Ha(a)) return "ok";
1189
1289
  }
1190
- return document.querySelector('[style*="var(--twd-"]') ? "ok" : t ? "unknown" : "unused";
1290
+ return document.querySelector('[style*="var(--twd-"]') ? "ok" : e ? "unknown" : "unused";
1191
1291
  }
1192
- function Fa() {
1193
- const t = /* @__PURE__ */ new Set(), e = (n) => {
1194
- for (const a of n.matchAll(/var\(\s*(--twd-[\w-]+)/g)) t.add(a[1]);
1292
+ function _a() {
1293
+ const e = /* @__PURE__ */ new Set(), t = (n) => {
1294
+ for (const a of n.matchAll(/var\(\s*(--twd-[\w-]+)/g)) e.add(a[1]);
1195
1295
  };
1196
1296
  for (const n of document.styleSheets) {
1197
1297
  if (n.ownerNode?.id === "twiddle-scoped") continue;
@@ -1201,19 +1301,19 @@ function Fa() {
1201
1301
  } catch {
1202
1302
  continue;
1203
1303
  }
1204
- for (const o of Ue(a))
1205
- o.style.cssText.split(";").forEach((r) => {
1206
- const s = r.slice(0, r.indexOf(":")).trim();
1207
- s && !s.startsWith("--") && e(r);
1304
+ for (const o of _e(a))
1305
+ o.style.cssText.split(";").forEach((s) => {
1306
+ const r = s.slice(0, s.indexOf(":")).trim();
1307
+ r && !r.startsWith("--") && t(s);
1208
1308
  });
1209
1309
  }
1210
- return document.querySelectorAll('[style*="var(--twd-"]').forEach((n) => e(n.getAttribute("style"))), t;
1310
+ return document.querySelectorAll('[style*="var(--twd-"]').forEach((n) => t(n.getAttribute("style"))), e;
1211
1311
  }
1212
- const sn = [
1213
- { label: "With a build tool, in your entry file", code: `import '${on}/tokens.css';` },
1312
+ const fn = [
1313
+ { label: "With a build tool, in your entry file", code: `import '${un}/tokens.css';` },
1214
1314
  // Pinned to this build's version: unversioned jsDelivr links can lag a release by days, and unpkg once failed for a fresh one.
1215
- { label: "Plain HTML, in <head>", code: `<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/${on}@${Ma}/dist/twiddle-tokens.css">` }
1216
- ], Na = `Convert this prototype's styles to use the Panel design tokens (the --twd-* CSS custom properties from twiddle-panel), so the panel can tweak them live.
1315
+ { label: "Plain HTML, in <head>", code: `<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/${un}@${Ua}/dist/twiddle-tokens.css">` }
1316
+ ], Ka = `Convert this prototype's styles to use the Panel design tokens (the --twd-* CSS custom properties from twiddle-panel), so the panel can tweak them live.
1217
1317
 
1218
1318
  1. Keep the current look. In a :root rule loaded after twiddle-tokens.css, set each token below to the value this project already uses (for example, if buttons use #0f766e, set --twd-primary: #0f766e). Only set the tokens the project actually needs.
1219
1319
  2. Replace fixed values in component styles with var(--twd-…). Map each value to the closest token by role:
@@ -1231,205 +1331,205 @@ const sn = [
1231
1331
  3. Leave values that don't match a role as they are (layout widths, one-off illustrations, icon sizes). Don't invent new --twd-* tokens.
1232
1332
  4. Don't change the layout or how the page looks; after the change it should look the same until someone uses the panel.
1233
1333
  5. When done, list the files you changed and any values you left fixed, with the reason.`;
1234
- function B(t) {
1235
- const e = document.createElement("div");
1236
- e.className = "field";
1334
+ function U(e) {
1335
+ const t = document.createElement("div");
1336
+ t.className = "field";
1237
1337
  const n = document.createElement("div");
1238
1338
  n.className = "field-head";
1239
1339
  const a = document.createElement("span");
1240
- a.className = "field-label", a.textContent = t;
1340
+ a.className = "field-label", a.textContent = e;
1241
1341
  const o = document.createElement("span");
1242
- return o.className = "field-value", o.hidden = !0, n.append(a, o), e.append(n), {
1243
- el: e,
1244
- setValue(r) {
1245
- o.textContent = r || "", o.hidden = !r;
1342
+ return o.className = "field-value", o.hidden = !0, n.append(a, o), t.append(n), {
1343
+ el: t,
1344
+ setValue(s) {
1345
+ o.textContent = s || "", o.hidden = !s;
1246
1346
  },
1247
- append(...r) {
1248
- e.append(...r);
1347
+ append(...s) {
1348
+ t.append(...s);
1249
1349
  }
1250
1350
  };
1251
1351
  }
1252
- function ja({ label: t, ends: e, cols: n = 9, rows: a = 5, onInput: o }) {
1253
- const r = document.createElement("div");
1254
- r.className = "pad", r.tabIndex = 0, r.setAttribute("role", "group"), r.setAttribute("aria-roledescription", "2D slider");
1255
- const s = (b, u) => {
1256
- const v = document.createElement("span");
1257
- return v.className = `pad-edge ${b}`, v.textContent = u, v.setAttribute("aria-hidden", "true"), v;
1352
+ function Wa({ label: e, ends: t, cols: n = 9, rows: a = 5, onInput: o }) {
1353
+ const s = document.createElement("div");
1354
+ s.className = "pad", s.tabIndex = 0, s.setAttribute("role", "group"), s.setAttribute("aria-roledescription", "2D slider");
1355
+ const r = (b, h) => {
1356
+ const y = document.createElement("span");
1357
+ return y.className = `pad-edge ${b}`, y.textContent = h, y.setAttribute("aria-hidden", "true"), y;
1258
1358
  };
1259
- r.append(s("top", e.top), s("bottom", e.bottom), s("left", e.left), s("right", e.right));
1359
+ s.append(r("top", t.top), r("bottom", t.bottom), r("left", t.left), r("right", t.right));
1260
1360
  const i = document.createElement("div");
1261
1361
  i.className = "pad-area";
1262
1362
  for (let b = 0; b < a; b++)
1263
- for (let u = 0; u < n; u++) {
1264
- const v = document.createElement("i");
1265
- v.className = "pad-dot", v.style.left = `${u / (n - 1) * 100}%`, v.style.top = `${b / (a - 1) * 100}%`, i.append(v);
1363
+ for (let h = 0; h < n; h++) {
1364
+ const y = document.createElement("i");
1365
+ y.className = "pad-dot", y.style.left = `${h / (n - 1) * 100}%`, y.style.top = `${b / (a - 1) * 100}%`, i.append(y);
1266
1366
  }
1267
1367
  const l = document.createElement("span");
1268
- l.className = "pad-knob", i.append(l), r.append(i);
1269
- let p = 0.5, c = 0.5;
1270
- const h = (b, u) => Math.round(Math.min(1, Math.max(0, b)) * (u - 1)) / (u - 1);
1271
- function y() {
1272
- l.style.left = `${p * 100}%`, l.style.top = `${c * 100}%`;
1368
+ l.className = "pad-knob", i.append(l), s.append(i);
1369
+ let d = 0.5, p = 0.5;
1370
+ const u = (b, h) => Math.round(Math.min(1, Math.max(0, b)) * (h - 1)) / (h - 1);
1371
+ function v() {
1372
+ l.style.left = `${d * 100}%`, l.style.top = `${p * 100}%`;
1273
1373
  }
1274
- function f(b, u, v) {
1275
- b = v === "x" ? p : h(b, n), u = v === "y" ? c : h(u, a), !(b === p && u === c) && (p = b, c = u, y(), o(p, c));
1374
+ function f(b, h, y) {
1375
+ b = y === "x" ? d : u(b, n), h = y === "y" ? p : u(h, a), !(b === d && h === p) && (d = b, p = h, v(), o(d, p));
1276
1376
  }
1277
1377
  const m = (b) => {
1278
- const u = i.getBoundingClientRect();
1279
- f((b.clientX - u.left) / u.width, (b.clientY - u.top) / u.height);
1378
+ const h = i.getBoundingClientRect();
1379
+ f((b.clientX - h.left) / h.width, (b.clientY - h.top) / h.height);
1280
1380
  };
1281
- r.addEventListener("pointerdown", (b) => {
1282
- r.setPointerCapture(b.pointerId), r.classList.add("dragging"), m(b);
1283
- }), r.addEventListener("pointermove", (b) => {
1284
- r.hasPointerCapture(b.pointerId) && m(b);
1381
+ s.addEventListener("pointerdown", (b) => {
1382
+ s.setPointerCapture(b.pointerId), s.classList.add("dragging"), m(b);
1383
+ }), s.addEventListener("pointermove", (b) => {
1384
+ s.hasPointerCapture(b.pointerId) && m(b);
1285
1385
  });
1286
1386
  const x = (b) => {
1287
- r.hasPointerCapture(b.pointerId) && r.releasePointerCapture(b.pointerId), r.classList.remove("dragging");
1387
+ s.hasPointerCapture(b.pointerId) && s.releasePointerCapture(b.pointerId), s.classList.remove("dragging");
1288
1388
  };
1289
- return r.addEventListener("pointerup", x), r.addEventListener("pointercancel", x), r.addEventListener("keydown", (b) => {
1290
- const u = { ArrowLeft: [-1, 0], ArrowRight: [1, 0], ArrowUp: [0, -1], ArrowDown: [0, 1] }[b.key];
1291
- u && (b.preventDefault(), u[0] ? f(h(p, n) + u[0] / (n - 1), c, "y") : f(p, h(c, a) + u[1] / (a - 1), "x"));
1389
+ return s.addEventListener("pointerup", x), s.addEventListener("pointercancel", x), s.addEventListener("keydown", (b) => {
1390
+ const h = { ArrowLeft: [-1, 0], ArrowRight: [1, 0], ArrowUp: [0, -1], ArrowDown: [0, 1] }[b.key];
1391
+ h && (b.preventDefault(), h[0] ? f(u(d, n) + h[0] / (n - 1), p, "y") : f(d, u(p, a) + h[1] / (a - 1), "x"));
1292
1392
  }), {
1293
- el: r,
1393
+ el: s,
1294
1394
  /** Show a position without telling anyone (used to sync). Off-grid values are shown as they are. */
1295
- setValue(b, u, v) {
1296
- p = Math.min(1, Math.max(0, b)), c = Math.min(1, Math.max(0, u)), y(), r.setAttribute("aria-label", v ? `${t}: ${v}` : t);
1395
+ setValue(b, h, y) {
1396
+ d = Math.min(1, Math.max(0, b)), p = Math.min(1, Math.max(0, h)), v(), s.setAttribute("aria-label", y ? `${e}: ${y}` : e);
1297
1397
  }
1298
1398
  };
1299
1399
  }
1300
- function Rn({ label: t, min: e, max: n, step: a = 1, format: o = String, onInput: r }) {
1301
- const s = document.createElement("div");
1302
- s.className = "slider";
1400
+ function Vn({ label: e, min: t, max: n, step: a = 1, format: o = String, onInput: s }) {
1401
+ const r = document.createElement("div");
1402
+ r.className = "slider";
1303
1403
  const i = document.createElement("input");
1304
- i.type = "range", i.min = String(e), i.max = String(n), i.step = String(a), i.setAttribute("aria-label", t);
1404
+ i.type = "range", i.min = String(t), i.max = String(n), i.step = String(a), i.setAttribute("aria-label", e);
1305
1405
  const l = document.createElement("span");
1306
- l.className = "slider-value", l.setAttribute("aria-hidden", "true"), s.append(i, l);
1307
- const p = (c) => {
1308
- const h = (Number(i.value) - e) / (n - e) * 100;
1309
- i.style.setProperty("--fill", `${h}%`), l.textContent = c ?? o(Number(i.value)), i.setAttribute("aria-valuetext", l.textContent);
1406
+ l.className = "slider-value", l.setAttribute("aria-hidden", "true"), r.append(i, l);
1407
+ const d = (p) => {
1408
+ const u = (Number(i.value) - t) / (n - t) * 100;
1409
+ i.style.setProperty("--fill", `${u}%`), l.textContent = p ?? o(Number(i.value)), i.setAttribute("aria-valuetext", l.textContent);
1310
1410
  };
1311
1411
  return i.addEventListener("input", () => {
1312
- p(), r(Number(i.value));
1412
+ d(), s(Number(i.value));
1313
1413
  }), {
1314
- el: s,
1414
+ el: r,
1315
1415
  /** Show a value without reporting it (sync after reset / URL load). */
1316
- setValue(c, h) {
1317
- i.value = String(c), p(h);
1416
+ setValue(p, u) {
1417
+ i.value = String(p), d(u);
1318
1418
  }
1319
1419
  };
1320
1420
  }
1321
- function Ra({ label: t, options: e, columns: n = 3, className: a = "", onSelect: o }) {
1322
- const r = document.createElement("div");
1323
- r.className = `tiles ${a}`.trim(), r.style.setProperty("--cols", n), r.setAttribute("role", "radiogroup"), r.setAttribute("aria-label", t);
1324
- const s = e.map((c) => {
1325
- const h = document.createElement("button");
1326
- return h.type = "button", h.className = "tile", c.wide && h.classList.add("wide"), h.setAttribute("role", "radio"), h.dataset.value = c.value, c.title && (h.title = c.title), h.innerHTML = `${c.preview ? `<span class="tile-preview" aria-hidden="true">${c.preview}</span>` : ""}<span class="tile-text"><span class="tile-label"></span>${c.sub ? '<span class="tile-sub"></span>' : ""}</span>`, h.querySelector(".tile-label").textContent = c.label, c.sub && (h.querySelector(".tile-sub").textContent = c.sub), h.addEventListener("click", () => p(c.value)), h;
1421
+ function Ga({ label: e, options: t, columns: n = 3, className: a = "", onSelect: o }) {
1422
+ const s = document.createElement("div");
1423
+ s.className = `tiles ${a}`.trim(), s.style.setProperty("--cols", n), s.setAttribute("role", "radiogroup"), s.setAttribute("aria-label", e);
1424
+ const r = t.map((p) => {
1425
+ const u = document.createElement("button");
1426
+ return u.type = "button", u.className = "tile", p.wide && u.classList.add("wide"), u.setAttribute("role", "radio"), u.dataset.value = p.value, p.title && (u.title = p.title), u.innerHTML = `${p.preview ? `<span class="tile-preview" aria-hidden="true">${p.preview}</span>` : ""}<span class="tile-text"><span class="tile-label"></span>${p.sub ? '<span class="tile-sub"></span>' : ""}</span>`, u.querySelector(".tile-label").textContent = p.label, p.sub && (u.querySelector(".tile-sub").textContent = p.sub), u.addEventListener("click", () => d(p.value)), u;
1327
1427
  });
1328
- r.append(...s);
1428
+ s.append(...r);
1329
1429
  let i = null;
1330
- function l(c) {
1331
- i = c, s.forEach((y) => y.setAttribute("aria-checked", String(y.dataset.value === c)));
1332
- const h = Math.max(0, s.findIndex((y) => y.dataset.value === c));
1333
- s.forEach((y, f) => {
1334
- y.tabIndex = f === h ? 0 : -1;
1430
+ function l(p) {
1431
+ i = p, r.forEach((v) => v.setAttribute("aria-checked", String(v.dataset.value === p)));
1432
+ const u = Math.max(0, r.findIndex((v) => v.dataset.value === p));
1433
+ r.forEach((v, f) => {
1434
+ v.tabIndex = f === u ? 0 : -1;
1335
1435
  });
1336
1436
  }
1337
- function p(c, h) {
1338
- c !== i && (l(c), o(c)), h && s.find((y) => y.dataset.value === c).focus();
1437
+ function d(p, u) {
1438
+ p !== i && (l(p), o(p)), u && r.find((v) => v.dataset.value === p).focus();
1339
1439
  }
1340
- return r.addEventListener("keydown", (c) => {
1341
- const h = s.indexOf(c.target.closest(".tile"));
1342
- if (h < 0) return;
1343
- const y = { ArrowRight: 1, ArrowDown: 1, ArrowLeft: -1, ArrowUp: -1 }[c.key];
1440
+ return s.addEventListener("keydown", (p) => {
1441
+ const u = r.indexOf(p.target.closest(".tile"));
1442
+ if (u < 0) return;
1443
+ const v = { ArrowRight: 1, ArrowDown: 1, ArrowLeft: -1, ArrowUp: -1 }[p.key];
1344
1444
  let f;
1345
- if (y) f = (h + y + s.length) % s.length;
1346
- else if (c.key === "Home") f = 0;
1347
- else if (c.key === "End") f = s.length - 1;
1445
+ if (v) f = (u + v + r.length) % r.length;
1446
+ else if (p.key === "Home") f = 0;
1447
+ else if (p.key === "End") f = r.length - 1;
1348
1448
  else return;
1349
- c.preventDefault(), p(s[f].dataset.value, !0);
1350
- }), l(null), { el: r, setValue: l };
1449
+ p.preventDefault(), d(r[f].dataset.value, !0);
1450
+ }), l(null), { el: s, setValue: l };
1351
1451
  }
1352
- const F = (t) => getComputedStyle(document.documentElement).getPropertyValue(t).trim();
1353
- function ye(t, e) {
1354
- const n = /^(-?\d*\.?\d+)([a-z%]*)$/i.exec(String(t).trim());
1452
+ const F = (e) => getComputedStyle(document.documentElement).getPropertyValue(e).trim();
1453
+ function Se(e, t) {
1454
+ const n = /^(-?\d*\.?\d+)([a-z%]*)$/i.exec(String(e).trim());
1355
1455
  if (!n) return null;
1356
- const a = parseFloat(n[1]) * e, o = n[2] || "px";
1456
+ const a = parseFloat(n[1]) * t, o = n[2] || "px";
1357
1457
  return o === "px" ? `${Math.round(a)}px` : `${+a.toFixed(3)}${o}`;
1358
1458
  }
1359
- const za = 999;
1360
- function zn({ id: t, title: e, hint: n, token: a, uses: o = [], presets: r, max: s, extra: i = [], extraTokens: l = () => ({}), shape: p = "card" }) {
1361
- const c = (f) => r.find((m) => m.value === f), h = (f) => f >= za ? "full" : `${f}px`, y = (f) => {
1459
+ const Ya = 999;
1460
+ function _n({ id: e, title: t, hint: n, token: a, uses: o = [], presets: s, max: r, extra: i = [], extraTokens: l = () => ({}), shape: d = "card" }) {
1461
+ const p = (f) => s.find((m) => m.value === f), u = (f) => f >= Ya ? "full" : `${f}px`, v = (f) => {
1362
1462
  if (typeof f.radius == "number" && isFinite(f.radius))
1363
- return { [a]: `${Math.min(Math.max(f.radius, 0), s)}px` };
1364
- const m = c(f.corners);
1463
+ return { [a]: `${Math.min(Math.max(f.radius, 0), r)}px` };
1464
+ const m = p(f.corners);
1365
1465
  return m ? { [a]: `${m.radius}px` } : {};
1366
1466
  };
1367
1467
  return {
1368
- id: t,
1369
- title: e,
1468
+ id: e,
1469
+ title: t,
1370
1470
  hint: n,
1371
- presets: r,
1471
+ presets: s,
1372
1472
  uses: [a, ...o],
1373
1473
  tokens(f) {
1374
- const m = f[t] || {};
1375
- return { ...y(m), ...l(m) };
1474
+ const m = f[e] || {};
1475
+ return { ...v(m), ...l(m) };
1376
1476
  },
1377
1477
  // One render function per knob, so a layout can place them separately.
1378
1478
  // Each takes an optional label (e.g. "Buttons" on the Corners page).
1379
1479
  parts: {
1380
1480
  corners(f, m, { label: x = "Corners" } = {}) {
1381
- const b = B(x), u = re({
1382
- label: `${e} corners`,
1383
- options: r.map((E) => ({ value: E.value, label: E.label, title: h(E.radius) })),
1384
- onSelect: (E) => m.setSection(t, { corners: E, radius: null })
1385
- }), v = Rn({
1386
- label: `${e} corner radius`,
1481
+ const b = U(x), h = ce({
1482
+ label: `${t} corners`,
1483
+ options: s.map((E) => ({ value: E.value, label: E.label, title: u(E.radius) })),
1484
+ onSelect: (E) => m.setSection(e, { corners: E, radius: null })
1485
+ }), y = Vn({
1486
+ label: `${t} corner radius`,
1387
1487
  min: 0,
1388
- max: s,
1389
- format: h,
1390
- onInput: (E) => m.setSection(t, { radius: E, corners: null })
1488
+ max: r,
1489
+ format: u,
1490
+ onInput: (E) => m.setSection(e, { radius: E, corners: null })
1391
1491
  });
1392
- b.append(u.el, v.el), f.append(b.el);
1492
+ b.append(h.el, y.el), f.append(b.el);
1393
1493
  const L = () => {
1394
- const E = parseFloat((m.readToken || F)(a)), J = m.getState()[t] || {}, K = c(J.corners) || r.find((G) => G.radius === E);
1395
- if (u.setValue(K ? K.value : null), isNaN(E)) {
1396
- v.setValue(0, "—"), b.setValue("");
1494
+ const E = parseFloat((m.readToken || F)(a)), ee = m.getState()[e] || {}, K = p(ee.corners) || s.find((W) => W.radius === E);
1495
+ if (h.setValue(K ? K.value : null), isNaN(E)) {
1496
+ y.setValue(0, "—"), b.setValue("");
1397
1497
  return;
1398
1498
  }
1399
- v.setValue(Math.min(E, s), h(E)), b.setValue(h(E));
1499
+ y.setValue(Math.min(E, r), u(E)), b.setValue(u(E));
1400
1500
  };
1401
1501
  L(), m.onChange(L);
1402
1502
  },
1403
- ...Object.fromEntries(i.map((f) => [f.key, (m, x, { label: b } = {}) => m.append(ke(x, { section: t, ...f, label: b || f.label }))])),
1503
+ ...Object.fromEntries(i.map((f) => [f.key, (m, x, { label: b } = {}) => m.append($e(x, { section: e, ...f, label: b || f.label }))])),
1404
1504
  // Tabbed layout: presets as tiles showing the real rounding.
1405
1505
  cornerTiles(f, m, { label: x = "Corners" } = {}) {
1406
- const b = (u) => `<span class="shape shape-${p}" style="border-radius:${Math.min(u, p === "button" ? 12 : 16)}px"></span>`;
1506
+ const b = (h) => `<span class="shape shape-${d}" style="border-radius:${Math.min(h, d === "button" ? 12 : 16)}px"></span>`;
1407
1507
  f.append(V(m, {
1408
- section: t,
1508
+ section: e,
1409
1509
  key: "corners",
1410
1510
  label: x,
1411
- columns: r.length,
1412
- options: r.map((u) => ({ value: u.value, label: u.label, title: h(u.radius), preview: b(u.radius) })),
1413
- current: (u) => {
1414
- const v = u[t] || {};
1415
- if (v.radius != null) return null;
1511
+ columns: s.length,
1512
+ options: s.map((h) => ({ value: h.value, label: h.label, title: u(h.radius), preview: b(h.radius) })),
1513
+ current: (h) => {
1514
+ const y = h[e] || {};
1515
+ if (y.radius != null) return null;
1416
1516
  const L = parseFloat((m.readToken || F)(a));
1417
- return (c(v.corners) || r.find((E) => E.radius === L))?.value || null;
1517
+ return (p(y.corners) || s.find((E) => E.radius === L))?.value || null;
1418
1518
  },
1419
- select: (u) => m.setSection(t, { corners: u, radius: null }),
1519
+ select: (h) => m.setSection(e, { corners: h, radius: null }),
1420
1520
  valueText: () => {
1421
- const u = parseFloat((m.readToken || F)(a));
1422
- return isNaN(u) ? "" : h(u);
1521
+ const h = parseFloat((m.readToken || F)(a));
1522
+ return isNaN(h) ? "" : u(h);
1423
1523
  }
1424
1524
  }));
1425
1525
  },
1426
1526
  ...Object.fromEntries(i.filter((f) => f.preview).map((f) => [`${f.key}Tiles`, (m, x, { label: b } = {}) => m.append(V(x, {
1427
- section: t,
1527
+ section: e,
1428
1528
  key: f.key,
1429
1529
  label: b || f.label,
1430
1530
  fallback: f.fallback,
1431
1531
  columns: f.columns || f.options.length,
1432
- options: f.options.map((u) => ({ ...u, preview: f.preview(u.value) }))
1532
+ options: f.options.map((h) => ({ ...h, preview: f.preview(h.value) }))
1433
1533
  }))]))
1434
1534
  },
1435
1535
  render(f, m) {
@@ -1437,50 +1537,50 @@ function zn({ id: t, title: e, hint: n, token: a, uses: o = [], presets: r, max:
1437
1537
  }
1438
1538
  };
1439
1539
  }
1440
- function ke(t, { section: e, key: n, label: a, options: o, fallback: r, valueText: s }) {
1441
- const i = B(a), l = re({
1540
+ function $e(e, { section: t, key: n, label: a, options: o, fallback: s, valueText: r }) {
1541
+ const i = U(a), l = ce({
1442
1542
  label: a,
1443
1543
  options: o,
1444
- onSelect: (c) => t.setSection(e, { [n]: c })
1544
+ onSelect: (p) => e.setSection(t, { [n]: p })
1445
1545
  });
1446
1546
  i.append(l.el);
1447
- const p = () => {
1448
- const c = (t.getState()[e] || {})[n] || r;
1449
- l.setValue(c), i.setValue(s ? s(c) : "");
1547
+ const d = () => {
1548
+ const p = (e.getState()[t] || {})[n] || s;
1549
+ l.setValue(p), i.setValue(r ? r(p) : "");
1450
1550
  };
1451
- return p(), t.onChange(p), i.el;
1452
- }
1453
- function Ia(t, { section: e, key: n, label: a, min: o, max: r, fallback: s, format: i }) {
1454
- const l = B(a), p = Rn({ label: a, min: o, max: r, format: i, onInput: (h) => t.setSection(e, { [n]: h }) });
1455
- l.append(p.el);
1456
- const c = () => {
1457
- const h = (t.getState()[e] || {})[n];
1458
- p.setValue(typeof h == "number" ? h : s);
1551
+ return d(), e.onChange(d), i.el;
1552
+ }
1553
+ function Xa(e, { section: t, key: n, label: a, min: o, max: s, fallback: r, format: i }) {
1554
+ const l = U(a), d = Vn({ label: a, min: o, max: s, format: i, onInput: (u) => e.setSection(t, { [n]: u }) });
1555
+ l.append(d.el);
1556
+ const p = () => {
1557
+ const u = (e.getState()[t] || {})[n];
1558
+ d.setValue(typeof u == "number" ? u : r);
1459
1559
  };
1460
- return c(), t.onChange(c), l.el;
1560
+ return p(), e.onChange(p), l.el;
1461
1561
  }
1462
- function V(t, { section: e, key: n, label: a, options: o, columns: r = 3, className: s = "", fallback: i, valueText: l, current: p, select: c }) {
1463
- const h = B(a), y = Ra({
1562
+ function V(e, { section: t, key: n, label: a, options: o, columns: s = 3, className: r = "", fallback: i, valueText: l, current: d, select: p }) {
1563
+ const u = U(a), v = Ga({
1464
1564
  label: a,
1465
1565
  options: o,
1466
- columns: r,
1467
- className: s,
1468
- onSelect: (m) => c ? c(m) : t.setSection(e, { [n]: m })
1566
+ columns: s,
1567
+ className: r,
1568
+ onSelect: (m) => p ? p(m) : e.setSection(t, { [n]: m })
1469
1569
  });
1470
- h.append(y.el);
1570
+ u.append(v.el);
1471
1571
  const f = () => {
1472
- const m = t.getState(), x = p ? p(m) : (m[e] || {})[n] || i || null;
1473
- y.setValue(x), h.setValue(l ? l(x, m) : "");
1572
+ const m = e.getState(), x = d ? d(m) : (m[t] || {})[n] || i || null;
1573
+ v.setValue(x), u.setValue(l ? l(x, m) : "");
1474
1574
  };
1475
- return f(), t.onChange(f), h.el;
1575
+ return f(), e.onChange(f), u.el;
1476
1576
  }
1477
- const ln = (t, e) => e.map((n) => `${t}${n}`), In = {
1577
+ const gn = (e, t) => t.map((n) => `${e}${n}`), Kn = {
1478
1578
  fonts: { label: "Tokens", tokens: ["font-heading", "font-body"], kind: "text" },
1479
- type: { label: "Tokens", tokens: ln("text-", ["xs", "sm", "base", "lg", "xl", "2xl", "3xl"]), kind: "length" },
1579
+ type: { label: "Tokens", tokens: gn("text-", ["xs", "sm", "base", "lg", "xl", "2xl", "3xl"]), kind: "length" },
1480
1580
  leading: { label: "Line height", tokens: ["leading-tight", "leading-normal"], kind: "number" },
1481
1581
  weights: { label: "Weights", tokens: ["weight-heading", "weight-strong"], kind: "number" },
1482
1582
  colours: { label: "Tokens", tokens: ["bg", "surface", "surface-raised", "text", "text-muted", "border", "primary", "primary-hover", "on-primary", "focus", "success", "warning", "danger"], kind: "colour" },
1483
- spacing: { label: "Steps", tokens: ln("space-", [1, 2, 3, 4, 5, 6, 7, 8]), kind: "length", bar: 64 },
1583
+ spacing: { label: "Steps", tokens: gn("space-", [1, 2, 3, 4, 5, 6, 7, 8]), kind: "length", bar: 64 },
1484
1584
  controls: { label: "Controls", tokens: ["control-height", "control-pad-x"], kind: "length" },
1485
1585
  corners: { label: "Tokens", tokens: ["radius-control", "radius-container"], kind: "length" },
1486
1586
  borders: { label: "Tokens", tokens: ["border-width", "border-width-container"], kind: "length" },
@@ -1488,124 +1588,124 @@ const ln = (t, e) => e.map((n) => `${t}${n}`), In = {
1488
1588
  buttonStyle: { label: "Button", tokens: ["button-bg", "button-fg", "button-border", "button-hover"], kind: "colour" },
1489
1589
  cardStyle: { label: "Card", tokens: ["card-bg", "card-border"], kind: "colour" },
1490
1590
  cardShadow: { label: "Card shadow", tokens: ["card-shadow"], kind: "text" }
1491
- }, _ = (t) => `--twd-${t}`, Qe = (t, e) => Object.prototype.hasOwnProperty.call(t.getState().custom || {}, e), dn = (t, e) => In[e].tokens.some((n) => (t.custom || {})[n] != null);
1492
- function Da(t, e) {
1493
- const n = /^(-?\d*\.?\d+)(px|rem)?$/.exec(String(t).trim());
1494
- return !n || e === "text" || e === "colour" ? null : n[2] === "rem" ? { n: +(parseFloat(n[1]) * 16).toFixed(1), unit: "px" } : { n: +parseFloat(n[1]).toFixed(3), unit: n[2] || (e === "length" ? "px" : "") };
1591
+ }, _ = (e) => `--twd-${e}`, at = (e, t) => Object.prototype.hasOwnProperty.call(e.getState().custom || {}, t), mn = (e, t) => Kn[t].tokens.some((n) => (e.custom || {})[n] != null);
1592
+ function Za(e, t) {
1593
+ const n = /^(-?\d*\.?\d+)(px|rem)?$/.exec(String(e).trim());
1594
+ return !n || t === "text" || t === "colour" ? null : n[2] === "rem" ? { n: +(parseFloat(n[1]) * 16).toFixed(1), unit: "px" } : { n: +parseFloat(n[1]).toFixed(3), unit: n[2] || (t === "length" ? "px" : "") };
1495
1595
  }
1496
- function Ba(t, e) {
1497
- const n = t.trim();
1498
- return n ? e === "length" && /^-?\d*\.?\d+$/.test(n) ? `${n}px` : n : null;
1596
+ function Ja(e, t) {
1597
+ const n = e.trim();
1598
+ return n ? t === "length" && /^-?\d*\.?\d+$/.test(n) ? `${n}px` : n : null;
1499
1599
  }
1500
- function Ua(t, e, n, a) {
1600
+ function Qa(e, t, n, a) {
1501
1601
  const o = document.createElement("div");
1502
1602
  o.className = `token-row kind-${n}`, o.innerHTML = `
1503
- <code class="token-name" title="${_(e)}">${e}</code>
1603
+ <code class="token-name" title="${_(t)}">${t}</code>
1504
1604
  ${a ? '<span class="token-bar"><span></span></span>' : '<span class="token-gap"></span>'}
1505
1605
  <span class="custom-tag" hidden>Custom</span>
1506
1606
  <label class="token-value">
1507
1607
  ${n === "colour" ? '<span class="token-swatch"></span>' : ""}
1508
- <input type="text" spellcheck="false" aria-label="${_(e)}" ${n === "length" || n === "number" ? 'inputmode="decimal"' : ""}>
1608
+ <input type="text" spellcheck="false" aria-label="${_(t)}" ${n === "length" || n === "number" ? 'inputmode="decimal"' : ""}>
1509
1609
  <span class="token-unit"></span>
1510
1610
  </label>
1511
- <button class="icon-btn token-reset" title="Back to the page’s value" aria-label="Reset ${_(e)}" hidden>${A.reset}</button>
1611
+ <button class="icon-btn token-reset" title="Back to the page’s value" aria-label="Reset ${_(t)}" hidden>${A.reset}</button>
1512
1612
  `;
1513
- const r = o.querySelector("input"), s = o.querySelector(".token-unit"), i = o.querySelector(".token-bar span"), l = o.querySelector(".token-swatch"), p = o.querySelector(".custom-tag"), c = o.querySelector(".token-reset");
1514
- let h = "";
1515
- const y = () => {
1516
- let m = r.value;
1517
- h && /^-?\d*\.?\d+$/.test(m.trim()) && (m = `${m.trim()}${h}`);
1518
- const x = Ba(m, n), b = F(_(e));
1613
+ const s = o.querySelector("input"), r = o.querySelector(".token-unit"), i = o.querySelector(".token-bar span"), l = o.querySelector(".token-swatch"), d = o.querySelector(".custom-tag"), p = o.querySelector(".token-reset");
1614
+ let u = "";
1615
+ const v = () => {
1616
+ let m = s.value;
1617
+ u && /^-?\d*\.?\d+$/.test(m.trim()) && (m = `${m.trim()}${u}`);
1618
+ const x = Ja(m, n), b = F(_(t));
1519
1619
  if (x === null) {
1520
- Qe(t, e) ? t.setSection("custom", { [e]: null }) : f();
1620
+ at(e, t) ? e.setSection("custom", { [t]: null }) : f();
1521
1621
  return;
1522
1622
  }
1523
- if (x === b && !Qe(t, e)) return f();
1524
- t.setSection("custom", { [e]: x });
1623
+ if (x === b && !at(e, t)) return f();
1624
+ e.setSection("custom", { [t]: x });
1525
1625
  };
1526
- r.addEventListener("change", y), r.addEventListener("blur", () => f()), r.addEventListener("keydown", (m) => {
1527
- m.key === "Enter" && (m.preventDefault(), r.blur()), m.key === "Escape" && (m.stopPropagation(), f(), r.blur());
1528
- }), c.addEventListener("click", () => t.setSection("custom", { [e]: null }));
1626
+ s.addEventListener("change", v), s.addEventListener("blur", () => f()), s.addEventListener("keydown", (m) => {
1627
+ m.key === "Enter" && (m.preventDefault(), s.blur()), m.key === "Escape" && (m.stopPropagation(), f(), s.blur());
1628
+ }), p.addEventListener("click", () => e.setSection("custom", { [t]: null }));
1529
1629
  function f() {
1530
- const m = F(_(e)), x = Qe(t, e), b = Da(m, n);
1531
- h = b ? b.unit : "", document.activeElement !== r && o.getRootNode().activeElement !== r && (r.value = b ? String(b.n) : m), r.title = m, s.textContent = h, s.hidden = !h, i && (i.style.width = `${Math.max(2, Math.min(100, (b ? b.n : 0) / a * 100))}%`), l && (l.style.background = m), o.classList.toggle("edited", x), p.hidden = !x, c.hidden = !x;
1630
+ const m = F(_(t)), x = at(e, t), b = Za(m, n);
1631
+ u = b ? b.unit : "", document.activeElement !== s && o.getRootNode().activeElement !== s && (s.value = b ? String(b.n) : m), s.title = m, r.textContent = u, r.hidden = !u, i && (i.style.width = `${Math.max(2, Math.min(100, (b ? b.n : 0) / a * 100))}%`), l && (l.style.background = m), o.classList.toggle("edited", x), d.hidden = !x, p.hidden = !x;
1532
1632
  }
1533
- return f(), t.onChange(f), o;
1633
+ return f(), e.onChange(f), o;
1534
1634
  }
1535
- const qa = {
1635
+ const eo = {
1536
1636
  id: "custom",
1537
1637
  title: "Custom tokens",
1538
1638
  hint: "Any token, set to a fixed value",
1539
1639
  uses: [],
1540
1640
  // not offered for a single picked element (yet)
1541
- tokens(t) {
1542
- return Object.fromEntries(Object.entries(t.custom || {}).map(([e, n]) => [_(e), n]));
1641
+ tokens(e) {
1642
+ return Object.fromEntries(Object.entries(e.custom || {}).map(([t, n]) => [_(t), n]));
1543
1643
  },
1544
- describe: (t, e) => ({ label: _(t), value: String(e) }),
1644
+ describe: (e, t) => ({ label: _(e), value: String(t) }),
1545
1645
  // One part per Edit page group, e.g. parts.spacing → rows for --twd-space-1 … 8.
1546
- parts: Object.fromEntries(Object.entries(In).map(([t, e]) => [t, (n, a, { label: o = e.label } = {}) => {
1547
- const r = B(o);
1548
- r.el.classList.add("token-field");
1549
- const s = e.tokens[0], i = e.tokens[e.tokens.length - 1];
1550
- r.setValue(e.tokens.length > 2 && /-\w+$/.test(s) ? `${_(s)} … ${i.split("-").pop()}` : "");
1646
+ parts: Object.fromEntries(Object.entries(Kn).map(([e, t]) => [e, (n, a, { label: o = t.label } = {}) => {
1647
+ const s = U(o);
1648
+ s.el.classList.add("token-field");
1649
+ const r = t.tokens[0], i = t.tokens[t.tokens.length - 1];
1650
+ s.setValue(t.tokens.length > 2 && /-\w+$/.test(r) ? `${_(r)} … ${i.split("-").pop()}` : "");
1551
1651
  const l = document.createElement("div");
1552
- l.className = "token-rows", e.tokens.forEach((p) => l.append(Ua(a, p, e.kind, e.bar))), r.append(l), n.append(r.el);
1652
+ l.className = "token-rows", t.tokens.forEach((d) => l.append(Qa(a, d, t.kind, t.bar))), s.append(l), n.append(s.el);
1553
1653
  }]))
1554
- }, we = { compact: 0.75, regular: 1, comfy: 1.25 }, xt = [0.75, 1.25], cn = [1, 2, 3, 4, 5, 6, 7, 8].map((t) => `--twd-space-${t}`);
1555
- function te(t = {}) {
1556
- return typeof t.factor == "number" && isFinite(t.factor) ? Math.min(xt[1], Math.max(xt[0], t.factor)) : we[t.spacing] || 1;
1654
+ }, ve = { compact: 0.75, regular: 1, comfy: 1.25 }, kt = [0.75, 1.25], bn = [1, 2, 3, 4, 5, 6, 7, 8].map((e) => `--twd-space-${e}`);
1655
+ function se(e = {}) {
1656
+ return typeof e.factor == "number" && isFinite(e.factor) ? Math.min(kt[1], Math.max(kt[0], e.factor)) : ve[e.spacing] || 1;
1557
1657
  }
1558
- const et = (t) => `${Math.round(t * 100)}%`, Va = {
1658
+ const ot = (e) => `${Math.round(e * 100)}%`, to = {
1559
1659
  id: "density",
1560
1660
  title: "Density",
1561
1661
  hint: "Compact · regular · comfortable",
1562
- uses: cn,
1563
- tokens(t) {
1564
- const e = te(t.density);
1565
- if (e === 1) return {};
1662
+ uses: bn,
1663
+ tokens(e) {
1664
+ const t = se(e.density);
1665
+ if (t === 1) return {};
1566
1666
  const n = {};
1567
- return cn.forEach((a) => {
1568
- const o = ye(F(a), e);
1667
+ return bn.forEach((a) => {
1668
+ const o = Se(F(a), t);
1569
1669
  o && (n[a] = o);
1570
1670
  }), n;
1571
1671
  },
1572
1672
  /** How the Changes list and prompt describe a setting. */
1573
- describe: (t, e) => t === "factor" ? { label: "Spacing", value: et(e) } : null,
1673
+ describe: (e, t) => e === "factor" ? { label: "Spacing", value: ot(t) } : null,
1574
1674
  parts: {
1575
1675
  // Tabbed layout: presets as tiles with a tiny layout sketch.
1576
- spacingTiles(t, e, { label: n = "Density" } = {}) {
1577
- const a = (o, r) => `<span class="sketch" style="padding:${r}px;gap:${o}px"><i></i><i></i><i></i></span>`;
1578
- t.append(V(e, {
1676
+ spacingTiles(e, t, { label: n = "Density" } = {}) {
1677
+ const a = (o, s) => `<span class="sketch" style="padding:${s}px;gap:${o}px"><i></i><i></i><i></i></span>`;
1678
+ e.append(V(t, {
1579
1679
  section: "density",
1580
1680
  key: "spacing",
1581
1681
  label: n,
1582
1682
  options: [{ value: "compact", label: "Compact", preview: a(2, 5) }, { value: "regular", label: "Regular", preview: a(4, 8) }, { value: "comfy", label: "Comfy", preview: a(6, 11) }],
1583
1683
  current: (o) => {
1584
- const r = te(o.density);
1585
- return Object.keys(we).find((s) => we[s] === r) || null;
1684
+ const s = se(o.density);
1685
+ return Object.keys(ve).find((r) => ve[r] === s) || null;
1586
1686
  },
1587
- select: (o) => e.setSection("density", { spacing: o, factor: null }),
1588
- valueText: (o, r) => dn(r, "spacing") ? "Custom" : o ? "" : et(te(r.density))
1687
+ select: (o) => t.setSection("density", { spacing: o, factor: null }),
1688
+ valueText: (o, s) => mn(s, "spacing") ? "Custom" : o ? "" : ot(se(s.density))
1589
1689
  }));
1590
1690
  },
1591
- spacing(t, e, { label: n = "Spacing" } = {}) {
1592
- const a = B(n), o = re({
1691
+ spacing(e, t, { label: n = "Spacing" } = {}) {
1692
+ const a = U(n), o = ce({
1593
1693
  label: n,
1594
1694
  options: [{ value: "compact", label: "Compact" }, { value: "regular", label: "Regular" }, { value: "comfy", label: "Comfy" }],
1595
- onSelect: (s) => e.setSection("density", { spacing: s, factor: null })
1695
+ onSelect: (r) => t.setSection("density", { spacing: r, factor: null })
1596
1696
  });
1597
1697
  a.append(o.el);
1598
- const r = () => {
1599
- const s = e.getState().density || {}, i = te(s), l = Object.keys(we).find((p) => we[p] === i);
1600
- o.setValue(l || null), a.setValue(dn(e.getState(), "spacing") ? "Custom" : l ? "" : et(i));
1698
+ const s = () => {
1699
+ const r = t.getState().density || {}, i = se(r), l = Object.keys(ve).find((d) => ve[d] === i);
1700
+ o.setValue(l || null), a.setValue(mn(t.getState(), "spacing") ? "Custom" : l ? "" : ot(i));
1601
1701
  };
1602
- r(), e.onChange(r), t.append(a.el);
1702
+ s(), t.onChange(s), e.append(a.el);
1603
1703
  }
1604
1704
  },
1605
- render(t, e) {
1606
- Object.entries(this.parts).filter(([n]) => !n.endsWith("Tiles")).forEach(([, n]) => n(t, e));
1705
+ render(e, t) {
1706
+ Object.entries(this.parts).filter(([n]) => !n.endsWith("Tiles")).forEach(([, n]) => n(e, t));
1607
1707
  }
1608
- }, Ha = '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M14 6l4 4M4 20l1-4 9.5-9.5a2.1 2.1 0 013 3L8 19l-4 1z"/></svg>', _a = [
1708
+ }, no = '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M14 6l4 4M4 20l1-4 9.5-9.5a2.1 2.1 0 013 3L8 19l-4 1z"/></svg>', ao = [
1609
1709
  [50, "white", 10],
1610
1710
  [100, "white", 20],
1611
1711
  [200, "white", 36],
@@ -1617,35 +1717,35 @@ const et = (t) => `${Math.round(t * 100)}%`, Va = {
1617
1717
  [800, "black", 58],
1618
1718
  [900, "black", 45],
1619
1719
  [950, "black", 30]
1620
- ], tt = (t) => {
1621
- let e = String(t).trim().replace(/^#/, "");
1622
- return /^[0-9a-f]{3}$/i.test(e) && (e = e.split("").map((n) => n + n).join("")), /^[0-9a-f]{6}$/i.test(e) ? `#${e.toUpperCase()}` : null;
1720
+ ], rt = (e) => {
1721
+ let t = String(e).trim().replace(/^#/, "");
1722
+ return /^[0-9a-f]{3}$/i.test(t) && (t = t.split("").map((n) => n + n).join("")), /^[0-9a-f]{6}$/i.test(t) ? `#${t.toUpperCase()}` : null;
1623
1723
  };
1624
- function Ka({ label: t, onInput: e }) {
1724
+ function oo({ label: e, onInput: t }) {
1625
1725
  const n = document.createElement("div");
1626
1726
  n.className = "colour";
1627
1727
  const a = document.createElement("div");
1628
1728
  a.className = "colour-field";
1629
1729
  const o = document.createElement("input");
1630
1730
  o.type = "color", o.className = "colour-picker", o.tabIndex = -1, o.setAttribute("aria-hidden", "true");
1631
- const r = document.createElement("button");
1632
- r.type = "button", r.className = "colour-swatch", r.setAttribute("aria-label", `${t}: open colour picker`);
1633
- const s = document.createElement("input");
1634
- s.type = "text", s.className = "colour-hex", s.spellcheck = !1, s.maxLength = 7, s.setAttribute("aria-label", `${t} hex value`);
1731
+ const s = document.createElement("button");
1732
+ s.type = "button", s.className = "colour-swatch", s.setAttribute("aria-label", `${e}: open colour picker`);
1733
+ const r = document.createElement("input");
1734
+ r.type = "text", r.className = "colour-hex", r.spellcheck = !1, r.maxLength = 7, r.setAttribute("aria-label", `${e} hex value`);
1635
1735
  const i = document.createElement("button");
1636
- i.type = "button", i.className = "colour-pipette", i.innerHTML = Ha, i.title = "Pick a colour from the screen", i.setAttribute("aria-label", "Pick a colour from the screen"), a.append(o, r, s, i);
1736
+ i.type = "button", i.className = "colour-pipette", i.innerHTML = no, i.title = "Pick a colour from the screen", i.setAttribute("aria-label", "Pick a colour from the screen"), a.append(o, s, r, i);
1637
1737
  const l = document.createElement("div");
1638
- l.className = "ramp", l.setAttribute("aria-hidden", "true"), l.innerHTML = _a.map(([f, m, x]) => {
1738
+ l.className = "ramp", l.setAttribute("aria-hidden", "true"), l.innerHTML = ao.map(([f, m, x]) => {
1639
1739
  const b = m ? `color-mix(in oklch, var(--brand) ${x}%, ${m})` : "var(--brand)";
1640
1740
  return `<span title="${f}" style="background:${b}"></span>`;
1641
1741
  }).join(""), n.append(a, l);
1642
- let p = "#000000";
1643
- const c = (f) => {
1644
- p = f, o.value = f.toLowerCase(), r.style.background = f, n.getRootNode().activeElement !== s && (s.value = f), n.style.setProperty("--brand", f);
1645
- }, h = (f) => {
1646
- f !== p && (c(f), e(f));
1742
+ let d = "#000000";
1743
+ const p = (f) => {
1744
+ d = f, o.value = f.toLowerCase(), s.style.background = f, n.getRootNode().activeElement !== r && (r.value = f), n.style.setProperty("--brand", f);
1745
+ }, u = (f) => {
1746
+ f !== d && (p(f), t(f));
1647
1747
  };
1648
- r.addEventListener("click", () => {
1748
+ s.addEventListener("click", () => {
1649
1749
  if (o.showPicker)
1650
1750
  try {
1651
1751
  o.showPicker();
@@ -1653,64 +1753,64 @@ function Ka({ label: t, onInput: e }) {
1653
1753
  } catch {
1654
1754
  }
1655
1755
  o.click();
1656
- }), o.addEventListener("input", () => h(o.value.toUpperCase())), s.addEventListener("input", () => {
1657
- const f = tt(s.value);
1658
- f && h(f);
1756
+ }), o.addEventListener("input", () => u(o.value.toUpperCase())), r.addEventListener("input", () => {
1757
+ const f = rt(r.value);
1758
+ f && u(f);
1659
1759
  });
1660
- const y = () => {
1661
- s.value = p;
1760
+ const v = () => {
1761
+ r.value = d;
1662
1762
  };
1663
- return s.addEventListener("blur", y), s.addEventListener("keydown", (f) => {
1664
- f.key === "Enter" && (y(), s.select());
1763
+ return r.addEventListener("blur", v), r.addEventListener("keydown", (f) => {
1764
+ f.key === "Enter" && (v(), r.select());
1665
1765
  }), i.addEventListener("click", async () => {
1666
1766
  if ("EyeDropper" in window)
1667
1767
  try {
1668
- const { sRGBHex: f } = await new window.EyeDropper().open(), m = tt(f);
1669
- m && h(m);
1768
+ const { sRGBHex: f } = await new window.EyeDropper().open(), m = rt(f);
1769
+ m && u(m);
1670
1770
  } catch {
1671
1771
  }
1672
1772
  else
1673
- r.click();
1674
- }), { el: n, setValue: (f) => c(tt(f) || p) };
1773
+ s.click();
1774
+ }), { el: n, setValue: (f) => p(rt(f) || d) };
1675
1775
  }
1676
- function Ga({ label: t, options: e, onSelect: n }) {
1776
+ function ro({ label: e, options: t, onSelect: n }) {
1677
1777
  const a = document.createElement("div");
1678
- a.className = "swatches", a.setAttribute("role", "radiogroup"), a.setAttribute("aria-label", t);
1679
- const o = e.map((l) => {
1680
- const p = document.createElement("button");
1681
- return p.type = "button", p.className = "swatch", p.setAttribute("role", "radio"), p.dataset.value = l.value, p.innerHTML = `<span class="swatch-ring"><span class="swatch-dot" style="background:${l.colour}"></span></span><span class="swatch-label">${l.label}</span>`, p.addEventListener("click", () => i(l.value)), p;
1778
+ a.className = "swatches", a.setAttribute("role", "radiogroup"), a.setAttribute("aria-label", e);
1779
+ const o = t.map((l) => {
1780
+ const d = document.createElement("button");
1781
+ return d.type = "button", d.className = "swatch", d.setAttribute("role", "radio"), d.dataset.value = l.value, d.innerHTML = `<span class="swatch-ring"><span class="swatch-dot" style="background:${l.colour}"></span></span><span class="swatch-label">${l.label}</span>`, d.addEventListener("click", () => i(l.value)), d;
1682
1782
  });
1683
1783
  a.append(...o);
1684
- let r = null;
1685
- function s(l) {
1686
- r = l, o.forEach((c) => c.setAttribute("aria-checked", String(c.dataset.value === l)));
1687
- const p = Math.max(0, o.findIndex((c) => c.dataset.value === l));
1688
- o.forEach((c, h) => {
1689
- c.tabIndex = h === p ? 0 : -1;
1784
+ let s = null;
1785
+ function r(l) {
1786
+ s = l, o.forEach((p) => p.setAttribute("aria-checked", String(p.dataset.value === l)));
1787
+ const d = Math.max(0, o.findIndex((p) => p.dataset.value === l));
1788
+ o.forEach((p, u) => {
1789
+ p.tabIndex = u === d ? 0 : -1;
1690
1790
  });
1691
1791
  }
1692
- function i(l, p) {
1693
- l !== r && (s(l), n(l)), p && o.find((c) => c.dataset.value === l).focus();
1792
+ function i(l, d) {
1793
+ l !== s && (r(l), n(l)), d && o.find((p) => p.dataset.value === l).focus();
1694
1794
  }
1695
1795
  return a.addEventListener("keydown", (l) => {
1696
- const p = o.indexOf(l.target.closest(".swatch"));
1697
- if (p < 0) return;
1698
- const c = { ArrowRight: 1, ArrowDown: 1, ArrowLeft: -1, ArrowUp: -1 }[l.key];
1699
- let h;
1700
- if (c) h = (p + c + o.length) % o.length;
1701
- else if (l.key === "Home") h = 0;
1702
- else if (l.key === "End") h = o.length - 1;
1796
+ const d = o.indexOf(l.target.closest(".swatch"));
1797
+ if (d < 0) return;
1798
+ const p = { ArrowRight: 1, ArrowDown: 1, ArrowLeft: -1, ArrowUp: -1 }[l.key];
1799
+ let u;
1800
+ if (p) u = (d + p + o.length) % o.length;
1801
+ else if (l.key === "Home") u = 0;
1802
+ else if (l.key === "End") u = o.length - 1;
1703
1803
  else return;
1704
- l.preventDefault(), i(o[h].dataset.value, !0);
1705
- }), s(null), { el: a, setValue: s };
1706
- }
1707
- const Wa = [50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 950], me = (t) => Object.fromEntries(t.split(" ").map((e, n) => [Wa[n], `#${e}`])), nt = {
1708
- slate: me("f8fafc f1f5f9 e2e8f0 cbd5e1 94a3b8 64748b 475569 334155 1e293b 0f172a 020617"),
1709
- gray: me("f9fafb f3f4f6 e5e7eb d1d5db 9ca3af 6b7280 4b5563 374151 1f2937 111827 030712"),
1710
- zinc: me("fafafa f4f4f5 e4e4e7 d4d4d8 a1a1aa 71717a 52525b 3f3f46 27272a 18181b 09090b"),
1711
- neutral: me("fafafa f5f5f5 e5e5e5 d4d4d4 a3a3a3 737373 525252 404040 262626 171717 0a0a0a"),
1712
- stone: me("fafaf9 f5f5f4 e7e5e4 d6d3d1 a8a29e 78716c 57534e 44403c 292524 1c1917 0c0a09")
1713
- }, N = (t) => Object.fromEntries(t.split(" ").map((e, n) => [400 + 100 * n, `#${e}`])), at = {
1804
+ l.preventDefault(), i(o[u].dataset.value, !0);
1805
+ }), r(null), { el: a, setValue: r };
1806
+ }
1807
+ const so = [50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 950], xe = (e) => Object.fromEntries(e.split(" ").map((t, n) => [so[n], `#${t}`])), st = {
1808
+ slate: xe("f8fafc f1f5f9 e2e8f0 cbd5e1 94a3b8 64748b 475569 334155 1e293b 0f172a 020617"),
1809
+ gray: xe("f9fafb f3f4f6 e5e7eb d1d5db 9ca3af 6b7280 4b5563 374151 1f2937 111827 030712"),
1810
+ zinc: xe("fafafa f4f4f5 e4e4e7 d4d4d8 a1a1aa 71717a 52525b 3f3f46 27272a 18181b 09090b"),
1811
+ neutral: xe("fafafa f5f5f5 e5e5e5 d4d4d4 a3a3a3 737373 525252 404040 262626 171717 0a0a0a"),
1812
+ stone: xe("fafaf9 f5f5f4 e7e5e4 d6d3d1 a8a29e 78716c 57534e 44403c 292524 1c1917 0c0a09")
1813
+ }, N = (e) => Object.fromEntries(e.split(" ").map((t, n) => [400 + 100 * n, `#${t}`])), it = {
1714
1814
  red: N("f87171 ef4444 dc2626 b91c1c 991b1b"),
1715
1815
  orange: N("fb923c f97316 ea580c c2410c 9a3412"),
1716
1816
  amber: N("fbbf24 f59e0b d97706 b45309 92400e"),
@@ -1728,17 +1828,17 @@ const Wa = [50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 950], me = (t) => O
1728
1828
  fuchsia: N("e879f9 d946ef c026d3 a21caf 86198f"),
1729
1829
  pink: N("f472b6 ec4899 db2777 be185d 9d174d"),
1730
1830
  rose: N("fb7185 f43f5e e11d48 be123c 9f1239")
1731
- }, ze = [
1831
+ }, Be = [
1732
1832
  { value: "indigo-zinc", label: "Indigo · Zinc", primary: "indigo", neutral: "zinc" },
1733
1833
  { value: "emerald-stone", label: "Emerald · Stone", primary: "emerald", neutral: "stone" },
1734
1834
  { value: "rose-slate", label: "Rose · Slate", primary: "rose", neutral: "slate" },
1735
1835
  { value: "amber-neutral", label: "Amber · Neutral", primary: "amber", neutral: "neutral" }
1736
- ], pn = "#4F46E5", Ya = "#A0743C", Za = [
1836
+ ], xn = "#4F46E5", io = "#A0743C", lo = [
1737
1837
  // Figma Swatch row sample colours
1738
1838
  { value: "pure", label: "Pure", colour: "#8E8E93" },
1739
1839
  { value: "subtle", label: "Subtle", colour: "#8A89A6" },
1740
1840
  { value: "warm", label: "Warm", colour: "#9C9389" }
1741
- ], un = ["bg", "surface", "surface-raised", "text", "text-muted", "border"], Xa = {
1841
+ ], wn = ["bg", "surface", "surface-raised", "text", "text-muted", "border"], co = {
1742
1842
  // proposal
1743
1843
  bg: "#0F0F11",
1744
1844
  surface: "#18181B",
@@ -1746,118 +1846,118 @@ const Wa = [50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 950], me = (t) => O
1746
1846
  text: "#F4F4F5",
1747
1847
  "text-muted": "#A1A1AA",
1748
1848
  border: "#2E2E33"
1749
- }, Ja = { bg: 4, surface: 2, "surface-raised": 2, text: 5, "text-muted": 12, border: 10 }, Y = (t) => /^#[0-9a-f]{6}$/i.test(t || ""), ot = (t) => [500, 600, 700].includes(Number(t.shade)) ? Number(t.shade) : 600, rt = (t) => t[0].toUpperCase() + t.slice(1);
1750
- function hn(t) {
1751
- const e = (l) => (l /= 255, l <= 0.03928 ? l / 12.92 : ((l + 0.055) / 1.055) ** 2.4), [n, a, o] = [1, 3, 5].map((l) => e(parseInt(t.slice(l, l + 2), 16))), r = 0.2126 * n + 0.7152 * a + 0.0722 * o, s = 1.05 / (r + 0.05), i = (r + 0.05) / (98e-4 + 0.05);
1752
- return s >= i ? "#FFFFFF" : "#18181B";
1849
+ }, po = { bg: 4, surface: 2, "surface-raised": 2, text: 5, "text-muted": 12, border: 10 }, X = (e) => /^#[0-9a-f]{6}$/i.test(e || ""), lt = (e) => [500, 600, 700].includes(Number(e.shade)) ? Number(e.shade) : 600, dt = (e) => e[0].toUpperCase() + e.slice(1);
1850
+ function yn(e) {
1851
+ const t = (l) => (l /= 255, l <= 0.03928 ? l / 12.92 : ((l + 0.055) / 1.055) ** 2.4), [n, a, o] = [1, 3, 5].map((l) => t(parseInt(e.slice(l, l + 2), 16))), s = 0.2126 * n + 0.7152 * a + 0.0722 * o, r = 1.05 / (s + 0.05), i = (s + 0.05) / (98e-4 + 0.05);
1852
+ return r >= i ? "#FFFFFF" : "#18181B";
1753
1853
  }
1754
- const De = {
1854
+ const He = {
1755
1855
  id: "colour",
1756
1856
  title: "Colour",
1757
1857
  hint: "Brand colour · neutral tint · mode",
1758
- uses: ["--twd-primary", "--twd-primary-hover", "--twd-on-primary", "--twd-focus", ...un.map((t) => `--twd-${t}`)],
1759
- tokens(t) {
1760
- const e = t.colour || {}, n = {}, a = e.mode === "dark", o = at[e.primary];
1761
- if (!Y(e.brand) && o) {
1762
- const i = ot(e);
1763
- n["--twd-primary"] = o[i], n["--twd-primary-hover"] = o[a ? i - 100 : i + 100], n["--twd-on-primary"] = hn(o[i]), n["--twd-focus"] = o[500];
1858
+ uses: ["--twd-primary", "--twd-primary-hover", "--twd-on-primary", "--twd-focus", ...wn.map((e) => `--twd-${e}`)],
1859
+ tokens(e) {
1860
+ const t = e.colour || {}, n = {}, a = t.mode === "dark", o = it[t.primary];
1861
+ if (!X(t.brand) && o) {
1862
+ const i = lt(t);
1863
+ n["--twd-primary"] = o[i], n["--twd-primary-hover"] = o[a ? i - 100 : i + 100], n["--twd-on-primary"] = yn(o[i]), n["--twd-focus"] = o[500];
1764
1864
  }
1765
- Y(e.brand) && (n["--twd-primary"] = e.brand, n["--twd-primary-hover"] = a ? `color-mix(in oklch, ${e.brand} 85%, white)` : `color-mix(in oklch, ${e.brand} 86%, black)`, n["--twd-on-primary"] = hn(e.brand), n["--twd-focus"] = e.brand);
1766
- const r = e.tint === "subtle" ? Y(e.brand) ? e.brand : F("--twd-primary") || pn : e.tint === "warm" ? Ya : null, s = nt[e.neutral];
1767
- if (s) {
1768
- const i = a ? { bg: s[950], surface: s[900], "surface-raised": s[800], text: s[100], "text-muted": s[400], border: s[800] } : { bg: s[50], surface: "#ffffff", "surface-raised": "#ffffff", text: s[900], "text-muted": s[500], border: s[200] };
1769
- Object.entries(i).forEach(([l, p]) => {
1770
- n[`--twd-${l}`] = p;
1865
+ X(t.brand) && (n["--twd-primary"] = t.brand, n["--twd-primary-hover"] = a ? `color-mix(in oklch, ${t.brand} 85%, white)` : `color-mix(in oklch, ${t.brand} 86%, black)`, n["--twd-on-primary"] = yn(t.brand), n["--twd-focus"] = t.brand);
1866
+ const s = t.tint === "subtle" ? X(t.brand) ? t.brand : F("--twd-primary") || xn : t.tint === "warm" ? io : null, r = st[t.neutral];
1867
+ if (r) {
1868
+ const i = a ? { bg: r[950], surface: r[900], "surface-raised": r[800], text: r[100], "text-muted": r[400], border: r[800] } : { bg: r[50], surface: "#ffffff", "surface-raised": "#ffffff", text: r[900], "text-muted": r[500], border: r[200] };
1869
+ Object.entries(i).forEach(([l, d]) => {
1870
+ n[`--twd-${l}`] = d;
1771
1871
  });
1772
- } else (a || r) && un.forEach((i) => {
1773
- const l = a ? Xa[i] : F(`--twd-${i}`);
1774
- l && (n[`--twd-${i}`] = r ? `color-mix(in oklch, ${l} ${100 - Ja[i]}%, ${r})` : l);
1872
+ } else (a || s) && wn.forEach((i) => {
1873
+ const l = a ? co[i] : F(`--twd-${i}`);
1874
+ l && (n[`--twd-${i}`] = s ? `color-mix(in oklch, ${l} ${100 - po[i]}%, ${s})` : l);
1775
1875
  });
1776
1876
  return n;
1777
1877
  },
1778
1878
  parts: {
1779
1879
  // ---- Tabbed layout (Colour tab): themes, then Tailwind fine-tuning ----
1780
- themeTiles(t, e, { label: n = "Theme" } = {}) {
1880
+ themeTiles(e, t, { label: n = "Theme" } = {}) {
1781
1881
  const a = (o) => {
1782
- const r = nt[o.neutral], s = at[o.primary];
1783
- return `<span class="theme" style="--bg:${r[50]};--line:${r[200]};--ink:${r[900]};--soft:${r[500]};--pri:${s[600]}"><i><b></b><s></s><u></u></i></span><span class="palette">${[s[400], s[500], s[600], s[700], s[800], r[200], r[500], r[900]].map((i) => `<i style="background:${i}"></i>`).join("")}</span>`;
1882
+ const s = st[o.neutral], r = it[o.primary];
1883
+ return `<span class="theme" style="--bg:${s[50]};--line:${s[200]};--ink:${s[900]};--soft:${s[500]};--pri:${r[600]}"><i><b></b><s></s><u></u></i></span><span class="palette">${[r[400], r[500], r[600], r[700], r[800], s[200], s[500], s[900]].map((i) => `<i style="background:${i}"></i>`).join("")}</span>`;
1784
1884
  };
1785
- t.append(V(e, {
1885
+ e.append(V(t, {
1786
1886
  section: "colour",
1787
1887
  key: "theme",
1788
1888
  label: n,
1789
1889
  columns: 2,
1790
1890
  className: "tiles-themes",
1791
- options: ze.map((o) => ({ value: o.value, label: o.label, preview: a(o) })),
1891
+ options: Be.map((o) => ({ value: o.value, label: o.label, preview: a(o) })),
1792
1892
  current: (o) => {
1793
- const r = o.colour || {};
1794
- return Y(r.brand) ? null : ze.find((s) => s.primary === r.primary && s.neutral === r.neutral)?.value || null;
1893
+ const s = o.colour || {};
1894
+ return X(s.brand) ? null : Be.find((r) => r.primary === s.primary && r.neutral === s.neutral)?.value || null;
1795
1895
  },
1796
1896
  select: (o) => {
1797
- const r = ze.find((s) => s.value === o);
1798
- e.setSection("colour", { primary: r.primary, neutral: r.neutral, shade: null, brand: null, tint: null });
1897
+ const s = Be.find((r) => r.value === o);
1898
+ t.setSection("colour", { primary: s.primary, neutral: s.neutral, shade: null, brand: null, tint: null });
1799
1899
  },
1800
- valueText: (o, r) => {
1801
- const s = r.colour || {};
1802
- return o ? "" : s.primary || s.neutral || s.brand ? "Custom" : "Prototype colours";
1900
+ valueText: (o, s) => {
1901
+ const r = s.colour || {};
1902
+ return o ? "" : r.primary || r.neutral || r.brand ? "Custom" : "Prototype colours";
1803
1903
  }
1804
1904
  }));
1805
1905
  },
1806
- primary(t, e, { label: n = "Primary" } = {}) {
1807
- const a = B(n), o = V(e, {
1906
+ primary(e, t, { label: n = "Primary" } = {}) {
1907
+ const a = U(n), o = V(t, {
1808
1908
  section: "colour",
1809
1909
  key: "primary",
1810
1910
  label: `${n} colour family`,
1811
1911
  columns: 9,
1812
1912
  className: "tiles-swatches",
1813
- options: Object.entries(at).map(([i, l]) => ({ value: i, label: rt(i), title: rt(i), preview: `<span class="dot" style="background:${l[500]}"></span>` })),
1913
+ options: Object.entries(it).map(([i, l]) => ({ value: i, label: dt(i), title: dt(i), preview: `<span class="dot" style="background:${l[500]}"></span>` })),
1814
1914
  current: (i) => {
1815
1915
  const l = i.colour || {};
1816
- return Y(l.brand) ? null : l.primary || null;
1916
+ return X(l.brand) ? null : l.primary || null;
1817
1917
  },
1818
- select: (i) => e.setSection("colour", { primary: i, brand: null })
1918
+ select: (i) => t.setSection("colour", { primary: i, brand: null })
1819
1919
  });
1820
1920
  o.querySelector(".field-head").remove();
1821
- const r = re({ label: "Shade", options: [500, 600, 700].map((i) => ({ value: String(i), label: String(i) })), onSelect: (i) => e.setSection("colour", { shade: Number(i) }) });
1822
- a.append(o, r.el);
1823
- const s = () => {
1824
- const i = e.getState().colour || {};
1825
- r.setValue(String(ot(i))), a.setValue(Y(i.brand) ? i.brand.toLowerCase() : i.primary ? `${i.primary}-${ot(i)}` : "");
1921
+ const s = ce({ label: "Shade", options: [500, 600, 700].map((i) => ({ value: String(i), label: String(i) })), onSelect: (i) => t.setSection("colour", { shade: Number(i) }) });
1922
+ a.append(o, s.el);
1923
+ const r = () => {
1924
+ const i = t.getState().colour || {};
1925
+ s.setValue(String(lt(i))), a.setValue(X(i.brand) ? i.brand.toLowerCase() : i.primary ? `${i.primary}-${lt(i)}` : "");
1826
1926
  };
1827
- s(), e.onChange(s), t.append(a.el);
1927
+ r(), t.onChange(r), e.append(a.el);
1828
1928
  },
1829
- neutralTiles(t, e, { label: n = "Neutral" } = {}) {
1830
- t.append(V(e, {
1929
+ neutralTiles(e, t, { label: n = "Neutral" } = {}) {
1930
+ e.append(V(t, {
1831
1931
  section: "colour",
1832
1932
  key: "neutral",
1833
1933
  label: n,
1834
1934
  columns: 5,
1835
1935
  className: "tiles-greys",
1836
- options: Object.entries(nt).map(([a, o]) => ({ value: a, label: rt(a), preview: `<span class="greys">${[o[50], o[200], o[500], o[900]].map((r) => `<i style="background:${r}"></i>`).join("")}</span>` })),
1837
- select: (a) => e.setSection("colour", { neutral: a, tint: null }),
1936
+ options: Object.entries(st).map(([a, o]) => ({ value: a, label: dt(a), preview: `<span class="greys">${[o[50], o[200], o[500], o[900]].map((s) => `<i style="background:${s}"></i>`).join("")}</span>` })),
1937
+ select: (a) => t.setSection("colour", { neutral: a, tint: null }),
1838
1938
  valueText: (a) => a || ""
1839
1939
  }));
1840
1940
  },
1841
- custom(t, e) {
1941
+ custom(e, t) {
1842
1942
  const n = document.createElement("details");
1843
1943
  n.className = "more more-custom", n.innerHTML = '<summary><span class="rainbow" aria-hidden="true"></span><span>Custom colour…</span><span class="muted">any hex</span></summary>';
1844
1944
  const a = document.createElement("div");
1845
- a.className = "more-body", n.append(a), De.parts.brand(a, e, { label: "Brand" }), Y((e.getState().colour || {}).brand) && (n.open = !0), t.append(n);
1945
+ a.className = "more-body", n.append(a), He.parts.brand(a, t, { label: "Brand" }), X((t.getState().colour || {}).brand) && (n.open = !0), e.append(n);
1846
1946
  },
1847
- brand(t, e, { label: n = "Brand" } = {}) {
1848
- const a = B(n), o = Ka({ label: "Brand colour", onInput: (s) => e.setSection("colour", { brand: s }) });
1947
+ brand(e, t, { label: n = "Brand" } = {}) {
1948
+ const a = U(n), o = oo({ label: "Brand colour", onInput: (r) => t.setSection("colour", { brand: r }) });
1849
1949
  a.append(o.el);
1850
- const r = () => o.setValue((e.getState().colour || {}).brand || (e.readToken || F)("--twd-primary") || pn);
1851
- r(), e.onChange(r), t.append(a.el);
1950
+ const s = () => o.setValue((t.getState().colour || {}).brand || (t.readToken || F)("--twd-primary") || xn);
1951
+ s(), t.onChange(s), e.append(a.el);
1852
1952
  },
1853
- neutral(t, e, { label: n = "Neutral" } = {}) {
1854
- const a = B(n), o = Ga({ label: "Neutral tint", options: Za, onSelect: (s) => e.setSection("colour", { tint: s }) });
1953
+ neutral(e, t, { label: n = "Neutral" } = {}) {
1954
+ const a = U(n), o = ro({ label: "Neutral tint", options: lo, onSelect: (r) => t.setSection("colour", { tint: r }) });
1855
1955
  a.append(o.el);
1856
- const r = () => o.setValue((e.getState().colour || {}).tint || "pure");
1857
- r(), e.onChange(r), t.append(a.el);
1956
+ const s = () => o.setValue((t.getState().colour || {}).tint || "pure");
1957
+ s(), t.onChange(s), e.append(a.el);
1858
1958
  },
1859
- mode(t, e, { label: n = "Mode" } = {}) {
1860
- t.append(ke(e, {
1959
+ mode(e, t, { label: n = "Mode" } = {}) {
1960
+ e.append($e(t, {
1861
1961
  section: "colour",
1862
1962
  key: "mode",
1863
1963
  label: n,
@@ -1866,15 +1966,15 @@ const De = {
1866
1966
  }));
1867
1967
  }
1868
1968
  },
1869
- render(t, e) {
1870
- ["brand", "neutral", "mode"].forEach((n) => this.parts[n](t, e));
1969
+ render(e, t) {
1970
+ ["brand", "neutral", "mode"].forEach((n) => this.parts[n](e, t));
1871
1971
  }
1872
- }, Le = (t, e = "var(--twd-surface)") => `color-mix(in oklch, var(--twd-primary) ${t}%, ${e})`, Qa = {
1972
+ }, Ne = (e, t = "var(--twd-surface)") => `color-mix(in oklch, var(--twd-primary) ${e}%, ${t})`, uo = {
1873
1973
  // proposal recipes; Solid leaves tokens.css defaults
1874
- soft: { bg: Le(14), fg: "var(--twd-primary)", border: "transparent", hover: Le(22) },
1875
- outline: { bg: "transparent", fg: "var(--twd-primary)", border: "var(--twd-primary)", hover: Le(8, "transparent") },
1876
- ghost: { bg: "transparent", fg: "var(--twd-primary)", border: "transparent", hover: Le(10, "transparent") }
1877
- }, eo = { small: { height: 0.8, pad: 0.75 }, large: { height: 1.2, pad: 1.25 } }, Dn = zn({
1974
+ soft: { bg: Ne(14), fg: "var(--twd-primary)", border: "transparent", hover: Ne(22) },
1975
+ outline: { bg: "transparent", fg: "var(--twd-primary)", border: "var(--twd-primary)", hover: Ne(8, "transparent") },
1976
+ ghost: { bg: "transparent", fg: "var(--twd-primary)", border: "transparent", hover: Ne(10, "transparent") }
1977
+ }, ho = { small: { height: 0.8, pad: 0.75 }, large: { height: 1.2, pad: 1.25 } }, Wn = _n({
1878
1978
  id: "buttons",
1879
1979
  title: "Buttons",
1880
1980
  hint: "Style · corners · size",
@@ -1893,15 +1993,15 @@ const De = {
1893
1993
  // Figma: "8px"
1894
1994
  { value: "pill", label: "Pill", radius: 999 }
1895
1995
  ],
1896
- extraTokens(t) {
1897
- const e = {}, n = Qa[t.style];
1898
- n && Object.entries(n).forEach(([s, i]) => {
1899
- e[`--twd-button-${s}`] = i;
1996
+ extraTokens(e) {
1997
+ const t = {}, n = uo[e.style];
1998
+ n && Object.entries(n).forEach(([r, i]) => {
1999
+ t[`--twd-button-${r}`] = i;
1900
2000
  });
1901
- const a = eo[t.size];
1902
- if (!a) return e;
1903
- const o = ye(F("--twd-control-height"), a.height), r = ye(F("--twd-control-pad-x"), a.pad);
1904
- return o && (e["--twd-control-height"] = o), r && (e["--twd-control-pad-x"] = r), e;
2001
+ const a = ho[e.size];
2002
+ if (!a) return t;
2003
+ const o = Se(F("--twd-control-height"), a.height), s = Se(F("--twd-control-pad-x"), a.pad);
2004
+ return o && (t["--twd-control-height"] = o), s && (t["--twd-control-pad-x"] = s), t;
1905
2005
  },
1906
2006
  extra: [
1907
2007
  {
@@ -1909,23 +2009,23 @@ const De = {
1909
2009
  label: "Style",
1910
2010
  fallback: "solid",
1911
2011
  columns: 4,
1912
- preview: (t) => `<span class="shape shape-button style-${t}"></span>`,
2012
+ preview: (e) => `<span class="shape shape-button style-${e}"></span>`,
1913
2013
  options: [{ value: "solid", label: "Solid" }, { value: "soft", label: "Soft" }, { value: "outline", label: "Outline" }, { value: "ghost", label: "Ghost" }]
1914
2014
  },
1915
2015
  {
1916
2016
  key: "size",
1917
2017
  label: "Size",
1918
2018
  fallback: "medium",
1919
- preview: (t) => `<span class="shape shape-button size-${t}"></span>`,
2019
+ preview: (e) => `<span class="shape shape-button size-${e}"></span>`,
1920
2020
  options: [{ value: "small", label: "Small" }, { value: "medium", label: "Medium" }, { value: "large", label: "Large" }]
1921
2021
  }
1922
2022
  ]
1923
- }), to = {
2023
+ }), fo = {
1924
2024
  // proposal recipes; Filled leaves tokens.css defaults
1925
2025
  outlined: { bg: "transparent", border: "color-mix(in oklch, var(--twd-border) 60%, var(--twd-text))", shadow: "none" },
1926
2026
  elevated: { bg: "var(--twd-surface-raised)", border: "transparent", shadow: "var(--twd-shadow-md)" },
1927
2027
  tinted: { bg: "color-mix(in oklch, var(--twd-primary) 8%, var(--twd-surface))", border: "color-mix(in oklch, var(--twd-primary) 25%, var(--twd-surface))", shadow: "none" }
1928
- }, wt = zn({
2028
+ }, St = _n({
1929
2029
  id: "cards",
1930
2030
  title: "Cards",
1931
2031
  hint: "Style · corners · border",
@@ -1944,15 +2044,15 @@ const De = {
1944
2044
  { value: "large", label: "Large", radius: 20 }
1945
2045
  // proposal (cards don't go fully pill)
1946
2046
  ],
1947
- extraTokens(t) {
1948
- const e = {}, n = to[t.style];
2047
+ extraTokens(e) {
2048
+ const t = {}, n = fo[e.style];
1949
2049
  if (n && Object.entries(n).forEach(([a, o]) => {
1950
- e[`--twd-card-${a}`] = o;
1951
- }), t.border === "none" && (e["--twd-border-width-container"] = "0px"), t.border === "thick") {
1952
- const a = ye(F("--twd-border-width-container"), 2);
1953
- a && (e["--twd-border-width-container"] = a);
2050
+ t[`--twd-card-${a}`] = o;
2051
+ }), e.border === "none" && (t["--twd-border-width-container"] = "0px"), e.border === "thick") {
2052
+ const a = Se(F("--twd-border-width-container"), 2);
2053
+ a && (t["--twd-border-width-container"] = a);
1954
2054
  }
1955
- return e;
2055
+ return t;
1956
2056
  },
1957
2057
  extra: [
1958
2058
  {
@@ -1960,49 +2060,49 @@ const De = {
1960
2060
  label: "Style",
1961
2061
  fallback: "filled",
1962
2062
  columns: 4,
1963
- preview: (t) => `<span class="shape shape-card style-${t}"></span>`,
2063
+ preview: (e) => `<span class="shape shape-card style-${e}"></span>`,
1964
2064
  options: [{ value: "filled", label: "Filled" }, { value: "outlined", label: "Outlined" }, { value: "elevated", label: "Elevated" }, { value: "tinted", label: "Tinted" }]
1965
2065
  },
1966
2066
  {
1967
2067
  key: "border",
1968
2068
  label: "Border",
1969
2069
  fallback: "thin",
1970
- preview: (t) => `<span class="shape shape-card border-${t}"></span>`,
2070
+ preview: (e) => `<span class="shape shape-card border-${e}"></span>`,
1971
2071
  options: [{ value: "none", label: "None" }, { value: "thin", label: "Thin" }, { value: "thick", label: "Thick" }]
1972
2072
  }
1973
2073
  ]
1974
- }), ne = 60, st = {
2074
+ }), ie = 60, ct = {
1975
2075
  soft: { sm: [0, 1, 2, 0, 0.06], md: [0, 4, 12, 0, 0.08], lg: [0, 12, 32, 0, 0.12] },
1976
2076
  // = tokens.css
1977
2077
  crisp: { sm: [0, 1, 1, 0, 0.12], md: [0, 2, 4, 0, 0.14], lg: [0, 4, 8, 0, 0.16] },
1978
2078
  bold: { sm: [2, 2, 0, 0, 0.85], md: [3, 3, 0, 0, 0.85], lg: [5, 5, 0, 0, 0.85] }
1979
- }, Pe = ([t, e, n, a, o], r) => {
1980
- const s = Math.min(1, +(o * r).toFixed(3));
1981
- return s <= 0 ? "none" : `${t}px ${e}px ${n}px ${a}px rgb(0 0 0 / ${s})`;
1982
- }, vt = {
2079
+ }, je = ([e, t, n, a, o], s) => {
2080
+ const r = Math.min(1, +(o * s).toFixed(3));
2081
+ return r <= 0 ? "none" : `${e}px ${t}px ${n}px ${a}px rgb(0 0 0 / ${r})`;
2082
+ }, $t = {
1983
2083
  id: "depth",
1984
2084
  title: "Depth",
1985
2085
  hint: "Flat · soft · crisp · bold",
1986
2086
  uses: ["--twd-shadow-sm", "--twd-shadow-md", "--twd-shadow-lg"],
1987
- tokens(t) {
1988
- const e = t.depth || {}, n = e.style || "soft", a = typeof e.intensity == "number" ? Math.min(100, Math.max(0, e.intensity)) : ne;
2087
+ tokens(e) {
2088
+ const t = e.depth || {}, n = t.style || "soft", a = typeof t.intensity == "number" ? Math.min(100, Math.max(0, t.intensity)) : ie;
1989
2089
  if (n === "flat")
1990
2090
  return { "--twd-shadow-sm": "none", "--twd-shadow-md": "none", "--twd-shadow-lg": "none" };
1991
- const o = st[n];
2091
+ const o = ct[n];
1992
2092
  if (!o) return {};
1993
- if (n === "soft" && a === ne) return {};
1994
- const r = a / ne;
2093
+ if (n === "soft" && a === ie) return {};
2094
+ const s = a / ie;
1995
2095
  return {
1996
- "--twd-shadow-sm": Pe(o.sm, r),
1997
- "--twd-shadow-md": Pe(o.md, r),
1998
- "--twd-shadow-lg": Pe(o.lg, r)
2096
+ "--twd-shadow-sm": je(o.sm, s),
2097
+ "--twd-shadow-md": je(o.md, s),
2098
+ "--twd-shadow-lg": je(o.lg, s)
1999
2099
  };
2000
2100
  },
2001
2101
  parts: {
2002
2102
  // Tabbed layout: each style as a small card wearing that shadow.
2003
- styleTiles(t, e, { label: n = "Style" } = {}) {
2004
- const a = (o) => `<span class="shape shape-card shadow-${o}" style="box-shadow:${st[o] ? Pe(st[o].md, 1) : "none"}"></span>`;
2005
- t.append(V(e, {
2103
+ styleTiles(e, t, { label: n = "Style" } = {}) {
2104
+ const a = (o) => `<span class="shape shape-card shadow-${o}" style="box-shadow:${ct[o] ? je(ct[o].md, 1) : "none"}"></span>`;
2105
+ e.append(V(t, {
2006
2106
  section: "depth",
2007
2107
  key: "style",
2008
2108
  label: n,
@@ -2011,8 +2111,8 @@ const De = {
2011
2111
  options: ["flat", "soft", "crisp", "bold"].map((o) => ({ value: o, label: o[0].toUpperCase() + o.slice(1), preview: a(o) }))
2012
2112
  }));
2013
2113
  },
2014
- style(t, e, { label: n = "Style" } = {}) {
2015
- t.append(ke(e, {
2114
+ style(e, t, { label: n = "Style" } = {}) {
2115
+ e.append($e(t, {
2016
2116
  section: "depth",
2017
2117
  key: "style",
2018
2118
  label: n,
@@ -2020,160 +2120,160 @@ const De = {
2020
2120
  options: [{ value: "flat", label: "Flat" }, { value: "soft", label: "Soft" }, { value: "crisp", label: "Crisp" }, { value: "bold", label: "Bold" }]
2021
2121
  }));
2022
2122
  },
2023
- intensity(t, e, { label: n = "Intensity" } = {}) {
2024
- t.append(Ia(e, { section: "depth", key: "intensity", label: n, min: 0, max: 100, fallback: ne, format: (a) => `${a}%` }));
2123
+ intensity(e, t, { label: n = "Intensity" } = {}) {
2124
+ e.append(Xa(t, { section: "depth", key: "intensity", label: n, min: 0, max: 100, fallback: ie, format: (a) => `${a}%` }));
2025
2125
  }
2026
2126
  },
2027
- render(t, e) {
2028
- Object.entries(this.parts).filter(([n]) => !n.endsWith("Tiles")).forEach(([, n]) => n(t, e));
2127
+ render(e, t) {
2128
+ Object.entries(this.parts).filter(([n]) => !n.endsWith("Tiles")).forEach(([, n]) => n(e, t));
2029
2129
  }
2030
- }, no = (t) => Math.min(1, Math.max(0, t)), fn = (t) => Math.round(t / 50) * 50, it = (t) => `${+t.toFixed(1)}%`;
2031
- function $t(t = {}) {
2032
- return typeof t.bold == "number" && isFinite(t.bold) && t.bold !== 0.5 ? no(t.bold) : null;
2130
+ }, go = (e) => Math.min(1, Math.max(0, e)), vn = (e) => Math.round(e / 50) * 50, pt = (e) => `${+e.toFixed(1)}%`;
2131
+ function Tt(e = {}) {
2132
+ return typeof e.bold == "number" && isFinite(e.bold) && e.bold !== 0.5 ? go(e.bold) : null;
2033
2133
  }
2034
- const Oe = (t, e, n) => (t.tokens(e) || {})[n] || F(n), ao = {
2134
+ const Re = (e, t, n) => (e.tokens(t) || {})[n] || F(n), mo = {
2035
2135
  id: "character",
2036
2136
  title: "Character",
2037
2137
  hint: "Subtle ↔ Bold",
2038
2138
  uses: [],
2039
2139
  // set from the Simple page only
2040
- tokens(t) {
2041
- const e = $t(t.character);
2042
- if (e === null) return {};
2043
- const n = {}, a = 2 * e - 1, o = Math.max(0, a), r = Math.max(0, -a);
2044
- n["--twd-weight-heading"] = String(fn(600 + 200 * a)), n["--twd-weight-strong"] = String(fn(600 + 100 * a));
2045
- const s = Oe(De, t, "--twd-border");
2046
- if (r && (n["--twd-border"] = `color-mix(in oklch, ${s} ${it(100 - 50 * r)}, var(--twd-bg))`), o) {
2047
- n["--twd-border"] = `color-mix(in oklch, ${s} ${it(100 - 90 * o)}, var(--twd-text))`;
2048
- const l = Oe(De, t, "--twd-text-muted");
2049
- n["--twd-text-muted"] = `color-mix(in oklch, ${l} ${it(100 - 35 * o)}, var(--twd-text))`, ["--twd-border-width", "--twd-border-width-container"].forEach((p) => {
2050
- const c = p === "--twd-border-width-container" ? Oe(wt, t, p) : F(p), h = parseFloat(c);
2051
- isNaN(h) || (n[p] = `${Math.round(h * (1 + o))}px`);
2052
- }), [[Dn, "--twd-radius-control"], [wt, "--twd-radius-container"]].forEach(([p, c]) => {
2053
- const h = parseFloat(Oe(p, t, c));
2054
- !isNaN(h) && h < 999 && (n[c] = ye(`${h}px`, 1 - 0.5 * o));
2140
+ tokens(e) {
2141
+ const t = Tt(e.character);
2142
+ if (t === null) return {};
2143
+ const n = {}, a = 2 * t - 1, o = Math.max(0, a), s = Math.max(0, -a);
2144
+ n["--twd-weight-heading"] = String(vn(600 + 200 * a)), n["--twd-weight-strong"] = String(vn(600 + 100 * a));
2145
+ const r = Re(He, e, "--twd-border");
2146
+ if (s && (n["--twd-border"] = `color-mix(in oklch, ${r} ${pt(100 - 50 * s)}, var(--twd-bg))`), o) {
2147
+ n["--twd-border"] = `color-mix(in oklch, ${r} ${pt(100 - 90 * o)}, var(--twd-text))`;
2148
+ const l = Re(He, e, "--twd-text-muted");
2149
+ n["--twd-text-muted"] = `color-mix(in oklch, ${l} ${pt(100 - 35 * o)}, var(--twd-text))`, ["--twd-border-width", "--twd-border-width-container"].forEach((d) => {
2150
+ const p = d === "--twd-border-width-container" ? Re(St, e, d) : F(d), u = parseFloat(p);
2151
+ isNaN(u) || (n[d] = `${Math.round(u * (1 + o))}px`);
2152
+ }), [[Wn, "--twd-radius-control"], [St, "--twd-radius-container"]].forEach(([d, p]) => {
2153
+ const u = parseFloat(Re(d, e, p));
2154
+ !isNaN(u) && u < 999 && (n[p] = Se(`${u}px`, 1 - 0.5 * o));
2055
2155
  });
2056
2156
  }
2057
- const i = t.depth || {};
2157
+ const i = e.depth || {};
2058
2158
  if (!i.style) {
2059
- const l = typeof i.intensity == "number" ? i.intensity : ne, p = r ? vt.tokens({ depth: { style: "soft", intensity: l * (1 - r) } }) : vt.tokens({ depth: { style: "bold", intensity: ne * o } });
2060
- Object.assign(n, p);
2159
+ const l = typeof i.intensity == "number" ? i.intensity : ie, d = s ? $t.tokens({ depth: { style: "soft", intensity: l * (1 - s) } }) : $t.tokens({ depth: { style: "bold", intensity: ie * o } });
2160
+ Object.assign(n, d);
2061
2161
  }
2062
2162
  return n;
2063
2163
  },
2064
- describe: (t, e) => t === "bold" ? { label: "Subtle ↔ Bold", value: `${Math.round(e * 100)}%` } : null
2065
- }, oo = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 9l6 6 6-6"/></svg>', ro = '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5 12.5l4.5 4.5L19 7.5"/></svg>';
2066
- let so = 0;
2067
- function io({ label: t, options: e, onSelect: n, placeholder: a, onOpen: o }) {
2068
- const r = `twd-fonts-${++so}`, s = document.createElement("div");
2069
- s.className = "font-select";
2164
+ describe: (e, t) => e === "bold" ? { label: "Subtle ↔ Bold", value: `${Math.round(t * 100)}%` } : null
2165
+ }, bo = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 9l6 6 6-6"/></svg>', xo = '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5 12.5l4.5 4.5L19 7.5"/></svg>';
2166
+ let wo = 0;
2167
+ function yo({ label: e, options: t, onSelect: n, placeholder: a, onOpen: o }) {
2168
+ const s = `twd-fonts-${++wo}`, r = document.createElement("div");
2169
+ r.className = "font-select";
2070
2170
  const i = document.createElement("button");
2071
- i.type = "button", i.className = "font-trigger", i.setAttribute("aria-haspopup", "listbox"), i.setAttribute("aria-expanded", "false"), i.setAttribute("aria-controls", r), i.innerHTML = `
2171
+ i.type = "button", i.className = "font-trigger", i.setAttribute("aria-haspopup", "listbox"), i.setAttribute("aria-expanded", "false"), i.setAttribute("aria-controls", s), i.innerHTML = `
2072
2172
  <span class="font-aa" aria-hidden="true">Aa</span>
2073
2173
  <span class="font-name"></span>
2074
2174
  <span class="font-fonts"></span>
2075
- <span class="font-chevron">${oo}</span>`;
2175
+ <span class="font-chevron">${bo}</span>`;
2076
2176
  const l = document.createElement("div");
2077
- l.className = "font-menu", l.id = r, l.setAttribute("role", "listbox"), l.setAttribute("aria-label", t), l.hidden = !0;
2078
- const p = e.map((u) => {
2079
- const v = document.createElement("div");
2080
- return v.className = "font-option", v.setAttribute("role", "option"), v.tabIndex = -1, v.dataset.value = u.value, v.innerHTML = `
2081
- <span class="font-aa" style="font-family:${u.preview}" aria-hidden="true">Aa</span>
2082
- <span class="font-text"><span class="font-name">${u.name}</span><span class="font-fonts">${u.fonts}</span></span>
2083
- <span class="font-check">${ro}</span>`, v.addEventListener("click", () => b(u.value)), v;
2177
+ l.className = "font-menu", l.id = s, l.setAttribute("role", "listbox"), l.setAttribute("aria-label", e), l.hidden = !0;
2178
+ const d = t.map((h) => {
2179
+ const y = document.createElement("div");
2180
+ return y.className = "font-option", y.setAttribute("role", "option"), y.tabIndex = -1, y.dataset.value = h.value, y.innerHTML = `
2181
+ <span class="font-aa" style="font-family:${h.preview}" aria-hidden="true">Aa</span>
2182
+ <span class="font-text"><span class="font-name">${h.name}</span><span class="font-fonts">${h.fonts}</span></span>
2183
+ <span class="font-check">${xo}</span>`, y.addEventListener("click", () => b(h.value)), y;
2084
2184
  });
2085
- l.append(...p), s.append(i, l);
2086
- let c = null;
2087
- function h(u) {
2088
- const v = e.find((E) => E.value === u), L = v || a || e[0];
2089
- c = v ? v.value : a ? null : e[0].value, i.querySelector(".font-name").textContent = L.name, i.querySelector(".font-fonts").textContent = L.fonts, i.querySelector(".font-aa").style.fontFamily = L.preview, i.setAttribute("aria-label", `${t}: ${L.name}, ${L.fonts}`), p.forEach((E) => E.setAttribute("aria-selected", String(E.dataset.value === c)));
2185
+ l.append(...d), r.append(i, l);
2186
+ let p = null;
2187
+ function u(h) {
2188
+ const y = t.find((E) => E.value === h), L = y || a || t[0];
2189
+ p = y ? y.value : a ? null : t[0].value, i.querySelector(".font-name").textContent = L.name, i.querySelector(".font-fonts").textContent = L.fonts, i.querySelector(".font-aa").style.fontFamily = L.preview, i.setAttribute("aria-label", `${e}: ${L.name}, ${L.fonts}`), d.forEach((E) => E.setAttribute("aria-selected", String(E.dataset.value === p)));
2090
2190
  }
2091
- function y() {
2092
- const u = i.getBoundingClientRect(), v = l.offsetHeight, E = window.innerHeight - u.bottom - 8 >= v ? u.bottom + 4 : Math.max(8, u.top - 4 - v);
2093
- l.style.top = `${E}px`, l.style.left = `${u.left}px`, l.style.width = `${u.width}px`;
2191
+ function v() {
2192
+ const h = i.getBoundingClientRect(), y = l.offsetHeight, E = window.innerHeight - h.bottom - 8 >= y ? h.bottom + 4 : Math.max(8, h.top - 4 - y);
2193
+ l.style.top = `${E}px`, l.style.left = `${h.left}px`, l.style.width = `${h.width}px`;
2094
2194
  }
2095
2195
  function f() {
2096
- l.hidden && (l.hidden = !1, i.setAttribute("aria-expanded", "true"), o && o(), y(), (p.find((u) => u.dataset.value === c) || p[0]).focus(), setTimeout(() => document.addEventListener("pointerdown", x, !0)), window.addEventListener("resize", m), s.closest(".body")?.addEventListener("scroll", m, { once: !0 }));
2196
+ l.hidden && (l.hidden = !1, i.setAttribute("aria-expanded", "true"), o && o(), v(), (d.find((h) => h.dataset.value === p) || d[0]).focus(), setTimeout(() => document.addEventListener("pointerdown", x, !0)), window.addEventListener("resize", m), r.closest(".body")?.addEventListener("scroll", m, { once: !0 }));
2097
2197
  }
2098
- function m(u) {
2099
- l.hidden || (l.hidden = !0, i.setAttribute("aria-expanded", "false"), document.removeEventListener("pointerdown", x, !0), window.removeEventListener("resize", m), u === !0 && i.focus());
2198
+ function m(h) {
2199
+ l.hidden || (l.hidden = !0, i.setAttribute("aria-expanded", "false"), document.removeEventListener("pointerdown", x, !0), window.removeEventListener("resize", m), h === !0 && i.focus());
2100
2200
  }
2101
- function x(u) {
2102
- u.composedPath().includes(s) || m();
2201
+ function x(h) {
2202
+ h.composedPath().includes(r) || m();
2103
2203
  }
2104
- function b(u) {
2105
- u !== c && (h(u), n(u)), m(!0);
2204
+ function b(h) {
2205
+ h !== p && (u(h), n(h)), m(!0);
2106
2206
  }
2107
- return i.addEventListener("click", () => l.hidden ? f() : m(!0)), i.addEventListener("keydown", (u) => {
2108
- (u.key === "ArrowDown" || u.key === "ArrowUp") && (u.preventDefault(), f());
2109
- }), l.addEventListener("keydown", (u) => {
2110
- const v = p.indexOf(u.target);
2207
+ return i.addEventListener("click", () => l.hidden ? f() : m(!0)), i.addEventListener("keydown", (h) => {
2208
+ (h.key === "ArrowDown" || h.key === "ArrowUp") && (h.preventDefault(), f());
2209
+ }), l.addEventListener("keydown", (h) => {
2210
+ const y = d.indexOf(h.target);
2111
2211
  let L = null;
2112
- if (u.key === "ArrowDown") L = Math.min(p.length - 1, v + 1);
2113
- else if (u.key === "ArrowUp") L = Math.max(0, v - 1);
2114
- else if (u.key === "Home") L = 0;
2115
- else if (u.key === "End") L = p.length - 1;
2116
- else if (u.key === "Enter" || u.key === " ") {
2117
- u.preventDefault(), b(u.target.dataset.value);
2212
+ if (h.key === "ArrowDown") L = Math.min(d.length - 1, y + 1);
2213
+ else if (h.key === "ArrowUp") L = Math.max(0, y - 1);
2214
+ else if (h.key === "Home") L = 0;
2215
+ else if (h.key === "End") L = d.length - 1;
2216
+ else if (h.key === "Enter" || h.key === " ") {
2217
+ h.preventDefault(), b(h.target.dataset.value);
2118
2218
  return;
2119
- } else if (u.key === "Escape") {
2120
- u.preventDefault(), u.stopPropagation(), m(!0);
2219
+ } else if (h.key === "Escape") {
2220
+ h.preventDefault(), h.stopPropagation(), m(!0);
2121
2221
  return;
2122
- } else if (u.key === "Tab") {
2222
+ } else if (h.key === "Tab") {
2123
2223
  m();
2124
2224
  return;
2125
2225
  }
2126
- L !== null && (u.preventDefault(), p[L].focus());
2127
- }), h(a ? null : e[0].value), { el: s, setValue: h };
2226
+ L !== null && (h.preventDefault(), d[L].focus());
2227
+ }), u(a ? null : t[0].value), { el: r, setValue: u };
2128
2228
  }
2129
- const I = 'ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif', Fe = 'Georgia, "Times New Roman", serif', Z = [
2229
+ const I = 'ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif', ze = 'Georgia, "Times New Roman", serif', J = [
2130
2230
  { value: "neutral", name: "Neutral", fonts: "Inter · Inter", heading: `"Inter", ${I}`, body: `"Inter", ${I}`, load: ["Inter:wght@400;500;600;700"] },
2131
2231
  { value: "modern", name: "Modern", fonts: "Geist · Geist", heading: `"Geist", ${I}`, body: `"Geist", ${I}`, load: ["Geist:wght@400;500;600;700"] },
2132
- { value: "editorial", name: "Editorial", fonts: "Fraunces · Inter", heading: `"Fraunces", ${Fe}`, body: `"Inter", ${I}`, load: ["Fraunces:wght@400;600;700", "Inter:wght@400;500;600;700"] },
2232
+ { value: "editorial", name: "Editorial", fonts: "Fraunces · Inter", heading: `"Fraunces", ${ze}`, body: `"Inter", ${I}`, load: ["Fraunces:wght@400;600;700", "Inter:wght@400;500;600;700"] },
2133
2233
  { value: "technical", name: "Technical", fonts: "Space Grotesk · DM Sans", heading: `"Space Grotesk", ${I}`, body: `"DM Sans", ${I}`, load: ["Space+Grotesk:wght@500;600;700", "DM+Sans:wght@400;500;600;700"] },
2134
- { value: "classic", name: "Classic", fonts: "Playfair · DM Sans", heading: `"Playfair Display", ${Fe}`, body: `"DM Sans", ${I}`, load: ["Playfair+Display:wght@600;700", "DM+Sans:wght@400;500;600;700"] },
2135
- { value: "plex", name: "Plex", fonts: "Plex Serif · Plex Sans", heading: `"IBM Plex Serif", ${Fe}`, body: `"IBM Plex Sans", ${I}`, load: ["IBM+Plex+Serif:wght@500;600;700", "IBM+Plex+Sans:wght@400;500;600"] },
2136
- { value: "elegant", name: "Elegant", fonts: "Instrument Serif · Inter", heading: `"Instrument Serif", ${Fe}`, body: `"Inter", ${I}`, load: ["Instrument+Serif", "Inter:wght@400;500;600;700"] },
2234
+ { value: "classic", name: "Classic", fonts: "Playfair · DM Sans", heading: `"Playfair Display", ${ze}`, body: `"DM Sans", ${I}`, load: ["Playfair+Display:wght@600;700", "DM+Sans:wght@400;500;600;700"] },
2235
+ { value: "plex", name: "Plex", fonts: "Plex Serif · Plex Sans", heading: `"IBM Plex Serif", ${ze}`, body: `"IBM Plex Sans", ${I}`, load: ["IBM+Plex+Serif:wght@500;600;700", "IBM+Plex+Sans:wght@400;500;600"] },
2236
+ { value: "elegant", name: "Elegant", fonts: "Instrument Serif · Inter", heading: `"Instrument Serif", ${ze}`, body: `"Inter", ${I}`, load: ["Instrument+Serif", "Inter:wght@400;500;600;700"] },
2137
2237
  { value: "friendly", name: "Friendly", fonts: "Manrope · Manrope", heading: `"Manrope", ${I}`, body: `"Manrope", ${I}`, load: ["Manrope:wght@400;500;600;700"] }
2138
- ], be = { compact: 1.125, balanced: 1.2, expressive: 1.333 }, gn = { xs: -2, sm: -1, base: 0, lg: 1, xl: 2, "2xl": 3, "3xl": 4 }, mn = /* @__PURE__ */ new Set();
2139
- function lt(t) {
2140
- const e = t.filter((a) => !mn.has(a));
2141
- if (!e.length) return;
2142
- e.forEach((a) => mn.add(a));
2238
+ ], we = { compact: 1.125, balanced: 1.2, expressive: 1.333 }, kn = { xs: -2, sm: -1, base: 0, lg: 1, xl: 2, "2xl": 3, "3xl": 4 }, Sn = /* @__PURE__ */ new Set();
2239
+ function ut(e) {
2240
+ const t = e.filter((a) => !Sn.has(a));
2241
+ if (!t.length) return;
2242
+ t.forEach((a) => Sn.add(a));
2143
2243
  const n = document.createElement("link");
2144
- n.rel = "stylesheet", n.href = `https://fonts.googleapis.com/css2?${e.map((a) => `family=${a}`).join("&")}&display=swap`, n.dataset.twiddle = "fonts", document.head.appendChild(n);
2244
+ n.rel = "stylesheet", n.href = `https://fonts.googleapis.com/css2?${t.map((a) => `family=${a}`).join("&")}&display=swap`, n.dataset.twiddle = "fonts", document.head.appendChild(n);
2145
2245
  }
2146
- const lo = {
2246
+ const vo = {
2147
2247
  id: "typography",
2148
2248
  title: "Typography",
2149
2249
  hint: "Font pairing · base size · scale",
2150
- uses: ["--twd-font-heading", "--twd-font-body", ...Object.keys(gn).map((t) => `--twd-text-${t}`)],
2151
- tokens(t) {
2152
- const e = t.typography || {}, n = {}, a = Z.find((o) => o.value === e.font);
2153
- if (a && (lt(a.load), n["--twd-font-heading"] = a.heading, n["--twd-font-body"] = a.body), e.base || e.scale) {
2154
- const o = Number(e.base) || 16, r = be[e.scale] || be.balanced;
2155
- Object.entries(gn).forEach(([s, i]) => {
2156
- n[`--twd-text-${s}`] = `${+(o * r ** i / 16).toFixed(3)}rem`;
2250
+ uses: ["--twd-font-heading", "--twd-font-body", ...Object.keys(kn).map((e) => `--twd-text-${e}`)],
2251
+ tokens(e) {
2252
+ const t = e.typography || {}, n = {}, a = J.find((o) => o.value === t.font);
2253
+ if (a && (ut(a.load), n["--twd-font-heading"] = a.heading, n["--twd-font-body"] = a.body), t.base || t.scale) {
2254
+ const o = Number(t.base) || 16, s = we[t.scale] || we.balanced;
2255
+ Object.entries(kn).forEach(([r, i]) => {
2256
+ n[`--twd-text-${r}`] = `${+(o * s ** i / 16).toFixed(3)}rem`;
2157
2257
  });
2158
2258
  }
2159
2259
  return n;
2160
2260
  },
2161
2261
  parts: {
2162
- fonts(t, e, { label: n = "Fonts" } = {}) {
2163
- const a = B(n), o = io({
2262
+ fonts(e, t, { label: n = "Fonts" } = {}) {
2263
+ const a = U(n), o = yo({
2164
2264
  label: "Font pairing",
2165
- options: Z.map((s) => ({ value: s.value, name: s.name, fonts: s.fonts, preview: s.heading })),
2265
+ options: J.map((r) => ({ value: r.value, name: r.name, fonts: r.fonts, preview: r.heading })),
2166
2266
  placeholder: { name: "Default", fonts: "Prototype fonts", preview: I },
2167
2267
  // Load every pairing's fonts when the list opens, so the "Aa" previews are real.
2168
- onOpen: () => lt([...new Set(Z.flatMap((s) => s.load))]),
2169
- onSelect: (s) => e.setSection("typography", { font: s })
2268
+ onOpen: () => ut([...new Set(J.flatMap((r) => r.load))]),
2269
+ onSelect: (r) => t.setSection("typography", { font: r })
2170
2270
  });
2171
2271
  a.append(o.el);
2172
- const r = () => o.setValue((e.getState().typography || {}).font || null);
2173
- r(), e.onChange(r), t.append(a.el);
2272
+ const s = () => o.setValue((t.getState().typography || {}).font || null);
2273
+ s(), t.onChange(s), e.append(a.el);
2174
2274
  },
2175
- base(t, e, { label: n = "Base size" } = {}) {
2176
- t.append(ke(e, {
2275
+ base(e, t, { label: n = "Base size" } = {}) {
2276
+ e.append($e(t, {
2177
2277
  section: "typography",
2178
2278
  key: "base",
2179
2279
  label: n,
@@ -2184,8 +2284,8 @@ const lo = {
2184
2284
  },
2185
2285
  // Tabbed layout: pairings as tiles, each "Aa" in its own heading font.
2186
2286
  // The fonts load (from Google Fonts) only once the tiles are on screen.
2187
- fontTiles(t, e, { label: n = "Fonts" } = {}) {
2188
- const a = (i) => i.replace(/"/g, "&quot;"), o = (i) => `<span class="tile-aa" style="font-family:${a(i)}">Aa</span>`, r = V(e, {
2287
+ fontTiles(e, t, { label: n = "Fonts" } = {}) {
2288
+ const a = (i) => i.replace(/"/g, "&quot;"), o = (i) => `<span class="tile-aa" style="font-family:${a(i)}">Aa</span>`, s = V(t, {
2189
2289
  section: "typography",
2190
2290
  key: "font",
2191
2291
  label: n,
@@ -2193,165 +2293,165 @@ const lo = {
2193
2293
  className: "tiles-fonts",
2194
2294
  options: [
2195
2295
  { value: "default", label: "Prototype fonts", sub: "Default", preview: o(I), wide: !0 },
2196
- ...Z.map((i) => ({ value: i.value, label: i.name, sub: i.fonts, preview: o(i.heading) }))
2296
+ ...J.map((i) => ({ value: i.value, label: i.name, sub: i.fonts, preview: o(i.heading) }))
2197
2297
  ],
2198
2298
  current: (i) => (i.typography || {}).font || "default",
2199
- select: (i) => e.setSection("typography", { font: i === "default" ? null : i }),
2200
- valueText: (i) => i === "default" ? "Default" : Z.find((l) => l.value === i)?.name || ""
2201
- }), s = new IntersectionObserver((i) => {
2202
- i.some((l) => l.isIntersecting) && (lt([...new Set(Z.flatMap((l) => l.load))]), s.disconnect());
2299
+ select: (i) => t.setSection("typography", { font: i === "default" ? null : i }),
2300
+ valueText: (i) => i === "default" ? "Default" : J.find((l) => l.value === i)?.name || ""
2301
+ }), r = new IntersectionObserver((i) => {
2302
+ i.some((l) => l.isIntersecting) && (ut([...new Set(J.flatMap((l) => l.load))]), r.disconnect());
2203
2303
  });
2204
- s.observe(r), t.append(r);
2304
+ r.observe(s), e.append(s);
2205
2305
  },
2206
- scaleTiles(t, e, { label: n = "Scale" } = {}) {
2207
- const a = (o) => `<span class="ladder">${[-1, 0, 1, 2].map((r) => `<b style="font-size:${Math.round(13 * o ** r)}px">A</b>`).join("")}</span>`;
2208
- t.append(V(e, {
2306
+ scaleTiles(e, t, { label: n = "Scale" } = {}) {
2307
+ const a = (o) => `<span class="ladder">${[-1, 0, 1, 2].map((s) => `<b style="font-size:${Math.round(13 * o ** s)}px">A</b>`).join("")}</span>`;
2308
+ e.append(V(t, {
2209
2309
  section: "typography",
2210
2310
  key: "scale",
2211
2311
  label: n,
2212
2312
  fallback: "balanced",
2213
- options: Object.entries(be).map(([o, r]) => ({ value: o, label: o[0].toUpperCase() + o.slice(1), preview: a(r) })),
2214
- valueText: (o) => String(be[o])
2313
+ options: Object.entries(we).map(([o, s]) => ({ value: o, label: o[0].toUpperCase() + o.slice(1), preview: a(s) })),
2314
+ valueText: (o) => String(we[o])
2215
2315
  }));
2216
2316
  },
2217
- scale(t, e, { label: n = "Scale" } = {}) {
2218
- t.append(ke(e, {
2317
+ scale(e, t, { label: n = "Scale" } = {}) {
2318
+ e.append($e(t, {
2219
2319
  section: "typography",
2220
2320
  key: "scale",
2221
2321
  label: n,
2222
2322
  fallback: "balanced",
2223
2323
  options: [{ value: "compact", label: "Compact" }, { value: "balanced", label: "Balanced" }, { value: "expressive", label: "Expressive" }],
2224
- valueText: (a) => String(be[a])
2324
+ valueText: (a) => String(we[a])
2225
2325
  }));
2226
2326
  }
2227
2327
  },
2228
- render(t, e) {
2229
- Object.entries(this.parts).filter(([n]) => !n.endsWith("Tiles")).forEach(([, n]) => n(t, e));
2328
+ render(e, t) {
2329
+ Object.entries(this.parts).filter(([n]) => !n.endsWith("Tiles")).forEach(([, n]) => n(e, t));
2230
2330
  }
2231
- }, [bn, Ne] = xt;
2232
- function co(t) {
2233
- return t > 0.56 ? "Bold" : t < 0.44 ? "Subtle" : "Balanced";
2331
+ }, [$n, Ie] = kt;
2332
+ function ko(e) {
2333
+ return e > 0.56 ? "Bold" : e < 0.44 ? "Subtle" : "Balanced";
2234
2334
  }
2235
- function Bn(t) {
2236
- return t > 1.06 ? "Comfy" : t < 0.94 ? "Compact" : "Regular";
2335
+ function Gn(e) {
2336
+ return e > 1.06 ? "Comfy" : e < 0.94 ? "Compact" : "Regular";
2237
2337
  }
2238
- const je = (t) => t && t[0].toUpperCase() + t.slice(1), xn = [
2239
- ["colour", "Colour", (t) => {
2240
- const e = t.colour || {}, n = ze.find((o) => o.primary === e.primary && o.neutral === e.neutral);
2241
- return { text: `${e.brand ? e.brand.toUpperCase() : n && !e.shade ? n.label : e.primary ? `${je(e.primary)}${e.neutral ? ` · ${je(e.neutral)}` : ""}` : "Prototype colours"}${e.mode === "dark" ? " · Dark" : ""}`, swatch: F("--twd-primary") };
2338
+ const De = (e) => e && e[0].toUpperCase() + e.slice(1), En = [
2339
+ ["colour", "Colour", (e) => {
2340
+ const t = e.colour || {}, n = Be.find((o) => o.primary === t.primary && o.neutral === t.neutral);
2341
+ return { text: `${t.brand ? t.brand.toUpperCase() : n && !t.shade ? n.label : t.primary ? `${De(t.primary)}${t.neutral ? ` · ${De(t.neutral)}` : ""}` : "Prototype colours"}${t.mode === "dark" ? " · Dark" : ""}`, swatch: F("--twd-primary") };
2242
2342
  }],
2243
- ["type", "Type", (t) => {
2244
- const e = t.typography || {};
2245
- return { text: `${Z.find((n) => n.value === e.font)?.name || "Prototype fonts"} · ${e.base || 16}px` };
2343
+ ["type", "Type", (e) => {
2344
+ const t = e.typography || {};
2345
+ return { text: `${J.find((n) => n.value === t.font)?.name || "Prototype fonts"} · ${t.base || 16}px` };
2246
2346
  }],
2247
- ["spacing", "Spacing", (t) => ({ text: Bn(te(t.density)) })],
2248
- ["shape", "Shape", (t) => ({ text: `${je((t.buttons || {}).corners) || "Default"} corners` })],
2249
- ["shadows", "Shadows", (t) => ({ text: je((t.depth || {}).style) || ($t(t.character) !== null ? "From the pad" : "Default") })]
2250
- ], po = {
2251
- summary(t, e, { label: n = "Current look", goTab: a } = {}) {
2252
- const o = B(n), r = document.createElement("div");
2253
- r.className = "summary", xn.forEach(([i, l]) => {
2254
- const p = document.createElement("button");
2255
- p.type = "button", p.className = "summary-row", p.dataset.summary = i, p.innerHTML = `<span class="summary-name"></span><span class="summary-swatch" hidden></span><span class="summary-value"></span><span class="chev" aria-hidden="true">${A.chevron}</span>`, p.querySelector(".summary-name").textContent = l, p.addEventListener("click", () => a && a(i)), r.append(p);
2256
- }), o.append(r);
2257
- const s = () => {
2258
- const i = e.getState();
2259
- xn.forEach(([l, , p]) => {
2260
- const c = r.querySelector(`[data-summary="${l}"]`), h = p(i);
2261
- c.querySelector(".summary-value").textContent = h.text;
2262
- const y = c.querySelector(".summary-swatch");
2263
- y.hidden = !h.swatch, h.swatch && (y.style.background = h.swatch);
2347
+ ["spacing", "Spacing", (e) => ({ text: Gn(se(e.density)) })],
2348
+ ["shape", "Shape", (e) => ({ text: `${De((e.buttons || {}).corners) || "Default"} corners` })],
2349
+ ["shadows", "Shadows", (e) => ({ text: De((e.depth || {}).style) || (Tt(e.character) !== null ? "From the pad" : "Default") })]
2350
+ ], So = {
2351
+ summary(e, t, { label: n = "Current look", goTab: a } = {}) {
2352
+ const o = U(n), s = document.createElement("div");
2353
+ s.className = "summary", En.forEach(([i, l]) => {
2354
+ const d = document.createElement("button");
2355
+ d.type = "button", d.className = "summary-row", d.dataset.summary = i, d.innerHTML = `<span class="summary-name"></span><span class="summary-swatch" hidden></span><span class="summary-value"></span><span class="chev" aria-hidden="true">${A.chevron}</span>`, d.querySelector(".summary-name").textContent = l, d.addEventListener("click", () => a && a(i)), s.append(d);
2356
+ }), o.append(s);
2357
+ const r = () => {
2358
+ const i = t.getState();
2359
+ En.forEach(([l, , d]) => {
2360
+ const p = s.querySelector(`[data-summary="${l}"]`), u = d(i);
2361
+ p.querySelector(".summary-value").textContent = u.text;
2362
+ const v = p.querySelector(".summary-swatch");
2363
+ v.hidden = !u.swatch, u.swatch && (v.style.background = u.swatch);
2264
2364
  });
2265
2365
  };
2266
- s(), e.onChange(s), t.append(o.el);
2366
+ r(), t.onChange(r), e.append(o.el);
2267
2367
  },
2268
- character(t, e, { label: n = "Character" } = {}) {
2269
- const a = B(n), o = ja({
2368
+ character(e, t, { label: n = "Character" } = {}) {
2369
+ const a = U(n), o = Wa({
2270
2370
  label: n,
2271
2371
  ends: { top: "Spacious", bottom: "Compact", left: "Subtle", right: "Bold" },
2272
- onInput: (s, i) => {
2273
- const l = +(Ne - i * (Ne - bn)).toFixed(3), p = +s.toFixed(3);
2274
- e.setSections({
2372
+ onInput: (r, i) => {
2373
+ const l = +(Ie - i * (Ie - $n)).toFixed(3), d = +r.toFixed(3);
2374
+ t.setSections({
2275
2375
  density: { factor: l === 1 ? null : l, spacing: null },
2276
- character: { bold: p === 0.5 ? null : p }
2376
+ character: { bold: d === 0.5 ? null : d }
2277
2377
  }, "character");
2278
2378
  }
2279
2379
  });
2280
2380
  a.append(o.el);
2281
- const r = () => {
2282
- const s = e.getState(), i = te(s.density), l = $t(s.character), p = l === null ? 0.5 : l, c = `${Bn(i)} · ${co(p)}`;
2283
- o.setValue(p, (Ne - i) / (Ne - bn), c), a.setValue(c);
2381
+ const s = () => {
2382
+ const r = t.getState(), i = se(r.density), l = Tt(r.character), d = l === null ? 0.5 : l, p = `${Gn(i)} · ${ko(d)}`;
2383
+ o.setValue(d, (Ie - i) / (Ie - $n), p), a.setValue(p);
2284
2384
  };
2285
- r(), e.onChange(r), t.append(a.el);
2385
+ s(), t.onChange(s), e.append(a.el);
2286
2386
  }
2287
- }, dt = [
2387
+ }, ht = [
2288
2388
  { key: "page-pad", label: "Page padding", step: 5 },
2289
2389
  { key: "section-gap", label: "Section gap", step: 6 },
2290
2390
  { key: "card-pad", label: "Card padding", step: 5 },
2291
2391
  { key: "card-gap", label: "Card gap", step: 2 },
2292
2392
  { key: "control-gap", label: "Control gap", step: 3 },
2293
2393
  { key: "control-pad-x", label: "Control padding", step: null }
2294
- ], uo = [1, 2, 3, 4, 5, 6, 7, 8], ho = /^var\(\s*--twd-space-(\d)\s*\)$/, ct = (t) => {
2295
- const e = /^(-?\d*\.?\d+)(px|rem)?$/.exec(String(t).trim());
2296
- return e ? +(parseFloat(e[1]) * (e[2] === "rem" ? 16 : 1)).toFixed(1) : null;
2394
+ ], $o = [1, 2, 3, 4, 5, 6, 7, 8], Eo = /^var\(\s*--twd-space-(\d)\s*\)$/, ft = (e) => {
2395
+ const t = /^(-?\d*\.?\d+)(px|rem)?$/.exec(String(e).trim());
2396
+ return t ? +(parseFloat(t[1]) * (t[2] === "rem" ? 16 : 1)).toFixed(1) : null;
2297
2397
  };
2298
- function fo(t, e, n) {
2299
- const a = `--twd-${e.key}`, o = document.createElement("div");
2398
+ function Co(e, t, n) {
2399
+ const a = `--twd-${t.key}`, o = document.createElement("div");
2300
2400
  o.className = "role-row", n || o.classList.add("unused"), o.innerHTML = `
2301
- <span class="role-label" title="${a}${n ? "" : " (not used on this page)"}">${e.label}</span>
2302
- <label class="token-value role-fixed" hidden><input type="text" inputmode="decimal" spellcheck="false" aria-label="${e.label}, fixed value"><span class="token-unit">px</span></label>
2401
+ <span class="role-label" title="${a}${n ? "" : " (not used on this page)"}">${t.label}</span>
2402
+ <label class="token-value role-fixed" hidden><input type="text" inputmode="decimal" spellcheck="false" aria-label="${t.label}, fixed value"><span class="token-unit">px</span></label>
2303
2403
  <span class="step-select">
2304
- <select aria-label="${e.label}"></select>
2404
+ <select aria-label="${t.label}"></select>
2305
2405
  <span class="step-chevron" aria-hidden="true">${A.down}</span>
2306
2406
  </span>
2307
- <button class="icon-btn token-reset" title="Back to the default" aria-label="Reset ${e.label}" hidden>${A.reset}</button>
2407
+ <button class="icon-btn token-reset" title="Back to the default" aria-label="Reset ${t.label}" hidden>${A.reset}</button>
2308
2408
  `;
2309
- const r = o.querySelector("select"), s = o.querySelector(".role-fixed"), i = s.querySelector("input"), l = o.querySelector(".token-reset"), p = () => (t.getState().custom || {})[e.key], c = () => {
2310
- const x = p();
2311
- if (x == null) return e.step ? { step: e.step } : { fixed: F(a) };
2312
- const b = ho.exec(x);
2409
+ const s = o.querySelector("select"), r = o.querySelector(".role-fixed"), i = r.querySelector("input"), l = o.querySelector(".token-reset"), d = () => (e.getState().custom || {})[t.key], p = () => {
2410
+ const x = d();
2411
+ if (x == null) return t.step ? { step: t.step } : { fixed: F(a) };
2412
+ const b = Eo.exec(x);
2313
2413
  return b ? { step: +b[1] } : { fixed: x };
2314
- }, h = (x) => t.setSection("custom", { [e.key]: x }), y = (x) => e.step ? x === `var(--twd-space-${e.step})` : !1;
2315
- r.addEventListener("change", () => {
2316
- if (r.value === "fixed") {
2317
- const b = ct(F(a));
2318
- h(b != null ? `${b}px` : F(a)), i.focus(), i.select();
2414
+ }, u = (x) => e.setSection("custom", { [t.key]: x }), v = (x) => t.step ? x === `var(--twd-space-${t.step})` : !1;
2415
+ s.addEventListener("change", () => {
2416
+ if (s.value === "fixed") {
2417
+ const b = ft(F(a));
2418
+ u(b != null ? `${b}px` : F(a)), i.focus(), i.select();
2319
2419
  return;
2320
2420
  }
2321
- const x = `var(--twd-space-${r.value})`;
2322
- h(y(x) ? null : x);
2421
+ const x = `var(--twd-space-${s.value})`;
2422
+ u(v(x) ? null : x);
2323
2423
  });
2324
2424
  const f = () => {
2325
2425
  const x = i.value.trim();
2326
- if (!x) return h(null);
2327
- h(/^-?\d*\.?\d+$/.test(x) ? `${x}px` : x);
2426
+ if (!x) return u(null);
2427
+ u(/^-?\d*\.?\d+$/.test(x) ? `${x}px` : x);
2328
2428
  };
2329
2429
  i.addEventListener("change", f), i.addEventListener("blur", () => m()), i.addEventListener("keydown", (x) => {
2330
2430
  x.key === "Enter" && (x.preventDefault(), i.blur()), x.key === "Escape" && (x.stopPropagation(), m(), i.blur());
2331
- }), l.addEventListener("click", () => h(null));
2431
+ }), l.addEventListener("click", () => u(null));
2332
2432
  function m() {
2333
- const x = c();
2334
- if (r.innerHTML = uo.map((u) => `<option value="${u}">space-${u} · ${ct(F(`--twd-space-${u}`)) ?? "?"}</option>`).join("") + '<option value="fixed">Fixed</option>', r.value = x.step ? String(x.step) : "fixed", s.hidden = !x.fixed, o.classList.toggle("is-fixed", !!x.fixed), x.fixed && o.getRootNode().activeElement !== i) {
2335
- const u = ct(x.fixed);
2336
- i.value = u != null ? String(u) : x.fixed;
2433
+ const x = p();
2434
+ if (s.innerHTML = $o.map((h) => `<option value="${h}">space-${h} · ${ft(F(`--twd-space-${h}`)) ?? "?"}</option>`).join("") + '<option value="fixed">Fixed</option>', s.value = x.step ? String(x.step) : "fixed", r.hidden = !x.fixed, o.classList.toggle("is-fixed", !!x.fixed), x.fixed && o.getRootNode().activeElement !== i) {
2435
+ const h = ft(x.fixed);
2436
+ i.value = h != null ? String(h) : x.fixed;
2337
2437
  }
2338
- const b = p() != null;
2438
+ const b = d() != null;
2339
2439
  o.classList.toggle("edited", b), l.hidden = !b;
2340
2440
  }
2341
- return m(), t.onChange(m), o;
2441
+ return m(), e.onChange(m), o;
2342
2442
  }
2343
- const go = {
2344
- spacing(t, e, { label: n = "Roles" } = {}) {
2345
- const a = Fa(), o = B(n);
2443
+ const Mo = {
2444
+ spacing(e, t, { label: n = "Roles" } = {}) {
2445
+ const a = _a(), o = U(n);
2346
2446
  o.el.classList.add("token-field");
2347
- const r = dt.filter((l) => !a.has(`--twd-${l.key}`));
2348
- o.setValue(r.length === dt.length ? "not used on this page" : "");
2349
- const s = document.createElement("div");
2350
- s.className = "token-rows", dt.forEach((l) => s.append(fo(e, l, a.has(`--twd-${l.key}`)))), o.append(s), t.append(o.el);
2447
+ const s = ht.filter((l) => !a.has(`--twd-${l.key}`));
2448
+ o.setValue(s.length === ht.length ? "not used on this page" : "");
2449
+ const r = document.createElement("div");
2450
+ r.className = "token-rows", ht.forEach((l) => r.append(Co(t, l, a.has(`--twd-${l.key}`)))), o.append(r), e.append(o.el);
2351
2451
  const i = document.createElement("p");
2352
- i.className = "role-note", i.textContent = r.length ? `Only elements styled with these tokens change. Not used on this page: ${r.map((l) => l.label).join(", ")}.` : "Each role points at a spacing step, so Density still scales it. Pick another step, or a fixed value.", t.append(i);
2452
+ i.className = "role-note", i.textContent = s.length ? `Only elements styled with these tokens change. Not used on this page: ${s.map((l) => l.label).join(", ")}.` : "Each role points at a spacing step, so Density still scales it. Pick another step, or a fixed value.", e.append(i);
2353
2453
  }
2354
- }, xe = [
2454
+ }, ye = [
2355
2455
  { id: "overview", title: "Overview", icon: "tabOverview", parts: [["simple", "character"], ["simple", "summary"]] },
2356
2456
  { id: "colour", title: "Colour", icon: "tabColour", parts: [
2357
2457
  ["colour", "themeTiles"],
@@ -2391,18 +2491,18 @@ const go = {
2391
2491
  ["depth", "intensity"],
2392
2492
  { more: "Shadow tokens", parts: [["custom", "shadows"]] }
2393
2493
  ] }
2394
- ], O = (t) => String(t).replace(/[&<>"]/g, (e) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;" })[e]);
2395
- function mo({ sections: t, store: e, view: n }) {
2494
+ ], O = (e) => String(e).replace(/[&<>"]/g, (t) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;" })[t]);
2495
+ function To({ sections: e, store: t, view: n }) {
2396
2496
  const a = document.createElement("div");
2397
2497
  a.id = "twiddle-root", document.body.appendChild(a);
2398
- const o = a.attachShadow({ mode: "open" }), r = (d) => d.map((g) => `
2498
+ const o = a.attachShadow({ mode: "open" }), s = (c) => c.map((g) => `
2399
2499
  <details data-section="${g.id}">
2400
2500
  <summary>${g.title}<span class="chev">${A.chevron}</span></summary>
2401
2501
  <div class="section-body"><div class="placeholder">${g.hint}</div></div>
2402
2502
  </details>
2403
2503
  `).join("");
2404
2504
  o.innerHTML = `
2405
- <style>${ua}</style>
2505
+ <style>${va}</style>
2406
2506
  <div class="pick-box" hidden><span class="pick-tag"></span></div>
2407
2507
  <div class="wrap">
2408
2508
  <div class="toast" role="status"></div>
@@ -2413,6 +2513,7 @@ function mo({ sections: t, store: e, view: n }) {
2413
2513
  <button class="icon-btn" data-action="compare" title="Hold to see the original (Alt+C toggles)" aria-label="Compare with original" aria-pressed="false">${A.compare}</button>
2414
2514
  <button class="icon-btn" data-action="copy" title="Copy link to this version" aria-label="Copy link">${A.link}</button>
2415
2515
  <button class="icon-btn" data-action="reset" title="Reset to defaults" aria-label="Reset">${A.reset}</button>
2516
+ <button class="icon-btn" data-action="minimise" title="Minimise to the tabs" aria-label="Minimise to the tabs">${A.minimise}</button>
2416
2517
  <button class="icon-btn" data-action="close" title="Close (Alt+T)" aria-label="Close">${A.close}</button>
2417
2518
  </div>
2418
2519
  <div class="actions">
@@ -2431,15 +2532,15 @@ function mo({ sections: t, store: e, view: n }) {
2431
2532
  </div>
2432
2533
  <div class="original-pill" hidden>Showing original</div>
2433
2534
  <div class="body" data-view="global"><div class="setup-hint" role="status" hidden></div>
2434
- ${xe.map((d) => `<div class="page page-fields" data-page="${d.id}" hidden></div>`).join("")}
2535
+ ${ye.map((c) => `<div class="page page-fields" data-page="${c.id}" hidden></div>`).join("")}
2435
2536
  </div>
2436
2537
  <div class="body" data-view="local" hidden></div>
2437
2538
  <div class="body" data-view="changes" hidden></div>
2438
2539
  <nav class="tabbar" aria-label="Areas">
2439
- ${xe.map((d) => `<button class="tab" data-tab="${d.id}" aria-pressed="false">${A[d.icon]}<span>${d.title}</span></button>`).join("")}
2540
+ ${ye.map((c) => `<button class="tab" data-tab="${c.id}" aria-pressed="false">${A[c.icon]}<span>${c.title}</span></button>`).join("")}
2440
2541
  </nav>
2441
2542
  </div>
2442
- <button class="launcher" aria-label="Open Panel" aria-expanded="false">${A.panelMark}</button>
2543
+ <button class="launcher" aria-label="Open Panel" aria-expanded="false" title="Panel · drag to move, double-click to reset">${A.panelMark}</button>
2443
2544
  </div>
2444
2545
  <div class="dialog-backdrop" hidden>
2445
2546
  <div class="dialog" role="dialog" aria-modal="true" aria-labelledby="twd-dialog-title">
@@ -2455,160 +2556,168 @@ function mo({ sections: t, store: e, view: n }) {
2455
2556
  </div>
2456
2557
  </div>
2457
2558
  `;
2458
- const s = (d) => o.querySelector(d), i = s(".panel"), l = s(".launcher"), p = s(".toast"), c = { global: s('[data-view="global"]'), local: s('[data-view="local"]'), changes: s('[data-view="changes"]') }, h = s(".pick-box"), y = s(".pick-tag"), f = { getState: e.getState, setSection: e.setSection, setSections: e.setSections, onChange: e.onChange };
2459
- function m(d, g, w) {
2559
+ const r = (c) => o.querySelector(c), i = r(".panel"), l = r(".launcher"), d = r(".toast"), p = { global: r('[data-view="global"]'), local: r('[data-view="local"]'), changes: r('[data-view="changes"]') }, u = r(".pick-box"), v = r(".pick-tag"), f = { getState: t.getState, setSection: t.setSection, setSections: t.setSections, onChange: t.onChange };
2560
+ function m(c, g, w) {
2460
2561
  g.forEach((k) => {
2461
2562
  if (typeof k.render != "function") return;
2462
- const C = d.querySelector(`[data-section="${k.id}"] .section-body`);
2563
+ const C = c.querySelector(`[data-section="${k.id}"] .section-body`);
2463
2564
  C.innerHTML = "", k.render(C, w);
2464
2565
  });
2465
- const $ = [...d.querySelectorAll("details")];
2566
+ const $ = [...c.querySelectorAll("details")];
2466
2567
  return $.forEach((k) => k.addEventListener("toggle", () => {
2467
2568
  k.open && $.forEach((C) => {
2468
2569
  C !== k && (C.open = !1);
2469
2570
  });
2470
2571
  })), $;
2471
2572
  }
2472
- const x = Object.fromEntries(t.map((d) => [d.id, d]));
2473
- function b(d, g) {
2573
+ const x = Object.fromEntries(e.map((c) => [c.id, c]));
2574
+ function b(c, g) {
2474
2575
  g.forEach((w) => {
2475
2576
  if (w === "warnings") {
2476
2577
  const S = document.createElement("div");
2477
- S.className = "warnings page-warnings", d.append(S);
2578
+ S.className = "warnings page-warnings", c.append(S);
2478
2579
  return;
2479
2580
  }
2480
2581
  if (w.heading) {
2481
2582
  const S = document.createElement("div");
2482
- S.className = "page-heading", S.innerHTML = `<span></span>${w.note ? '<span class="muted"></span>' : ""}`, S.firstChild.textContent = w.heading, w.note && (S.lastChild.textContent = w.note), d.append(S);
2583
+ S.className = "page-heading", S.innerHTML = `<span></span>${w.note ? '<span class="muted"></span>' : ""}`, S.firstChild.textContent = w.heading, w.note && (S.lastChild.textContent = w.note), c.append(S);
2483
2584
  return;
2484
2585
  }
2485
2586
  if (w.more) {
2486
2587
  const S = document.createElement("details");
2487
- S.className = "more", S.innerHTML = `<summary><span class="chev" aria-hidden="true">${A.chevron}</span><span></span></summary><div class="more-body"></div>`, S.querySelector("summary span:last-child").textContent = w.more, b(S.querySelector(".more-body"), w.parts), d.append(S);
2588
+ S.className = "more", S.innerHTML = `<summary><span class="chev" aria-hidden="true">${A.chevron}</span><span></span></summary><div class="more-body"></div>`, S.querySelector("summary span:last-child").textContent = w.more, b(S.querySelector(".more-body"), w.parts), c.append(S);
2488
2589
  return;
2489
2590
  }
2490
2591
  if (w.tabs) {
2491
2592
  const S = w.tabs.map((j) => {
2492
2593
  const z = document.createElement("div");
2493
2594
  return z.className = "tab-pane", b(z, j.parts), z;
2494
- }), he = re({
2595
+ }), be = ce({
2495
2596
  label: "View",
2496
2597
  options: w.tabs.map((j, z) => ({ value: String(z), label: j.label })),
2497
2598
  onSelect: (j) => {
2498
- he.setValue(j), S.forEach((z, Zn) => {
2499
- z.hidden = String(Zn) !== j;
2599
+ be.setValue(j), S.forEach((z, ra) => {
2600
+ z.hidden = String(ra) !== j;
2500
2601
  });
2501
2602
  }
2502
2603
  });
2503
- he.setValue("0"), S.forEach((j, z) => {
2604
+ be.setValue("0"), S.forEach((j, z) => {
2504
2605
  j.hidden = z !== 0;
2505
- }), d.append(he.el, ...S);
2606
+ }), c.append(be.el, ...S);
2506
2607
  return;
2507
2608
  }
2508
- const [$, k, C = {}] = w, q = $ === "simple" ? po[k] : $ === "roles" ? go[k] : x[$]?.parts?.[k];
2509
- q && q(d, f, { ...C, buttons: x.buttons, cards: x.cards, goTab: (S) => {
2510
- G("global"), E(S);
2609
+ const [$, k, C = {}] = w, q = $ === "simple" ? So[k] : $ === "roles" ? Mo[k] : x[$]?.parts?.[k];
2610
+ q && q(c, f, { ...C, buttons: x.buttons, cards: x.cards, goTab: (S) => {
2611
+ W("global"), E(S);
2511
2612
  } });
2512
2613
  });
2513
2614
  }
2514
- xe.forEach((d) => b(c.global.querySelector(`[data-page="${d.id}"]`), d.parts));
2515
- const u = [...o.querySelectorAll(".tab")];
2516
- let v = xe.some((d) => d.id === fe("tab")) ? fe("tab") : xe[0].id;
2615
+ ye.forEach((c) => b(p.global.querySelector(`[data-page="${c.id}"]`), c.parts));
2616
+ const h = [...o.querySelectorAll(".tab")];
2617
+ let y = ye.some((c) => c.id === Z("tab")) ? Z("tab") : ye[0].id;
2517
2618
  function L() {
2518
- u.forEach((d) => d.setAttribute("aria-pressed", String(K === "global" && d.dataset.tab === v)));
2619
+ h.forEach((c) => c.setAttribute("aria-pressed", String(K === "global" && c.dataset.tab === y)));
2519
2620
  }
2520
- function E(d) {
2521
- v = d, ge("tab", d), c.global.querySelectorAll(".page").forEach((g) => {
2522
- g.hidden = g.dataset.page !== d;
2523
- }), c.global.scrollTop = 0, L();
2621
+ function E(c) {
2622
+ y = c, oe("tab", c), p.global.querySelectorAll(".page").forEach((g) => {
2623
+ g.hidden = g.dataset.page !== c;
2624
+ }), p.global.scrollTop = 0, L();
2524
2625
  }
2525
- const J = (d) => d.map((g) => `<div class="warning">${A.warn}<span>${O(g.text)}</span></div>`).join("");
2626
+ const ee = (c) => c.map((g) => `<div class="warning">${A.warn}<span>${O(g.text)}</span></div>`).join("");
2526
2627
  let K = "global";
2527
- function G(d) {
2528
- K = d, Object.entries(c).forEach(([g, w]) => {
2529
- w.hidden = g !== d;
2530
- }), s('[data-action="changes"]').setAttribute("aria-pressed", String(d === "changes")), d !== "local" && Hn(), d === "changes" && Ut(), L();
2531
- }
2532
- E(v);
2533
- const qe = "cubic-bezier(0.3, 0.7, 0.2, 1)", Ve = "cubic-bezier(0.2, 0.8, 0.2, 1)", Se = 640, ie = 100, He = 50, Un = () => l.querySelector(".mark-knob"), qn = () => [l.querySelector(".mark-t1"), l.querySelector(".mark-t2")];
2534
- function Et(d) {
2628
+ function W(c) {
2629
+ K = c, Object.entries(p).forEach(([g, w]) => {
2630
+ w.hidden = g !== c;
2631
+ }), r('[data-action="changes"]').setAttribute("aria-pressed", String(c === "changes")), c !== "local" && Qn(), c === "changes" && Wt(), L();
2632
+ }
2633
+ E(y);
2634
+ const Ke = "cubic-bezier(0.3, 0.7, 0.2, 1)", We = "cubic-bezier(0.2, 0.8, 0.2, 1)", Ee = 640, ue = 100, Ge = 50, Yn = () => l.querySelector(".mark-knob"), Xn = () => [l.querySelector(".mark-t1"), l.querySelector(".mark-t2")];
2635
+ function At(c) {
2535
2636
  if (matchMedia("(prefers-reduced-motion: reduce)").matches || !l.animate) return;
2536
- const g = Un(), [w, $] = qn(), k = (C) => `translateY(${C}px)`;
2537
- if (d) {
2538
- g.animate([{ transform: k(32) }, { transform: k(-6), offset: 0.78 }, { transform: k(0) }], { duration: Se, easing: qe });
2637
+ const g = Yn(), [w, $] = Xn(), k = (C) => `translateY(${C}px)`;
2638
+ if (c) {
2639
+ g.animate([{ transform: k(32) }, { transform: k(-6), offset: 0.78 }, { transform: k(0) }], { duration: Ee, easing: Ke });
2539
2640
  const C = [{ transform: "scale(0.4)", opacity: 0 }, { transform: "scale(1)", opacity: 1 }];
2540
- $.animate(C, { duration: ie * 2, delay: Se * 0.12, easing: qe, fill: "backwards" }), w.animate(C, { duration: ie * 2, delay: Se * 0.32 + He, easing: qe, fill: "backwards" });
2641
+ $.animate(C, { duration: ue * 2, delay: Ee * 0.12, easing: Ke, fill: "backwards" }), w.animate(C, { duration: ue * 2, delay: Ee * 0.32 + Ge, easing: Ke, fill: "backwards" });
2541
2642
  } else {
2542
2643
  const C = [{ opacity: 1 }, { opacity: 0 }];
2543
- w.animate(C, { duration: ie, easing: Ve }), $.animate(C, { duration: ie, delay: He / 2, easing: Ve, fill: "backwards" }), g.animate([{ transform: k(0) }, { transform: k(34), offset: 0.8 }, { transform: k(32) }], { duration: Se * 0.7, delay: ie + He / 2, easing: Ve, fill: "backwards" });
2644
+ w.animate(C, { duration: ue, easing: We }), $.animate(C, { duration: ue, delay: Ge / 2, easing: We, fill: "backwards" }), g.animate([{ transform: k(0) }, { transform: k(34), offset: 0.8 }, { transform: k(32) }], { duration: Ee * 0.7, delay: ue + Ge / 2, easing: We, fill: "backwards" });
2544
2645
  }
2545
2646
  }
2546
- function le({ animate: d = !0 } = {}) {
2547
- i.hidden && d && Et(!0), i.hidden = !1, l.setAttribute("aria-expanded", "true"), l.setAttribute("aria-label", "Close Panel"), ge("panel", "open");
2647
+ const Ye = r(".wrap"), Lt = $a({ wrapEl: Ye, launcherEl: l });
2648
+ let Pt = !1;
2649
+ function Ce(c) {
2650
+ Pt = c, i.classList.toggle("minimised", c), i.hidden || oe("panel", c ? "tabs" : "open");
2651
+ }
2652
+ function G({ animate: c = !0 } = {}) {
2653
+ i.hidden && c && At(!0), i.hidden = !1, Ye.classList.add("is-open"), l.setAttribute("aria-expanded", "true"), l.setAttribute("aria-label", "Close Panel"), Ce(!1);
2548
2654
  }
2549
- function _e() {
2550
- i.hidden || Et(!1), i.hidden = !0, l.setAttribute("aria-expanded", "false"), l.setAttribute("aria-label", "Open Panel"), ge("panel", "closed"), $e();
2655
+ function Zn() {
2656
+ i.hidden && G(), Ce(!0);
2551
2657
  }
2552
- function Ke() {
2553
- i.hidden ? le() : _e();
2658
+ function Xe() {
2659
+ i.hidden || At(!1), i.hidden = !0, Ye.classList.remove("is-open"), l.setAttribute("aria-expanded", "false"), l.setAttribute("aria-label", "Open Panel"), oe("panel", "closed"), Te();
2554
2660
  }
2555
- let Ct;
2556
- function H(d) {
2557
- p.textContent = d, p.classList.add("show"), clearTimeout(Ct), Ct = setTimeout(() => p.classList.remove("show"), 1600);
2661
+ function Me() {
2662
+ i.hidden ? G() : Xe();
2558
2663
  }
2559
- function de(d, g) {
2560
- navigator.clipboard.writeText(d).then(() => H(g)).catch(() => H("Couldn’t copy — select the text and copy it instead"));
2664
+ let Ot;
2665
+ function H(c) {
2666
+ d.textContent = c, d.classList.add("show"), clearTimeout(Ot), Ot = setTimeout(() => d.classList.remove("show"), 1600);
2561
2667
  }
2562
- let Ge = null;
2563
- function Q(d, g) {
2564
- if (Ge = d, !d) {
2565
- h.hidden = !0;
2668
+ function he(c, g) {
2669
+ navigator.clipboard.writeText(c).then(() => H(g)).catch(() => H("Couldn’t copy — select the text and copy it instead"));
2670
+ }
2671
+ let Ze = null;
2672
+ function te(c, g) {
2673
+ if (Ze = c, !c) {
2674
+ u.hidden = !0;
2566
2675
  return;
2567
2676
  }
2568
- const w = d.getBoundingClientRect();
2569
- Object.assign(h.style, { top: `${w.top}px`, left: `${w.left}px`, width: `${w.width}px`, height: `${w.height}px` }), y.textContent = g || bt(d), h.hidden = !1;
2677
+ const w = c.getBoundingClientRect();
2678
+ Object.assign(u.style, { top: `${w.top}px`, left: `${w.left}px`, width: `${w.width}px`, height: `${w.height}px` }), v.textContent = g || vt(c), u.hidden = !1;
2570
2679
  }
2571
- const Tt = () => {
2572
- Ge && !h.hidden && Q(Ge, y.textContent);
2680
+ const Ft = () => {
2681
+ Ze && !u.hidden && te(Ze, v.textContent);
2573
2682
  };
2574
- window.addEventListener("scroll", Tt, !0), window.addEventListener("resize", Tt);
2575
- let ce = !1;
2576
- const Mt = s('[data-action="pick"]'), At = (d) => d.composedPath().includes(a);
2577
- function Lt(d) {
2578
- if (At(d)) return;
2579
- const g = d.target;
2580
- g && g.nodeType === 1 && g !== document.documentElement && g !== document.body && Q(g);
2581
- }
2582
- function Pt(d) {
2583
- if (At(d) || (d.preventDefault(), d.stopPropagation(), d.type !== "click")) return;
2584
- const g = d.target;
2585
- !g || g === document.documentElement || g === document.body || ($e(), jt(g));
2586
- }
2587
- function Ot(d) {
2588
- d.key === "Escape" && (d.preventDefault(), $e(), M && Q(M.el));
2589
- }
2590
- const Ft = ["pointerdown", "mousedown", "mouseup", "click"];
2591
- function Nt() {
2592
- if (!ce) {
2593
- if (ce = !0, !document.getElementById("twiddle-picking")) {
2594
- const d = document.createElement("style");
2595
- d.id = "twiddle-picking", d.textContent = "html.twd-picking, html.twd-picking * { cursor: crosshair !important; }", document.head.appendChild(d);
2683
+ window.addEventListener("scroll", Ft, !0), window.addEventListener("resize", Ft);
2684
+ let fe = !1;
2685
+ const Nt = r('[data-action="pick"]'), jt = (c) => c.composedPath().includes(a);
2686
+ function Rt(c) {
2687
+ if (jt(c)) return;
2688
+ const g = c.target;
2689
+ g && g.nodeType === 1 && g !== document.documentElement && g !== document.body && te(g);
2690
+ }
2691
+ function zt(c) {
2692
+ if (jt(c) || (c.preventDefault(), c.stopPropagation(), c.type !== "click")) return;
2693
+ const g = c.target;
2694
+ !g || g === document.documentElement || g === document.body || (Te(), Bt(g));
2695
+ }
2696
+ function It(c) {
2697
+ c.key === "Escape" && (c.preventDefault(), Te(), T && te(T.el));
2698
+ }
2699
+ const Dt = ["pointerdown", "mousedown", "mouseup", "click"];
2700
+ function Ut() {
2701
+ if (!fe) {
2702
+ if (fe = !0, !document.getElementById("twiddle-picking")) {
2703
+ const c = document.createElement("style");
2704
+ c.id = "twiddle-picking", c.textContent = "html.twd-picking, html.twd-picking * { cursor: crosshair !important; }", document.head.appendChild(c);
2596
2705
  }
2597
- le(), Mt.setAttribute("aria-pressed", "true"), document.documentElement.classList.add("twd-picking"), document.addEventListener("pointermove", Lt, !0), Ft.forEach((d) => document.addEventListener(d, Pt, !0)), document.addEventListener("keydown", Ot, !0), H("Click an element · Esc to cancel");
2706
+ G(), Nt.setAttribute("aria-pressed", "true"), document.documentElement.classList.add("twd-picking"), document.addEventListener("pointermove", Rt, !0), Dt.forEach((c) => document.addEventListener(c, zt, !0)), document.addEventListener("keydown", It, !0), H("Click an element · Esc to cancel");
2598
2707
  }
2599
2708
  }
2600
- function $e() {
2601
- ce && (ce = !1, Mt.setAttribute("aria-pressed", "false"), document.documentElement.classList.remove("twd-picking"), document.removeEventListener("pointermove", Lt, !0), Ft.forEach((d) => document.removeEventListener(d, Pt, !0)), document.removeEventListener("keydown", Ot, !0), Q(M ? M.el : null));
2709
+ function Te() {
2710
+ fe && (fe = !1, Nt.setAttribute("aria-pressed", "false"), document.documentElement.classList.remove("twd-picking"), document.removeEventListener("pointermove", Rt, !0), Dt.forEach((c) => document.removeEventListener(c, zt, !0)), document.removeEventListener("keydown", It, !0), te(T ? T.el : null));
2602
2711
  }
2603
- let M = null;
2604
- function Vn(d, g) {
2712
+ let T = null;
2713
+ function Jn(c, g) {
2605
2714
  const w = [];
2606
2715
  return {
2607
2716
  ctx: {
2608
- getState: () => (e.getState().scopes || {})[d] || {},
2609
- setSection: ($, k) => e.setScope(d, $, k),
2717
+ getState: () => (t.getState().scopes || {})[c] || {},
2718
+ setSection: ($, k) => t.setScope(c, $, k),
2610
2719
  onChange: ($) => {
2611
- const k = e.onChange($);
2720
+ const k = t.onChange($);
2612
2721
  return w.push(k), k;
2613
2722
  },
2614
2723
  readToken: ($) => getComputedStyle(g).getPropertyValue($).trim()
@@ -2616,81 +2725,81 @@ function mo({ sections: t, store: e, view: n }) {
2616
2725
  dispose: () => w.forEach(($) => $())
2617
2726
  };
2618
2727
  }
2619
- function jt(d, g) {
2620
- const w = xa(d), $ = e.getState().scopes || {};
2621
- g || (g = !$[w.elementSelector] && w.classSelector && $[w.classSelector] ? "class" : "element"), M && M.dispose(), M = { el: d, info: w, target: g, dispose: () => {
2622
- } }, We(), G("local"), le(), Q(d);
2728
+ function Bt(c, g) {
2729
+ const w = La(c), $ = t.getState().scopes || {};
2730
+ g || (g = !$[w.elementSelector] && w.classSelector && $[w.classSelector] ? "class" : "element"), T && T.dispose(), T = { el: c, info: w, target: g, dispose: () => {
2731
+ } }, Je(), W("local"), G(), te(c);
2623
2732
  }
2624
- const Rt = () => M.target === "class" ? M.info.classSelector : M.info.elementSelector;
2625
- function We() {
2626
- M.dispose();
2627
- const { info: d } = M, g = t.filter((j) => (j.uses || []).some((z) => d.tokens.has(z))), w = c.local;
2733
+ const qt = () => T.target === "class" ? T.info.classSelector : T.info.elementSelector;
2734
+ function Je() {
2735
+ T.dispose();
2736
+ const { info: c } = T, g = e.filter((j) => (j.uses || []).some((z) => c.tokens.has(z))), w = p.local;
2628
2737
  if (w.innerHTML = `
2629
2738
  <div class="scope-bar">
2630
2739
  <div class="scope-head">
2631
2740
  <button class="icon-btn" data-action="back" title="Back to the whole page" aria-label="Back to the whole page">${A.back}</button>
2632
2741
  <div class="scope-name">
2633
- <code>${O(bt(d.el))}</code>
2634
- ${d.text ? `<span class="muted">“${O(d.text)}”</span>` : ""}
2742
+ <code>${O(vt(c.el))}</code>
2743
+ ${c.text ? `<span class="muted">“${O(c.text)}”</span>` : ""}
2635
2744
  </div>
2636
2745
  </div>
2637
2746
  <div class="scope-target"></div>
2638
2747
  <div class="warnings"></div>
2639
2748
  </div>
2640
- ${g.length ? r(g) : '<div class="empty">This element doesn’t use Panel tokens yet, so there’s nothing to tweak here. Style it with <code>var(--twd-…)</code> to make it tweakable.</div>'}
2641
- `, d.classSelector) {
2642
- const j = re({
2749
+ ${g.length ? s(g) : '<div class="empty">This element doesn’t use Panel tokens yet, so there’s nothing to tweak here. Style it with <code>var(--twd-…)</code> to make it tweakable.</div>'}
2750
+ `, c.classSelector) {
2751
+ const j = ce({
2643
2752
  label: "Apply to",
2644
2753
  options: [
2645
2754
  { value: "element", label: "This element" },
2646
- { value: "class", label: `All ${d.classSelector} (${d.classCount})`, title: `Every element matching ${d.classSelector}` }
2755
+ { value: "class", label: `All ${c.classSelector} (${c.classCount})`, title: `Every element matching ${c.classSelector}` }
2647
2756
  ],
2648
2757
  onSelect: (z) => {
2649
- z === "class" && e.moveScope(d.elementSelector, d.classSelector), M.target = z, We();
2758
+ z === "class" && t.moveScope(c.elementSelector, c.classSelector), T.target = z, Je();
2650
2759
  }
2651
2760
  });
2652
- j.setValue(M.target), w.querySelector(".scope-target").append(j.el);
2761
+ j.setValue(T.target), w.querySelector(".scope-target").append(j.el);
2653
2762
  }
2654
- const { ctx: $, dispose: k } = Vn(Rt(), d.el), C = m(w, g, $);
2763
+ const { ctx: $, dispose: k } = Jn(qt(), c.el), C = m(w, g, $);
2655
2764
  C[0] && (C[0].open = !0);
2656
2765
  const q = w.querySelector(".warnings"), S = () => {
2657
- const j = d.tokens.size ? Ta(d.el) : null;
2658
- q.innerHTML = j ? J([j]) : "";
2766
+ const j = c.tokens.size ? Da(c.el) : null;
2767
+ q.innerHTML = j ? ee([j]) : "";
2659
2768
  };
2660
2769
  S();
2661
- const he = e.onChange(S);
2662
- M.dispose = () => {
2663
- k(), he();
2770
+ const be = t.onChange(S);
2771
+ T.dispose = () => {
2772
+ k(), be();
2664
2773
  };
2665
2774
  }
2666
- function zt() {
2667
- if (!M || !M.info.classSelector) return;
2668
- const d = e.getState().scopes || {}, g = M.target === "class" ? "element" : "class", w = g === "class" ? M.info.classSelector : M.info.elementSelector;
2669
- !d[Rt()] && d[w] && (M.target = g, We());
2670
- }
2671
- const It = () => {
2672
- e.undo(), zt();
2673
- }, Dt = () => {
2674
- e.redo(), zt();
2775
+ function Ht() {
2776
+ if (!T || !T.info.classSelector) return;
2777
+ const c = t.getState().scopes || {}, g = T.target === "class" ? "element" : "class", w = g === "class" ? T.info.classSelector : T.info.elementSelector;
2778
+ !c[qt()] && c[w] && (T.target = g, Je());
2779
+ }
2780
+ const Vt = () => {
2781
+ t.undo(), Ht();
2782
+ }, _t = () => {
2783
+ t.redo(), Ht();
2675
2784
  };
2676
- function Hn() {
2677
- M && (M.dispose(), M = null, c.local.innerHTML = "", Q(null));
2678
- }
2679
- const Bt = (d) => d.kind === "global" ? "Whole page" : d.kind === "class" ? `All ${d.selector}` : "One element";
2680
- function Ut() {
2681
- const d = en(e.getState(), t), g = Je(), w = c.changes;
2682
- if (!d.length) {
2683
- w.innerHTML = `<div class="empty">No changes yet. Tweak a section, or pick an element to change it on its own.</div>${J(g)}`;
2785
+ function Qn() {
2786
+ T && (T.dispose(), T = null, p.local.innerHTML = "", te(null));
2787
+ }
2788
+ const Kt = (c) => c.kind === "global" ? "Whole page" : c.kind === "class" ? `All ${c.selector}` : "One element";
2789
+ function Wt() {
2790
+ const c = ln(t.getState(), e), g = nt(), w = p.changes;
2791
+ if (!c.length) {
2792
+ w.innerHTML = `<div class="empty">No changes yet. Tweak a section, or pick an element to change it on its own.</div>${ee(g)}`;
2684
2793
  return;
2685
2794
  }
2686
2795
  const $ = /* @__PURE__ */ new Map();
2687
- d.forEach((k) => {
2796
+ c.forEach((k) => {
2688
2797
  const C = k.selector || "";
2689
2798
  $.has(C) || $.set(C, []), $.get(C).push(k);
2690
- }), w.innerHTML = (g.length ? `<div class="warnings">${J(g)}</div>` : "") + [...$].map(([k, C]) => `
2799
+ }), w.innerHTML = (g.length ? `<div class="warnings">${ee(g)}</div>` : "") + [...$].map(([k, C]) => `
2691
2800
  <div class="change-group">
2692
2801
  <div class="change-scope">
2693
- ${k ? `<button class="link-btn" data-select="${O(k)}" title="Select it on the page">${O(Bt(C[0]))}</button><code>${O(k)}</code>` : `<span>${Bt(C[0])}</span>`}
2802
+ ${k ? `<button class="link-btn" data-select="${O(k)}" title="Select it on the page">${O(Kt(C[0]))}</button><code>${O(k)}</code>` : `<span>${Kt(C[0])}</span>`}
2694
2803
  </div>
2695
2804
  ${C.map((q) => `
2696
2805
  <div class="change-row">
@@ -2702,65 +2811,65 @@ function mo({ sections: t, store: e, view: n }) {
2702
2811
  </div>`).join("")}
2703
2812
  </div>`).join("");
2704
2813
  }
2705
- function _n(d, g) {
2706
- if (g) return e.removeScope(g, d);
2707
- const w = e.getState()[d] || {};
2708
- e.setSection(d, Object.fromEntries(Object.keys(w).map(($) => [$, null])));
2814
+ function ea(c, g) {
2815
+ if (g) return t.removeScope(g, c);
2816
+ const w = t.getState()[c] || {};
2817
+ t.setSection(c, Object.fromEntries(Object.keys(w).map(($) => [$, null])));
2709
2818
  }
2710
- const pe = s(".apply-menu"), qt = s('[data-action="apply-menu"]'), Vt = () => fe("apply-mode") === "copy" ? "copy" : "preview";
2711
- function Kn() {
2712
- pe.querySelectorAll("[data-apply-mode]").forEach((d) => d.setAttribute("aria-checked", String(d.dataset.applyMode === Vt())));
2819
+ const ge = r(".apply-menu"), Gt = r('[data-action="apply-menu"]'), Yt = () => Z("apply-mode") === "copy" ? "copy" : "preview";
2820
+ function ta() {
2821
+ ge.querySelectorAll("[data-apply-mode]").forEach((c) => c.setAttribute("aria-checked", String(c.dataset.applyMode === Yt())));
2713
2822
  }
2714
- function Ee(d) {
2715
- pe.hidden = !d, qt.setAttribute("aria-expanded", String(d)), d && Kn();
2823
+ function Ae(c) {
2824
+ ge.hidden = !c, Gt.setAttribute("aria-expanded", String(c)), c && ta();
2716
2825
  }
2717
- function Ht() {
2718
- return Sa(en(e.getState(), t), { warnings: Je() });
2719
- }
2720
- const Ce = s(".dialog-backdrop"), _t = s(".prompt-text");
2721
- function Gn() {
2722
- const d = Ht();
2723
- if (!d) return H("No changes to copy yet");
2724
- if (Vt() === "copy") return de(d, "Prompt copied · paste it into your agent");
2725
- _t.value = d, Ce.hidden = !1, s('[data-action="dialog-copy"]').focus();
2726
- }
2727
- function Ye() {
2728
- Ce.hidden = !0, s('[data-action="apply"]').focus();
2729
- }
2730
- const ee = s('[data-action="compare"]'), Wn = s(".original-pill");
2731
- function Te(d) {
2732
- d ? n.suspend() : n.resume(), ee.setAttribute("aria-pressed", String(d)), Wn.hidden = !d;
2733
- }
2734
- let Kt = null, ue = !1;
2735
- ee.addEventListener("pointerdown", () => {
2736
- ue = !1, Kt = setTimeout(() => {
2737
- ue = !0, Te(!0);
2826
+ function Xt() {
2827
+ return ja(ln(t.getState(), e), { warnings: nt() });
2828
+ }
2829
+ const Le = r(".dialog-backdrop"), Zt = r(".prompt-text");
2830
+ function na() {
2831
+ const c = Xt();
2832
+ if (!c) return H("No changes to copy yet");
2833
+ if (Yt() === "copy") return he(c, "Prompt copied · paste it into your agent");
2834
+ Zt.value = c, Le.hidden = !1, r('[data-action="dialog-copy"]').focus();
2835
+ }
2836
+ function Qe() {
2837
+ Le.hidden = !0, r('[data-action="apply"]').focus();
2838
+ }
2839
+ const ne = r('[data-action="compare"]'), aa = r(".original-pill");
2840
+ function Pe(c) {
2841
+ c ? n.suspend() : n.resume(), ne.setAttribute("aria-pressed", String(c)), aa.hidden = !c;
2842
+ }
2843
+ let Jt = null, me = !1;
2844
+ ne.addEventListener("pointerdown", () => {
2845
+ me = !1, Jt = setTimeout(() => {
2846
+ me = !0, Pe(!0);
2738
2847
  }, 180);
2739
2848
  });
2740
- const Gt = () => {
2741
- clearTimeout(Kt), ue && (Te(!1), ue = !1);
2849
+ const Qt = () => {
2850
+ clearTimeout(Jt), me && (Pe(!1), me = !1);
2742
2851
  };
2743
- ee.addEventListener("pointerup", Gt), ee.addEventListener("pointerleave", Gt);
2744
- const Me = s(".setup-hint"), Wt = `setup-hint-dismissed:${location.pathname}`, Yn = {
2852
+ ne.addEventListener("pointerup", Qt), ne.addEventListener("pointerleave", Qt);
2853
+ const Oe = r(".setup-hint"), en = `setup-hint-dismissed:${location.pathname}`, oa = {
2745
2854
  "no-tokens": {
2746
2855
  title: "The Panel tokens aren’t loaded",
2747
2856
  text: "The knobs change the --twd-* variables, but this page doesn’t load them. Add the token stylesheet before your own CSS:",
2748
- snippets: sn,
2749
- link: `${rn}/#install`
2857
+ snippets: fn,
2858
+ link: `${hn}/#install`
2750
2859
  },
2751
2860
  unused: {
2752
2861
  title: "Nothing here uses the Panel tokens yet",
2753
2862
  text: "The tokens are loaded, but no style on this page uses var(--twd-…), so the knobs won’t change anything. Ask your coding agent to convert the styles:",
2754
2863
  copy: "Copy prompt for your agent",
2755
2864
  copied: "Prompt copied",
2756
- text2copy: Na,
2757
- link: `${rn}/#tokens`
2865
+ text2copy: Ka,
2866
+ link: `${hn}/#tokens`
2758
2867
  }
2759
2868
  };
2760
2869
  let D = null;
2761
- function Ae() {
2762
- const d = Oa();
2763
- D = fe(Wt) === d ? null : Yn[d] || null, l.toggleAttribute("data-hint", !!D), Me.hidden = !D, D && (Me.innerHTML = `
2870
+ function Fe() {
2871
+ const c = Va();
2872
+ D = Z(en) === c ? null : oa[c] || null, l.toggleAttribute("data-hint", !!D), Oe.hidden = !D, D && (Oe.innerHTML = `
2764
2873
  <div class="setup-head">${A.warn}<b>${O(D.title)}</b>
2765
2874
  <button class="icon-btn" data-action="setup-dismiss" title="Hide this message" aria-label="Hide this message">${A.close}</button>
2766
2875
  </div>
@@ -2773,103 +2882,106 @@ function mo({ sections: t, store: e, view: n }) {
2773
2882
  <div class="setup-actions">
2774
2883
  ${D.copy ? `<button class="apply-btn" data-action="setup-copy">${O(D.copy)}</button>` : ""}
2775
2884
  <a href="${D.link}" target="_blank" rel="noopener">Setup guide</a>
2776
- </div>`, Me.dataset.status = d);
2777
- }
2778
- Ae(), document.readyState !== "complete" && window.addEventListener("load", Ae, { once: !0 }), setTimeout(Ae, 1500);
2779
- const Yt = s(".badge");
2780
- function Zt() {
2781
- s('[data-action="undo"]').disabled = !e.canUndo(), s('[data-action="redo"]').disabled = !e.canRedo();
2782
- const d = ya(e.getState());
2783
- Yt.textContent = d, Yt.hidden = !d;
2784
- const g = J(Je());
2785
- c.global.querySelectorAll(".page-warnings").forEach((w) => {
2885
+ </div>`, Oe.dataset.status = c);
2886
+ }
2887
+ Fe(), document.readyState !== "complete" && window.addEventListener("load", Fe, { once: !0 }), setTimeout(Fe, 1500);
2888
+ const tn = r(".badge");
2889
+ function nn() {
2890
+ r('[data-action="undo"]').disabled = !t.canUndo(), r('[data-action="redo"]').disabled = !t.canRedo();
2891
+ const c = Fa(t.getState());
2892
+ tn.textContent = c, tn.hidden = !c;
2893
+ const g = ee(nt());
2894
+ p.global.querySelectorAll(".page-warnings").forEach((w) => {
2786
2895
  w.innerHTML = g;
2787
- }), K === "changes" && Ut();
2896
+ }), K === "changes" && Wt();
2788
2897
  }
2789
- return e.onChange(Zt), Zt(), o.addEventListener("click", (d) => {
2790
- if (d.target === Ce) return Ye();
2791
- const g = d.target.closest("button");
2898
+ return t.onChange(nn), nn(), o.addEventListener("click", (c) => {
2899
+ if (c.target === Le) return Qe();
2900
+ const g = c.target.closest("button");
2792
2901
  if (g) {
2793
- if (g !== qt && !pe.contains(g) && Ee(!1), g === l) return Ke();
2902
+ if (g !== Gt && !ge.contains(g) && Ae(!1), g === l)
2903
+ return Lt.consumeClick() ? void 0 : c.detail === 2 ? (Me(), Lt.reset(), H("Panel moved back to the corner")) : Me();
2794
2904
  if (g.dataset.select) {
2795
2905
  const w = document.querySelector(g.dataset.select);
2796
- return w ? jt(w) : H("That element isn’t on this page");
2906
+ return w ? Bt(w) : H("That element isn’t on this page");
2797
2907
  }
2798
- if (g.dataset.setupSnippet) return de(sn[g.dataset.setupSnippet].code, "Copied");
2908
+ if (g.dataset.setupSnippet) return he(fn[g.dataset.setupSnippet].code, "Copied");
2799
2909
  if (g.dataset.tab)
2800
- return G("global"), E(g.dataset.tab);
2801
- if (g.dataset.remove) return _n(g.dataset.remove, g.dataset.removeScope);
2910
+ return W("global"), Pt && Ce(!1), E(g.dataset.tab);
2911
+ if (g.dataset.remove) return ea(g.dataset.remove, g.dataset.removeScope);
2802
2912
  if (g.dataset.applyMode)
2803
- return ge("apply-mode", g.dataset.applyMode), Ee(!1), H(g.dataset.applyMode === "copy" ? "Copy prompt copies straight away" : "Copy prompt shows a preview first");
2913
+ return oe("apply-mode", g.dataset.applyMode), Ae(!1), H(g.dataset.applyMode === "copy" ? "Copy prompt copies straight away" : "Copy prompt shows a preview first");
2804
2914
  switch (g.dataset.action) {
2805
2915
  case "close":
2806
- return _e();
2916
+ return Xe();
2917
+ case "minimise":
2918
+ return Zn(), (o.querySelector('.tab[aria-pressed="true"]') || h[0]).focus();
2807
2919
  case "reset":
2808
- return e.resetState(), H("Reset to defaults · ⌘Z to undo");
2920
+ return t.resetState(), H("Reset to defaults · ⌘Z to undo");
2809
2921
  case "copy":
2810
- return de(location.href, "Link copied");
2922
+ return he(location.href, "Link copied");
2811
2923
  case "pick":
2812
- return ce ? $e() : Nt();
2924
+ return fe ? Te() : Ut();
2813
2925
  case "compare":
2814
- ue || Te(ee.getAttribute("aria-pressed") !== "true");
2926
+ me || Pe(ne.getAttribute("aria-pressed") !== "true");
2815
2927
  return;
2816
2928
  case "undo":
2817
- return It();
2929
+ return Vt();
2818
2930
  case "redo":
2819
- return Dt();
2931
+ return _t();
2820
2932
  case "back":
2821
- return G("global");
2933
+ return W("global");
2822
2934
  case "changes":
2823
- return G(K === "changes" ? "global" : "changes");
2935
+ return W(K === "changes" ? "global" : "changes");
2824
2936
  case "apply":
2825
- return Gn();
2937
+ return na();
2826
2938
  case "apply-menu":
2827
- return Ee(pe.hidden);
2939
+ return Ae(ge.hidden);
2828
2940
  case "dialog-close":
2829
- return Ye();
2941
+ return Qe();
2830
2942
  case "dialog-copy":
2831
- return de(_t.value, "Prompt copied · paste it into your agent");
2943
+ return he(Zt.value, "Prompt copied · paste it into your agent");
2832
2944
  case "setup-copy":
2833
- return D && de(D.text2copy, D.copied);
2945
+ return D && he(D.text2copy, D.copied);
2834
2946
  case "setup-dismiss":
2835
- return ge(Wt, Me.dataset.status), Ae();
2947
+ return oe(en, Oe.dataset.status), Fe();
2836
2948
  }
2837
2949
  }
2838
- }), o.addEventListener("keydown", (d) => {
2839
- if (d.key === "Escape") {
2840
- if (!Ce.hidden) return Ye();
2841
- if (!pe.hidden) return Ee(!1);
2950
+ }), o.addEventListener("keydown", (c) => {
2951
+ if (c.key === "Escape") {
2952
+ if (!Le.hidden) return Qe();
2953
+ if (!ge.hidden) return Ae(!1);
2842
2954
  }
2843
- !(d.metaKey || d.ctrlKey) || d.key.toLowerCase() !== "z" || d.target.matches?.('input[type="text"], textarea') || (d.preventDefault(), d.shiftKey ? Dt() : It());
2844
- }), document.addEventListener("keydown", (d) => {
2845
- d.altKey && (d.code === "KeyT" && (d.preventDefault(), Ke()), d.code === "KeyC" && (d.preventDefault(), Te(ee.getAttribute("aria-pressed") !== "true")));
2846
- }), fe("panel") === "open" && le({ animate: !1 }), { open: le, close: _e, toggle: Ke, toast: H, pick: Nt, prompt: Ht };
2955
+ !(c.metaKey || c.ctrlKey) || c.key.toLowerCase() !== "z" || c.target.matches?.('input[type="text"], textarea') || (c.preventDefault(), c.shiftKey ? _t() : Vt());
2956
+ }), document.addEventListener("keydown", (c) => {
2957
+ c.altKey && (c.code === "KeyT" && (c.preventDefault(), Me()), c.code === "KeyC" && (c.preventDefault(), Pe(ne.getAttribute("aria-pressed") !== "true")));
2958
+ }), Z("panel") === "open" ? G({ animate: !1 }) : Z("panel") === "tabs" && (G({ animate: !1 }), Ce(!0)), { open: G, close: Xe, toggle: Me, toast: H, pick: Ut, prompt: Xt };
2847
2959
  }
2848
- const wn = [lo, De, Dn, wt, Va, vt, ao, qa], bo = ':root{--twd-font-heading: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;--twd-font-body: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;--twd-text-xs: .694rem;--twd-text-sm: .833rem;--twd-text-base: 1rem;--twd-text-lg: 1.2rem;--twd-text-xl: 1.44rem;--twd-text-2xl: 1.728rem;--twd-text-3xl: 2.074rem;--twd-leading-tight: 1.2;--twd-leading-normal: 1.5;--twd-weight-heading: 600;--twd-weight-strong: 600;--twd-bg: #fafafa;--twd-surface: #ffffff;--twd-surface-raised: #ffffff;--twd-text: #18181b;--twd-text-muted: #71717a;--twd-border: #e4e4e7;--twd-primary: #4f46e5;--twd-primary-hover: #4338ca;--twd-on-primary: #ffffff;--twd-focus: #6366f1;--twd-success: #16a34a;--twd-warning: #d97706;--twd-danger: #dc2626;--twd-radius-control: 8px;--twd-radius-container: 12px;--twd-space-1: 4px;--twd-space-2: 8px;--twd-space-3: 12px;--twd-space-4: 16px;--twd-space-5: 24px;--twd-space-6: 32px;--twd-space-7: 48px;--twd-space-8: 64px;--twd-control-height: 40px;--twd-control-pad-x: 16px;--twd-page-pad: var(--twd-space-5);--twd-section-gap: var(--twd-space-6);--twd-card-pad: var(--twd-space-5);--twd-card-gap: var(--twd-space-2);--twd-control-gap: var(--twd-space-3);--twd-border-width: 1px;--twd-border-width-container: var(--twd-border-width);--twd-shadow-sm: 0 1px 2px rgb(0 0 0 / .06);--twd-shadow-md: 0 4px 12px rgb(0 0 0 / .08);--twd-shadow-lg: 0 12px 32px rgb(0 0 0 / .12);--twd-button-bg: var(--twd-primary);--twd-button-fg: var(--twd-on-primary);--twd-button-border: transparent;--twd-button-hover: var(--twd-primary-hover);--twd-card-bg: var(--twd-surface);--twd-card-border: var(--twd-border);--twd-card-shadow: var(--twd-shadow-sm)}';
2849
- function xo() {
2960
+ const Cn = [vo, He, Wn, St, to, $t, mo, eo], Ao = ':root{--twd-font-heading: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;--twd-font-body: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;--twd-text-xs: .694rem;--twd-text-sm: .833rem;--twd-text-base: 1rem;--twd-text-lg: 1.2rem;--twd-text-xl: 1.44rem;--twd-text-2xl: 1.728rem;--twd-text-3xl: 2.074rem;--twd-leading-tight: 1.2;--twd-leading-normal: 1.5;--twd-weight-heading: 600;--twd-weight-strong: 600;--twd-bg: #fafafa;--twd-surface: #ffffff;--twd-surface-raised: #ffffff;--twd-text: #18181b;--twd-text-muted: #71717a;--twd-border: #e4e4e7;--twd-primary: #4f46e5;--twd-primary-hover: #4338ca;--twd-on-primary: #ffffff;--twd-focus: #6366f1;--twd-success: #16a34a;--twd-warning: #d97706;--twd-danger: #dc2626;--twd-radius-control: 8px;--twd-radius-container: 12px;--twd-space-1: 4px;--twd-space-2: 8px;--twd-space-3: 12px;--twd-space-4: 16px;--twd-space-5: 24px;--twd-space-6: 32px;--twd-space-7: 48px;--twd-space-8: 64px;--twd-control-height: 40px;--twd-control-pad-x: 16px;--twd-page-pad: var(--twd-space-5);--twd-section-gap: var(--twd-space-6);--twd-card-pad: var(--twd-space-5);--twd-card-gap: var(--twd-space-2);--twd-control-gap: var(--twd-space-3);--twd-border-width: 1px;--twd-border-width-container: var(--twd-border-width);--twd-shadow-sm: 0 1px 2px rgb(0 0 0 / .06);--twd-shadow-md: 0 4px 12px rgb(0 0 0 / .08);--twd-shadow-lg: 0 12px 32px rgb(0 0 0 / .12);--twd-button-bg: var(--twd-primary);--twd-button-fg: var(--twd-on-primary);--twd-button-border: transparent;--twd-button-hover: var(--twd-primary-hover);--twd-card-bg: var(--twd-surface);--twd-card-border: var(--twd-border);--twd-card-shadow: var(--twd-shadow-sm)}';
2961
+ function Lo() {
2850
2962
  if (getComputedStyle(document.documentElement).getPropertyValue("--twd-radius-control").trim() || document.getElementById("twiddle-tokens")) return;
2851
- const e = document.createElement("style");
2852
- e.id = "twiddle-tokens", e.textContent = bo, document.head.prepend(e);
2963
+ const t = document.createElement("style");
2964
+ t.id = "twiddle-tokens", t.textContent = Ao, document.head.prepend(t);
2853
2965
  }
2854
2966
  if (!window.Twiddle) {
2855
- xo(), wn.forEach((n) => sa(n.tokens)), $n(gt);
2856
- let t;
2857
- const e = () => {
2858
- gt(ut()), t = mo({ sections: wn, store: ra, view: { suspend: ca, resume: pa } });
2967
+ Lo(), Cn.forEach((n) => ga(n.tokens)), Pn(wt);
2968
+ let e;
2969
+ const t = () => {
2970
+ wt(mt()), e = To({ sections: Cn, store: fa, view: { suspend: wa, resume: ya } });
2859
2971
  };
2860
- document.readyState === "loading" ? document.addEventListener("DOMContentLoaded", e) : e(), window.Twiddle = {
2861
- open: () => t && t.open(),
2862
- close: () => t && t.close(),
2863
- toggle: () => t && t.toggle(),
2864
- reset: () => yn(),
2865
- undo: () => kn(),
2866
- redo: () => Sn(),
2867
- pick: () => t && t.pick(),
2868
- prompt: () => t ? t.prompt() : "",
2869
- getState: ut
2972
+ document.readyState === "loading" ? document.addEventListener("DOMContentLoaded", t) : t(), window.Twiddle = {
2973
+ open: () => e && e.open(),
2974
+ close: () => e && e.close(),
2975
+ toggle: () => e && e.toggle(),
2976
+ reset: () => Tn(),
2977
+ undo: () => An(),
2978
+ redo: () => Ln(),
2979
+ pick: () => e && e.pick(),
2980
+ prompt: () => e ? e.prompt() : "",
2981
+ getState: mt
2870
2982
  };
2871
2983
  }
2872
- const wo = window.Twiddle;
2984
+ const Po = window.Twiddle;
2873
2985
  export {
2874
- wo as Twiddle
2986
+ Po as Twiddle
2875
2987
  };