beast-agent 2.38.3 → 2.39.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.
@@ -194,6 +194,8 @@ const els = {
194
194
  finPlanTime: $('#finPlanTime'),
195
195
  finReviewTime: $('#finReviewTime'),
196
196
  finShadow: $('#finShadow'),
197
+ finAdvWrap: $('#finAdvWrap'),
198
+ finAdvToggle: $('#finAdvToggle'),
197
199
  finSymBtn: $('#finSymBtn'),
198
200
  finRoles: $('#finRoles'),
199
201
  finWatchClear: $('#finWatchClear'),
@@ -209,6 +211,11 @@ const els = {
209
211
  finSymAdd: $('#finSymAdd'),
210
212
  finSymOverlay: $('#finSymOverlay'),
211
213
  finSymClose: $('#finSymClose'),
214
+ finTeamOverlay: $('#finTeamOverlay'),
215
+ finTeamClose: $('#finTeamClose'),
216
+ finTeamPick: $('#finTeamPick'),
217
+ finTeamCount: $('#finTeamCount'),
218
+ finSetupBtn: $('#finSetupBtn'),
212
219
  finSkillsBtn: $('#finSkillsBtn'),
213
220
  finSkillsOverlay: $('#finSkillsOverlay'),
214
221
  finSkillsClose: $('#finSkillsClose'),
@@ -9355,15 +9362,31 @@ function finTraderInputsSet(cfg) {
9355
9362
  if (els.finTraderModel && ae !== els.finTraderModel && cfg.traderSel) els.finTraderModel.value = cfg.traderSel;
9356
9363
  }
9357
9364
 
9358
- /* ANALİZ EKİBİ: SADECE SAYI seçilir — rolleri sistem otomatik atar
9359
- (FIN_ROLES_AUTO sırası: Teknik → Risk → Haber). Elle rol kutucukları
9360
- kaldırıldı; analysisAuto sunucuda daima açık. Katalog sunucudan gelir. */
9365
+ /* ANALİZ EKİBİ: tek satır çoklu seçim picker'ı — hangi uzman ajanların
9366
+ koşacağını seçersin (Risk / Teknik / Haber / Görsel); sayaç rozeti seçili
9367
+ ajan sayısını gösterir. Katalog sunucudan gelir (finance:state roles). */
9368
+ /* Ekip düğmesi ikonu: emoji yerine çizgi SVG (beyin) — tema rengiyle uyumlu */
9369
+ const FIN_TEAM_SVG =
9370
+ '<svg viewBox="0 0 24 24" width="12" height="12" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:-2px">' +
9371
+ '<path d="M12 5a3 3 0 1 0-5.997.125 4 4 0 0 0-2.526 5.77 4 4 0 0 0 .556 6.588A4 4 0 1 0 12 18Z"/>' +
9372
+ '<path d="M12 5a3 3 0 1 1 5.997.125 4 4 0 0 1 2.526 5.77 4 4 0 0 1-.556 6.588A4 4 0 1 1 12 18Z"/>' +
9373
+ '<path d="M15 13a4.5 4.5 0 0 1-3-4 4.5 4.5 0 0 1-3 4"/>' +
9374
+ '<path d="M17.599 6.5a3 3 0 0 0 .399-1.375"/>' +
9375
+ '<path d="M6.003 5.125A3 3 0 0 0 6.401 6.5"/>' +
9376
+ '<path d="M3.477 10.896a4 4 0 0 1 .585-.396"/>' +
9377
+ '<path d="M19.938 10.5a4 4 0 0 1 .585.396"/>' +
9378
+ '<path d="M6 18a4 4 0 0 1-1.967-.516"/>' +
9379
+ '<path d="M19.967 17.484A4 4 0 0 1 18 18"/></svg>';
9380
+ function finTeamBtnPaint(btn, n) {
9381
+ if (btn) btn.innerHTML = FIN_TEAM_SVG + '<span>Ekip: ' + n + ' ajan</span>';
9382
+ }
9361
9383
  let finRolesCatalog = [
9362
9384
  { id: 'risk', label: 'Risk Ajanı', desc: 'marj/kaldıraç/SL disiplini, exposure ve günlük kayıp hızı denetimi' },
9363
9385
  { id: 'technic', label: 'Teknik Analiz', desc: 'trend/yapı/destek-direnç/momentum okuma, AL-SAT-BEKLE önerileri' },
9364
9386
  { id: 'macro', label: 'Haber / Makro', desc: 'haber akışı + ekonomik takvim, DXY/emtia bağıntıları, yön eğilimi' },
9387
+ { id: 'visual', label: 'Görsel Ajan', desc: 'MT5 grafiği/ekran görüntüsü — trend, formasyon, seviye ve mum yapısını görsel doğrulama' },
9365
9388
  ];
9366
- let finRolesAuto = ['technic', 'risk', 'macro'];
9389
+ let finRolesAuto = ['technic', 'risk', 'macro', 'visual'];
9367
9390
  let finRolesDirty = false; /* tık→kaydet arası snapshot ezmesin */
9368
9391
  let finRolesSyncTimer = null;
9369
9392
 
@@ -9372,45 +9395,93 @@ function finRoleLabel(id) {
9372
9395
  return d ? d.label : String(id || '');
9373
9396
  }
9374
9397
 
9398
+ /* Etkin ekip listesi: oto modda ilk N rol, elle modda seçilen roller */
9399
+ function finTeamSelectedIds(cfg) {
9400
+ const c = cfg || finCfgCache || {};
9401
+ if (c.analysisAuto !== false) {
9402
+ const n = Math.max(0, Math.min((finRolesAuto || []).length || 4, Math.round(Number(c.analysisCount) || 0)));
9403
+ return (finRolesAuto || []).slice(0, n);
9404
+ }
9405
+ return (Array.isArray(c.analysisTeam) ? c.analysisTeam : []).filter((id) =>
9406
+ (finRolesCatalog || []).some((d) => d.id === id)
9407
+ );
9408
+ }
9409
+
9410
+ /* tek satır: buton (seçili sayı) + seçili rol adları özeti */
9375
9411
  function finRenderRoles(cfg) {
9376
9412
  const box = els.finRoles;
9377
9413
  if (!box) return;
9378
9414
  if (finRolesDirty) return; /* bekleyen kullanıcı değişikliği korunur */
9379
- const max = Math.max(1, (finRolesAuto || []).length || 3);
9380
- const count = Math.max(0, Math.min(max, Math.round(Number(cfg && cfg.analysisCount) || 0)));
9415
+ const selected = finTeamSelectedIds(cfg);
9381
9416
  box.innerHTML = '';
9382
9417
  const wrap = document.createElement('div');
9383
9418
  wrap.className = 'fin-team';
9384
- const cnt = document.createElement('input');
9385
- cnt.type = 'number';
9386
- cnt.className = 'fin-role-count';
9387
- cnt.min = '0';
9388
- cnt.max = String(max);
9389
- cnt.step = '1';
9390
- cnt.value = String(count);
9391
- cnt.title = 'Kaç uzman ajan koşacak (0 = ekip yok, en fazla ' + max + '). Roller otomatik atanır.';
9419
+ const btn = document.createElement('button');
9420
+ btn.type = 'button';
9421
+ btn.className = 'fin-sym-select fin-team-btn';
9422
+ finTeamBtnPaint(btn, selected.length);
9423
+ btn.title = 'Uzman ajanları çoktan seç — seçilen her rol AYRI sürekli ajan olarak koşar (işlem açmaz)';
9392
9424
  const hint = document.createElement('span');
9393
9425
  hint.className = 'fin-team-hint';
9394
- const names = (n) => finRolesAuto.slice(0, n).map(finRoleLabel);
9395
- const paint = (n) => {
9396
- hint.textContent = n > 0 ? 'otomatik: ' + names(n).join(' · ') : 'ekip kapalı — sadece trader koşar';
9397
- };
9398
- cnt.addEventListener('change', () => {
9399
- const n = Math.max(0, Math.min(max, Math.round(Number(cnt.value) || 0)));
9400
- cnt.value = String(n);
9401
- finRolesDirty = true;
9402
- finSaveCfg({ analysisAuto: true, analysisCount: n });
9403
- paint(n);
9404
- toast(n ? 'Analiz ekibi: ' + n + ' ajan — ' + names(n).join(', ') : 'Analiz ekibi kapatıldı');
9405
- clearTimeout(finRolesSyncTimer);
9406
- finRolesSyncTimer = setTimeout(() => { finRolesDirty = false; finSnapshot(); }, 900);
9407
- });
9408
- paint(count);
9409
- wrap.appendChild(cnt);
9426
+ hint.textContent = selected.length
9427
+ ? ((cfg && cfg.analysisAuto !== false) ? 'oto: ' : '') + selected.map(finRoleLabel).join(', ')
9428
+ : 'ekip kapalı — sadece trader koşar';
9429
+ btn.addEventListener('click', finTeamPickerOpen);
9430
+ wrap.appendChild(btn);
9410
9431
  wrap.appendChild(hint);
9411
9432
  box.appendChild(wrap);
9412
9433
  }
9413
9434
 
9435
+ /* picker modalı: çoklu checkbox listesi — her değişiklik ANINDA kaydedilir */
9436
+ function finTeamPickerPaint() {
9437
+ const pick = els.finTeamPick;
9438
+ if (!pick) return;
9439
+ const sel = new Set(finTeamSelectedIds());
9440
+ if (els.finTeamCount) els.finTeamCount.textContent = sel.size ? sel.size + ' seçili' : 'kapalı';
9441
+ pick.innerHTML = '';
9442
+ for (const d of finRolesCatalog || []) {
9443
+ const row = document.createElement('label');
9444
+ row.className = 'fin-team-item';
9445
+ row.title = d.desc || '';
9446
+ const cb = document.createElement('input');
9447
+ cb.type = 'checkbox';
9448
+ cb.checked = sel.has(d.id);
9449
+ cb.dataset.role = d.id;
9450
+ cb.addEventListener('change', () => {
9451
+ const ids = [...pick.querySelectorAll('input[data-role]')].filter((x) => x.checked).map((x) => x.dataset.role);
9452
+ finRolesDirty = true;
9453
+ finCfgCache = finCfgCache || {};
9454
+ finCfgCache.analysisAuto = false;
9455
+ finCfgCache.analysisTeam = ids;
9456
+ finSaveCfg({ analysisAuto: false, analysisTeam: ids });
9457
+ if (els.finTeamCount) els.finTeamCount.textContent = ids.length ? ids.length + ' seçili' : 'kapalı';
9458
+ const btn = els.finRoles && els.finRoles.querySelector('.fin-team-btn');
9459
+ const hint = els.finRoles && els.finRoles.querySelector('.fin-team-hint');
9460
+ finTeamBtnPaint(btn, ids.length);
9461
+ if (hint) hint.textContent = ids.length ? ids.map(finRoleLabel).join(', ') : 'ekip kapalı — sadece trader koşar';
9462
+ toast(ids.length ? 'Analiz ekibi: ' + ids.length + ' ajan — ' + ids.map(finRoleLabel).join(', ') : 'Analiz ekibi kapatıldı');
9463
+ clearTimeout(finRolesSyncTimer);
9464
+ finRolesSyncTimer = setTimeout(() => { finRolesDirty = false; finSnapshot(); }, 900);
9465
+ });
9466
+ const txt = document.createElement('span');
9467
+ txt.className = 'fin-team-txt';
9468
+ txt.innerHTML = '<b>' + escapeHtml(d.label) + '</b><i>' + escapeHtml(d.desc || '') + '</i>';
9469
+ row.appendChild(cb);
9470
+ row.appendChild(txt);
9471
+ pick.appendChild(row);
9472
+ }
9473
+ }
9474
+
9475
+ function finTeamPickerOpen() {
9476
+ if (!els.finTeamOverlay) return;
9477
+ finTeamPickerPaint();
9478
+ els.finTeamOverlay.hidden = false;
9479
+ }
9480
+
9481
+ function finTeamPickerClose() {
9482
+ if (els.finTeamOverlay) els.finTeamOverlay.hidden = true;
9483
+ }
9484
+
9414
9485
  function finRenderTrader(trader, cfg) {
9415
9486
  if (!els.finTraderBtn) return;
9416
9487
  const on = !!(trader && trader.on);
@@ -9958,6 +10029,27 @@ if (els.finShadow) {
9958
10029
  : 'Shadow mod kapalı — gerçek emirler aktif');
9959
10030
  });
9960
10031
  }
