made-bridge 0.0.0-stage → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +45 -2
  3. package/builtin/plugins/README.md +22 -0
  4. package/builtin/themes/README.md +64 -0
  5. package/builtin/themes/sunset/theme.json +19 -0
  6. package/config.example.json +20 -0
  7. package/demo-project/README.md +5 -0
  8. package/demo-project/index.mjs +14 -0
  9. package/package.json +48 -4
  10. package/public/app.js +1028 -0
  11. package/public/assistants.html +67 -0
  12. package/public/bean-icon.png +0 -0
  13. package/public/i18n-messages.js +967 -0
  14. package/public/i18n-static.js +615 -0
  15. package/public/i18n.js +50 -0
  16. package/public/index.html +149 -0
  17. package/public/made-icon.png +0 -0
  18. package/public/plugins.html +64 -0
  19. package/public/plugins.js +335 -0
  20. package/public/provider-icon.js +77 -0
  21. package/public/styles.css +445 -0
  22. package/public/tasks.html +85 -0
  23. package/src/agent-icons.mjs +138 -0
  24. package/src/agents/codex.mjs +94 -0
  25. package/src/agents/cursor.mjs +31 -0
  26. package/src/agents/events.mjs +92 -0
  27. package/src/agents/index.mjs +7 -0
  28. package/src/agents/qoder.mjs +34 -0
  29. package/src/agents/shared.mjs +191 -0
  30. package/src/agents/workbuddy.mjs +121 -0
  31. package/src/board-link.mjs +266 -0
  32. package/src/config.mjs +196 -0
  33. package/src/doctor.mjs +146 -0
  34. package/src/i18n.mjs +257 -0
  35. package/src/installed.mjs +291 -0
  36. package/src/jobs.mjs +369 -0
  37. package/src/language-settings.mjs +38 -0
  38. package/src/model-settings.mjs +63 -0
  39. package/src/openai-key.mjs +47 -0
  40. package/src/oray.mjs +243 -0
  41. package/src/pairing.mjs +203 -0
  42. package/src/plugin-package.mjs +132 -0
  43. package/src/plugin-runtime.mjs +169 -0
  44. package/src/plugins/contracts.d.ts +184 -0
  45. package/src/plugins/enhance.mjs +156 -0
  46. package/src/plugins/registry.mjs +146 -0
  47. package/src/providers.mjs +75 -0
  48. package/src/server.mjs +950 -0
  49. package/src/sessions.mjs +134 -0
  50. package/src/speech/index.mjs +10 -0
  51. package/src/speech/openai.mjs +48 -0
  52. package/src/speech/whisper.mjs +59 -0
  53. package/src/speech-settings.mjs +209 -0
  54. package/src/transcribe.mjs +85 -0
  55. package/src/tunnel.mjs +531 -0
  56. package/src/usb-receiver.mjs +664 -0
  57. package/src/voice-settings.mjs +69 -0