10032
+ /* GELİŞMİŞ SEÇENEKLER: disiplin + shadow bloğu varsayılan KAPALI; durum hatırlanır */
10033
+ const finAdvKey = 'beast.financeAdvanced';
10034
+ function finAdvPaint() {
10035
+ const on = (() => { try { return localStorage.getItem(finAdvKey) === '1'; } catch { return false; } })();
10036
+ if (els.finAdvWrap) els.finAdvWrap.hidden = !on;
10037
+ if (els.finAdvToggle) {
10038
+ els.finAdvToggle.classList.toggle('on', on);
10039
+ els.finAdvToggle.setAttribute('aria-expanded', on ? 'true' : 'false');
10040
+ els.finAdvToggle.title = on
10041
+ ? 'Gelişmiş açık — disiplin kuralları + shadow mod (kapat)'
10042
+ : 'Gelişmiş — disiplin kuralları + shadow mod';
10043
+ }
10044
+ }
10045
+ if (els.finAdvToggle) {
10046
+ els.finAdvToggle.addEventListener('click', () => {
10047
+ const on = (() => { try { return localStorage.getItem(finAdvKey) === '1'; } catch { return false; } })();
10048
+ try { localStorage.setItem(finAdvKey, on ? '0' : '1'); } catch {}
10049
+ finAdvPaint();
10050
+ });
10051
+ }
10052
+ finAdvPaint();
9961
10053
  if (els.finDailyLossAction) {
9962
10054
  els.finDailyLossAction.addEventListener('change', () => {
9963
10055
  finSaveCfg({ dailyLossAction: els.finDailyLossAction.value });
@@ -10029,6 +10121,20 @@ if (els.finModelRefreshBtn) {
10029
10121
  }
10030
10122
  });
10031
10123
  }