@@ -0,0 +1,77 @@
1
+ /** Portable bridge icon format: two palette indices per byte, high nibble first. */
2
+ const COLOR = /^#[0-9a-f]{6}$/i;
3
+ const PALETTE_COLOR = /^[0-9a-f]{8}$/i;
4
+ const BASE64 = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/;
5
+
6
+ /** Returns a bounded key without allocating decoded pixels, or null for malformed metadata. */
7
+ export function providerIconKey(icon) {
8
+ if (!icon || typeof icon !== 'object' || Array.isArray(icon) || icon.format !== 'indexed4') return null;
9
+ const { width, height, palette, data, background, accent } = icon;
10
+ if (!Number.isInteger(width) || width < 1 || width > 48 || !Number.isInteger(height) || height < 1 || height > 48) return null;
11
+ if (!Array.isArray(palette) || palette.length < 1 || palette.length > 16 ||
12
+ !palette.every((color) => typeof color === 'string' && PALETTE_COLOR.test(color))) return null;
13
+ const byteCount = Math.ceil(width * height / 2);
14
+ if (typeof data !== 'string' || data.length !== 4 * Math.ceil(byteCount / 3) || !BASE64.test(data) ||
15
+ typeof background !== 'string' || !COLOR.test(background) || typeof accent !== 'string' || !COLOR.test(accent)) return null;
16
+ return JSON.stringify([width, height, palette, data, background, accent]);
17
+ }
18
+
19
+ /** Decode only bridge-supplied pixels; invalid icons never fall back to a built-in animal. */
20
+ export function decodeProviderIcon(icon) {
21
+ const key = providerIconKey(icon);
22
+ if (key === null) return null;
23
+ let packed;
24
+ try {
25
+ packed = atob(icon.data);
26
+ if (btoa(packed) !== icon.data) return null;
27
+ } catch { return null; }
28
+ const pixelCount = icon.width * icon.height;
29
+ if (packed.length !== Math.ceil(pixelCount / 2) || (pixelCount % 2 && (packed.charCodeAt(packed.length - 1) & 15) !== 0)) return null;
30
+ const palette = icon.palette.map((color) => [0, 2, 4, 6].map((offset) => parseInt(color.slice(offset, offset + 2), 16)));
31
+ const rgba = new Uint8ClampedArray(pixelCount * 4);
32
+ for (let pixel = 0; pixel < pixelCount; pixel++) {
33
+ const byte = packed.charCodeAt(pixel >> 1);
34
+ const index = pixel % 2 ? byte & 15 : byte >> 4;
35
+ if (!palette[index]) return null;
36
+ rgba.set(palette[index], pixel * 4);
37
+ }
38
+ return { width: icon.width, height: icon.height, rgba, background: icon.background, accent: icon.accent, key };
39
+ }
40
+
41
+ const renderedIcons = new WeakMap();
42
+ export function clearProviderIcon(node) {
43
+ renderedIcons.delete(node);
44
+ node.replaceChildren();
45
+ node.style.removeProperty('background');
46
+ node.style.removeProperty('border-color');
47
+ }
48
+
49
+ /** Reuses existing pixels until the complete icon payload changes, including for the same agent ID. */
50
+ export function renderProviderIcon(node, icon) {
51
+ const key = providerIconKey(icon);
52
+ if (renderedIcons.has(node) && renderedIcons.get(node) === key) return;
53
+ renderedIcons.set(node, key);
54
+ const decoded = decodeProviderIcon(icon);
55
+ node.replaceChildren();
56
+ node.style.background = decoded?.background || '#26383d';
57
+ node.style.borderColor = decoded?.accent || '#657a81';
58
+ if (!decoded) {
59
+ const placeholder = document.createElement('span');
60
+ placeholder.className = 'provider-icon-placeholder';
61
+ placeholder.textContent = '·';
62
+ placeholder.setAttribute('aria-hidden', 'true');
63
+ node.append(placeholder);
64
+ return;
65
+ }
66
+ const canvas = document.createElement('canvas');
67
+ canvas.width = decoded.width;
68
+ canvas.height = decoded.height;
69
+ canvas.className = 'provider-icon-image';
70
+ canvas.setAttribute('aria-hidden', 'true');
71
+ const context = canvas.getContext('2d');
72
+ if (!context) return;
73
+ const pixels = context.createImageData(decoded.width, decoded.height);
74
+ pixels.data.set(decoded.rgba);
75
+ context.putImageData(pixels, 0, 0);
76
+ node.append(canvas);
77
+ }
@@ -0,0 +1,445 @@
1
+ /* 码得桥接器 · 柔和工作室主题
2
+ 方向:奶油底 + 墨绿主色 + 珊瑚橘强调,大圆角、药丸标签、亲和产品感。
3
+ 浅色主题;面板是暖白圆角卡片,重点动作只有一个珊瑚橘。 */
4
+ :root {
5
+ color-scheme: light;
6
+ --bg: #faf3e7;
7
+ --panel: #ffffff;
8
+ --panel-2: #fdf8ee;
9
+ --panel-3: #f4ecdb;
10
+ --line: rgba(88, 74, 58, .14);
11
+ --line-strong: rgba(47, 107, 79, .45);
12
+ --text: #2d2a26;
13
+ --muted: #5f5a50;
14
+ --dim: #857d6e;
15
+ --green: #2f6b4f;
16
+ --green-deep: #235540;
17
+ --green-soft: #e7efe4;
18
+ --coral: #e0663d;
19
+ --coral-soft: #fbe9e0;
20
+ --orange: #a86a1a;
21
+ --red: #c0432f;
22
+ --blue: #2f6da8;
23
+ --purple: #8a5db8;
24
+ --radius: 12px;
25
+ --radius-lg: 20px;
26
+ --font-body: 17px;
27
+ --font-small: 15px;
28
+ --font-heading: 24px;
29
+ font-family: "Avenir Next", "Quicksand", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", ui-sans-serif, sans-serif;
30
+ }
31
+ * { box-sizing: border-box; }
32
+ html { scroll-behavior: smooth; }
33
+ body { margin: 0; min-width: 320px; min-height: 100vh; font-size: var(--font-body); line-height: 1.6; background: var(--bg); color: var(--text); }
34
+ ::selection { background: var(--green); color: #fff; }
35
+ button, input, textarea, select { font: inherit; }
36
+ button { cursor: pointer; }
37
+ button:disabled { cursor: not-allowed; opacity: .45; }
38
+ button:focus-visible, input:focus-visible, textarea:focus-visible, select:focus-visible, a:focus-visible { outline: 2px solid var(--coral); outline-offset: 3px; }
39
+ a { color: inherit; text-decoration: none; }
40
+ ::-webkit-scrollbar { width: 10px; height: 10px; }
41
+ ::-webkit-scrollbar-thumb { background: #ddcfb4; border: 3px solid var(--bg); border-radius: 99px; }
42
+ ::-webkit-scrollbar-thumb:hover { background: #c9b895; }
43
+ ::-webkit-scrollbar-track { background: transparent; }
44
+ .page-glow { position: fixed; inset: 0; pointer-events: none; z-index: -1; background: radial-gradient(900px 620px at 85% -10%, rgba(47, 107, 79, .07), transparent 70%), radial-gradient(700px 560px at -12% 42%, rgba(224, 102, 61, .05), transparent 72%); }
45
+ .app { width: min(2240px, 100%); margin: auto; padding: 24px clamp(24px, 2.2vw, 48px) 48px; }
46
+ .topbar { position: sticky; top: 0; z-index: 8; display: flex; align-items: center; gap: 26px; min-height: 68px; background: rgba(250, 243, 231, .9); backdrop-filter: blur(12px); }
47
+ .brand { display: flex; align-items: center; gap: 12px; min-width: 232px; }
48
+ .brand-mark { width: 46px; height: 46px; flex: 0 0 auto; border-radius: 15px; display: grid; place-items: center; box-shadow: 0 4px 12px rgba(47, 107, 79, .14); }
49
+ .brand-mark img { display: block; width: 100%; height: 100%; object-fit: contain; border-radius: inherit; }
50
+ .brand-name { display: block; font-size: 21px; font-weight: 700; letter-spacing: .06em; line-height: 1; color: var(--green-deep); }
51
+ .brand-sub { display: block; color: var(--dim); font-size: 12px; font-weight: 600; letter-spacing: .14em; margin-top: 6px; }
52
+ .topnav { display: flex; align-items: center; gap: 4px; padding: 4px; border-radius: 99px; background: rgba(255, 255, 255, .7); border: 1px solid var(--line); }
53
+ .topnav a { padding: 10px 19px; color: var(--muted); font-size: var(--font-body); font-weight: 600; border-radius: 99px; transition: .18s; }
54
+ .topnav a { white-space: nowrap; }
55
+ .topnav a:hover { color: var(--text); background: var(--panel-3); }
56
+ .topnav a.active { background: var(--green); color: #fff; box-shadow: 0 3px 10px rgba(47, 107, 79, .25); }
57
+ .top-actions { margin-left: auto; display: flex; align-items: center; gap: 12px; }
58
+ #preview, #pairing, #receiver, #tunnel, #connections, #modelSettings, #voiceSettings { scroll-margin-top: 118px; }
59
+ .connection-pill { display: inline-flex; align-items: center; gap: 8px; padding: 9px 14px; border: 1px solid var(--line); border-radius: 99px; background: rgba(255, 255, 255, .7); font-size: var(--font-small); font-weight: 600; color: var(--muted); white-space: nowrap; }
60
+ .connection-pill .dot, .status-dot { width: 8px; height: 8px; flex: 0 0 auto; border-radius: 50%; background: #c9c0ad; }
61
+ .connection-pill.online .dot, .status-dot.on { background: var(--green); box-shadow: 0 0 0 3px rgba(47, 107, 79, .14); }
62
+ .icon-button { display: grid; place-items: center; width: 44px; height: 44px; border-radius: 13px; border: 1px solid var(--line); background: var(--panel); color: var(--muted); transition: .18s; }
63
+ .icon-button:hover { color: var(--green); border-color: var(--line-strong); background: var(--green-soft); }
64
+ .icon-button svg { width: 20px; height: 20px; }
65
+ .hero { display: grid; grid-template-columns: minmax(0,1fr) auto; align-items: end; gap: 36px; padding: 54px 1px 34px; }
66
+ .eyebrow, .section-kicker { display: inline-flex; align-items: center; gap: 9px; color: var(--coral); font-size: var(--font-small); font-weight: 700; letter-spacing: .1em; }
67
+ .eyebrow::before { content: ""; width: 8px; height: 8px; border-radius: 50%; background: var(--coral); }
68
+ h1, h2, h3, p { margin: 0; }
69
+ h1 { margin-top: 17px; font-size: clamp(32px, 4vw, 56px); font-weight: 800; line-height: 1.18; letter-spacing: .01em; }
70
+ h1 em { color: var(--coral); font-style: normal; }
71
+ .hero-desc { max-width: 78ch; margin-top: 15px; color: var(--muted); font-size: var(--font-body); line-height: 1.75; }
72
+ .hero-stats { display: flex; align-items: stretch; gap: 10px; padding-bottom: 5px; }
73
+ .stat { min-width: 126px; padding: 14px 17px; border: 1px solid var(--line); border-radius: 16px; background: var(--panel); box-shadow: 0 3px 10px rgba(88, 74, 58, .05); }
74
+ .stat-value { display: block; font-size: 27px; font-weight: 800; line-height: 1.15; font-variant-numeric: tabular-nums; color: var(--green-deep); }
75
+ .stat-label { display: block; margin-top: 5px; color: var(--dim); font-size: var(--font-small); font-weight: 600; }
76
+ .workspace { display: grid; grid-template-columns: minmax(0, 1.52fr) minmax(350px, .9fr); align-items: start; gap: 18px; }
77
+ .dashboard-layout { display: grid; grid-template-columns: minmax(420px, 1fr) minmax(0, 1.1fr); align-items: start; gap: 22px; }
78
+ .assistants-layout { grid-template-columns: repeat(2, minmax(0, 1fr)); align-items: stretch; }
79
+ .assistants-layout > .stack { grid-template-rows: 1fr; }
80
+ .assistants-layout > .stack > .panel { height: 100%; }
81
+ .providers-panel { display: flex; flex-direction: column; }
82
+ .providers-panel .provider-grid { flex: 1; grid-template-rows: repeat(2, minmax(0, 1fr)); }
83
+ .providers-panel .provider { display: flex; flex-direction: column; }
84
+ .providers-panel .provider-name { font-size: calc(var(--font-body) + 2px); margin-top: auto; padding-top: 12px; }
85
+ .tasks-workspace { display: grid; grid-template-columns: clamp(340px, 28vw, 460px) minmax(0, 1fr); align-items: start; gap: 22px; }
86
+ .conversation-column { display: grid; gap: 18px; min-width: 0; }
87
+ .sessions-panel { min-width: 0; padding: 24px; }
88
+ .sessions-panel .panel-head > div { min-width: 0; }
89
+ .sessions-panel .small-button { flex: 0 0 auto; }
90
+ .session-create-form { display: grid; gap: 11px; margin-top: 18px; padding: 15px; border: 1px dashed var(--line); border-radius: 14px; background: var(--panel-2); }
91
+ .session-create-form[hidden], .rename-session-form[hidden] { display: none; }
92
+ .session-create-form .compose-field { margin: 0; }
93
+ .session-create-form .panel-intro { margin: 0; }
94
+ .session-list { display: grid; gap: 8px; margin-top: 18px; max-height: min(660px, 72vh); overflow-y: auto; }
95
+ .session-list[hidden], .legacy-sessions[hidden] { display: none; }
96
+ .legacy-sessions { margin-top: 20px; padding-top: 16px; border-top: 1px solid var(--line); }
97
+ .legacy-session-toggle { display: flex; align-items: center; justify-content: space-between; width: 100%; min-height: 42px; padding: 7px 12px; border: 0; border-radius: 10px; color: var(--dim); background: var(--panel-2); font-size: var(--font-small); font-weight: 600; text-align: left; cursor: pointer; }
98
+ .legacy-session-toggle:hover, .legacy-session-toggle:focus-visible { color: var(--text); background: var(--panel-3); }
99
+ .legacy-session-toggle::after { content: '+'; font-size: var(--font-body); }
100
+ .legacy-session-toggle[aria-expanded="true"]::after { content: '–'; }
101
+ .legacy-session-list { margin-top: 8px; max-height: 330px; }
102
+ .session-item { display: grid; gap: 5px; width: 100%; min-width: 0; padding: 13px 14px; border: 1px solid var(--line); border-radius: 13px; color: var(--text); background: var(--panel-2); text-align: left; cursor: pointer; transition: border-color .18s, background .18s, transform .18s; }
103
+ .session-item:hover, .session-item:focus-visible { border-color: var(--line-strong); transform: translateY(-1px); }
104
+ .session-item.active { border-color: var(--green); background: var(--green-soft); }
105
+ .session-item-title { overflow: hidden; font-size: var(--font-body); font-weight: 600; text-overflow: ellipsis; white-space: nowrap; }
106
+ .session-item-meta, .session-item-updated, .session-list-empty { color: var(--dim); font-size: var(--font-small); line-height: 1.5; overflow-wrap: anywhere; }
107
+ .session-list-empty { padding: 13px; }
108
+ .conversation-head { min-width: 0; padding: 22px 24px; }
109
+ .conversation-heading { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; }
110
+ .conversation-heading > div { min-width: 0; }
111
+ .conversation-heading h2 { margin-top: 6px; overflow-wrap: anywhere; }
112
+ .conversation-heading .small-button { flex: 0 0 auto; }
113
+ .session-context-warning { margin-top: 16px; padding: 11px 14px; border: 1px solid rgba(168, 106, 26, .3); border-radius: 12px; color: #8a5a14; background: #fdf3dd; font-size: var(--font-body); line-height: 1.55; }
114
+ .session-context-warning[hidden] { display: none; }
115
+ .rename-session-form { display: grid; gap: 8px; margin-top: 18px; }
116
+ .rename-session-row { display: flex; flex-wrap: wrap; gap: 8px; }
117
+ .rename-session-row .secret-input { min-width: 0; flex: 1; }
118
+ .rename-session-row .primary-button, .rename-session-row .small-button { flex: 0 0 auto; min-height: 52px; }
119
+ .compose-actions { display: flex; justify-content: flex-end; margin-top: 13px; }
120
+ .turn-answer { white-space: pre-wrap; overflow-wrap: anywhere; }
121
+ .turn-number { color: var(--green); }
122
+ .stack { display: grid; align-content: start; gap: 22px; min-width: 0; }
123
+ .panel { border: 1px solid var(--line); border-radius: var(--radius-lg); background: var(--panel); box-shadow: 0 6px 22px rgba(88, 74, 58, .06); }
124
+ .panel-head { display: flex; flex-wrap: wrap; align-items: start; justify-content: space-between; gap: 12px; }
125
+ .panel h2 { font-size: var(--font-heading); letter-spacing: 0; font-weight: 700; }
126
+ .panel h2::before { content: ""; display: inline-block; width: 10px; height: 10px; margin-right: 10px; border-radius: 50%; background: var(--green); vertical-align: 2px; }
127
+ .panel-intro { max-width: 84ch; overflow-wrap: anywhere; margin-top: 8px; color: var(--dim); font-size: var(--font-body); line-height: 1.6; }
128
+ .composer { padding: 24px; }
129
+ .composer-top { display: flex; align-items: center; justify-content: space-between; gap: 14px; }
130
+ .tag { display: inline-flex; align-items: center; gap: 7px; border: 1px solid var(--line); color: var(--muted); border-radius: 99px; padding: 7px 12px; font-size: var(--font-small); font-weight: 600; white-space: nowrap; background: var(--panel-2); }
131
+ .tag svg { width: 12px; height: 12px; }
132
+ .compose-field { display: block; margin-top: 20px; }
133
+ .field-label { display: block; margin-bottom: 9px; color: var(--muted); font-size: var(--font-small); font-weight: 600; }
134
+ textarea, select { width: 100%; color: var(--text); border: 1px solid var(--line); background: var(--panel-2); outline: 0; transition: border-color .18s, background .18s; }
135
+ textarea:hover, select:hover { border-color: var(--line-strong); }
136
+ textarea:focus, select:focus { border-color: var(--green); background: var(--panel); }
137
+ textarea { min-height: 100px; resize: vertical; padding: 15px 17px; border-radius: 14px; line-height: 1.6; font-size: var(--font-body); }
138
+ textarea::placeholder { color: #b3a88f; }
139
+ .compose-row { display: grid; grid-template-columns: 1fr 1fr auto; align-items: end; gap: 10px; margin-top: 12px; }
140
+ .compose-row .compose-field { margin-top: 0; }
141
+ select { min-height: 52px; appearance: none; border-radius: 13px; padding: 0 36px 0 14px; font-size: var(--font-body); font-weight: 600; background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20' fill='none'%3E%3Cpath d='m5 7 5 5 5-5' stroke='%23857d6e' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E"); background-repeat: no-repeat; background-position: right 12px center; background-size: 16px; }
142
+ .primary-button { display: inline-flex; align-items: center; justify-content: center; gap: 8px; min-height: 52px; padding: 10px 20px; border: 1px solid var(--green); border-radius: 14px; background: var(--green); color: #fff; font-size: var(--font-body); font-weight: 700; white-space: nowrap; transition: transform .18s, filter .18s, box-shadow .18s; box-shadow: 0 4px 14px rgba(47, 107, 79, .22); }
143
+ .primary-button:hover:not(:disabled) { transform: translateY(-2px); filter: brightness(1.05); box-shadow: 0 7px 18px rgba(47, 107, 79, .26); }
144
+ .primary-button:active:not(:disabled) { transform: translateY(0); }
145
+ .primary-button svg { width: 15px; height: 15px; }
146
+ .form-message { min-height: 17px; margin-top: 9px; color: var(--red); font-size: var(--font-small); }
147
+ .form-message.success { color: var(--green); }
148
+ .queue { padding: 24px 24px 15px; min-height: 250px; }
149
+ .count-badge { display: inline-flex; align-items: center; justify-content: center; min-width: 30px; min-height: 30px; padding: 0 9px; color: var(--green-deep); background: var(--green-soft); border-radius: 99px; font-size: var(--font-small); font-weight: 700; }
150
+ .filters { display: flex; gap: 4px; padding: 4px; margin-top: 22px; width: fit-content; max-width: 100%; border: 1px solid var(--line); border-radius: 99px; overflow-x: auto; background: var(--panel-2); }
151
+ .filter { flex: 0 0 auto; border: 0; padding: 8px 14px; background: transparent; color: var(--dim); border-radius: 99px; font-size: var(--font-small); font-weight: 600; }
152
+ .filter:hover { color: var(--text); }
153
+ .filter.active { background: var(--green); color: #fff; }
154
+ .job-list { display: grid; gap: 9px; margin-top: 18px; }
155
+ .job-card { border: 1px solid var(--line); border-radius: 15px; background: var(--panel-2); padding: 15px 16px; transition: border-color .18s, background .18s, transform .18s; }
156
+ .job-card:hover, .job-card.selected { border-color: var(--line-strong); background: var(--green-soft); transform: translateY(-1px); }
157
+ .job-top, .job-bottom { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 12px; }
158
+ .job-meta, .job-detail { display: flex; align-items: center; flex-wrap: wrap; gap: 6px; min-width: 0; color: var(--dim); font-size: var(--font-small); font-weight: 600; }
159
+ .job-meta .sep, .job-detail .sep { color: #cdbf9f; }
160
+ .job-status { display: inline-flex; align-items: center; gap: 6px; flex: 0 0 auto; font-size: var(--font-small); font-weight: 700; }
161
+ .job-status::before { content: ""; width: 7px; height: 7px; border-radius: 50%; background: currentColor; }
162
+ .status-waiting { color: var(--orange); }
163
+ .status-active { color: var(--blue); }
164
+ .status-completed { color: var(--green); }
165
+ .status-handed { color: var(--purple); }
166
+ .status-failed { color: var(--red); }
167
+ .status-cancelled { color: var(--dim); }
168
+ .job-instruction { display: block; width: 100%; margin: 11px 0 12px; padding: 0; border: 0; background: none; color: var(--text); text-align: left; font-size: var(--font-body); font-weight: 600; line-height: 1.55; white-space: pre-wrap; overflow-wrap: anywhere; }
169
+ .job-summary { margin: -4px 0 11px; color: var(--muted); font-size: var(--font-body); line-height: 1.6; overflow-wrap: anywhere; }
170
+ .job-summary.error { color: var(--red); }
171
+ .job-retry-notice { margin: 0 0 12px; padding: 11px 14px; border: 1px solid rgba(168, 106, 26, .3); border-radius: 12px; color: #8a5a14; background: #fdf3dd; font-size: var(--font-small); line-height: 1.6; }
172
+ .job-handoff { display: grid; gap: 7px; margin: 0 0 12px; padding: 10px; border: 1px dashed var(--line); border-radius: 12px; background: var(--panel); }
173
+ .handoff-line { display: flex; align-items: center; gap: 7px; min-width: 0; }
174
+ .handoff-line code { display: block; flex: 1; min-width: 0; overflow-x: auto; white-space: nowrap; color: var(--green-deep); font-size: var(--font-small); }
175
+ .handoff-line .small-button { flex: 0 0 auto; }
176
+ .handoff-note { color: var(--dim); font-size: var(--font-small); line-height: 1.55; }
177
+ .handoff-open { display: inline-flex; align-items: center; justify-content: center; width: fit-content; }
178
+ .job-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
179
+ .small-button { min-height: 46px; padding: 8px 14px; border: 1px solid var(--line); border-radius: 12px; color: var(--muted); background: var(--panel-2); font-size: var(--font-small); font-weight: 600; transition: border-color .18s, color .18s, background .18s; }
180
+ .small-button:hover:not(:disabled) { border-color: var(--line-strong); color: var(--green-deep); background: var(--green-soft); }
181
+ .small-button.confirm { background: var(--coral); border-color: var(--coral); color: #fff; box-shadow: 0 4px 12px rgba(224, 102, 61, .24); }
182
+ .small-button.confirm:hover:not(:disabled) { filter: brightness(1.06); }
183
+ .empty { display: grid; place-content: center; text-align: center; min-height: 210px; padding: 20px; color: var(--dim); }
184
+ .empty-icon { display: grid; place-items: center; width: 50px; height: 50px; margin: 0 auto 13px; border-radius: 16px; border: 1px dashed var(--line-strong); color: var(--green); font-size: 25px; background: var(--green-soft); }
185
+ .empty strong { display: block; color: var(--text); font-size: var(--font-small); }
186
+ .empty span { display: block; margin-top: 7px; font-size: var(--font-small); line-height: 1.6; }
187
+ .preview { padding: 23px 20px 21px; overflow: hidden; }
188
+ .preview .panel-head { padding: 0 4px; }
189
+ .preview-label { margin-top: 4px; color: var(--dim); font-size: var(--font-small); font-weight: 600; white-space: nowrap; }
190
+ .board-stage { display: grid; place-items: center; padding: 21px 0 8px; }
191
+ .board-shell { width: min(360px, 100%); aspect-ratio: 1; padding: 9px; border-radius: 50%; background: linear-gradient(135deg, #efe4cb, #d9cba6 45%, #c4b28c 100%); box-shadow: 0 22px 48px rgba(88, 74, 58, .2), inset 0 0 0 1px rgba(255, 255, 255, .55); }
192
+ .board-screen { line-height: normal; position: relative; width: 100%; height: 100%; overflow: hidden; border-radius: 50%; border: 1px solid rgba(47, 107, 79, .25); background: radial-gradient(circle at 50% 32%, rgba(47, 107, 79, .18), transparent 58%), #12261c; box-shadow: inset 0 0 26px rgba(0, 0, 0, .4); touch-action: none; user-select: none; }
193
+ .board-screen::after { position: absolute; inset: 0; content: ""; pointer-events: none; border-radius: 50%; background: repeating-linear-gradient(0deg, rgba(255, 255, 255, .012) 0 3px, transparent 3px 4px); }
194
+ .board-screen > * { position: absolute; z-index: 1; }
195
+ .board-page { top: 30.5%; right: 13%; color: #9fc4a9; font-size: 11px; font-weight: 700; }
196
+ .board-animal { left: 50%; top: 7.8%; display: grid; place-items: center; width: 20.6%; aspect-ratio: 1; border: 2px solid rgba(255, 255, 255, .28); border-radius: 50%; transform: translateX(-50%); }
197
+ .board-new-session { top: 16.4%; left: 75%; display: grid; place-items: center; width: 11.1%; aspect-ratio: 1; padding: 0; border: 1px solid rgba(255, 255, 255, .55); border-radius: 50%; color: #fff; background: rgba(224, 102, 61, .85); font-size: 26px; line-height: 1; cursor: pointer; }
198
+ .board-new-session:hover, .board-new-session:focus-visible { background: var(--coral); outline: 2px solid rgba(255, 255, 255, .6); }
199
+ .board-animal .provider-icon-image { width: 87%; height: 87%; }
200
+ .board-provider-name { left: 22%; right: 22%; top: 30%; text-align: center; color: #f4f9f1; font-size: 21px; font-weight: 700; line-height: 1.2; }
201
+ .board-status { left: 13.9%; width: 72.2%; top: 40.3%; overflow: hidden; color: #a9cdb2; font-size: 11px; font-weight: 600; line-height: 1.3; text-align: center; white-space: nowrap; text-overflow: ellipsis; }
202
+ .board-title { left: 13.9%; width: 72.2%; top: 47%; overflow: hidden; color: #eef6ea; font-size: 12px; font-weight: 700; line-height: 1.3; text-align: center; white-space: nowrap; text-overflow: ellipsis; }
203
+ .board-result { left: 17.8%; width: 64.4%; top: 53.1%; height: 35.3%; padding: 0 5%; overflow-x: hidden; overflow-y: auto; overscroll-behavior: contain; color: #cfe2cc; font-size: 16px; line-height: 1.35; text-align: left; white-space: pre-wrap; overflow-wrap: anywhere; scrollbar-width: thin; scrollbar-color: rgba(255,255,255,.25) transparent; }
204
+ .board-result:focus-visible { outline: 2px solid rgba(255, 255, 255, .6); outline-offset: 2px; }
205
+ .board-footnote { max-width: 68ch; margin: 18px auto 0; color: var(--dim); text-align: center; font-size: var(--font-small); line-height: 1.55; }
206
+ .info-panel { padding: 28px 30px; }
207
+ .info-rows { display: grid; gap: 1px; margin-top: 16px; }
208
+ .plugin-groups { display: grid; gap: 22px; }
209
+ .plugin-groups[hidden], #pluginDetail[hidden] { display: none; }
210
+ .catalog-list { display: grid; gap: 12px; margin-top: 16px; }
211
+ .plugin-tiles { display: grid; grid-template-columns: repeat(auto-fill, minmax(168px, 1fr)); gap: 14px; margin-top: 16px; }
212
+ .plugin-tiles > .catalog-meta { grid-column: 1 / -1; margin: 0; }
213
+ .plugin-tile { position: relative; aspect-ratio: 1; min-width: 0; }
214
+ .plugin-tile-open { position: absolute; inset: 0; display: flex; flex-direction: column; align-items: flex-start; gap: 6px; width: 100%; padding: 16px 16px 72px; border: 1px solid var(--line); border-radius: 16px; background: var(--panel-2); color: inherit; text-align: left; cursor: pointer; transition: border-color .18s, transform .18s, box-shadow .18s; }
215
+ .plugin-tile-open:hover, .plugin-tile-open:focus-visible { border-color: var(--line-strong); transform: translateY(-2px); box-shadow: 0 8px 18px rgba(88, 74, 58, .08); }
216
+ .plugin-tile.is-off .plugin-tile-open { color: var(--dim); background: var(--panel-2); opacity: .75; }
217
+ .plugin-tile-open .catalog-title, .plugin-tile-open .catalog-meta { display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; overflow: hidden; }
218
+ .plugin-tile-toggle { position: absolute; z-index: 1; left: 14px; bottom: 14px; min-height: 40px; }
219
+ .catalog-card { display: grid; gap: 8px; padding: 16px 18px; border: 1px solid var(--line); border-radius: 16px; background: var(--panel-2); }
220
+ .catalog-card h3, .catalog-title { margin: 0; font-size: var(--font-body); font-weight: 700; }
221
+ .plugin-back { display: inline-flex; margin: 0 0 16px; padding: 0; border: 0; background: transparent; color: var(--green); font-size: var(--font-small); font-weight: 700; }
222
+ .plugin-back::before { content: '← '; }
223
+ .plugin-detail-actions { margin-top: 16px; }
224
+ .catalog-meta { color: var(--dim); font-size: var(--font-small); line-height: 1.5; }
225
+ .catalog-actions { display: flex; flex-wrap: wrap; gap: 8px; }
226
+ .catalog-empty { margin-top: 16px; color: var(--dim); font-size: var(--font-small); }
227
+ .info-row { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 15px; min-height: 52px; border-top: 1px solid var(--line); font-size: var(--font-body); }
228
+ .info-row:first-child { border-top: 0; }
229
+ .info-name { display: flex; align-items: center; gap: 10px; color: var(--muted); font-weight: 600; }
230
+ .info-value { max-width: 65%; color: var(--text); font-size: var(--font-small); text-align: right; overflow-wrap: anywhere; }
231
+ .info-value.ok { color: var(--green); }
232
+ .info-value.warn { color: var(--orange); }
233
+ .info-value.off { color: var(--dim); }
234
+ .voice-settings-form { display: grid; gap: 11px; margin-top: 16px; }
235
+ .voice-settings-form .field-label { margin-bottom: 6px; }
236
+ .voice-presets { display: flex; align-items: center; gap: 7px; color: var(--dim); font-size: var(--font-small); }
237
+ .secret-input { display: block; width: 100%; min-height: 52px; padding: 0 15px; border: 1px solid var(--line); border-radius: 13px; outline: none; background: var(--panel-2); color: var(--text); font: inherit; font-size: var(--font-body); transition: border-color .18s, background .18s; }
238
+ .secret-input:focus { border-color: var(--green); background: var(--panel); }
239
+ .secret-input::placeholder { color: #b3a88f; }
240
+ .voice-settings-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 10px; }
241
+ .voice-settings-actions .primary-button { min-height: 52px; }
242
+ .voice-settings-actions .small-button { min-height: 52px; }
243
+ .voice-settings-status { margin-top: 13px; color: var(--muted); font-size: var(--font-small); line-height: 1.55; }
244
+ .voice-settings-status strong { color: var(--text); }
245
+ .voice-settings-message { min-height: 17px; margin-top: 7px; color: var(--red); font-size: var(--font-small); }
246
+ .voice-settings-message.success { color: var(--green); }
247
+ .provider-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; margin-top: 20px; }
248
+ .provider { min-width: 0; min-height: 132px; padding: 18px; border: 1px solid var(--line); border-radius: 16px; background: var(--panel-2); transition: border-color .18s, transform .18s, box-shadow .18s; }
249
+ .provider:hover { border-color: var(--line-strong); transform: translateY(-2px); box-shadow: 0 8px 18px rgba(88, 74, 58, .07); }
250
+ .provider-top { display: flex; align-items: center; justify-content: space-between; gap: 5px; }
251
+ .provider-mark { display: grid; place-items: center; width: 44px; height: 44px; border-radius: 13px; color: #fff; background: var(--green); font-size: var(--font-body); font-weight: 700; box-shadow: 0 3px 10px rgba(47, 107, 79, .2); }
252
+ .provider-mark .provider-icon-image { width: 40px; height: 40px; }
253
+ .provider-icon-image { object-fit: contain; image-rendering: pixelated; }
254
+ .provider-icon-placeholder { color: var(--muted); font-size: 30px; line-height: 1; }
255
+ .board-animal[hidden], .board-new-session[hidden] { display: none; }
256
+ .provider-mark.off { background: var(--panel-3); color: var(--dim); box-shadow: none; }
257
+ .provider-name { display: block; line-height: 1.4; margin-top: 14px; font-size: var(--font-body); font-weight: 700; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
258
+ .provider-desc { display: block; margin-top: 6px; color: var(--dim); font-size: var(--font-small); line-height: 1.5; overflow-wrap: anywhere; }
259
+ .pairing-head { align-items: center; }
260
+ .pairing-actions { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; justify-content: flex-end; }
261
+ .pairing-state { margin-top: 18px; padding: 15px 17px; border: 1px solid var(--line); border-radius: 14px; background: var(--panel-2); color: var(--muted); font-size: var(--font-body); line-height: 1.6; }
262
+ .pairing-state.open { border-color: rgba(224, 102, 61, .4); background: var(--coral-soft); color: #a34a28; }
263
+ .receiver-details { display: grid; gap: 9px; margin-top: 14px; }
264
+ .receiver-details[hidden] { display: none; }
265
+ .receiver-detail { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; min-width: 0; padding: 10px 13px; border: 1px solid var(--line); border-radius: 12px; background: var(--panel-2); }
266
+ .receiver-detail > span { min-width: 4em; flex: 0 0 auto; color: var(--dim); font-size: var(--font-small); font-weight: 600; }
267
+ .receiver-detail code { flex: 1 1 190px; min-width: 0; color: var(--green-deep); font-size: var(--font-body); font-weight: 600; overflow-wrap: anywhere; user-select: all; }
268
+ .receiver-detail .small-button { flex: 0 0 auto; }
269
+ .receiver-help { margin-top: 15px; line-height: 1.65; }
270
+ .tunnel-settings-form { display: grid; gap: 10px; margin-top: 16px; }
271
+ .tunnel-settings-form .field-label { margin-bottom: 6px; }
272
+ .tunnel-settings-form > .small-button { justify-self: start; }
273
+ .tunnel-named-fields { display: grid; gap: 10px; }
274
+ .tunnel-named-fields[hidden] { display: none; }
275
+ .tunnel-named-fields .panel-intro { margin: 0; }
276
+ .tunnel-address-row { display: flex; align-items: center; gap: 8px; min-width: 0; margin-top: 10px; }
277
+ .tunnel-address-row[hidden] { display: none; }
278
+ .tunnel-address-row code { flex: 1; min-width: 0; padding: 10px 13px; border: 1px solid var(--line); border-radius: 12px; color: var(--green-deep); background: var(--green-soft); font-size: var(--font-small); font-weight: 600; overflow-wrap: anywhere; user-select: all; }
279
+ .tunnel-address-row .small-button { flex: 0 0 auto; }
280
+ .pairing-list { display: grid; gap: 8px; margin-top: 10px; }
281
+ .pairing-device { display: flex; align-items: center; gap: 10px; min-width: 0; padding: 10px 13px; border: 1px solid var(--line); border-radius: 13px; background: var(--panel-2); }
282
+ .pairing-device .status-dot.on { background: var(--green); box-shadow: 0 0 0 3px rgba(47, 107, 79, .14); }
283
+ .pairing-device-text { display: grid; gap: 3px; min-width: 0; }
284
+ .pairing-device-name { overflow: hidden; color: var(--text); font-size: var(--font-body); font-weight: 700; text-overflow: ellipsis; white-space: nowrap; }
285
+ .pairing-device-meta { color: var(--dim); font-size: var(--font-small); line-height: 1.35; }
286
+ .pairing-device .small-button { flex: 0 0 auto; margin-left: auto; }
287
+ .pairing-overlay { position: fixed; inset: 0; z-index: 20; display: grid; place-items: center; padding: 20px; background: rgba(74, 62, 44, .45); backdrop-filter: blur(6px); }
288
+ .pairing-overlay[hidden] { display: none; }
289
+ .pairing-dialog { width: min(540px, 100%); max-height: calc(100dvh - 40px); overflow-y: auto; padding: 27px; border: 1px solid var(--line); border-radius: 24px; background: var(--panel); box-shadow: 0 28px 70px rgba(74, 62, 44, .3); }
290
+ .pairing-dialog-top { display: flex; align-items: flex-start; justify-content: space-between; gap: 15px; }
291
+ .pairing-dialog h2 { margin-top: 13px; font-size: var(--font-heading); line-height: 1.25; letter-spacing: 0; }
292
+ .pairing-dialog p { margin-top: 12px; color: var(--muted); font-size: var(--font-body); line-height: 1.7; }
293
+ .pairing-dialog .pairing-endpoint { color: var(--green-deep); font-weight: 600; overflow-wrap: anywhere; }
294
+ .pairing-code { display: block; margin-top: 24px; padding: 18px; border: 2px dashed rgba(224, 102, 61, .5); border-radius: 18px; background: var(--coral-soft); color: var(--coral); font-size: clamp(30px, 9vw, 45px); font-weight: 800; letter-spacing: .16em; line-height: 1.1; text-align: center; font-variant-numeric: tabular-nums; }
295
+ .pairing-remaining { display: block; margin-top: 9px; color: var(--dim); font-size: var(--font-small); text-align: center; }
296
+ .pairing-dialog-actions { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 9px; margin-top: 24px; }
297
+ .pairing-dialog-actions .small-button { min-height: 44px; padding: 0 16px; font-size: var(--font-body); }
298
+ .footer { display: flex; justify-content: space-between; gap: 12px; margin-top: 25px; color: var(--dim); font-size: var(--font-small); }
299
+ .toast { position: fixed; right: 22px; bottom: 20px; z-index: 10; max-width: min(370px, calc(100vw - 40px)); padding: 12px 16px; border: 1px solid rgba(47, 107, 79, .3); border-radius: 14px; color: var(--green-deep); background: var(--green-soft); box-shadow: 0 14px 34px rgba(74, 62, 44, .18); font-size: var(--font-body); line-height: 1.5; opacity: 0; transform: translateY(8px); pointer-events: none; transition: opacity .2s, transform .2s; }
300
+ .toast.visible { opacity: 1; transform: translateY(0); }
301
+ .toast.error { border-color: rgba(192, 67, 47, .35); color: #8f2f1e; background: #fbe7e0; }
302
+ @media (min-width: 1600px) {
303
+ :root { --font-body: 19px; --font-small: 17px; --font-heading: 27px; }
304
+ #voiceSettingsForm { grid-template-columns: minmax(0, 1fr); }
305
+ }
306
+ @media (max-width: 1120px) {
307
+ .hero { display: block; }
308
+ .hero-stats { margin-top: 28px; }
309
+ .workspace { grid-template-columns: minmax(0, 1.35fr) minmax(320px, .9fr); }
310
+ .topbar { flex-wrap: wrap; gap: 8px 18px; padding-bottom: 8px; }
311
+ .topnav { order: 3; width: 100%; overflow-x: auto; }
312
+ .topnav a { flex: 0 0 auto; }
313
+ }
314
+ @media (max-width: 1050px) {
315
+ .app { padding: 18px 20px 38px; }
316
+ .workspace { grid-template-columns: 1fr; }
317
+ .dashboard-layout, .tasks-workspace { grid-template-columns: 1fr; }
318
+ .assistants-layout > .stack > .panel { height: auto; }
319
+ .providers-panel .provider-grid { grid-template-rows: auto; }
320
+ }
321
+ @media (max-width: 650px) {
322
+ :root { --font-heading: 23px; }
323
+ .voice-settings-actions > button { flex: 1 1 auto; }
324
+ .pairing-dialog { padding: 22px 18px; }
325
+ .pairing-dialog-actions > button { width: 100%; }
326
+ .app { padding: 15px 13px 30px; }
327
+ .brand { min-width: auto; }
328
+ .brand-sub { display: none; }
329
+ .brand-mark { width: 40px; height: 40px; border-radius: 13px; }
330
+ .topnav { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); }
331
+ .topnav a { display: grid; place-items: center; min-height: 44px; padding: 9px 4px; font-size: var(--font-body); }
332
+ .connection-pill { max-width: 133px; overflow: hidden; text-overflow: ellipsis; padding: 8px 10px; }
333
+ .top-actions .connection-pill, .top-actions .icon-button { display: none; }
334
+ .connection-pill span:last-child { overflow: hidden; text-overflow: ellipsis; }
335
+ .hero { padding: 41px 3px 25px; }
336
+ h1 { font-size: clamp(30px, 8.6vw, 42px); }
337
+ .hero h1 em { display: block; }
338
+ .hero-desc { font-size: var(--font-body); line-height: 1.65; }
339
+ .hero-stats { gap: 7px; }
340
+ .stat { min-width: 0; flex: 1; padding: 11px; }
341
+ .stat-value { font-size: 22px; }
342
+ .stat-label { font-size: var(--font-small); }
343
+ .compose-row { grid-template-columns: 1fr 1fr; }
344
+ .compose-row .primary-button { grid-column: span 2; width: 100%; }
345
+ .info-panel { padding: 22px 20px; }
346
+ .panel h2 { font-size: var(--font-heading); }
347
+ .preview .panel-head { flex-wrap: wrap; }
348
+ .pairing-head { align-items: flex-start; flex-wrap: wrap; }
349
+ .pairing-actions { width: 100%; justify-content: flex-start; }
350
+ .secret-input { font-size: var(--font-body); }
351
+ .small-button { min-height: 44px; font-size: var(--font-body); }
352
+ .voice-presets { flex-wrap: wrap; }
353
+ .footer { flex-direction: column; }
354
+ body[data-page="tasks"] .hero-desc { font-size: var(--font-body); line-height: 1.65; }
355
+ body[data-page="tasks"] .hero-stats { margin-top: 23px; }
356
+ body[data-page="tasks"] .stat-label { font-size: var(--font-small); }
357
+ body[data-page="tasks"] .composer, body[data-page="tasks"] .queue { padding: 21px 18px; }
358
+ body[data-page="tasks"] .sessions-panel, body[data-page="tasks"] .conversation-head { padding: 21px 18px; }
359
+ body[data-page="tasks"] .session-item-title { font-size: var(--font-body); }
360
+ body[data-page="tasks"] .session-item-meta, body[data-page="tasks"] .session-item-updated { font-size: var(--font-small); }
361
+ body[data-page="tasks"] .session-list { max-height: 270px; }
362
+ body[data-page="tasks"] .session-create-form { padding: 13px; }
363
+ body[data-page="tasks"] .secret-input { font-size: var(--font-body); }
364
+ body[data-page="tasks"] .rename-session-row { flex-wrap: wrap; }
365
+ body[data-page="tasks"] .rename-session-row .secret-input { flex-basis: 100%; }
366
+ body[data-page="tasks"] .panel h2 { font-size: var(--font-heading); }
367
+ body[data-page="tasks"] .panel-intro { font-size: var(--font-small); line-height: 1.6; }
368
+ body[data-page="tasks"] .compose-row { grid-template-columns: minmax(0, 1fr); gap: 13px; }
369
+ body[data-page="tasks"] .compose-row .primary-button { grid-column: auto; }
370
+ body[data-page="tasks"] .field-label { font-size: var(--font-small); }
371
+ body[data-page="tasks"] textarea { min-height: 120px; font-size: var(--font-body); }
372
+ body[data-page="tasks"] select { min-height: 54px; font-size: var(--font-body); }
373
+ body[data-page="tasks"] .primary-button { min-height: 54px; font-size: var(--font-body); }
374
+ body[data-page="tasks"] .form-message { font-size: var(--font-small); }
375
+ body[data-page="tasks"] .filters { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); width: 100%; gap: 3px; margin-top: 18px; overflow: visible; }
376
+ body[data-page="tasks"] .filter { min-width: 0; min-height: 44px; padding: 0 3px; font-size: var(--font-small); white-space: nowrap; }
377
+ body[data-page="tasks"] .job-list { gap: 12px; }
378
+ body[data-page="tasks"] .job-card { padding: 16px; }
379
+ body[data-page="tasks"] .job-top { align-items: flex-start; gap: 9px; }
380
+ body[data-page="tasks"] .job-meta, body[data-page="tasks"] .job-detail { font-size: var(--font-small); line-height: 1.5; overflow-wrap: anywhere; }
381
+ body[data-page="tasks"] .job-status { font-size: var(--font-small); white-space: nowrap; }
382
+ body[data-page="tasks"] .job-instruction { min-height: 44px; margin: 14px 0 12px; font-size: var(--font-body); line-height: 1.5; overflow-wrap: anywhere; }
383
+ body[data-page="tasks"] .job-summary { font-size: var(--font-body); line-height: 1.55; }
384
+ body[data-page="tasks"] .job-retry-notice { font-size: var(--font-small); }
385
+ body[data-page="tasks"] .job-bottom { flex-wrap: wrap; gap: 12px; }
386
+ body[data-page="tasks"] .job-actions { width: 100%; gap: 8px; }
387
+ body[data-page="tasks"] .job-actions:empty { display: none; }
388
+ body[data-page="tasks"] .job-actions .small-button { flex: 1; min-height: 44px; font-size: var(--font-body); }
389
+ body[data-page="tasks"] .job-handoff { gap: 10px; padding: 12px; }
390
+ body[data-page="tasks"] .handoff-line { flex-wrap: wrap; }
391
+ body[data-page="tasks"] .handoff-line code { font-size: var(--font-small); }
392
+ body[data-page="tasks"] .handoff-line .small-button, body[data-page="tasks"] .handoff-open { min-height: 44px; font-size: var(--font-small); }
393
+ body[data-page="tasks"] .handoff-note { font-size: var(--font-small); }
394
+ body[data-page="tasks"] .empty strong { font-size: var(--font-body); }
395
+ body[data-page="tasks"] .empty span { font-size: var(--font-small); }
396
+ }
397
+ @media (max-width: 390px) {
398
+ .app { padding-left: 10px; padding-right: 10px; }
399
+ .topbar { gap: 8px; }
400
+ .brand-name { font-size: 19px; }
401
+ .top-actions { gap: 6px; }
402
+ .panel { border-radius: 17px; }
403
+ .composer, .queue, .info-panel { padding-left: 17px; padding-right: 17px; }
404
+ .preview { padding: 20px 10px; }
405
+ .board-stage { padding-left: 0; padding-right: 0; }
406
+ body[data-page="tasks"] .composer, body[data-page="tasks"] .queue { padding-left: 15px; padding-right: 15px; }
407
+ body[data-page="tasks"] .sessions-panel, body[data-page="tasks"] .conversation-head { padding-left: 15px; padding-right: 15px; }
408
+ body[data-page="tasks"] .composer-top .tag { display: none; }
409
+ body[data-page="tasks"] .job-top { flex-wrap: wrap; }
410
+ body[data-page="tasks"] .job-meta { width: 100%; }
411
+ }
412
+ @media (max-width: 580px) {
413
+ .provider-grid { grid-template-columns: minmax(0, 1fr); }
414
+ }
415
+
416
+ .language-picker { margin-left: auto; display: flex; align-items: center; gap: 8px; flex: 0 0 auto; }
417
+ .language-picker label { color: var(--muted); font-size: var(--font-small); white-space: nowrap; }
418
+ .language-picker select { min-height: 44px; min-width: 132px; padding: 8px 28px 8px 12px; border: 1px solid var(--line); border-radius: 12px; background: var(--panel); color: var(--text); font-size: 16px; }
419
+ .language-picker + .top-actions { margin-left: 0; }
420
+ html[lang="en"] .board-provider-name { overflow-wrap: anywhere; }
421
+ @media (max-width: 1300px) {
422
+ .topbar { flex-wrap: wrap; gap: 8px 18px; padding-bottom: 8px; }
423
+ .topnav { order: 3; width: 100%; overflow-x: auto; }
424
+ }
425
+ @media (max-width: 650px) {
426
+ .language-picker { display: grid; gap: 2px; margin-left: auto; }
427
+ .language-picker label { font-size: 13px; }
428
+ .language-picker select { min-height: 40px; min-width: 124px; max-width: 150px; padding-top: 6px; padding-bottom: 6px; }
429
+ .language-picker + .top-actions { display: none; }
430
+ .topbar { gap: 8px; }
431
+ html[lang="en"] .topnav a { font-size: 16px; }
432
+ }
433
+ .tunnel-service-guide { min-width: 0; padding: 16px; border: 1px dashed var(--line); border-radius: 16px; background: var(--green-soft); }
434
+ .tunnel-service-guide-head { display: flex; align-items: baseline; flex-wrap: wrap; gap: 4px 12px; }
435
+ .tunnel-service-guide-head strong { font-size: var(--font-body); }
436
+ #tunnelServiceName { color: var(--muted); font-size: var(--font-small); overflow-wrap: anywhere; }
437
+ .tunnel-official-links { display: flex; flex-wrap: wrap; gap: 8px; margin: 12px 0; }
438
+ .tunnel-official-link { display: inline-flex; align-items: center; justify-content: center; gap: 6px; min-height: 44px; max-width: 100%; padding: 8px 12px; border: 1px solid var(--line); border-radius: 12px; color: var(--green-deep); background: var(--panel); font-size: var(--font-small); font-weight: 600; overflow-wrap: anywhere; }
439
+ .tunnel-official-link::after { content: '↗'; flex: 0 0 auto; }
440
+ .tunnel-official-link:hover { background: var(--panel-2); border-color: var(--line-strong); }
441
+ .tunnel-service-guide .panel-intro { margin: 0; }
442
+ @media (max-width: 390px) {
443
+ .tunnel-service-guide { padding: 12px; }
444
+ .tunnel-official-link { flex: 1 1 auto; }
445
+ }