10124
+ if (els.finSetupBtn) {
10125
+ els.finSetupBtn.addEventListener('click', async () => {
10126
+ els.finSetupBtn.disabled = true;
10127
+ const r = await beast.financeMt5Setup().catch((e) => ({ ok: false, error: String((e && e.message) || e) }));
10128
+ els.finSetupBtn.disabled = false;
10129
+ if (r && r.ok) {
10130
+ toast(r.changed
10131
+ ? 'MT5 kurulumu uygulandı' + (r.restartRequired ? ' — MT5 yeniden başlatılınca etkin' : '')
10132
+ : 'MT5 kurulumu zaten güncel');
10133
+ } else {
10134
+ toast('MT5 kurulumu: ' + ((r && r.error) || 'başarısız'));
10135
+ }
10136
+ });
10137
+ }
10032
10138
  if (els.finTraderBtn) {
10033
10139
  els.finTraderBtn.addEventListener('click', async () => {
10034
10140
  if (els.finTraderBtn.classList.contains('stop')) {
@@ -10040,8 +10146,12 @@ if (els.finTraderBtn) {
10040
10146
  else if (r && r.ok) {
10041
10147
  const cfg = (await beast.financeState().catch(() => null));
10042
10148
  const c = (cfg && cfg.cfg) || {};
10043
- const team = Math.round(Number(c.analysisCount) || 0);
10149
+ const team = c.analysisAuto === false
10150
+ ? (Array.isArray(c.analysisTeam) ? c.analysisTeam.length : 0)
10151
+ : Math.round(Number(c.analysisCount) || 0);
10044
10152
  toast(team ? 'Trade ajanı + analiz ekibi (' + team + ' uzman) başladı — turlar panelde izlenir' : 'Trade ajanı başladı — turlar panelde izlenir');
10153
+ /* ajanlar arası konuşma için AJAN DM konsolu da açılır */
10154
+ try { toggleDmRail(false); } catch {}
10045
10155
  }
10046
10156
  }
10047
10157
  finSnapshot();
@@ -10208,7 +10318,7 @@ function finRenderSkills() {
10208
10318
  const map = cfg.roleSkills && typeof cfg.roleSkills === 'object' ? cfg.roleSkills : {};
10209
10319
  /* ANA KARAR VERİCİ (trader) da bir satır: playbook skill'i zorunlu tek seçim */
10210
10320
  const roles = [
10211
- { id: 'trader', label: 'TRADER (Ana Karar Verici)', desc: 'playbook: hangi setup\'lar serbest, giriş/çıkış/iptal kuralları, seans ve risk disiplini — asıl karar vericinin zorunlu skill\'i' },
10321
+ { id: 'trader', label: 'TRADER (Ana Karar Verici)', desc: 'playbook: hangi setup\'lar serbest, giriş/çıkış/iptal kuralları, seans ve risk disiplini — asıl karar vericinin skill\'i (boş = ajan kendi seçer)' },
10212
10322
  ].concat(Array.isArray(finRolesCatalog) ? finRolesCatalog : []);
10213
10323
  box.innerHTML = '';
10214
10324
  if (!roles.length) {
@@ -10229,10 +10339,34 @@ function finRenderSkills() {
10229
10339
  '<span>' + escapeHtml(String(r.desc || '').slice(0, 110)) + '</span>';
10230
10340
  const cur = Array.isArray(map[r.id]) ? map[r.id].map((x) => String(x || '').trim()).filter(Boolean) : [];
10231
10341
  const active = cur.length ? cur[0] : '';
10342
+ /* ÇÖP: rolün skill atamasını kaldır — boş bırakmak da geçerli bir seçim
10343
+ (o zaman ajan skill'i göreve göre SKILLS kataloğundan kendi seçer) */
10344
+ const clearBtn = document.createElement('button');
10345
+ clearBtn.type = 'button';
10346
+ clearBtn.className = 'fin-skill-clear';
10347
+ clearBtn.title = active
10348
+ ? 'Skill atamasını kaldır (' + active + ') — boş kalırsa ajan göreve göre kendi seçer'
10349
+ : 'Atama yok — boş bırakılabilir';
10350
+ clearBtn.innerHTML =
10351
+ '<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:-2px"><path d="M3 6h18"/><path d="M8 6V4a1 1 0 0 1 1-1h6a1 1 0 0 1 1 1v2"/><path d="M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6"/><path d="M10 11v6"/><path d="M14 11v6"/></svg>';
10352
+ clearBtn.addEventListener('click', (e) => {
10353
+ e.preventDefault();
10354
+ e.stopPropagation();
10355
+ const was = active;
10356
+ finCfgCache = finCfgCache || {};
10357
+ finCfgCache.roleSkills = finCfgCache.roleSkills || {};
10358
+ finCfgCache.roleSkills[r.id] = [];
10359
+ finSaveCfg({ roleSkills: { [r.id]: [] } });
10360
+ toast(was
10361
+ ? (r.label || r.id) + ' → ' + was + ' ataması kaldırıldı — ajan göreve göre kendi seçer'
10362
+ : (r.label || r.id) + ' → atama zaten boş');
10363
+ finRenderSkills();
10364
+ });
10365
+ head.appendChild(clearBtn);
10232
10366
  if (!active) {
10233
10367
  const warn = document.createElement('span');
10234
10368
  warn.className = 'fin-skill-req';
10235
- warn.textContent = 'ZORUNLU — skill seçilmedi';
10369
+ warn.textContent = 'atama yok — ajan göreve göre kendi seçer';
10236
10370
  head.appendChild(warn);
10237
10371
  }
10238
10372
  row.appendChild(head);
@@ -10247,7 +10381,7 @@ function finRenderSkills() {
10247
10381
  cb.type = 'radio';
10248
10382
  cb.name = 'fin-skill-role-' + r.id;
10249
10383
  cb.checked = on;
10250
- /* TEK + ZORUNLU: yeni skill seçilir; mevcut seçim kaldırılamaz */
10384
+ /* TEK seçim: yeni skill seçilir; kaldırmak için çöp ikonu kullanılır */
10251
10385
  cb.addEventListener('change', () => {
10252
10386
  if (!cb.checked) return;
10253
10387
  const next = [s.name];
@@ -10260,7 +10394,7 @@ function finRenderSkills() {
10260
10394
  const w = head.querySelector('.fin-skill-req');
10261
10395
  if (w) w.remove();
10262
10396
  finSaveCfg({ roleSkills: { [r.id]: next } });
10263
- toast((r.label || r.id) + ' → ' + s.name + ' (zorunlu tek skill)');
10397
+ toast((r.label || r.id) + ' → ' + s.name + ' (tek skill)');
10264
10398
  });
10265
10399
  chip.appendChild(cb);
10266
10400
  chip.appendChild(document.createTextNode(s.name));
@@ -10315,6 +10449,15 @@ if (els.finSymOverlay) {
10315
10449
  if (e.key === 'Escape') { e.stopPropagation(); finSymPickerClose(); }
10316
10450
  });
10317
10451
  }
10452
+ if (els.finTeamClose) els.finTeamClose.addEventListener('click', finTeamPickerClose);
10453
+ if (els.finTeamOverlay) {
10454
+ els.finTeamOverlay.addEventListener('click', (e) => {
10455
+ if (e.target === els.finTeamOverlay) finTeamPickerClose();
10456
+ });
10457
+ els.finTeamOverlay.addEventListener('keydown', (e) => {
10458
+ if (e.key === 'Escape') { e.stopPropagation(); finTeamPickerClose(); }
10459
+ });
10460
+ }
10318
10461
  if (els.finSymSearch) {
10319
10462
  els.finSymSearch.addEventListener('input', () => {
10320
10463
  clearTimeout(finSymSearchTimer);
@@ -4912,6 +4912,17 @@ body.finance-mode #composerWrap { margin-right: var(--finW, 400px); }
4912
4912
  .fin-alert .fa-note { color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
4913
4913
  /* ekib sayısı + otomatik atama özeti */
4914
4914
  .fin-team { display: flex; align-items: center; gap: 8px; min-width: 0; }
4915
+ .fin-team-btn { font-weight: 600; display: inline-flex; align-items: center; gap: 5px; }
4916
+ .fin-team-item {
4917
+ display: flex; align-items: flex-start; gap: 8px;
4918
+ padding: 6px 8px; border-radius: 6px;
4919
+ cursor: pointer;
4920
+ }
4921
+ .fin-team-item:hover { background: var(--panel2); }
4922
+ .fin-team-item input { flex: none; width: auto; margin: 2px 0 0 0; }
4923
+ .fin-team-txt { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
4924
+ .fin-team-txt b { font-size: 12px; color: var(--text); }
4925
+ .fin-team-txt i { font-size: 10.5px; font-style: normal; color: var(--muted); }
4915
4926
  .fin-team-hint {
4916
4927
  flex: 1;
4917
4928
  min-width: 0;
@@ -5016,10 +5027,11 @@ body.finance-mode #composerWrap { margin-right: var(--finW, 400px); }
5016
5027
  .fin-start:hover { filter: brightness(1.08); box-shadow: 0 1px 6px rgba(0, 0, 0, 0.22); }
5017
5028
  .fin-start:active { transform: translateY(1px); filter: brightness(0.96); }
5018
5029
  .fin-start.stop {
5019
- background: #ef4444;
5020
- border-color: #ef4444;
5030
+ background: #b91c1c;
5031
+ border-color: #b91c1c;
5021
5032
  color: #fff; /* kırmızı zeminde beyaz her iki temada da okunur */
5022
5033
  }
5034
+ .fin-start.stop:hover { background: #991b1b; border-color: #991b1b; }
5023
5035
 
5024
5036
  /* TRADE AJANI kartı — vurgulu görünüm (accent çerçeve + yumuşak zemin) */
5025
5037
  .fin-card-trader {
@@ -5134,6 +5146,7 @@ body.finance-mode #composerWrap { margin-right: var(--finW, 400px); }
5134
5146
  border-radius: 5px;
5135
5147
  }
5136
5148
  .fin-card-title button:hover { background: var(--accent-dim); color: var(--accent); }
5149
+ .fin-card-title button.on { background: var(--accent-dim); color: var(--accent); }
5137
5150
 
5138
5151
  /* MT5 sembol seçici modalı */
5139
5152
  .fin-sym-dialog { width: 460px; max-width: calc(100vw - 40px); }
@@ -5219,6 +5232,7 @@ body.finance-mode #composerWrap { margin-right: var(--finW, 400px); }
5219
5232
  gap: 10px;
5220
5233
  }
5221
5234
  .fin-skill-role {
5235
+ position: relative;
5222
5236
  border: 1px solid var(--border);
5223
5237
  border-radius: 8px;
5224
5238
  padding: 8px 9px;
@@ -5227,13 +5241,26 @@ body.finance-mode #composerWrap { margin-right: var(--finW, 400px); }
5227
5241
  .fin-skill-role-head { display: flex; flex-direction: column; gap: 1px; margin-bottom: 7px; }
5228
5242
  .fin-skill-role-head b { font-size: 11.5px; color: var(--text); }
5229
5243
  .fin-skill-role-head span { font-size: 10px; color: var(--muted); }
5244
+ .fin-skill-clear {
5245
+ position: absolute;
5246
+ top: 6px;
5247
+ right: 6px;
5248
+ background: none;
5249
+ border: none;
5250
+ color: var(--muted);
5251
+ line-height: 0;
5252
+ padding: 3px 5px;
5253
+ border-radius: 6px;
5254
+ cursor: pointer;
5255
+ }
5256
+ .fin-skill-clear:hover { color: #ef4444; background: var(--accent-dim); }
5230
5257
  .fin-skill-req {
5231
5258
  align-self: flex-start;
5232
5259
  margin-top: 2px;
5233
5260
  font-size: 9.5px;
5234
5261
  font-weight: 800;
5235
5262
  letter-spacing: 0.5px;
5236
- color: #ef4444;
5263
+ color: var(--muted);
5237
5264
  }
5238
5265
  .fin-skill-chips { display: flex; flex-wrap: wrap; gap: 4px; }
5239
5266
  .fin-skill-chip {