@viberaven/cli 1.4.0 → 1.4.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.
package/dist/cli.js CHANGED
@@ -11558,7 +11558,7 @@ var VERSION;
11558
11558
  var init_version = __esm({
11559
11559
  "src/version.ts"() {
11560
11560
  "use strict";
11561
- VERSION = "1.4.0";
11561
+ VERSION = "1.4.1";
11562
11562
  }
11563
11563
  });
11564
11564
 
@@ -14872,6 +14872,8 @@ var init_providerLogos = __esm({
14872
14872
  aws: '<svg viewBox="0 0 24 24" preserveAspectRatio="xMidYMid meet" aria-hidden="true"><path fill="#FF9900" d="M6.763 10.582c4.95-2.283 10.563-2.283 15.475 0 .532.243.848.741.848 1.256 0 .532-.323 1.026-.85 1.256-4.912 2.283-10.525 2.283-15.474 0-.528-.23-.851-.724-.851-.279 0-.544.098-.754.243-4.95 2.283-10.563 2.283-15.475 0-.532-.243-.848-.741-.848-1.256 0-.532.323-1.026.85-1.256.228-.098.492-.196.754-.196.279 0 .544.098.754.243Z"/><path fill="#FF9900" d="M12 3.65c-2.772 0-5.027 1.147-5.027 2.553S9.228 8.756 12 8.756s5.027-1.147 5.027-2.553S14.772 3.65 12 3.65Z"/></svg>',
14873
14873
  sentry: '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 3 22 20H2L12 3Zm0 5.1L6.8 17h2.1l3.1-5.3 3.1 5.3h2.1L12 8.1Z"/></svg>',
14874
14874
  posthog: '<svg viewBox="0 0 24 24" aria-hidden="true"><path fill="#1D4AFF" d="M5.2 6.1h8.2c3 0 5.3 2 5.3 5.1 0 3.2-2.3 5.2-5.3 5.2H5.2V6.1Z"/><path fill="#F54E00" d="M4.1 4h4.2a2.35 2.35 0 0 1 0 4.7H4.1V4Z"/><circle cx="13.7" cy="11.75" r="1.1" fill="#F9BD2B"/><path fill="#F54E00" d="m16.5 9.35 2-1.15.85 1.3-2.15 1.3-.7-1.45Zm.38 4.78 2.42.86-.78 1.6-2.4-1.02.76-1.44Z"/></svg>',
14875
+ resend: '<svg viewBox="0 0 24 24" aria-hidden="true"><path fill="#FFFFFF" d="M14.679 0c4.648 0 7.413 2.765 7.413 6.434s-2.765 6.434-7.413 6.434H12.33L24 24h-8.245l-8.88-8.44c-.636-.588-.93-1.273-.93-1.86 0-.831.587-1.565 1.713-1.883l4.574-1.224c1.737-.465 2.936-1.81 2.936-3.572 0-2.153-1.761-3.4-3.939-3.4H0V0z"/></svg>',
14876
+ upstash: '<svg viewBox="0 0 24 24" aria-hidden="true"><path fill="#00E9A3" d="M13.8027 0C11.193 0 8.583.9952 6.5918 2.9863c-3.9823 3.9823-3.9823 10.4396 0 14.4219 1.9911 1.9911 5.2198 1.9911 7.211 0 1.991-1.9911 1.991-5.2198 0-7.211L12 12c.9956.9956.9956 2.6098 0 3.6055-.9956.9955-2.6099.9955-3.6055 0-2.9866-2.9868-2.9866-7.8297 0-10.8164 2.9868-2.9868 7.8297-2.9868 10.8164 0l1.8028-1.8028C19.0225.9952 16.4125 0 13.8027 0zM12 12c-.9956-.9956-.9956-2.6098 0-3.6055.9956-.9955 2.6098-.9955 3.6055 0 2.9867 2.9868 2.9867 7.8297 0 10.8164-2.9867 2.9868-7.8297 2.9868-10.8164 0l-1.8028 1.8028c3.9823 3.9822 10.4396 3.9822 14.4219 0 3.9823-3.9824 3.9823-10.4396 0-14.4219-.9956-.9956-2.3006-1.4922-3.6055-1.4922-1.3048 0-2.6099.4966-3.6054 1.4922-1.9912 1.9912-1.9912 5.2198 0 7.211z"/></svg>',
14875
14877
  playwright: '<svg viewBox="0 0 24 24" aria-hidden="true"><path fill="#2EAD33" d="M8.4 8.2 5.4 17.2h2.3l.8-2.8h2.7l.8 2.8h2.3L11.6 8.2H8.4Zm1.1 4.8.8-2.4.8 2.4H9.5Z"/><path fill="#E2574C" d="M15.6 8.2 12.6 17.2h2.3l.8-2.8h2.7l.8 2.8h2.3L18.8 8.2h-3.2Zm1.1 4.8.8-2.4.8 2.4h-1.6Z"/><ellipse fill="#2EAD33" cx="9.5" cy="13.1" rx="1.1" ry="1.4"/><ellipse fill="#E2574C" cx="16.5" cy="13.1" rx="1.1" ry="1.4"/></svg>',
14876
14878
  logrocket: '<svg viewBox="0 0 24 24" preserveAspectRatio="xMidYMid meet" aria-hidden="true"><rect x="2" y="2" width="20" height="20" rx="5" fill="#764ABC"/><path fill="#FFFFFF" d="M12 7.4c2.7 0 4.9 2.2 4.9 4.9s-2.2 4.9-4.9 4.9-4.9-2.2-4.9-4.9 4.9-4.9 4.9-4.9Zm0 1.9a3 3 0 1 0 0 6 3 3 0 0 0 0-6Zm-3.2 7.4h6.4v1.5H8.8v-1.5Z"/></svg>',
14877
14879
  figma: '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M8.5 3h3.5v6H8.5a3 3 0 1 1 0-6Zm3.5 6h3.5a3 3 0 1 0 0-6H12v6Zm0 0h3.5a3 3 0 1 1 0 6H12V9Zm-3.5 0H12v6H8.5a3 3 0 1 1 0-6ZM8.5 15H12v2.5A3.5 3.5 0 1 1 8.5 15Z"/></svg>',
@@ -16029,10 +16031,10 @@ const providerMarkAssets = {
16029
16031
  };
16030
16032
 
16031
16033
  const mascotAssets = {
16032
- idle: '/report/assets/viberaven-mascot.png?v=raven-cards-2',
16033
- fixing: '/report/assets/viberaven-mascot.png?v=raven-cards-2',
16034
- verified: '/report/assets/viberaven-mascot.png?v=raven-cards-2',
16035
- alert: '/report/assets/viberaven-mascot.png?v=raven-cards-2',
16034
+ idle: '/manga/raven-mark.png?v=manga1',
16035
+ fixing: '/manga/raven-mark.png?v=manga1',
16036
+ verified: '/manga/raven-mark.png?v=manga1',
16037
+ alert: '/manga/raven-mark.png?v=manga1',
16036
16038
  };
16037
16039
 
16038
16040
  const providerDashboardUrls = {
@@ -16268,10 +16270,22 @@ function providerAssetSrc(provider) {
16268
16270
  return providerMarkAssets[provider.id] || providerAssets[provider.id] || providerAssets.supabase;
16269
16271
  }
16270
16272
 
16273
+ function providerIconMarkup(provider) {
16274
+ const iconHtml = provider && provider.iconHtml ? String(provider.iconHtml) : '';
16275
+ if (iconHtml.trim()) return iconHtml;
16276
+ // Fallback must still be the PROVIDER icon, never the raven card-art. Build the real logo
16277
+ // from the provider id so a cold/offline boot shows Supabase/Vercel/etc, not a generic raven.
16278
+ const id = provider && provider.id ? String(provider.id).toLowerCase().replace(/[^a-z0-9]/g, '') : '';
16279
+ if (id) {
16280
+ const color = String(areaEnergy(provider.area) || '#e8c15a').replace('#', '');
16281
+ return '<img class="provider-logo__img" src="https://cdn.simpleicons.org/' + id + '/' + color + '" alt="" decoding="async" data-provider-logo-key="' + id + '" />';
16282
+ }
16283
+ return '<img src="' + providerAssetSrc(provider) + '" alt="" />';
16284
+ }
16285
+
16271
16286
  function ProviderToken(provider, extraClass) {
16272
16287
  const className = 'vr-provider-token' + (extraClass ? ' ' + extraClass : '');
16273
- const iconHtml = provider && provider.iconHtml ? String(provider.iconHtml) : '';
16274
- return '<span class="' + className + '">' + (iconHtml.trim() ? iconHtml : '<img src="' + providerAssetSrc(provider) + '" alt="" />') + '</span>';
16288
+ return '<span class="' + className + '">' + providerIconMarkup(provider) + '</span>';
16275
16289
  }
16276
16290
 
16277
16291
  function areaEnergy(area) {
@@ -16294,26 +16308,53 @@ function gradeMark(tier) {
16294
16308
  return '&#9679;';
16295
16309
  }
16296
16310
 
16311
+ // Full manga card art (shipped in assets/manga/cards). Providers with baked
16312
+ // art render the image as the whole face + a holographic foil overlay; the
16313
+ // rest fall back to the CSS-built card anatomy below.
16314
+ const MANGA_CARD_ARTS = { supabase: 1, vercel: 1, github: 1, posthog: 1, stripe: 1, resend: 1, sentry: 1, upstash: 1, clerk: 1, authjs: 1 };
16315
+ function cardArtSrc(provider) {
16316
+ const id = provider && provider.id ? String(provider.id).toLowerCase().replace(/[^a-z0-9]/g, '') : '';
16317
+ // ?v bump busts stale browser cache when a card art is redrawn
16318
+ return MANGA_CARD_ARTS[id] ? '/manga/cards/' + id + '.webp?v=5' : '';
16319
+ }
16320
+
16297
16321
  function TradingCard(provider, variant, options) {
16298
16322
  const opts = options || {};
16299
16323
  const size = variant === 'dealt' ? 'is-dealt' : variant === 'map' ? 'is-map' : 'is-hand';
16300
16324
  const grade = provider.grade || { value: null, tier: 'facedown', label: 'FACE DOWN' };
16301
16325
  const foil = escapeHtml(provider.id);
16302
16326
  const placedClass = opts.placed ? ' is-placed' : '';
16327
+ const tagOpen = opts.asDiv ? 'div' : 'button type="button"';
16328
+ const tagClose = opts.asDiv ? 'div' : 'button';
16303
16329
  if (grade.tier === 'facedown' && !opts.forceFace) {
16304
- return '<' + (opts.asDiv ? 'div' : 'button type="button"') + ' class="vr-tcard is-back ' + size + placedClass + '"' +
16330
+ return '<' + tagOpen + ' class="vr-tcard is-back ' + size + placedClass + '"' +
16305
16331
  (opts.asDiv ? '' : ' data-action="deal-provider" data-provider="' + foil + '" aria-label="Flip ' + escapeHtml(provider.name) + ' \u2014 run its first checks" title="' + escapeHtml(provider.name) + ' \xB7 face down \u2014 click to flip"') +
16306
16332
  (opts.style ? ' style="' + opts.style + '"' : '') + '>' +
16307
16333
  '<span class="vr-tcard-backart"><img src="' + mascotAssets.idle + '" alt="" /></span>' +
16308
16334
  '<span class="vr-tcard-backname">' + escapeHtml(provider.name) + '</span>' +
16309
- '</' + (opts.asDiv ? 'div' : 'button') + '>';
16335
+ '</' + tagClose + '>';
16310
16336
  }
16311
16337
  const flawless = grade.tier === 'flawless';
16312
- return '<' + (opts.asDiv ? 'div' : 'button type="button"') + ' class="vr-tcard ' + size + placedClass + (flawless ? ' is-flawless' : '') + '" data-foil="' + foil + '"' +
16338
+ const artSrc = cardArtSrc(provider);
16339
+ if (artSrc) {
16340
+ // full-art face: the image IS the card; holo foil + glare ride on top,
16341
+ // grade chip and RAVEN GRADE strip overlay the corners.
16342
+ return '<' + tagOpen + ' class="vr-tcard has-art ' + size + placedClass + (flawless ? ' is-flawless' : '') + '" data-foil="' + foil + '"' +
16343
+ (opts.asDiv ? '' : ' data-action="deal-provider" data-provider="' + foil + '" aria-label="Deal ' + escapeHtml(provider.name) + ' \u2014 checks run instantly in chat" title="' + escapeHtml(provider.name) + ' \xB7 ' + escapeHtml(grade.label) + '"') +
16344
+ (opts.style ? ' style="' + opts.style + '"' : '') + '>' +
16345
+ '<img class="vr-tcard-artfull" src="' + artSrc + '" alt="' + escapeHtml(provider.name) + '" draggable="false" />' +
16346
+ '<span class="vr-tcard-holofoil" aria-hidden="true"></span>' +
16347
+ '<span class="vr-tcard-glare" aria-hidden="true"></span>' +
16348
+ (opts.placed ? '<span class="vr-tcard-unplace" data-action="unplace-card" data-unplace="' + foil + '" title="Remove from the table" role="button" aria-label="Remove from the table">&#10005;</span>' : '') +
16349
+ '<span class="vr-tcard-hp is-onart"><span class="vr-tcard-energy" style="--vr-energy:' + areaEnergy(provider.area) + '"></span><i>' + (grade.value == null ? '?' : grade.value) + '</i></span>' +
16350
+ '<span class="vr-tcard-gradebar">' + (flawless ? '&#10038; ' : '') + 'RAVEN GRADE &middot; ' + escapeHtml(grade.label) + '</span>' +
16351
+ '</' + tagClose + '>';
16352
+ }
16353
+ return '<' + tagOpen + ' class="vr-tcard ' + size + placedClass + (flawless ? ' is-flawless' : '') + '" data-foil="' + foil + '"' +
16313
16354
  (opts.asDiv ? '' : ' data-action="deal-provider" data-provider="' + foil + '" aria-label="Deal ' + escapeHtml(provider.name) + ' \u2014 checks run instantly in chat" title="' + escapeHtml(provider.name) + ' \xB7 ' + escapeHtml(grade.label) + '"') +
16314
16355
  (opts.style ? ' style="' + opts.style + '"' : '') + '>' +
16315
16356
  '<span class="vr-tcard-sheen" aria-hidden="true"></span>' +
16316
- (opts.placed ? '<span class="vr-tcard-unplace" data-action="unplace-card" data-unplace="' + foil + '" title="Return to your hand" aria-hidden="true">&#10005;</span>' : '') +
16357
+ (opts.placed ? '<span class="vr-tcard-unplace" data-action="unplace-card" data-unplace="' + foil + '" title="Remove from the table" role="button" aria-label="Remove from the table">&#10005;</span>' : '') +
16317
16358
  '<span class="vr-tcard-inner">' +
16318
16359
  '<span class="vr-tcard-head"><b>' + escapeHtml(provider.name) + '</b><span class="vr-tcard-hp"><span class="vr-tcard-energy" style="--vr-energy:' + areaEnergy(provider.area) + '" title="' + escapeHtml(String(provider.area || '')) + '"></span><i>' + (grade.value == null ? '?' : grade.value) + '</i></span></span>' +
16319
16360
  '<span class="vr-tcard-artwin"><span class="vr-tcard-holo" aria-hidden="true"></span>' + ProviderToken(provider, 'is-tcard') + '</span>' +
@@ -16426,6 +16467,15 @@ function dealProviderCard(providerId) {
16426
16467
  chat.messages.push(message);
16427
16468
  demo.notice = provider.name + ' dealt into chat \u2014 checks running.';
16428
16469
  render({ scrollToBottom: chatIndex });
16470
+ const dealtAt = Date.now();
16471
+ window.requestAnimationFrame(animateDealtCardIntoChat);
16472
+ // The local check returns almost instantly; hold the results render until the
16473
+ // deal animation has played, so it is actually visible (otherwise the full
16474
+ // re-render replaces the animating card before you see it).
16475
+ const renderAfterDeal = () => {
16476
+ const wait = Math.max(0, 720 - (Date.now() - dealtAt));
16477
+ window.setTimeout(() => render(), wait);
16478
+ };
16429
16479
  fetch('/api/provider-check', {
16430
16480
  method: 'POST',
16431
16481
  headers: { 'content-type': 'application/json' },
@@ -16441,12 +16491,12 @@ function dealProviderCard(providerId) {
16441
16491
  message.done = true;
16442
16492
  const idx = providers.findIndex((item) => item.id === providerId);
16443
16493
  if (idx >= 0 && payload.provider) providers[idx] = normalizeProvider(payload.provider);
16444
- render();
16494
+ renderAfterDeal();
16445
16495
  })
16446
16496
  .catch(() => {
16447
16497
  message.error = 'Could not reach the local server \u2014 is the Studio still running?';
16448
16498
  message.done = true;
16449
- render();
16499
+ renderAfterDeal();
16450
16500
  });
16451
16501
  }
16452
16502
 
@@ -17501,6 +17551,17 @@ function runChatTask(kind, promptOverride) {
17501
17551
  clearChatTaskTimer();
17502
17552
  const chatIndex = Math.max(0, Math.min(demo.pickerChatIndex || 0, demo.activeChatCount - 1));
17503
17553
  const chat = chatContext(chatIndex);
17554
+ // Product contract: installed is NOT enough \u2014 force Test connection before
17555
+ // real agent control. If the selected CLI has not passed a connection probe,
17556
+ // route to the Terminal setup instead of spawning it.
17557
+ if (chat.cliId && chat.cliId !== 'terminal' && !cliAgentReady(chat.cliId)) {
17558
+ const agentLabel = (cliAgents.find((item) => item.id === chat.cliId)?.label) || chat.cliId;
17559
+ demo.pickerChatIndex = chatIndex;
17560
+ demo.notice = 'Connect ' + agentLabel + ' first \u2014 run Test connection in the Terminal tab, then send your mission.';
17561
+ selectTerminalCli(chat.cliId);
17562
+ render();
17563
+ return;
17564
+ }
17504
17565
  const mission = resolveQuickActionMission(kind, chatIndex, promptOverride);
17505
17566
  const meta = taskMeta(kind, chatIndex);
17506
17567
  const attachedProviderId = chat.providerId || null;
@@ -17531,6 +17592,7 @@ function runChatTask(kind, promptOverride) {
17531
17592
  error: '',
17532
17593
  providerId: taskProviderId,
17533
17594
  releaseId: taskReleaseId,
17595
+ startedAt: Date.now(),
17534
17596
  };
17535
17597
  if (chatIndex === 0) rememberRecentChat(label, provider, release, chat);
17536
17598
  chat.providerId = null;
@@ -17541,6 +17603,7 @@ function runChatTask(kind, promptOverride) {
17541
17603
  demo.activeStepId = kind === 'verify' ? 'verify' : kind === 'provider' ? 'provider' : 'agent';
17542
17604
  demo.notice = meta.notice;
17543
17605
  render({ scrollToBottom: chatIndex });
17606
+ startTaskElapsedTimer();
17544
17607
  const finish = (message, isError, cardsPayload) => {
17545
17608
  const taskContext = chat.activeTask || {};
17546
17609
  const responseText = isError
@@ -18715,18 +18778,40 @@ function ChatMissionCards(cards, chatIndex, options, usedActions) {
18715
18778
  return html;
18716
18779
  }
18717
18780
 
18781
+ let taskElapsedTimer = null;
18782
+ function formatElapsed(ms) {
18783
+ const s = Math.max(0, Math.floor(ms / 1000));
18784
+ return Math.floor(s / 60) + ':' + String(s % 60).padStart(2, '0');
18785
+ }
18786
+ function anyLaneWorking() {
18787
+ return (demo.chatContexts || []).some((c) => c && c.activeTask && c.activeTask.phase === 'working' && c.activeTask.startedAt);
18788
+ }
18789
+ // Live heartbeat: while the agent runs, tick the elapsed clock every second so it never looks frozen.
18790
+ function startTaskElapsedTimer() {
18791
+ if (taskElapsedTimer) return;
18792
+ taskElapsedTimer = window.setInterval(() => {
18793
+ if (!anyLaneWorking()) { window.clearInterval(taskElapsedTimer); taskElapsedTimer = null; return; }
18794
+ document.querySelectorAll('[data-task-elapsed]').forEach((el) => {
18795
+ const start = Number(el.getAttribute('data-task-elapsed')) || 0;
18796
+ if (start) el.textContent = formatElapsed(Date.now() - start);
18797
+ });
18798
+ }, 1000);
18799
+ }
18800
+
18718
18801
  function ChatTaskAnimation(chatIndex, compact) {
18719
18802
  const chat = chatContext(chatIndex);
18720
18803
  const task = chat.activeTask;
18721
18804
  if (!task || task.phase !== 'working') return '';
18722
18805
  const meta = taskMeta(task.kind);
18723
18806
  const statusText = task.kind === 'verify' ? 'Verifying...' : task.kind === 'diff' ? 'Reviewing...' : task.kind === 'explain' ? 'Explaining...' : 'Working...';
18724
- const thoughtText = 'reading launch context';
18807
+ const thoughtText = 'reading your repo';
18808
+ const started = task.startedAt || Date.now();
18809
+ const elapsedHtml = '<span class="vr-task-elapsed" data-task-elapsed="' + started + '">' + formatElapsed(Date.now() - started) + '</span>';
18725
18810
  const compactClass = compact ? ' is-compact' : '';
18726
18811
  const rail = compact ? '' : '<div class="vr-task-rail" aria-hidden="true"><span></span></div>';
18727
- const detail = compact ? '' : '<p>' + escapeHtml(meta.line) + '</p>';
18812
+ const detail = compact ? '' : '<p>' + escapeHtml(meta.line) + ' On a large repo this can take a minute.</p>';
18728
18813
  return '<section class="vr-chat-agent-task is-working' + compactClass + '" data-task="' + escapeHtml(task.kind) + '">' +
18729
- '<header><span class="vr-task-glyph" aria-hidden="true"></span><div><strong>' + escapeHtml(statusText) + '</strong><em>(' + escapeHtml(thoughtText) + ')</em></div><b>' + escapeHtml(meta.title) + '</b></header>' +
18814
+ '<header><span class="vr-task-glyph" aria-hidden="true"></span><div><strong>' + escapeHtml(statusText) + '</strong><em>(' + escapeHtml(thoughtText) + ')</em></div>' + elapsedHtml + '<b>' + escapeHtml(meta.title) + '</b></header>' +
18730
18815
  rail +
18731
18816
  detail +
18732
18817
  '</section>';
@@ -18740,16 +18825,19 @@ function formatAgentOutput(value) {
18740
18825
  .filter((line) => !/^(OpenAI Codex v|workdir:|model:|provider:|approval:|sandbox:|reasoning effort:|reasoning summaries:|session id:|--------$|user$)/.test(line.trim()))
18741
18826
  .join('\\n')
18742
18827
  .trim();
18743
- if (/IneligibleTierError|UNSUPPORTED_CLIENT|no longer supported for Gemini Code Assist/i.test(clean)) {
18828
+ // CLI-failure rewrites only apply to SHORT error output. A long real answer that merely
18829
+ // mentions "auth"/"sign in" (e.g. a production auth review) must never be mistaken for one.
18830
+ const compact = clean.length < 800;
18831
+ if (compact && /IneligibleTierError|UNSUPPORTED_CLIENT|no longer supported for Gemini Code Assist/i.test(clean)) {
18744
18832
  return 'Gemini CLI is installed, but this account/client is not eligible for the current Gemini Code Assist CLI. Connect a supported Gemini CLI account, then retry this chat.';
18745
18833
  }
18746
- if (/not running in a trusted directory|--skip-trust|GEMINI_CLI_TRUST_WORKSPACE/i.test(clean)) {
18834
+ if (compact && /not running in a trusted directory|--skip-trust|GEMINI_CLI_TRUST_WORKSPACE/i.test(clean)) {
18747
18835
  return 'Gemini CLI needs this project trusted for headless chat. VibeRaven now passes the trust flag for Gemini runs; retry after your Gemini account is eligible.';
18748
18836
  }
18749
- if (/command not found|not recognized as an internal or external command|ENOENT/i.test(clean)) {
18837
+ if (compact && /command not found|not recognized as an internal or external command|ENOENT/i.test(clean)) {
18750
18838
  return 'The selected CLI is not available on PATH. Install it or sign in, then reconnect it from the chat header.';
18751
18839
  }
18752
- if (/authentication|not logged in|login required|sign in/i.test(clean)) {
18840
+ if (compact && /not logged in|login required|not authenticated|authentication failed|unauthorized|please (run|sign|log)|run (codex|gemini|claude)[^\\n]{0,40}login/i.test(clean)) {
18753
18841
  return 'The selected CLI needs sign-in before VibeRaven can use it. Sign in with the CLI, then retry this mission.';
18754
18842
  }
18755
18843
  const missionIndex = clean.indexOf('User mission:');
@@ -19008,6 +19096,21 @@ function agentErrorPresentation(text) {
19008
19096
  return { headline: raw, raw: '' };
19009
19097
  }
19010
19098
 
19099
+ function looksLikeRawTranscript(text) {
19100
+ const nonEmpty = String(text || '').split('\\n').filter((line) => line.trim());
19101
+ if (nonEmpty.length < 24) return false;
19102
+ let grepLike = 0;
19103
+ let structured = 0;
19104
+ nonEmpty.forEach((line) => {
19105
+ const t = line.trim();
19106
+ if (/:\\d+:\\s/.test(t) || /\\.(json|jsonl|ts|tsx|js|md|txt):\\d+/.test(t)) grepLike++;
19107
+ if (/^#{1,3}\\s+/.test(t) || /^\\*\\*[^*]+\\*\\*/.test(t)) structured++;
19108
+ });
19109
+ if (grepLike >= 8) return true;
19110
+ if (nonEmpty.length > 60 && structured <= 1) return true;
19111
+ return false;
19112
+ }
19113
+
19011
19114
  function renderAgentAnswerHtml(rawText, chatIndex, cards, isError, usedActions) {
19012
19115
  const tick = String.fromCharCode(96);
19013
19116
  const fence = tick + tick + tick;
@@ -19022,6 +19125,16 @@ function renderAgentAnswerHtml(rawText, chatIndex, cards, isError, usedActions)
19022
19125
  '</div>';
19023
19126
  }
19024
19127
  }
19128
+ if (looksLikeRawTranscript(text)) {
19129
+ const headline = isError
19130
+ ? 'The agent explored your repo but did not finish with a clean summary.'
19131
+ : 'The agent returned raw work instead of a summary.';
19132
+ return '<div class="vr-agent-answer-body vr-agent-answer-raw">' +
19133
+ '<p class="vr-agent-raw-headline">' + escapeHtml(headline) + '</p>' +
19134
+ '<p class="vr-agent-raw-sub">Open the agent work below for the raw detail, or use Retry for a clean answer.</p>' +
19135
+ agentWorkLogHtml(text) +
19136
+ '</div>';
19137
+ }
19025
19138
  const split = splitAgentTelemetry(text);
19026
19139
  const workLog = split.work ? agentWorkLogHtml(split.work) : '';
19027
19140
  let visibleText = dedupeAgentMarkdownBody(split.answer, cards);
@@ -19218,8 +19331,9 @@ function ChatAgentResponse(meta, responseText, isError, providerId, releaseId, k
19218
19331
  : '';
19219
19332
  return '<section class="vr-chat-message is-agent vr-chat-agent-response vr-hud-card is-' + statusTone + ' ' + (isError ? 'is-error' : 'is-ready') + '">' +
19220
19333
  '<header class="vr-hud-strip">' +
19221
- '<span class="vr-hud-avatar"><img src="' + mascotUrl() + '" alt="" /></span>' +
19334
+ '<span class="vr-hud-avatar is-raven"><img src="/manga/emblem.png" alt="" /></span>' +
19222
19335
  '<strong>VibeRaven</strong>' +
19336
+ '<span class="vr-hud-aibadge">AI AGENT</span>' +
19223
19337
  '<span class="vr-hud-status is-' + statusTone + '"><i></i>' + statusWord + '</span>' +
19224
19338
  via +
19225
19339
  '<b>Just now</b>' +
@@ -19273,7 +19387,7 @@ function ProviderCardMessage(message) {
19273
19387
  const provider = providers.find((item) => item.id === message.providerId);
19274
19388
  if (!provider) return '';
19275
19389
  const grade = message.grade || provider.grade || { value: null, tier: 'facedown', label: 'FACE DOWN' };
19276
- const rows = message.rows === null
19390
+ const rows = !Array.isArray(message.rows)
19277
19391
  ? '<div class="vr-card-row is-running"><span>&#9676;</span> running checks&hellip;</div>'
19278
19392
  : message.rows.map((row) => {
19279
19393
  const ready = row.state === 'ready';
@@ -19362,8 +19476,8 @@ function renderChatMessages(chat, index) {
19362
19476
  }
19363
19477
  const meta = message.kind ? taskMeta(message.kind) : { title: 'VibeRaven Agent' };
19364
19478
  const messageId = String(index) + '-' + String(messageIndex);
19365
- return storyNode('is-agent' + (message.isError ? ' is-error' : ''),
19366
- Icon(storyIconFor(message.kind)),
19479
+ return storyNode('is-agent has-raven' + (message.isError ? ' is-error' : ''),
19480
+ '<img class="vr-raven-coin" src="/manga/emblem.png" alt="" />',
19367
19481
  ChatAgentResponse(meta, message.text, Boolean(message.isError), message.providerId, message.releaseId, message.kind, index, messageId, message.cards, { showPlan: demo.fixPlanExpandedChatIndex === index }));
19368
19482
  }).join('');
19369
19483
  return html;
@@ -19563,7 +19677,7 @@ function StudioChatLane(index) {
19563
19677
  (gateNotClear && introBlocker ? '<button class="vr-start-here-cta" type="button" data-action="start-agent-fix" data-chat-index="' + String(index) + '">' + Icon('bolt') + '<span>Fix it with the connected agent</span></button>' : '') +
19564
19678
  ChatEnvironmentConnectCard();
19565
19679
  const staticIntro = fresh
19566
- ? storyNode('is-agent is-intro', Icon('spark'), '<section class="vr-chat-message is-agent vr-chat-intro-message"><div><span><img src="' + mascotUrl() + '" alt="" /></span><p><strong>VibeRaven Agent</strong><b>Agent</b><em class="is-ready">Ready</em></p></div>' + introBody + '</section>')
19680
+ ? storyNode('is-agent is-intro has-raven', '<img class="vr-raven-coin" src="/manga/emblem.png" alt="" />', '<section class="vr-chat-message is-agent vr-chat-intro-message"><div><span class="vr-intro-raven"><img src="/manga/emblem.png" alt="" /></span><p><strong>VibeRaven Agent</strong><b>Agent</b><em class="is-ready">Ready</em></p></div>' + introBody + '</section>')
19567
19681
  : '';
19568
19682
  return '<article class="vr-chat-lane ' + (secondary ? 'is-secondary' : 'is-primary') + '" data-chat-index="' + String(index) + '">' +
19569
19683
  (secondary ? '<button class="vr-chat-close" type="button" data-action="close-chat" data-chat-index="' + String(index) + '" aria-label="Close chat">' + Icon('x') + '</button>' : '') +
@@ -19735,23 +19849,32 @@ function worstProviderId() {
19735
19849
  return worst ? worst.id : null;
19736
19850
  }
19737
19851
 
19852
+ // Top-left plaque = the live table STATS (real repo/provider data), replacing
19853
+ // the old crimson ribbon. Compact stat cells so it fits the header row.
19738
19854
  function TablePlaque() {
19739
19855
  const gate = (currentState && currentState.gate) || {};
19740
- const stamp = gate.status === 'clear'
19856
+ const gateClear = gate.status === 'clear';
19857
+ const stamp = gateClear
19741
19858
  ? '<span class="vr-stamp is-mini is-clear">RAVEN &middot; CLEAR</span>'
19742
19859
  : gate.status === 'not_clear'
19743
19860
  ? '<span class="vr-stamp is-mini is-notyet">NOT YET</span>'
19744
19861
  : '<span class="vr-stamp is-mini is-idle">NOT SCANNED</span>';
19745
19862
  const detected = providers.filter((item) => item.state !== 'not_detected');
19746
- const flawless = detected.filter((item) => item.grade && item.grade.tier === 'flawless').length;
19747
19863
  const review = detected.filter((item) => item.grade && item.grade.tier === 'review').length;
19748
19864
  const graded = detected.filter((item) => item.grade && typeof item.grade.value === 'number');
19749
19865
  const avg = graded.length ? graded.reduce((sum, item) => sum + item.grade.value, 0) / graded.length : 0;
19750
- return '<div class="vr-table-ribbon" aria-hidden="true">' +
19751
- '<strong>' + escapeHtml(projectName()).toUpperCase() + '</strong>' + stamp +
19752
- '<span class="vr-ribbon-meter" title="Table grade \u2014 the average raven grade of your cards"><i style="width:' + Math.max(3, Math.round(avg * 10)) + '%"></i></span>' +
19753
- '<b>' + (graded.length ? avg.toFixed(1) : '&#8212;') + '</b>' +
19754
- '<em>' + String(detected.length) + ' in play &middot; ' + String(flawless) + ' flawless &middot; ' + String(review) + ' to review</em>' +
19866
+ const dirty = (demo.worktree && Number(demo.worktree.dirtyCount)) || 0;
19867
+ const cells = [
19868
+ { label: 'Providers', value: String(detected.length), tone: 'gold' },
19869
+ { label: 'Grade', value: graded.length ? avg.toFixed(1) : '&#8212;', tone: 'gold' },
19870
+ { label: 'Review', value: String(review), tone: review > 0 ? 'orange' : 'muted' },
19871
+ { label: 'Uncommitted', value: String(dirty), tone: dirty > 0 ? 'orange' : 'green' },
19872
+ ];
19873
+ return '<div class="vr-table-ribbon">' +
19874
+ '<div class="vr-ribbon-brand"><strong>' + escapeHtml(projectName()).toUpperCase() + '</strong>' + stamp + '</div>' +
19875
+ '<div class="vr-ribbon-stats">' +
19876
+ cells.map((c) => '<span class="vr-ribbon-stat is-' + c.tone + '"><b>' + c.value + '</b><i>' + escapeHtml(c.label) + '</i></span>').join('') +
19877
+ '</div>' +
19755
19878
  '</div>';
19756
19879
  }
19757
19880
 
@@ -20567,7 +20690,7 @@ function TableRail() {
20567
20690
  ['worktree', 'repo', 'Worktree'],
20568
20691
  ];
20569
20692
  return '<aside class="vr-left-context-rail is-table-rail" aria-label="Studio navigation">' +
20570
- '<span class="vr-rail-raven" title="VibeRaven"><img src="' + mascotAssets.idle + '" alt="VibeRaven" /></span>' +
20693
+ '<span class="vr-rail-raven is-emblem" title="VibeRaven"><img src="/manga/emblem.png" alt="VibeRaven" /></span>' +
20571
20694
  '<button class="vr-rail-icon is-new" type="button" data-action="new-chat" title="New chat">' + Icon('plus') + '</button>' +
20572
20695
  '<button class="vr-rail-icon' + (demo.chatsFlyoutOpen ? ' is-active' : '') + '" type="button" data-action="toggle-chats-flyout" title="Recent chats">' + Icon('prompt') + '</button>' +
20573
20696
  '<span class="vr-rail-sep" aria-hidden="true"></span>' +
@@ -20664,12 +20787,118 @@ function closeComposerPopovers() {
20664
20787
  demo.dropActive = false;
20665
20788
  }
20666
20789
 
20790
+ // anime.js entrance for the region map: stagger the nodes in when the view opens.
20791
+ // The nodes are positioned with inline transform:translate(x,y), so we animate
20792
+ // OPACITY ONLY (never transform) and clear the inline value on complete. Gated by
20793
+ // a flag that resets when the view leaves, so it plays once per open, not on every
20794
+ // full re-render.
20795
+ let regionMapAnimated = false;
20796
+ function animateRegionMapOnce() {
20797
+ const nodes = app.querySelectorAll('.vr-studio-architecture.is-region-map .vr-arch-node');
20798
+ if (!nodes.length) { regionMapAnimated = false; return; }
20799
+ if (regionMapAnimated || !window.anime || typeof window.anime.animate !== 'function') return;
20800
+ regionMapAnimated = true;
20801
+ const clear = () => nodes.forEach((n) => { n.style.opacity = ''; });
20802
+ try {
20803
+ window.anime.animate(nodes, {
20804
+ opacity: [0, 1],
20805
+ duration: 460,
20806
+ delay: window.anime.stagger(34),
20807
+ ease: 'out(2)',
20808
+ onComplete: clear,
20809
+ });
20810
+ } catch (err) { clear(); }
20811
+ }
20812
+
20813
+ // Release timeline (Versions view): stagger the version cards in when it opens.
20814
+ // The cards carry a --vr-rot rotation transform, so we animate OPACITY ONLY
20815
+ // (translate/scale would clobber the rotation) and clear it on complete. Gated
20816
+ // once per open like the region map.
20817
+ let releaseTimelineAnimated = false;
20818
+ function animateReleaseTimelineOnce() {
20819
+ const spread = app.querySelector('.vr-version-spread');
20820
+ if (!spread) { releaseTimelineAnimated = false; return; }
20821
+ if (releaseTimelineAnimated || !window.anime || typeof window.anime.animate !== 'function') return;
20822
+ const cards = app.querySelectorAll('.vr-version-spread .vr-vcard');
20823
+ if (!cards.length) return;
20824
+ releaseTimelineAnimated = true;
20825
+ const clear = () => cards.forEach((c) => { c.style.opacity = ''; });
20826
+ try {
20827
+ window.anime.animate(cards, {
20828
+ opacity: [0, 1],
20829
+ duration: 440,
20830
+ delay: window.anime.stagger(50),
20831
+ ease: 'out(2)',
20832
+ onComplete: clear,
20833
+ });
20834
+ } catch (err) { clear(); }
20835
+ }
20836
+
20837
+ // Chat view: stagger the existing message rows in when the chat first shows, so
20838
+ // there is visible motion on the default view (opening / refreshing / switching
20839
+ // to chat). Story nodes have no positioning transform, so translateY is safe;
20840
+ // cleared on complete. Gated once per chat-view open (a new deal animates its own
20841
+ // card separately). Reset when leaving chat or when the transcript is empty.
20842
+ let chatEntranceAnimated = false;
20843
+ function animateChatEntranceOnce() {
20844
+ if (demo.activeStudioTab !== 'chat') { chatEntranceAnimated = false; return; }
20845
+ const nodes = app.querySelectorAll('.vr-chat-transcript .vr-story-node');
20846
+ if (!nodes.length) { chatEntranceAnimated = false; return; }
20847
+ if (chatEntranceAnimated || !window.anime || typeof window.anime.animate !== 'function') return;
20848
+ chatEntranceAnimated = true;
20849
+ const clear = () => nodes.forEach((n) => { n.style.opacity = ''; n.style.transform = ''; });
20850
+ try {
20851
+ window.anime.animate(nodes, {
20852
+ opacity: [0, 1],
20853
+ translateY: [14, 0],
20854
+ duration: 440,
20855
+ delay: window.anime.stagger(60),
20856
+ ease: 'out(2)',
20857
+ onComplete: clear,
20858
+ });
20859
+ } catch (err) { clear(); }
20860
+ }
20861
+
20862
+ // Card dealt into chat: fade + scale + lift the newest provider-card message in.
20863
+ // Called only from dealProviderCard (not the generic render) so it plays once on
20864
+ // deal, not on the later check-complete re-render.
20865
+ function animateDealtCardIntoChat() {
20866
+ if (!window.anime || typeof window.anime.animate !== 'function') return;
20867
+ const cards = app.querySelectorAll('.vr-card-message');
20868
+ const el = cards[cards.length - 1];
20869
+ if (!el) return;
20870
+ const clear = () => {
20871
+ el.style.opacity = '';
20872
+ el.style.transform = '';
20873
+ el.style.scale = '';
20874
+ el.style.rotate = '';
20875
+ el.style.translate = '';
20876
+ el.style.transformOrigin = '';
20877
+ };
20878
+ try {
20879
+ el.style.transformOrigin = '18% 60%';
20880
+ window.anime.animate(el, {
20881
+ opacity: [0, 1],
20882
+ scale: [0.55, 1],
20883
+ translateY: [46, 0],
20884
+ translateX: [-30, 0],
20885
+ rotate: [-7, 0],
20886
+ duration: 720,
20887
+ ease: 'out(3)',
20888
+ onComplete: clear,
20889
+ });
20890
+ } catch (err) { clear(); }
20891
+ }
20892
+
20667
20893
  function render(options) {
20668
20894
  const opts = options || {};
20669
20895
  const scrollState = opts.preserveScroll === false ? null : readChatScrollState();
20670
20896
  stableModalRender = Boolean(demo.activeModal && demo.activeModal === lastRenderedModal);
20671
20897
  app.innerHTML = VibeShell();
20672
20898
  window.requestAnimationFrame(() => {
20899
+ animateRegionMapOnce();
20900
+ animateReleaseTimelineOnce();
20901
+ animateChatEntranceOnce();
20673
20902
  if (opts.scrollToBottom != null) {
20674
20903
  restoreChatScrollState({}, { scrollToBottom: opts.scrollToBottom });
20675
20904
  } else if (scrollState) {
@@ -22085,6 +22314,7 @@ app.addEventListener('pointerdown', (event) => {
22085
22314
  offsetX: event.clientX - rect.left,
22086
22315
  offsetY: event.clientY - rect.top,
22087
22316
  width: rect.width,
22317
+ height: rect.height,
22088
22318
  moved: false,
22089
22319
  };
22090
22320
  });
@@ -22098,7 +22328,9 @@ app.addEventListener('pointermove', (event) => {
22098
22328
  tableDragState.moved = true;
22099
22329
  const ghost = tableDragState.el.cloneNode(true);
22100
22330
  ghost.classList.add('vr-drag-ghost');
22331
+ ghost.classList.remove('is-placed', 'is-hand');
22101
22332
  ghost.style.width = tableDragState.width + 'px';
22333
+ ghost.style.height = tableDragState.height + 'px';
22102
22334
  document.body.appendChild(ghost);
22103
22335
  tableDragState.ghost = ghost;
22104
22336
  tableDragState.el.classList.add('is-drag-source');
@@ -22438,8 +22670,18 @@ app.addEventListener('scroll', (event) => {
22438
22670
  }, true);
22439
22671
 
22440
22672
  loadRecentChats();
22673
+ // Persistence: on refresh/reopen, restore the most recent chat so work is never lost.
22441
22674
  try {
22442
- if (!window.localStorage.getItem('vrOnboarded') && !currentState.empty && !demo.activeModal && window.location.search.indexOf('shot') < 0) {
22675
+ const lastChat = recentChats[0];
22676
+ if (lastChat && Array.isArray(lastChat.messages) && lastChat.messages.length) {
22677
+ demo.activeRecentChatId = lastChat.id;
22678
+ demo.activeChatCount = 1;
22679
+ demo.chatContexts = [restoreRecentChat(lastChat)];
22680
+ syncDemoFromChatContext(demo.chatContexts[0]);
22681
+ }
22682
+ } catch { /* restore is best-effort */ }
22683
+ try {
22684
+ if (!window.localStorage.getItem('vrOnboarded') && !demo.activeRecentChatId && !currentState.empty && !demo.activeModal && window.location.search.indexOf('shot') < 0) {
22443
22685
  demo.activeModal = 'quickstart';
22444
22686
  } else if (
22445
22687
  !window.localStorage.getItem('vrPackOpened') && !currentState.empty && !demo.activeModal &&
@@ -22497,28 +22739,30 @@ var init_appCss = __esm({
22497
22739
  "src/local-ui/static/appCss.ts"() {
22498
22740
  "use strict";
22499
22741
  localUiCss = `
22742
+ @import url('https://fonts.googleapis.com/css2?family=Anton&display=swap');
22500
22743
  :root {
22501
- --vr-bg: #170709;
22502
- --vr-bg-2: #241012;
22503
- --vr-panel: rgba(40, 16, 18, 0.78);
22504
- --vr-panel-2: rgba(52, 22, 24, 0.7);
22744
+ --vr-bg: #0b0b0d;
22745
+ --vr-bg-2: #141418;
22746
+ --vr-panel: rgba(20, 20, 24, 0.8);
22747
+ --vr-panel-2: rgba(30, 30, 35, 0.72);
22505
22748
  --vr-border: rgba(232, 193, 90, 0.26);
22506
22749
  --vr-border-strong: rgba(232, 193, 90, 0.55);
22507
- --vr-text: #f7f2e9;
22508
- --vr-muted: #c49585;
22750
+ --vr-text: #f4f1e8;
22751
+ --vr-muted: #a89a86;
22509
22752
  --vr-purple: #e8c15a;
22510
22753
  --vr-purple-2: #ffd76e;
22511
22754
  --vr-blue: #8fa7ff;
22512
22755
  --vr-green: #22c55e;
22513
22756
  --vr-red: #ef4444;
22514
22757
  --vr-orange: #ffb84d;
22515
- --vr-glow-purple: 0 12px 32px rgba(0, 0, 0, 0.38);
22516
- --vr-glow-green: 0 12px 32px rgba(0, 0, 0, 0.38);
22758
+ --vr-glow-purple: 0 12px 32px rgba(0, 0, 0, 0.5);
22759
+ --vr-glow-green: 0 12px 32px rgba(0, 0, 0, 0.5);
22517
22760
  --vr-accent: #e8c15a;
22518
22761
  --vr-gold: #e8c15a;
22519
22762
  --vr-gold-bright: #ffe9ad;
22520
- --vr-plate: rgba(12, 6, 5, 0.85);
22763
+ --vr-plate: rgba(10, 10, 12, 0.85);
22521
22764
  --vr-foil: linear-gradient(135deg, #fff3c4, #c98f2e 35%, #ffe9a8 55%, #8a5a1a 82%, #ffd76e);
22765
+ --mg-display: 'Anton', 'Arial Narrow', sans-serif;
22522
22766
  }
22523
22767
 
22524
22768
  * { box-sizing: border-box; }
@@ -22527,6 +22771,8 @@ body {
22527
22771
  margin: 0;
22528
22772
  overflow: hidden;
22529
22773
  background: var(--vr-bg);
22774
+ background-image: radial-gradient(rgba(255, 255, 255, 0.045) 1px, transparent 1.4px);
22775
+ background-size: 6px 6px;
22530
22776
  color: var(--vr-text);
22531
22777
  font-family: Geist, Inter, "Segoe UI", ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, sans-serif;
22532
22778
  font-size: 14px;
@@ -22581,7 +22827,7 @@ a i[data-icon] {
22581
22827
  place-items: center;
22582
22828
  border: 1px solid rgba(148, 163, 184, 0.16);
22583
22829
  border-radius: 8px;
22584
- background: rgba(40, 14, 16, 0.72);
22830
+ background: rgba(24, 22, 28, 0.72);
22585
22831
  color: #ffe9ad;
22586
22832
  }
22587
22833
  .vr-context-icon-chip.is-input {
@@ -22618,7 +22864,7 @@ a i[data-icon] {
22618
22864
  .vr-bottom-tip-bar {
22619
22865
  border: 1px solid var(--vr-border);
22620
22866
  background:
22621
- linear-gradient(180deg, rgba(40, 14, 16, 0.68), rgba(22, 7, 9, 0.7)),
22867
+ linear-gradient(180deg, rgba(24, 22, 28, 0.68), rgba(13, 12, 16, 0.7)),
22622
22868
  var(--vr-panel);
22623
22869
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.025);
22624
22870
  }
@@ -22659,7 +22905,7 @@ a i[data-icon] {
22659
22905
  background:
22660
22906
  radial-gradient(circle at 42% 26%, rgba(34, 197, 94, 0.16), transparent 36%),
22661
22907
  radial-gradient(circle at 68% 76%, rgba(232, 193, 90, 0.5), transparent 54%),
22662
- linear-gradient(145deg, rgba(40, 14, 16, 0.98), rgba(22, 7, 9, 0.8));
22908
+ linear-gradient(145deg, rgba(24, 22, 28, 0.98), rgba(13, 12, 16, 0.8));
22663
22909
  box-shadow:
22664
22910
  inset 0 1px 0 rgba(255, 255, 255, 0.08),
22665
22911
  0 0 28px rgba(0, 0, 0, 0);
@@ -22857,7 +23103,7 @@ a i[data-icon] {
22857
23103
  border-radius: 7px;
22858
23104
  background:
22859
23105
  radial-gradient(circle at 34% 20%, rgba(255, 255, 255, 0.16), transparent 34%),
22860
- rgba(40, 14, 16, 0.78);
23106
+ rgba(24, 22, 28, 0.78);
22861
23107
  }
22862
23108
 
22863
23109
  .vr-inline-brand-mark img {
@@ -22870,7 +23116,7 @@ a i[data-icon] {
22870
23116
  .vr-workspace-switcher > span {
22871
23117
  width: 28px;
22872
23118
  height: 28px;
22873
- border: 2px solid rgba(22, 7, 9, 0.9);
23119
+ border: 2px solid rgba(13, 12, 16, 0.9);
22874
23120
  border-radius: 999px;
22875
23121
  background:
22876
23122
  radial-gradient(circle at 35% 28%, #f8fafc, transparent 10%),
@@ -22899,7 +23145,7 @@ a i[data-icon] {
22899
23145
  border-radius: 9px;
22900
23146
  background:
22901
23147
  linear-gradient(180deg, rgba(148, 163, 184, 0.050), rgba(14, 165, 233, 0.04)),
22902
- rgba(22, 7, 9, 0.34);
23148
+ rgba(13, 12, 16, 0.34);
22903
23149
  padding: 7px 8px;
22904
23150
  }
22905
23151
 
@@ -23051,7 +23297,7 @@ a i[data-icon] {
23051
23297
  border-width: 0 0 1px;
23052
23298
  border-radius: 0;
23053
23299
  padding: 0 14px;
23054
- background: rgba(22, 7, 9, 0.52);
23300
+ background: rgba(13, 12, 16, 0.52);
23055
23301
  }
23056
23302
  .vr-topbar-brand {
23057
23303
  display: inline-grid;
@@ -23366,7 +23612,7 @@ a i[data-icon] {
23366
23612
  border-width: 0 0 1px;
23367
23613
  border-radius: 0;
23368
23614
  padding: 0 28px;
23369
- background: rgba(22, 7, 9, 0.5);
23615
+ background: rgba(13, 12, 16, 0.5);
23370
23616
  }
23371
23617
 
23372
23618
  .vr-project-id {
@@ -23762,7 +24008,7 @@ body:has(.vr-app.is-dragging) {
23762
24008
  .vr-new-chat-button kbd {
23763
24009
  border: 1px solid rgba(226, 232, 240, 0.12);
23764
24010
  border-radius: 6px;
23765
- background: rgba(22, 7, 9, 0.5);
24011
+ background: rgba(13, 12, 16, 0.5);
23766
24012
  color: #cbd5e1;
23767
24013
  padding: 2px 6px;
23768
24014
  font: 10px/1.2 "JetBrains Mono", "SFMono-Regular", Consolas, monospace;
@@ -24115,7 +24361,7 @@ body:has(.vr-app.is-dragging) {
24115
24361
  margin: 8px 0 0;
24116
24362
  padding: 8px 10px;
24117
24363
  border-radius: 8px;
24118
- background: rgba(22, 7, 9, 0.72);
24364
+ background: rgba(13, 12, 16, 0.72);
24119
24365
  overflow: auto;
24120
24366
  }
24121
24367
  .vr-cli-setup-step pre code {
@@ -24653,10 +24899,26 @@ body:has(.vr-app.is-dragging) {
24653
24899
  min-height: 32px;
24654
24900
  padding: 0 12px;
24655
24901
  }
24656
- .vr-studio-chat:not(.is-split) .vr-chat-command-bar {
24657
- padding: 6px 0 0;
24902
+ /* Align the composer column with the chat messages. The transcript is a centered
24903
+ max-width column (min(1440px, 100% - 64px)) with 54px side padding, and messages
24904
+ are inset a further ~40px by the agent-avatar gutter. Match all of that here so
24905
+ the composer lines up with the message cards at every viewport width. Higher
24906
+ specificity (.vr-chat-lane) than the responsive command-bar padding passes. */
24907
+ .vr-studio-chat:not(.is-split) .vr-chat-lane .vr-chat-command-bar {
24908
+ width: min(1440px, calc(100% - 64px));
24909
+ max-width: min(1440px, calc(100% - 64px));
24910
+ margin-inline: auto;
24911
+ justify-self: center;
24912
+ padding: 6px 54px 10px 94px;
24658
24913
  align-self: end;
24659
24914
  }
24915
+ /* Size the composer to the same width as the chat messages and left-align it, so
24916
+ both edges line up with the cards (which are min(1120px, 100%)). */
24917
+ .vr-studio-chat:not(.is-split) .vr-chat-lane .vr-chat-command-bar > .vr-chat-composer {
24918
+ width: min(1120px, 100%);
24919
+ max-width: min(1120px, 100%);
24920
+ justify-self: start;
24921
+ }
24660
24922
  .vr-studio-chat:not(.is-split) .vr-chat-command-bar > .vr-chat-composer {
24661
24923
  min-height: 92px;
24662
24924
  border-color: rgba(226, 232, 240, 0.16);
@@ -24711,7 +24973,7 @@ body:has(.vr-app.is-dragging) {
24711
24973
  height: 39px;
24712
24974
  object-fit: contain;
24713
24975
  transform: translateY(4px);
24714
- filter: drop-shadow(0 0 10px rgba(40, 14, 16, 0.75));
24976
+ filter: drop-shadow(0 0 10px rgba(24, 22, 28, 0.75));
24715
24977
  }
24716
24978
  .vr-chat-transcript > header strong,
24717
24979
  .vr-chat-transcript > header em {
@@ -25093,7 +25355,7 @@ body:has(.vr-app.is-dragging) {
25093
25355
  margin: 0;
25094
25356
  padding: 8px 10px;
25095
25357
  border-radius: 8px;
25096
- background: rgba(22, 7, 9, 0.72);
25358
+ background: rgba(13, 12, 16, 0.72);
25097
25359
  overflow: auto;
25098
25360
  }
25099
25361
  .vr-cli-connect-expand pre code {
@@ -25161,7 +25423,7 @@ body:has(.vr-app.is-dragging) {
25161
25423
  margin: 0;
25162
25424
  padding: 14px 16px;
25163
25425
  border-radius: 12px;
25164
- background: rgba(22, 7, 9, 0.82);
25426
+ background: rgba(13, 12, 16, 0.82);
25165
25427
  border: 1px solid rgba(226, 232, 240, 0.1);
25166
25428
  }
25167
25429
  .vr-studio-terminal-log code {
@@ -25186,7 +25448,7 @@ body:has(.vr-app.is-dragging) {
25186
25448
  padding: 6px 12px;
25187
25449
  border-radius: 999px;
25188
25450
  border: 1px solid rgba(226, 232, 240, 0.14);
25189
- background: rgba(40, 14, 16, 0.72);
25451
+ background: rgba(24, 22, 28, 0.72);
25190
25452
  color: #e2e8f0;
25191
25453
  font-size: 12px;
25192
25454
  cursor: pointer;
@@ -25336,10 +25598,17 @@ body:has(.vr-app.is-dragging) {
25336
25598
  position: relative;
25337
25599
  z-index: 1;
25338
25600
  display: grid;
25339
- grid-template-columns: 18px auto minmax(0, 1fr);
25601
+ grid-template-columns: 18px auto auto minmax(0, 1fr);
25340
25602
  align-items: center;
25341
25603
  gap: 8px;
25342
25604
  }
25605
+ .vr-task-elapsed {
25606
+ font-family: var(--vr-hud-mono);
25607
+ font-size: 12px;
25608
+ font-variant-numeric: tabular-nums;
25609
+ color: var(--vr-gold);
25610
+ letter-spacing: 0.04em;
25611
+ }
25343
25612
  .vr-task-glyph {
25344
25613
  position: relative;
25345
25614
  width: 14px;
@@ -25453,6 +25722,11 @@ body:has(.vr-app.is-dragging) {
25453
25722
  display: inline;
25454
25723
  margin-left: 4px;
25455
25724
  }
25725
+ /* compact header only has 3 columns; the redundant title (<b>) would wrap to a
25726
+ clipped second row ("...nges"), so drop it \u2014 the status word already says it. */
25727
+ .vr-chat-command-bar > .vr-chat-agent-task.is-compact header b {
25728
+ display: none;
25729
+ }
25456
25730
  .vr-chat-agent-response {
25457
25731
  max-width: min(920px, 82%);
25458
25732
  display: grid;
@@ -25519,7 +25793,7 @@ body:has(.vr-app.is-dragging) {
25519
25793
  border-radius: 12px;
25520
25794
  background:
25521
25795
  radial-gradient(circle at 0% 0%, rgba(148, 163, 184, 0.050), transparent 42%),
25522
- rgba(40, 14, 16, 0.62);
25796
+ rgba(24, 22, 28, 0.62);
25523
25797
  padding: 10px 12px;
25524
25798
  min-width: 0;
25525
25799
  }
@@ -25551,7 +25825,7 @@ body:has(.vr-app.is-dragging) {
25551
25825
  border-color: rgba(34, 197, 94, 0.28);
25552
25826
  background:
25553
25827
  radial-gradient(circle at 0% 0%, rgba(34, 197, 94, 0.12), transparent 42%),
25554
- rgba(40, 14, 16, 0.62);
25828
+ rgba(24, 22, 28, 0.62);
25555
25829
  }
25556
25830
  .vr-agent-gate-strip[data-tone="clear"] svg {
25557
25831
  color: #86efac;
@@ -25560,7 +25834,7 @@ body:has(.vr-app.is-dragging) {
25560
25834
  border-color: rgba(248, 113, 113, 0.28);
25561
25835
  background:
25562
25836
  radial-gradient(circle at 0% 0%, rgba(248, 113, 113, 0.1), transparent 42%),
25563
- rgba(40, 14, 16, 0.62);
25837
+ rgba(24, 22, 28, 0.62);
25564
25838
  }
25565
25839
  .vr-agent-gate-strip[data-tone="blocked"] svg {
25566
25840
  color: #fca5a5;
@@ -25570,7 +25844,7 @@ body:has(.vr-app.is-dragging) {
25570
25844
  border-radius: 12px;
25571
25845
  background:
25572
25846
  radial-gradient(circle at 0% 0%, rgba(148, 163, 184, 0.050), transparent 42%),
25573
- rgba(40, 14, 16, 0.55);
25847
+ rgba(24, 22, 28, 0.55);
25574
25848
  padding: 12px 14px;
25575
25849
  display: grid;
25576
25850
  gap: 8px;
@@ -25656,7 +25930,7 @@ body:has(.vr-app.is-dragging) {
25656
25930
  .vr-agent-answer-body code {
25657
25931
  border: 1px solid rgba(148, 163, 184, 0.18);
25658
25932
  border-radius: 6px;
25659
- background: rgba(40, 14, 16, 0.72);
25933
+ background: rgba(24, 22, 28, 0.72);
25660
25934
  padding: 1px 6px;
25661
25935
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
25662
25936
  font-size: 12px;
@@ -26030,7 +26304,7 @@ body:has(.vr-app.is-dragging) {
26030
26304
  width: fit-content;
26031
26305
  border: 1px solid rgba(148, 163, 184, 0.22);
26032
26306
  border-radius: 999px;
26033
- background: rgba(40, 14, 16, 0.86);
26307
+ background: rgba(24, 22, 28, 0.86);
26034
26308
  color: #e2e8f0;
26035
26309
  padding: 7px 12px;
26036
26310
  }
@@ -26141,7 +26415,7 @@ body:has(.vr-app.is-dragging) {
26141
26415
  .vr-provider-drop-zone > .vr-provider-drop-label {
26142
26416
  border: 1px dashed rgba(148, 163, 184, 0.22);
26143
26417
  border-radius: 999px;
26144
- background: rgba(22, 7, 9, 0.2);
26418
+ background: rgba(13, 12, 16, 0.2);
26145
26419
  padding: 5px 10px;
26146
26420
  }
26147
26421
  .vr-provider-drop-zone.is-active > .vr-provider-drop-label {
@@ -26879,7 +27153,7 @@ body:has(.vr-app.is-dragging) {
26879
27153
  place-items: center;
26880
27154
  border: 1px solid rgba(120, 145, 190, 0.18);
26881
27155
  border-radius: 10px;
26882
- background: rgba(22, 7, 9, 0.44);
27156
+ background: rgba(13, 12, 16, 0.44);
26883
27157
  color: var(--vr-text);
26884
27158
  transition:
26885
27159
  transform 180ms cubic-bezier(0.23, 1, 0.32, 1),
@@ -27076,7 +27350,7 @@ body:has(.vr-app.is-dragging) {
27076
27350
  .vr-provider-card:hover .vr-provider-proof-chip,
27077
27351
  .vr-provider-card.is-selected .vr-provider-proof-chip {
27078
27352
  border-color: rgba(248, 250, 252, 0.22);
27079
- background: rgba(22, 7, 9, 0.72);
27353
+ background: rgba(13, 12, 16, 0.72);
27080
27354
  }
27081
27355
  .vr-provider-proof-chip svg {
27082
27356
  width: 13px;
@@ -27405,7 +27679,7 @@ body:has(.vr-app.is-dragging) {
27405
27679
  border: 1px solid rgba(120, 145, 190, 0.15);
27406
27680
  border-radius: 9px;
27407
27681
  background:
27408
- linear-gradient(180deg, rgba(40, 14, 16, 0.64), rgba(8, 13, 24, 0.7));
27682
+ linear-gradient(180deg, rgba(24, 22, 28, 0.64), rgba(8, 13, 24, 0.7));
27409
27683
  color: var(--vr-text);
27410
27684
  padding: 8px 10px;
27411
27685
  text-align: left;
@@ -27652,7 +27926,7 @@ body:has(.vr-app.is-dragging) {
27652
27926
  border-radius: 999px;
27653
27927
  background:
27654
27928
  radial-gradient(circle at 50% 50%, rgba(34, 197, 94, 0.22), transparent 46%),
27655
- rgba(40, 14, 16, 0.78);
27929
+ rgba(24, 22, 28, 0.78);
27656
27930
  box-shadow: 0 0 14px rgba(34, 197, 94, 0.2);
27657
27931
  }
27658
27932
  .vr-agent-will span i::before {
@@ -27801,7 +28075,7 @@ body:has(.vr-app.is-dragging) {
27801
28075
  gap: 7px;
27802
28076
  border: 1px solid rgba(120, 145, 190, 0.2);
27803
28077
  border-radius: 9px;
27804
- background: rgba(22, 7, 9, 0.38);
28078
+ background: rgba(13, 12, 16, 0.38);
27805
28079
  color: #dbeafe;
27806
28080
  padding: 0 10px;
27807
28081
  text-decoration: none;
@@ -27912,9 +28186,13 @@ body:has(.vr-app.is-dragging) {
27912
28186
  radial-gradient(circle at 72% 16%, rgba(34, 197, 94, 0.12), transparent 24%),
27913
28187
  linear-gradient(rgba(148, 163, 184, 0.03) 1px, transparent 1px),
27914
28188
  linear-gradient(90deg, rgba(148, 163, 184, 0.03) 1px, transparent 1px),
27915
- rgba(1, 4, 13, 0.78);
28189
+ rgba(1, 4, 13, 0.86);
27916
28190
  background-size: auto, auto, 42px 42px, 42px 42px, auto;
27917
- backdrop-filter: blur(18px);
28191
+ /* NOTE: no backdrop-filter here. A full-viewport backdrop-filter: blur()
28192
+ renders as SOLID BLACK on many real Windows GPU/driver combos (Intel/ANGLE),
28193
+ blacking out the whole Studio whenever a modal (e.g. the welcome modal) is
28194
+ open. Headless/software raster hides the bug. The dark base layer above
28195
+ gives the same focus without the GPU hazard. Do not re-add blur here. */
27918
28196
  animation: vrModalFade 180ms ease-out both;
27919
28197
  }
27920
28198
 
@@ -27933,7 +28211,7 @@ body:has(.vr-app.is-dragging) {
27933
28211
  radial-gradient(circle at 80% 15%, rgba(148, 163, 184, 0.050), transparent 27%),
27934
28212
  radial-gradient(circle at 19% 8%, rgba(56, 189, 248, 0.12), transparent 24%),
27935
28213
  radial-gradient(circle at 74% 70%, rgba(34, 197, 94, 0.08), transparent 28%),
27936
- linear-gradient(180deg, rgba(13, 20, 43, 0.98), rgba(22, 7, 9, 0.995));
28214
+ linear-gradient(180deg, rgba(13, 20, 43, 0.98), rgba(13, 12, 16, 0.995));
27937
28215
  box-shadow:
27938
28216
  0 0 0 1px rgba(255, 233, 173, 0.12),
27939
28217
  0 0 70px rgba(0, 0, 0, 0),
@@ -27959,7 +28237,7 @@ body:has(.vr-app.is-dragging) {
27959
28237
  }
27960
28238
 
27961
28239
  .vr-modal-card::-webkit-scrollbar-thumb {
27962
- border: 3px solid rgba(22, 7, 9, 0.92);
28240
+ border: 3px solid rgba(13, 12, 16, 0.92);
27963
28241
  border-radius: 999px;
27964
28242
  background: rgba(148, 163, 184, 0.4);
27965
28243
  }
@@ -28043,7 +28321,7 @@ body:has(.vr-app.is-dragging) {
28043
28321
  border: 1px solid rgba(96, 122, 177, 0.24);
28044
28322
  border-radius: 18px;
28045
28323
  background:
28046
- linear-gradient(90deg, rgba(148, 163, 184, 0.050), rgba(8, 17, 34, 0.48) 46%, rgba(40, 14, 16, 0.08)),
28324
+ linear-gradient(90deg, rgba(148, 163, 184, 0.050), rgba(8, 17, 34, 0.48) 46%, rgba(24, 22, 28, 0.08)),
28047
28325
  radial-gradient(circle at 86% 42%, rgba(148, 163, 184, 0.050), transparent 32%);
28048
28326
  padding: 22px 34px 22px 22px;
28049
28327
  padding-right: 34px;
@@ -28318,7 +28596,7 @@ body:has(.vr-app.is-dragging) {
28318
28596
  .vr-modal-checks li.pending > span,
28319
28597
  .vr-modal-tasklist li.pending > span {
28320
28598
  border-color: rgba(96, 122, 177, 0.7);
28321
- background: rgba(40, 14, 16, 0.5);
28599
+ background: rgba(24, 22, 28, 0.5);
28322
28600
  box-shadow: none;
28323
28601
  }
28324
28602
 
@@ -28411,7 +28689,7 @@ body:has(.vr-app.is-dragging) {
28411
28689
  border: 1px solid rgba(96, 122, 177, 0.28);
28412
28690
  border-radius: 16px;
28413
28691
  background:
28414
- linear-gradient(180deg, rgba(40, 14, 16, 0.86), rgba(7, 16, 33, 0.94));
28692
+ linear-gradient(180deg, rgba(24, 22, 28, 0.86), rgba(7, 16, 33, 0.94));
28415
28693
  padding: 18px;
28416
28694
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.03);
28417
28695
  }
@@ -28621,7 +28899,7 @@ body:has(.vr-app.is-dragging) {
28621
28899
  margin: 0 0 10px;
28622
28900
  border: 1px solid rgba(148, 163, 184, 0.14);
28623
28901
  border-radius: 12px;
28624
- background: rgba(40, 14, 16, 0.38);
28902
+ background: rgba(24, 22, 28, 0.38);
28625
28903
  padding: 10px 12px;
28626
28904
  }
28627
28905
 
@@ -28653,7 +28931,7 @@ body:has(.vr-app.is-dragging) {
28653
28931
  align-items: start;
28654
28932
  padding: 9px 11px;
28655
28933
  border-radius: 10px;
28656
- background: rgba(40, 14, 16, 0.38);
28934
+ background: rgba(24, 22, 28, 0.38);
28657
28935
  border: 1px solid rgba(148, 163, 184, 0.13);
28658
28936
  color: #e2e8f0;
28659
28937
  line-height: 1.35;
@@ -28859,7 +29137,7 @@ body:has(.vr-app.is-dragging) {
28859
29137
  border-radius: 12px;
28860
29138
  background:
28861
29139
  radial-gradient(circle at 0% 50%, rgba(148, 163, 184, 0.050), transparent 36%),
28862
- rgba(22, 7, 9, 0.5);
29140
+ rgba(13, 12, 16, 0.5);
28863
29141
  color: var(--vr-text);
28864
29142
  padding: 0 16px;
28865
29143
  text-align: left;
@@ -28901,7 +29179,7 @@ body:has(.vr-app.is-dragging) {
28901
29179
  border-radius: 14px;
28902
29180
  background:
28903
29181
  radial-gradient(circle at 86% 0%, rgba(148, 163, 184, 0.050), transparent 34%),
28904
- linear-gradient(180deg, rgba(40, 14, 16, 0.96), rgba(22, 7, 9, 0.96));
29182
+ linear-gradient(180deg, rgba(24, 22, 28, 0.96), rgba(13, 12, 16, 0.96));
28905
29183
  padding: 18px;
28906
29184
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.04);
28907
29185
  }
@@ -30346,7 +30624,7 @@ body:has(.vr-app.is-dragging) {
30346
30624
  border-color: rgba(239, 68, 68, 0.38);
30347
30625
  background:
30348
30626
  radial-gradient(circle at 100% 0%, rgba(239, 68, 68, 0.12), transparent 210px),
30349
- linear-gradient(135deg, rgba(31, 14, 18, 0.9), rgba(13, 16, 24, 0.94));
30627
+ linear-gradient(135deg, rgba(20, 19, 24, 0.9), rgba(13, 16, 24, 0.94));
30350
30628
  }
30351
30629
 
30352
30630
  .vr-chat-agent-response > div:first-child {
@@ -31812,13 +32090,19 @@ div.vr-branch-row {
31812
32090
  painted once as body background layers (no extra composited layers).
31813
32091
  PERF: tiles stay >= 22px and no backdrop-filter, ever. */
31814
32092
  body {
31815
- background-color: var(--vr-bg);
32093
+ /* manga skin: clean ink base. The full-bleed speed-line art was removed \u2014
32094
+ it read as disconnected wallpaper behind the chat. Just a fine screentone
32095
+ dot grid + a faint gold glow at the top edge. */
32096
+ background-color: #0a0a0d;
31816
32097
  background-image:
31817
- radial-gradient(circle, rgba(255, 210, 110, 0.05) 1.2px, transparent 1.5px),
31818
- radial-gradient(ellipse 65% 50% at 22% 0%, rgba(255, 80, 70, 0.2), transparent 65%),
31819
- radial-gradient(ellipse 50% 45% at 90% 38%, rgba(255, 170, 60, 0.14), transparent 65%),
31820
- radial-gradient(ellipse 55% 40% at 55% 108%, rgba(200, 40, 50, 0.14), transparent 65%);
31821
- background-size: 24px 24px, 100% 100%, 100% 100%, 100% 100%;
32098
+ radial-gradient(circle, rgba(255, 255, 255, 0.035) 1.2px, transparent 1.5px),
32099
+ radial-gradient(ellipse 72% 46% at 50% -6%, rgba(232, 193, 90, 0.05), transparent 62%);
32100
+ background-size: 24px 24px, 100% 100%;
32101
+ background-position: 0 0, center;
32102
+ background-repeat: repeat, no-repeat;
32103
+ /* scroll, not fixed: body never scrolls (overflow:hidden) so it looks
32104
+ identical, and fixed-attachment on the large art stalls headless capture. */
32105
+ background-attachment: scroll, scroll, scroll;
31822
32106
  }
31823
32107
 
31824
32108
  /* Glow buttons become solid, quiet primaries. */
@@ -33795,7 +34079,7 @@ body {
33795
34079
  ========================================================================== */
33796
34080
  :root {
33797
34081
  --vr-hud-mono: "JetBrains Mono", ui-monospace, SFMono-Regular, Consolas, monospace;
33798
- --vr-hud-surface: rgba(23, 7, 9, 0.92);
34082
+ --vr-hud-surface: rgba(13, 12, 16, 0.92);
33799
34083
  --vr-hud-line: rgba(232, 193, 90, 0.16);
33800
34084
  }
33801
34085
 
@@ -33911,6 +34195,20 @@ body {
33911
34195
  word-break: break-word;
33912
34196
  }
33913
34197
 
34198
+ /* Raw transcript guard: agent dumped grep/tool output instead of a clean answer.
34199
+ Show a themed headline + collapse the raw lines into the work log. */
34200
+ .vr-agent-answer-raw { display: flex; flex-direction: column; gap: 6px; }
34201
+ .vr-agent-raw-headline {
34202
+ margin: 0;
34203
+ font-weight: 700;
34204
+ color: var(--vr-gold-bright);
34205
+ }
34206
+ .vr-agent-raw-sub {
34207
+ margin: 0 0 4px;
34208
+ font-size: 12px;
34209
+ color: var(--vr-muted);
34210
+ }
34211
+
33914
34212
  /* Retire legacy state styling: the frame never changes with state.
33915
34213
  Only the three non-rail sides \u2014 border-left stays the signal color. */
33916
34214
  .vr-chat-agent-response.is-ready,
@@ -34824,7 +35122,7 @@ button.vr-chat-gate-chip.vr-stamp i { display: none; }
34824
35122
  Provider dots and version chips are untouched by design.
34825
35123
  ========================================================================== */
34826
35124
  :root {
34827
- --vr-display: "Bahnschrift SemiCondensed", "Bahnschrift", "Avenir Next Condensed", "Arial Narrow", "Segoe UI", sans-serif;
35125
+ --vr-display: "Anton", "Bahnschrift SemiCondensed", "Bahnschrift", "Avenir Next Condensed", "Arial Narrow", "Segoe UI", sans-serif;
34828
35126
  --vr-ink: rgba(232, 193, 90, 0.6);
34829
35127
  }
34830
35128
 
@@ -34903,13 +35201,15 @@ button.vr-chat-gate-chip.vr-stamp i { display: none; }
34903
35201
  HUD layer: full conversion \u2014 the display voice and flat tags everywhere.
34904
35202
  ========================================================================== */
34905
35203
 
34906
- /* Every button in the shell speaks the display voice (stamps stay mono). */
35204
+ /* Buttons default to the READABLE sans (was condensed display caps \u2014 the #1
35205
+ readability complaint). Marquee buttons (tabs, big CTAs) opt back into Anton
35206
+ via their own higher-specificity rules in the readability pass. */
34907
35207
  .vr-composed-shell button:not(.vr-stamp),
34908
35208
  .vr-modal-card button:not(.vr-stamp),
34909
35209
  .vr-composed-shell .vr-agent-inline-action {
34910
- font-family: var(--vr-display);
34911
- text-transform: uppercase;
34912
- letter-spacing: 0.05em;
35210
+ font-family: var(--vr-hud-sans, 'Geist', 'Inter', system-ui, -apple-system, 'Segoe UI', sans-serif);
35211
+ text-transform: none;
35212
+ letter-spacing: 0.01em;
34913
35213
  }
34914
35214
 
34915
35215
  /* Kill the pill: every badge/chip/tag becomes a flat mono tag.
@@ -35288,7 +35588,7 @@ button.vr-tcard { appearance: none; }
35288
35588
  border: 2.5px solid #a8743a;
35289
35589
  background:
35290
35590
  radial-gradient(circle at 50% 42%, rgba(232, 193, 90, 0.12), transparent 60%),
35291
- repeating-linear-gradient(45deg, #241012, #241012 7px, #2e1517 7px, #2e1517 14px);
35591
+ repeating-linear-gradient(45deg, #141418, #141418 7px, #1b1b22 7px, #1b1b22 14px);
35292
35592
  align-items: center;
35293
35593
  justify-content: center;
35294
35594
  gap: 6px;
@@ -35343,10 +35643,10 @@ button.vr-tcard { appearance: none; }
35343
35643
  }
35344
35644
 
35345
35645
  .vr-tcard[data-foil='sentry'] {
35346
- background: linear-gradient(160deg, #ffe3ea, #c2405e 30%, #ffd4de 52%, #7a1c30 78%, #ffd76e);
35646
+ background: linear-gradient(160deg, #ffe3ea, #c2405e 30%, #ffd4de 52%, #343040 78%, #ffd76e);
35347
35647
  }
35348
35648
  .vr-tcard[data-foil='sentry'] .vr-tcard-artwin {
35349
- background: radial-gradient(circle at 50% 42%, #5a2033, #170a10 78%);
35649
+ background: radial-gradient(circle at 50% 42%, #2b2732, #170a10 78%);
35350
35650
  }
35351
35651
  .vr-tcard[data-foil='sentry'] .vr-tcard-artwin .vr-provider-token.is-tcard {
35352
35652
  filter: drop-shadow(0 0 9px #ff8fa8);
@@ -35494,7 +35794,7 @@ button.vr-tcard { appearance: none; }
35494
35794
  place-items: center;
35495
35795
  border: 1.5px solid var(--vr-border);
35496
35796
  border-radius: 10px;
35497
- background: rgba(23, 7, 9, 0.55);
35797
+ background: rgba(13, 12, 16, 0.55);
35498
35798
  color: var(--vr-muted);
35499
35799
  cursor: pointer;
35500
35800
  transition: border-color 100ms ease, color 100ms ease, transform 100ms ease;
@@ -35557,7 +35857,7 @@ button.vr-tcard { appearance: none; }
35557
35857
  padding: 12px;
35558
35858
  border: 2.5px solid var(--vr-gold);
35559
35859
  border-radius: 50%;
35560
- background: radial-gradient(circle at 40% 35%, #3a1216, #170709);
35860
+ background: radial-gradient(circle at 40% 35%, #17171c, #0b0b0d);
35561
35861
  box-shadow: 0 0 16px rgba(232, 193, 90, 0.3);
35562
35862
  transition: transform 120ms ease, box-shadow 120ms ease;
35563
35863
  }
@@ -35583,7 +35883,7 @@ button.vr-tcard { appearance: none; }
35583
35883
  border: none;
35584
35884
  border-radius: 0;
35585
35885
  background:
35586
- radial-gradient(ellipse 65% 50% at 25% 0%, rgba(255, 80, 70, 0.2), transparent 65%),
35886
+ radial-gradient(ellipse 65% 50% at 25% 0%, rgba(232, 193, 90, 0.2), transparent 65%),
35587
35887
  radial-gradient(ellipse 50% 45% at 88% 75%, rgba(255, 170, 60, 0.14), transparent 65%),
35588
35888
  var(--vr-bg);
35589
35889
  }
@@ -35663,11 +35963,11 @@ button.vr-tcard { appearance: none; }
35663
35963
  inset: 8px 12px 6px 4px;
35664
35964
  border-radius: 30px;
35665
35965
  background:
35666
- radial-gradient(ellipse 85% 75% at 50% 42%, rgba(66, 20, 24, 0.92), rgba(34, 10, 12, 0.96) 78%),
35667
- #1c080a;
35966
+ radial-gradient(ellipse 85% 75% at 50% 42%, rgba(24, 24, 30, 0.92), rgba(12, 12, 16, 0.96) 78%),
35967
+ #0e0e12;
35668
35968
  border: 2px solid rgba(232, 193, 90, 0.32);
35669
35969
  box-shadow:
35670
- inset 0 0 0 7px rgba(52, 24, 14, 0.95),
35970
+ inset 0 0 0 7px rgba(26, 24, 30, 0.95),
35671
35971
  inset 0 0 0 9px rgba(232, 193, 90, 0.14),
35672
35972
  inset 0 0 90px rgba(0, 0, 0, 0.55),
35673
35973
  0 24px 60px rgba(0, 0, 0, 0.55);
@@ -35871,7 +36171,7 @@ button.vr-tcard { appearance: none; }
35871
36171
  padding: 10px 14px;
35872
36172
  border: 1.5px solid rgba(255, 184, 77, 0.55);
35873
36173
  border-radius: 10px;
35874
- background: rgba(60, 34, 8, 0.5);
36174
+ background: rgba(34, 30, 24, 0.5);
35875
36175
  color: var(--vr-text);
35876
36176
  font-size: 12.5px;
35877
36177
  }
@@ -36059,7 +36359,7 @@ button.vr-tcard { appearance: none; }
36059
36359
  /* chats flyout: opaque, above the table views, clicks land on it */
36060
36360
  .vr-left-context-rail.is-table-rail { z-index: 60; }
36061
36361
  .vr-chats-flyout {
36062
- background: #1c080a;
36362
+ background: #0e0e12;
36063
36363
  box-shadow: 0 18px 40px rgba(0, 0, 0, 0.75), 0 0 0 1px rgba(232, 193, 90, 0.12);
36064
36364
  z-index: 90;
36065
36365
  }
@@ -36107,7 +36407,7 @@ button.vr-tcard { appearance: none; }
36107
36407
  background:
36108
36408
  linear-gradient(160deg, rgba(232, 193, 90, 0.12), transparent 45%),
36109
36409
  var(--vr-plate);
36110
- box-shadow: 0 10px 22px rgba(0, 0, 0, 0.5), inset 0 0 0 1.5px rgba(20, 8, 6, 0.9), inset 0 0 0 2.5px rgba(232, 193, 90, 0.2);
36410
+ box-shadow: 0 10px 22px rgba(0, 0, 0, 0.5), inset 0 0 0 1.5px rgba(15, 14, 18, 0.9), inset 0 0 0 2.5px rgba(232, 193, 90, 0.2);
36111
36411
  padding: 10px 12px;
36112
36412
  }
36113
36413
  .vr-studio-architecture.is-region-map .vr-arch-node:not(.is-card) strong {
@@ -36117,7 +36417,7 @@ button.vr-tcard { appearance: none; }
36117
36417
  }
36118
36418
  .vr-studio-architecture.is-region-map .vr-arch-node:not(.is-card):hover {
36119
36419
  border-color: var(--vr-gold);
36120
- box-shadow: 0 12px 26px rgba(0, 0, 0, 0.55), 0 0 16px rgba(232, 193, 90, 0.25), inset 0 0 0 1.5px rgba(20, 8, 6, 0.9), inset 0 0 0 2.5px rgba(232, 193, 90, 0.3);
36420
+ box-shadow: 0 12px 26px rgba(0, 0, 0, 0.55), 0 0 16px rgba(232, 193, 90, 0.25), inset 0 0 0 1.5px rgba(15, 14, 18, 0.9), inset 0 0 0 2.5px rgba(232, 193, 90, 0.3);
36121
36421
  }
36122
36422
  /* chat messages wear the card chrome */
36123
36423
  .vr-chat-message.is-agent.vr-hud-card,
@@ -36253,7 +36553,7 @@ button.vr-tcard { appearance: none; }
36253
36553
  background-size: 7px 7px;
36254
36554
  }
36255
36555
  .vr-qs-card[data-step='1'] .vr-qs-art { background: radial-gradient(circle at 50% 42%, #1f5a35, #08170e 78%); }
36256
- .vr-qs-card[data-step='3'] .vr-qs-art { background: radial-gradient(circle at 50% 42%, #5a1c22, #170608 78%); }
36556
+ .vr-qs-card[data-step='3'] .vr-qs-art { background: radial-gradient(circle at 50% 42%, #2b2732, #170608 78%); }
36257
36557
  .vr-qs-card[data-step='4'] .vr-qs-art { background: radial-gradient(circle at 50% 42%, #1c3a5a, #060e17 78%); }
36258
36558
  .vr-qs-glyph { position: relative; }
36259
36559
  .vr-qs-glyph i[data-icon] { width: 30px; height: 30px; color: #ffe9ad; filter: drop-shadow(0 0 9px rgba(255, 233, 173, 0.8)); }
@@ -36440,10 +36740,10 @@ button.vr-tcard { appearance: none; }
36440
36740
  ========================================================================== */
36441
36741
  .vr-studio-architecture.is-region-map {
36442
36742
  background:
36443
- radial-gradient(ellipse 85% 75% at 50% 42%, rgba(66, 20, 24, 0.92), rgba(34, 10, 12, 0.96) 78%),
36444
- #1c080a;
36743
+ radial-gradient(ellipse 85% 75% at 50% 42%, rgba(24, 24, 30, 0.92), rgba(12, 12, 16, 0.96) 78%),
36744
+ #0e0e12;
36445
36745
  box-shadow:
36446
- inset 0 0 0 7px rgba(52, 24, 14, 0.95),
36746
+ inset 0 0 0 7px rgba(26, 24, 30, 0.95),
36447
36747
  inset 0 0 0 9px rgba(232, 193, 90, 0.14),
36448
36748
  inset 0 0 90px rgba(0, 0, 0, 0.55);
36449
36749
  border-radius: 22px;
@@ -36649,7 +36949,7 @@ button.vr-tcard { appearance: none; }
36649
36949
  width: min(460px, 92%);
36650
36950
  border: 2px solid var(--vr-border-strong);
36651
36951
  border-radius: 14px;
36652
- background: #1c080a;
36952
+ background: #0e0e12;
36653
36953
  box-shadow: 0 22px 48px rgba(0, 0, 0, 0.7), 0 0 24px rgba(232, 193, 90, 0.15);
36654
36954
  overflow: hidden;
36655
36955
  }
@@ -36790,32 +37090,54 @@ button.vr-tcard { appearance: none; }
36790
37090
  z-index: 25;
36791
37091
  display: flex;
36792
37092
  align-items: center;
36793
- gap: 10px;
37093
+ gap: 14px;
36794
37094
  margin: 0;
36795
- padding: 4px 14px;
36796
- border: 1.5px solid rgba(232, 193, 90, 0.24);
36797
- border-radius: 999px;
36798
- background: rgba(20, 7, 8, 0.62);
36799
- pointer-events: none;
37095
+ padding: 5px 8px 5px 4px;
37096
+ border: 0;
37097
+ background: none;
36800
37098
  white-space: nowrap;
36801
37099
  max-width: 100%;
36802
37100
  overflow: hidden;
36803
37101
  }
37102
+ .vr-ribbon-brand { display: flex; align-items: center; gap: 8px; }
36804
37103
  .vr-table-ribbon strong {
36805
- font-family: var(--vr-display);
36806
- font-size: 11px;
36807
- font-weight: 800;
36808
- letter-spacing: 2px;
36809
- color: var(--vr-gold-bright);
37104
+ font-family: var(--mg-display);
37105
+ font-size: 13px;
37106
+ font-weight: 400;
37107
+ letter-spacing: 1.5px;
37108
+ color: var(--vr-text);
37109
+ }
37110
+ /* the stats row that replaced the old crimson ribbon text */
37111
+ .vr-ribbon-stats { display: flex; align-items: stretch; gap: 0; }
37112
+ .vr-ribbon-stat {
37113
+ display: flex;
37114
+ flex-direction: column;
37115
+ justify-content: center;
37116
+ padding: 2px 13px;
37117
+ border-left: 1px solid var(--vr-border);
37118
+ }
37119
+ .vr-ribbon-stat:first-child { border-left: 0; }
37120
+ .vr-ribbon-stat b {
37121
+ font-family: var(--mg-display);
37122
+ font-weight: 400;
37123
+ font-size: 16px;
37124
+ line-height: 1;
37125
+ color: var(--vr-text);
36810
37126
  }
36811
- .vr-table-ribbon em {
37127
+ .vr-ribbon-stat i {
36812
37128
  font-style: normal;
37129
+ margin-top: 3px;
36813
37130
  font-family: var(--vr-hud-mono);
36814
- font-size: 8px;
36815
- letter-spacing: 1.4px;
36816
- color: var(--vr-muted);
37131
+ font-size: 8.5px;
37132
+ font-weight: 600;
37133
+ letter-spacing: 0.08em;
36817
37134
  text-transform: uppercase;
37135
+ color: var(--vr-muted);
36818
37136
  }
37137
+ .vr-ribbon-stat.is-gold b { color: var(--vr-gold-bright); }
37138
+ .vr-ribbon-stat.is-green b { color: var(--vr-green); }
37139
+ .vr-ribbon-stat.is-orange b { color: var(--vr-orange); }
37140
+ .vr-ribbon-stat.is-muted b { color: rgba(244, 241, 232, 0.55); }
36819
37141
  .vr-chat-lane.is-primary .vr-chat-transcript { padding-top: 16px; }
36820
37142
 
36821
37143
  /* -- the card dock: a SLIM strip on the table rim, like a real card game.
@@ -37413,7 +37735,7 @@ button.vr-tcard { appearance: none; }
37413
37735
  background:
37414
37736
  radial-gradient(circle at 50% 42%, rgba(232, 193, 90, 0.35), rgba(232, 193, 90, 0.08) 32%, transparent 44%),
37415
37737
  repeating-radial-gradient(circle at 50% 42%, rgba(232, 193, 90, 0.1) 0 2px, transparent 2px 10px),
37416
- radial-gradient(ellipse at 50% 44%, #3a1216, #1c080a 82%);
37738
+ radial-gradient(ellipse at 50% 44%, #17171c, #0e0e12 82%);
37417
37739
  }
37418
37740
  .vr-qs-back img { width: 54px; height: 54px; opacity: 0.9; }
37419
37741
  .vr-qs-back b {
@@ -37514,8 +37836,8 @@ button.vr-tcard { appearance: none; }
37514
37836
  background:
37515
37837
  radial-gradient(circle at 50% 44%, rgba(232, 193, 90, 0.42), rgba(232, 193, 90, 0.1) 36%, transparent 50%),
37516
37838
  repeating-radial-gradient(circle at 50% 44%, rgba(232, 193, 90, 0.1) 0 2px, transparent 2px 11px),
37517
- conic-gradient(from 210deg at 50% 46%, #240c0f, #451519 22%, #2a0d10 48%, #3f1317 74%, #240c0f),
37518
- #1c080a;
37839
+ conic-gradient(from 210deg at 50% 46%, #17171d, #232329 22%, #18181f 48%, #20202a 74%, #17171d),
37840
+ #0e0e12;
37519
37841
  }
37520
37842
  .vr-tcard.is-back::after {
37521
37843
  content: '';
@@ -37572,122 +37894,2300 @@ button.vr-tcard { appearance: none; }
37572
37894
  margin: 0;
37573
37895
  }
37574
37896
  .vr-discover-grid { gap: 40px 14px; padding-bottom: 30px; }
37575
- `;
37576
- }
37577
- });
37578
37897
 
37579
- // src/local-ui/static/appHtml.ts
37580
- function escapeJsonForHtml(value) {
37581
- return JSON.stringify(value).replace(/</g, "\\u003c");
37898
+ /* ==========================================================================
37899
+ manga ink pass \u2014 the display voice becomes Anton. The HUD layer above set
37900
+ headings to Bahnschrift (a generic condensed); this final override swaps the
37901
+ marquee surfaces to the real loaded poster face so the Studio reads manga,
37902
+ not "condensed sans." Buttons and small labels stay Bahnschrift for
37903
+ legibility \u2014 Anton at 11px all-caps turns to mud.
37904
+ ========================================================================== */
37905
+ .vr-composed-shell h1,
37906
+ .vr-composed-shell h2,
37907
+ .vr-composed-shell h3,
37908
+ .vr-modal-card h2,
37909
+ .vr-modal-card h3,
37910
+ .vr-quickstrike header,
37911
+ .vr-rail-launcher strong,
37912
+ .vr-arch-node > div strong,
37913
+ .vr-studio-architecture.is-region-map .vr-arch-node:not(.is-card) strong,
37914
+ .vr-card-portrait strong {
37915
+ font-family: var(--mg-display);
37916
+ text-transform: uppercase;
37917
+ letter-spacing: 0.03em;
37918
+ font-weight: 400;
37919
+ /* faint gold ink bleed so the poster type sits on the felt, not above it */
37920
+ text-shadow: 0 1px 0 rgba(0, 0, 0, 0.55);
37582
37921
  }
37583
- function renderLocalUiHtml(state) {
37584
- return `<!doctype html>
37585
- <html lang="en">
37586
- <head>
37587
- <meta charset="utf-8" />
37588
- <meta name="viewport" content="width=device-width, initial-scale=1" />
37589
- <meta name="theme-color" content="#050816" />
37590
- <title>VibeRaven</title>
37591
- <link rel="icon" type="image/png" href="/report/assets/viberaven-favicon.png?v=raven-cards-3" />
37592
- <link rel="shortcut icon" type="image/png" href="/report/assets/viberaven-favicon.png?v=raven-cards-3" />
37593
- <link rel="apple-touch-icon" href="/report/assets/viberaven-favicon.png?v=raven-cards-3" />
37594
- <link rel="preload" as="image" href="/report/assets/viberaven-mascot.png?v=raven-cards-2" />
37595
- <style>${localUiCss}</style>
37596
- </head>
37597
- <body>
37598
- <div id="vr-app" class="vr-app" aria-label="VibeRaven"></div>
37599
- <script id="vr-state" type="application/json">${escapeJsonForHtml(state)}</script>
37600
- <script>${localUiClientJs}</script>
37601
- </body>
37602
- </html>`;
37922
+ /* Anton has no bold \u2014 its native weight already reads heavy. Give the big
37923
+ section headers a touch more air so the caps don't crowd. */
37924
+ .vr-composed-shell h1 { letter-spacing: 0.02em; }
37925
+
37926
+ /* ==========================================================================
37927
+ manga chat: the VibeRaven voice wears the fierce raven emblem (refs 1 & 3
37928
+ put the raven bust beside the agent output). The coin frames the raven's
37929
+ face out of the transparent emblem; a gold ring seats it as an avatar.
37930
+ ========================================================================== */
37931
+ .vr-story-node.has-raven .vr-story-dot {
37932
+ width: 34px;
37933
+ height: 34px;
37934
+ padding: 0;
37935
+ overflow: hidden;
37936
+ border: 1.5px solid var(--vr-border-strong);
37937
+ background: radial-gradient(circle at 50% 40%, #1a1a1f, #0a0a0d);
37938
+ box-shadow: 0 0 0 3px rgba(0, 0, 0, 0.35), 0 0 12px rgba(232, 193, 90, 0.18);
37939
+ }
37940
+ .vr-raven-coin {
37941
+ width: 100%;
37942
+ height: 100%;
37943
+ object-fit: cover;
37944
+ /* frame the raven's face (right side of the emblem art), not the sign */
37945
+ object-position: 74% 40%;
37946
+ transform: scale(1.55);
37947
+ }
37948
+ .vr-story-node.is-error.has-raven .vr-story-dot { border-color: rgba(239, 68, 68, 0.6); }
37949
+ /* the intro card's inline raven badge */
37950
+ .vr-chat-intro-message .vr-intro-raven {
37951
+ display: inline-grid;
37952
+ place-items: center;
37953
+ width: 30px;
37954
+ height: 30px;
37955
+ border-radius: 50%;
37956
+ overflow: hidden;
37957
+ border: 1.5px solid var(--vr-border-strong);
37958
+ background: radial-gradient(circle at 50% 40%, #1a1a1f, #0a0a0d);
37959
+ }
37960
+ .vr-chat-intro-message .vr-intro-raven img {
37961
+ width: 100%;
37962
+ height: 100%;
37963
+ object-fit: cover;
37964
+ object-position: 74% 40%;
37965
+ transform: scale(1.55);
37603
37966
  }
37604
- var init_appHtml = __esm({
37605
- "src/local-ui/static/appHtml.ts"() {
37606
- "use strict";
37607
- init_appClient();
37608
- init_appCss();
37609
- }
37610
- });
37611
37967
 
37612
- // src/local-ui/server.ts
37613
- function normalizeShellCommand(value) {
37614
- return value.trim().replace(/\s+/g, " ");
37968
+ /* ==========================================================================
37969
+ manga card frame \u2014 match the gold-on-black provider-card asset (refs 3 & 4).
37970
+ Additive over the round-6/7 trading-card anatomy: ink-black inner (kills the
37971
+ last crimson tint at #140a0c), a gold halftone burst behind the glyph, a
37972
+ gold inset double-frame line, and Anton on the card name. Brand-colored
37973
+ glyphs stay (the binder mockup keeps provider colors on the gold frame).
37974
+ ========================================================================== */
37975
+ .vr-tcard:not(.is-back) .vr-tcard-inner {
37976
+ background: #0c0c10;
37977
+ /* the second, inner gold line \u2014 reads as the ornate double frame */
37978
+ box-shadow: inset 0 0 0 1px rgba(232, 193, 90, 0.28);
37979
+ }
37980
+ .vr-tcard:not(.is-back) .vr-tcard-artwin {
37981
+ border-color: rgba(232, 193, 90, 0.32);
37982
+ /* gold burst radiating from behind the provider glyph */
37983
+ background:
37984
+ radial-gradient(circle at 50% 46%, rgba(232, 193, 90, 0.22), rgba(232, 193, 90, 0.05) 42%, transparent 68%),
37985
+ #08080b;
37615
37986
  }
37616
- function resolveApprovedShellCommand(command, requestedCliId) {
37617
- const normalized = normalizeShellCommand(command);
37618
- const candidateIds = requestedCliId ? [requestedCliId] : ["codex", "claude", "gemini"];
37619
- for (const cliId of candidateIds) {
37620
- const setup = CLI_AGENT_SETUP[cliId];
37621
- const install = normalizeShellCommand(setup.installCommand);
37622
- if (normalized === install) {
37623
- return { cliId, step: "install", command: setup.installCommand };
37624
- }
37625
- const signIn = normalizeShellCommand(setup.signInCommand);
37626
- if (normalized === signIn) {
37627
- return { cliId, step: "sign-in", command: setup.signInCommand };
37628
- }
37629
- }
37630
- return null;
37987
+ /* fine gold sunburst rays over the halftone \u2014 the asset's signature shimmer */
37988
+ .vr-tcard:not(.is-back) .vr-tcard-artwin::after {
37989
+ content: '';
37990
+ position: absolute;
37991
+ inset: 0;
37992
+ background:
37993
+ repeating-conic-gradient(from 0deg at 50% 46%,
37994
+ rgba(232, 193, 90, 0.10) 0deg 2deg, transparent 2deg 10deg);
37995
+ -webkit-mask-image: radial-gradient(circle at 50% 46%, #000 0, transparent 70%);
37996
+ mask-image: radial-gradient(circle at 50% 46%, #000 0, transparent 70%);
37997
+ pointer-events: none;
37998
+ z-index: 0;
37631
37999
  }
37632
- async function connectProviderMcp(cwd, providerId) {
37633
- const recipe = MCP_SERVER_RECIPES[providerId];
37634
- if (!recipe) {
37635
- return { ok: false, message: `No known MCP server for ${providerId} yet. Add one manually to .mcp.json.` };
37636
- }
37637
- const configPath = (0, import_node_path30.join)(cwd, ".mcp.json");
37638
- let config = {};
37639
- try {
37640
- const raw = await (0, import_promises23.readFile)(configPath, "utf8");
37641
- const parsed = JSON.parse(raw.replace(/^\uFEFF/, ""));
37642
- if (isRecord4(parsed)) config = parsed;
37643
- } catch {
37644
- }
37645
- const servers = isRecord4(config.mcpServers) ? { ...config.mcpServers } : {};
37646
- if (servers[providerId]) {
37647
- return { ok: true, message: `${providerId} MCP server already configured in .mcp.json.`, path: ".mcp.json" };
37648
- }
37649
- servers[providerId] = recipe.entry;
37650
- const next = { ...config, mcpServers: servers };
37651
- await (0, import_promises23.writeFile)(configPath, `${JSON.stringify(next, null, 2)}
37652
- `, "utf8");
37653
- const suffix = recipe.note ? ` ${recipe.note}` : "";
37654
- return { ok: true, message: `Added ${providerId} MCP server to .mcp.json.${suffix}`, path: ".mcp.json" };
38000
+ .vr-tcard:not(.is-back) .vr-tcard-artwin .vr-provider-token.is-tcard { z-index: 1; }
38001
+ /* card name speaks the poster face */
38002
+ .vr-tcard-head b {
38003
+ font-family: var(--mg-display);
38004
+ font-weight: 400;
38005
+ letter-spacing: 0.03em;
38006
+ color: var(--vr-gold-bright);
37655
38007
  }
37656
- async function defaultScanRunner(flags, positional) {
37657
- const cli = await Promise.resolve().then(() => (init_cli(), cli_exports));
37658
- return cli.runScanCommand(flags, positional);
38008
+ /* corner ticks: four gold L-brackets tucked into the frame corners */
38009
+ .vr-tcard:not(.is-back) .vr-tcard-inner::before {
38010
+ content: '';
38011
+ position: absolute;
38012
+ inset: 3px;
38013
+ pointer-events: none;
38014
+ z-index: 3;
38015
+ background:
38016
+ linear-gradient(var(--vr-gold-bright), var(--vr-gold-bright)) left top,
38017
+ linear-gradient(var(--vr-gold-bright), var(--vr-gold-bright)) left top,
38018
+ linear-gradient(var(--vr-gold-bright), var(--vr-gold-bright)) right top,
38019
+ linear-gradient(var(--vr-gold-bright), var(--vr-gold-bright)) right top,
38020
+ linear-gradient(var(--vr-gold-bright), var(--vr-gold-bright)) left bottom,
38021
+ linear-gradient(var(--vr-gold-bright), var(--vr-gold-bright)) left bottom,
38022
+ linear-gradient(var(--vr-gold-bright), var(--vr-gold-bright)) right bottom,
38023
+ linear-gradient(var(--vr-gold-bright), var(--vr-gold-bright)) right bottom;
38024
+ background-repeat: no-repeat;
38025
+ background-size: 10px 2px, 2px 10px;
38026
+ opacity: 0.9;
38027
+ }
38028
+
38029
+ /* THE collector redesign: the frame is always GOLD FOIL (like the binder
38030
+ mockup + the gold-on-black provider-card asset). Provider colour lives only
38031
+ in the art-window burst and the glyph glow \u2014 not the frame. This is what
38032
+ turns the cards from "casino colored rectangle" into "premium graded slab". */
38033
+ .vr-tcard[data-foil]:not(.is-back) {
38034
+ background: var(--vr-foil);
37659
38035
  }
37660
- async function readPackageJson(cwd) {
37661
- try {
37662
- const raw = await (0, import_promises23.readFile)((0, import_node_path30.join)(cwd, "package.json"), "utf8");
37663
- const parsed = JSON.parse(raw.replace(/^\uFEFF/, ""));
37664
- return isRecord4(parsed) ? parsed : {};
37665
- } catch {
37666
- return {};
37667
- }
38036
+ .vr-tcard:not(.is-back) {
38037
+ border-radius: 12px;
38038
+ box-shadow:
38039
+ 0 10px 24px rgba(0, 0, 0, 0.6),
38040
+ inset 0 0 0 1px rgba(0, 0, 0, 0.4),
38041
+ inset 0 0 0 2px rgba(255, 233, 173, 0.35);
37668
38042
  }
37669
- function dependencyNames(packageJson) {
37670
- const names = /* @__PURE__ */ new Set();
37671
- for (const key of ["dependencies", "devDependencies", "peerDependencies", "optionalDependencies"]) {
37672
- const value = packageJson[key];
37673
- if (!isRecord4(value)) continue;
37674
- Object.keys(value).forEach((name) => names.add(name.toLowerCase()));
37675
- }
37676
- return names;
38043
+ .vr-tcard:not(.is-back):hover {
38044
+ box-shadow:
38045
+ 0 16px 32px rgba(0, 0, 0, 0.65),
38046
+ 0 0 20px rgba(232, 193, 90, 0.4),
38047
+ inset 0 0 0 1px rgba(0, 0, 0, 0.4),
38048
+ inset 0 0 0 2px rgba(255, 233, 173, 0.5);
37677
38049
  }
37678
- async function listProjectFiles(cwd, maxFiles = 1800) {
37679
- const skipped = /* @__PURE__ */ new Set([".git", "node_modules", ".next", "dist", "build", "coverage", ".turbo", ".viberaven"]);
37680
- const files = [];
37681
- async function walk(dir, prefix = "") {
37682
- if (files.length >= maxFiles) return;
37683
- let entries;
37684
- try {
37685
- entries = await (0, import_promises23.readdir)(dir, { withFileTypes: true });
37686
- } catch {
37687
- return;
37688
- }
37689
- for (const entry of entries) {
37690
- if (files.length >= maxFiles) return;
38050
+ /* the RAVEN GRADE line reads like a graded-slab label bar */
38051
+ .vr-tcard-grade {
38052
+ color: #241207;
38053
+ background: linear-gradient(180deg, #f6eed9, #e2d0a8);
38054
+ border-radius: 3px;
38055
+ padding: 2px 6px;
38056
+ font-weight: 700;
38057
+ }
38058
+
38059
+ /* ==========================================================================
38060
+ manga hero watermark \u2014 the empty chat void gets a faint raven emblem
38061
+ bleeding in from the right, like the faded raven behind the mockup chat.
38062
+ Fills dead space and carries the manga presence without hurting readability.
38063
+ ========================================================================== */
38064
+ .vr-chat-lane.is-primary { position: relative; }
38065
+ /* keep the messages above any lane background */
38066
+ .vr-chat-lane.is-primary > * { position: relative; z-index: 1; }
38067
+
38068
+ /* ==========================================================================
38069
+ manga agent message \u2014 the screenshot-5 styling on REAL answers: a fierce
38070
+ raven avatar, an "AI AGENT" badge, and a faded raven bleeding behind the
38071
+ message (like the eye behind the mockup chat). No fake data.
38072
+ ========================================================================== */
38073
+ .vr-chat-agent-response { position: relative; overflow: hidden; }
38074
+ .vr-chat-agent-response::before {
38075
+ content: '';
38076
+ position: absolute;
38077
+ right: -70px;
38078
+ top: 50%;
38079
+ transform: translateY(-50%);
38080
+ width: 340px;
38081
+ height: 340px;
38082
+ background: url('/manga/emblem.png') no-repeat center / contain;
38083
+ opacity: 0.06;
38084
+ pointer-events: none;
38085
+ z-index: 0;
38086
+ }
38087
+ .vr-chat-agent-response > * { position: relative; z-index: 1; }
38088
+ .vr-hud-avatar.is-raven {
38089
+ width: 34px;
38090
+ height: 34px;
38091
+ border-radius: 50%;
38092
+ overflow: hidden;
38093
+ border: 1.5px solid var(--vr-border-strong);
38094
+ background: radial-gradient(circle at 50% 40%, #1a1a1f, #0a0a0d);
38095
+ box-shadow: 0 0 12px rgba(232, 193, 90, 0.18);
38096
+ flex: none;
38097
+ }
38098
+ .vr-hud-avatar.is-raven img {
38099
+ width: 100%;
38100
+ height: 100%;
38101
+ object-fit: cover;
38102
+ object-position: 74% 40%;
38103
+ transform: scale(1.55);
38104
+ }
38105
+ .vr-hud-aibadge {
38106
+ font-family: var(--vr-hud-mono);
38107
+ font-size: 8.5px;
38108
+ font-weight: 700;
38109
+ letter-spacing: 0.12em;
38110
+ text-transform: uppercase;
38111
+ color: var(--vr-gold-bright);
38112
+ padding: 2px 7px;
38113
+ border-radius: 5px;
38114
+ background: rgba(232, 193, 90, 0.12);
38115
+ box-shadow: inset 0 0 0 1px rgba(232, 193, 90, 0.4);
38116
+ }
38117
+
38118
+ /* ==========================================================================
38119
+ full-art provider cards + holographic foil (the "shiny Pok\xE9mon" effect).
38120
+ The card art (assets/manga/cards/<id>.webp) is the whole face; an
38121
+ iridescent foil + a specular glare sweep ride on top and light up on hover.
38122
+ ========================================================================== */
38123
+ .vr-tcard.has-art {
38124
+ padding: 0;
38125
+ border: none;
38126
+ overflow: hidden;
38127
+ border-radius: 11px;
38128
+ background: #000;
38129
+ box-shadow:
38130
+ 0 10px 24px rgba(0, 0, 0, 0.6),
38131
+ inset 0 0 0 1px rgba(255, 233, 173, 0.3);
38132
+ }
38133
+ .vr-tcard.has-art .vr-tcard-artfull {
38134
+ display: block;
38135
+ width: 100%;
38136
+ height: 100%;
38137
+ object-fit: cover;
38138
+ border-radius: 11px;
38139
+ }
38140
+ /* iridescent holo foil \u2014 always faintly present, blooms on hover */
38141
+ .vr-tcard-holofoil {
38142
+ position: absolute;
38143
+ inset: 0;
38144
+ border-radius: 11px;
38145
+ pointer-events: none;
38146
+ z-index: 2;
38147
+ background: repeating-linear-gradient(115deg,
38148
+ rgba(255, 110, 150, 0.16) 0 7px,
38149
+ rgba(120, 200, 255, 0.16) 7px 14px,
38150
+ rgba(150, 255, 190, 0.16) 14px 21px,
38151
+ rgba(255, 240, 150, 0.16) 21px 28px);
38152
+ background-size: 220% 220%;
38153
+ mix-blend-mode: color-dodge;
38154
+ opacity: 0.16;
38155
+ transition: opacity 220ms ease;
38156
+ }
38157
+ /* gold sparkle glints over the foil */
38158
+ .vr-tcard-holofoil::after {
38159
+ content: '';
38160
+ position: absolute;
38161
+ inset: 0;
38162
+ border-radius: 11px;
38163
+ background:
38164
+ radial-gradient(circle at 28% 22%, rgba(255, 255, 255, 0.55), transparent 9%),
38165
+ radial-gradient(circle at 72% 58%, rgba(255, 233, 173, 0.5), transparent 8%),
38166
+ radial-gradient(circle at 52% 86%, rgba(255, 255, 255, 0.4), transparent 7%);
38167
+ mix-blend-mode: screen;
38168
+ opacity: 0.28;
38169
+ }
38170
+ /* specular glare bar that sweeps across on hover */
38171
+ .vr-tcard-glare {
38172
+ position: absolute;
38173
+ inset: 0;
38174
+ border-radius: 11px;
38175
+ pointer-events: none;
38176
+ z-index: 3;
38177
+ background: linear-gradient(115deg, transparent 36%, rgba(255, 255, 255, 0.3) 48%, rgba(255, 240, 200, 0.22) 54%, transparent 64%);
38178
+ background-size: 250% 250%;
38179
+ background-position: 120% 0%;
38180
+ opacity: 0;
38181
+ transition: opacity 200ms ease;
38182
+ }
38183
+ .vr-tcard.has-art:hover { transform: translateY(-6px) scale(1.04); }
38184
+ .vr-tcard.has-art:hover .vr-tcard-holofoil { opacity: 0.5; animation: vr-holo-shift 2.6s linear infinite; }
38185
+ .vr-tcard.has-art:hover .vr-tcard-glare { opacity: 1; animation: vr-glare-sweep 1.1s ease; }
38186
+ .vr-tcard.has-art.is-flawless { box-shadow: 0 12px 26px rgba(0, 0, 0, 0.6), 0 0 22px rgba(232, 193, 90, 0.5), inset 0 0 0 1px rgba(255, 233, 173, 0.55); }
38187
+ .vr-tcard.has-art.is-flawless .vr-tcard-holofoil { opacity: 0.34; }
38188
+ @keyframes vr-holo-shift { 0% { background-position: 0% 0%; } 100% { background-position: 220% 220%; } }
38189
+ @keyframes vr-glare-sweep { 0% { background-position: 120% 0%; } 100% { background-position: -40% 0%; } }
38190
+ /* grade medallion \u2014 a clean gold-ringed ink coin that suits the ornate art
38191
+ (was a pill with a colored energy dot that looked tacked-on). */
38192
+ .vr-tcard.has-art .vr-tcard-hp.is-onart {
38193
+ position: absolute;
38194
+ top: 7px;
38195
+ right: 7px;
38196
+ z-index: 4;
38197
+ display: grid;
38198
+ place-items: center;
38199
+ width: 21px;
38200
+ height: 21px;
38201
+ padding: 0;
38202
+ border-radius: 50%;
38203
+ background: radial-gradient(circle at 50% 34%, #2b2721, #0b0a0e);
38204
+ box-shadow: inset 0 0 0 1.5px rgba(255, 233, 173, 0.7), 0 1px 3px rgba(0, 0, 0, 0.6);
38205
+ }
38206
+ .vr-tcard.has-art .vr-tcard-hp.is-onart .vr-tcard-energy { display: none; }
38207
+ .vr-tcard.has-art .vr-tcard-hp.is-onart i {
38208
+ font-family: var(--mg-display);
38209
+ font-style: normal;
38210
+ font-size: 12px;
38211
+ line-height: 1;
38212
+ color: var(--vr-gold-bright);
38213
+ }
38214
+ .vr-tcard.is-hand.has-art .vr-tcard-hp.is-onart { width: 17px; height: 17px; top: 5px; right: 5px; }
38215
+ .vr-tcard.is-hand.has-art .vr-tcard-hp.is-onart i { font-size: 9.5px; }
38216
+ /* the baked art already carries the RAVEN GRADE band; hide the overlay strip */
38217
+ .vr-tcard.has-art .vr-tcard-gradebar { display: none; }
38218
+
38219
+ /* the agent message already carries the raven in its header (.vr-hud-avatar)
38220
+ and the intro carries .vr-intro-raven \u2014 so the timeline story-dot drops back
38221
+ to a small plain node instead of a second, redundant raven coin. */
38222
+ .vr-story-node.has-raven .vr-story-dot {
38223
+ width: 12px;
38224
+ height: 12px;
38225
+ margin-top: 16px;
38226
+ border-color: rgba(232, 193, 90, 0.5);
38227
+ background: radial-gradient(circle, var(--vr-gold) 0 2px, rgba(12, 13, 16, 0.96) 3px);
38228
+ box-shadow: 0 0 8px rgba(232, 193, 90, 0.25);
38229
+ }
38230
+ .vr-story-node.has-raven .vr-story-dot .vr-raven-coin { display: none; }
38231
+
38232
+ /* ==========================================================================
38233
+ manga comic panels \u2014 the real restructure. The chat panels were generic
38234
+ rounded-dark rectangles (why it "looked the same"). Now: hard notched
38235
+ corners (comic-panel cut), a bold gold spine on the left, faint gold
38236
+ halftone screentone inside, and a gold tab behind the header label.
38237
+ Applies to the MISSION cards and the VibeRaven agent answers.
38238
+ ========================================================================== */
38239
+ .vr-chat-message.is-user.is-mission,
38240
+ .vr-chat-agent-response {
38241
+ position: relative;
38242
+ border: 1.5px solid var(--vr-border-strong) !important;
38243
+ border-left-width: 3px !important;
38244
+ border-radius: 4px !important;
38245
+ /* two opposite corners cut = comic panel */
38246
+ clip-path: polygon(0 0, calc(100% - 20px) 0, 100% 20px, 100% 100%, 20px 100%, 0 calc(100% - 20px)) !important;
38247
+ background-color: rgba(13, 12, 16, 0.94) !important;
38248
+ background-image:
38249
+ radial-gradient(rgba(232, 193, 90, 0.055) 1px, transparent 1.4px),
38250
+ linear-gradient(160deg, rgba(232, 193, 90, 0.05), transparent 34%) !important;
38251
+ background-size: 9px 9px, auto !important;
38252
+ }
38253
+ /* MISSION panels lean cool-gold, agent panels get the warmer gold spine */
38254
+ .vr-chat-message.is-user.is-mission { border-left-color: var(--vr-gold) !important; }
38255
+ .vr-chat-agent-response.is-blocked { border-left-color: var(--vr-red) !important; }
38256
+ .vr-chat-agent-response.is-nominal { border-left-color: var(--vr-green) !important; }
38257
+
38258
+ /* header labels become inked tabs: MISSION / VIBERAVEN sit on a gold underline */
38259
+ .vr-chat-message.is-user.is-mission .vr-hud-strip strong,
38260
+ .vr-chat-message.is-user.is-mission > header strong,
38261
+ .vr-chat-agent-response .vr-hud-strip strong {
38262
+ position: relative;
38263
+ }
38264
+ .vr-chat-agent-response .vr-hud-strip {
38265
+ border-bottom: 1px solid rgba(232, 193, 90, 0.16);
38266
+ padding-bottom: 7px;
38267
+ margin-bottom: 2px;
38268
+ }
38269
+ /* a small gold corner bracket on the top-left of each panel */
38270
+ .vr-chat-message.is-user.is-mission::after,
38271
+ .vr-chat-agent-response::after {
38272
+ content: '';
38273
+ position: absolute;
38274
+ left: 7px;
38275
+ top: 7px;
38276
+ width: 12px;
38277
+ height: 12px;
38278
+ border-left: 2px solid rgba(232, 193, 90, 0.55);
38279
+ border-top: 2px solid rgba(232, 193, 90, 0.55);
38280
+ pointer-events: none;
38281
+ z-index: 2;
38282
+ }
38283
+
38284
+ /* ==========================================================================
38285
+ composer = a comic panel too (was a soft rounded "old-school" box). Same
38286
+ notched frame + halftone as the chat messages so the column is of-a-piece.
38287
+ ========================================================================== */
38288
+ .vr-chat-lane .vr-chat-command-bar > .vr-chat-composer,
38289
+ .vr-studio-chat:not(.is-split) .vr-chat-lane.is-primary .vr-chat-command-bar > .vr-chat-composer {
38290
+ border: 1.5px solid var(--vr-border-strong) !important;
38291
+ border-radius: 5px !important;
38292
+ /* NO clip-path: the model/command/access menus are descendants and a notch
38293
+ here clipped them when they popped up (looked "corrupted"). */
38294
+ background-color: rgba(13, 12, 16, 0.96) !important;
38295
+ background-image: radial-gradient(rgba(232, 193, 90, 0.045) 1px, transparent 1.4px) !important;
38296
+ background-size: 9px 9px !important;
38297
+ box-shadow: 0 0 24px rgba(232, 193, 90, 0.07) !important;
38298
+ overflow: visible !important;
38299
+ }
38300
+ .vr-chat-lane .vr-chat-command-bar > .vr-chat-composer:focus-within {
38301
+ border-color: var(--vr-gold) !important;
38302
+ box-shadow: 0 0 26px rgba(232, 193, 90, 0.16) !important;
38303
+ }
38304
+
38305
+ /* ==========================================================================
38306
+ the OTHER views join the manga theme: worktree branch cards, the commit
38307
+ banner, and the region-map nodes all become comic panels (notched corners,
38308
+ gold frame, halftone). A faded raven bleeds in from the right of the table
38309
+ views like the warrior in the reference mockup.
38310
+ ========================================================================== */
38311
+ .vr-bcard {
38312
+ position: relative;
38313
+ border: 1.5px solid var(--vr-border-strong) !important;
38314
+ border-radius: 4px !important;
38315
+ clip-path: polygon(0 0, calc(100% - 14px) 0, 100% 14px, 100% 100%, 14px 100%, 0 calc(100% - 14px));
38316
+ background-color: rgba(13, 12, 16, 0.95) !important;
38317
+ background-image: radial-gradient(rgba(232, 193, 90, 0.05) 1px, transparent 1.4px) !important;
38318
+ background-size: 9px 9px !important;
38319
+ }
38320
+ .vr-bcard.is-current {
38321
+ border-color: var(--vr-gold) !important;
38322
+ box-shadow: 0 0 18px rgba(232, 193, 90, 0.18);
38323
+ }
38324
+ .vr-table-warning {
38325
+ position: relative;
38326
+ border: 1.5px solid var(--vr-border-strong) !important;
38327
+ border-left: 3px solid var(--vr-gold) !important;
38328
+ border-radius: 4px !important;
38329
+ clip-path: polygon(0 0, calc(100% - 16px) 0, 100% 16px, 100% 100%, 0 100%);
38330
+ background-color: rgba(13, 12, 16, 0.95) !important;
38331
+ background-image:
38332
+ radial-gradient(rgba(232, 193, 90, 0.05) 1px, transparent 1.4px),
38333
+ linear-gradient(160deg, rgba(232, 193, 90, 0.06), transparent 40%) !important;
38334
+ background-size: 9px 9px, auto !important;
38335
+ }
38336
+ /* region-map + architecture nodes get the comic-panel notch + halftone */
38337
+ .vr-arch-node:not(.is-card) {
38338
+ clip-path: polygon(0 0, calc(100% - 12px) 0, 100% 12px, 100% 100%, 12px 100%, 0 calc(100% - 12px));
38339
+ background-image: radial-gradient(rgba(232, 193, 90, 0.05) 1px, transparent 1.4px) !important;
38340
+ background-size: 8px 8px !important;
38341
+ }
38342
+ /* the raven bleeds in from the right of the worktree/versions/region views */
38343
+ .vr-table-view,
38344
+ .vr-studio-architecture { position: relative; }
38345
+ .vr-table-view::after,
38346
+ .vr-studio-architecture.is-region-map::after {
38347
+ content: '';
38348
+ position: absolute;
38349
+ right: -40px;
38350
+ bottom: -20px;
38351
+ width: 460px;
38352
+ height: 460px;
38353
+ background: url('/manga/emblem.png') no-repeat right bottom / contain;
38354
+ opacity: 0.06;
38355
+ pointer-events: none;
38356
+ z-index: 0;
38357
+ }
38358
+ /* rail logo is the fierce raven, framed to its face */
38359
+ .vr-rail-raven.is-emblem {
38360
+ border-radius: 9px;
38361
+ overflow: hidden;
38362
+ border: 1px solid var(--vr-border-strong);
38363
+ background: radial-gradient(circle at 50% 40%, #1a1a1f, #0a0a0d);
38364
+ }
38365
+ .vr-rail-raven.is-emblem img {
38366
+ width: 100%;
38367
+ height: 100%;
38368
+ object-fit: cover;
38369
+ object-position: 74% 40%;
38370
+ transform: scale(1.5);
38371
+ }
38372
+
38373
+ /* ==========================================================================
38374
+ MANGA THEME PASS \u2014 dropdowns/menus, readability, world background, top bar,
38375
+ modal, chrome. One append block; wins by source order. No !important.
38376
+ ========================================================================== */
38377
+
38378
+ /* ----- Area 1: dropdowns & popup menus (kill the white selected row) ----- */
38379
+ .vr-model-picker > button {
38380
+ border: 1px solid var(--vr-border);
38381
+ border-radius: 5px;
38382
+ background-color: rgba(13, 12, 16, 0.95);
38383
+ color: var(--vr-text);
38384
+ }
38385
+ .vr-model-picker > button span { color: var(--vr-muted); }
38386
+ .vr-model-picker > button strong { color: var(--vr-gold-bright); }
38387
+ .vr-model-picker > button i { border-color: var(--vr-gold); }
38388
+ .vr-model-picker > button:hover { border-color: var(--vr-border-strong); background-color: rgba(20, 18, 12, 0.96); }
38389
+
38390
+ .vr-model-menu,
38391
+ .vr-scope-menu,
38392
+ .vr-chat-command-popover {
38393
+ border: 1px solid var(--vr-border-strong);
38394
+ border-left: 3px solid var(--vr-gold);
38395
+ border-radius: 5px;
38396
+ background-color: rgba(10, 10, 13, 0.99);
38397
+ background-image: radial-gradient(rgba(232, 193, 90, 0.045) 1px, transparent 1.4px);
38398
+ background-size: 9px 9px;
38399
+ box-shadow: 4px 6px 0 rgba(0, 0, 0, 0.55), 0 0 0 1px rgba(0, 0, 0, 0.5);
38400
+ color: var(--vr-text);
38401
+ overflow: hidden;
38402
+ }
38403
+ .vr-model-menu p,
38404
+ .vr-scope-menu p {
38405
+ font-family: var(--mg-display);
38406
+ text-transform: uppercase;
38407
+ letter-spacing: 0.08em;
38408
+ color: var(--vr-gold);
38409
+ }
38410
+ .vr-model-menu button,
38411
+ .vr-scope-menu button,
38412
+ .vr-chat-command-popover button { border-radius: 3px; color: rgba(244, 241, 232, 0.9); }
38413
+ .vr-model-menu button strong { color: rgba(244, 241, 232, 0.95); }
38414
+ .vr-model-menu button em { color: var(--vr-muted); font-size: 11.5px; }
38415
+ .vr-chat-command-popover button.is-primary { color: var(--vr-gold-bright); }
38416
+ .vr-chat-command-popover button.is-muted { color: var(--vr-muted); }
38417
+ .vr-model-menu button:hover,
38418
+ .vr-scope-menu button:hover,
38419
+ .vr-chat-command-popover button:hover {
38420
+ background: rgba(232, 193, 90, 0.12);
38421
+ box-shadow: inset 3px 0 0 var(--vr-gold);
38422
+ color: var(--vr-gold-bright);
38423
+ }
38424
+ /* selected row: gold-tinted ink + spine (JS marks the active row with a <b>) */
38425
+ .vr-model-menu button[aria-selected="true"],
38426
+ .vr-model-menu button.is-active,
38427
+ .vr-model-menu button:has(> b),
38428
+ .vr-scope-menu button:has(> b) {
38429
+ background: rgba(232, 193, 90, 0.16);
38430
+ box-shadow: inset 3px 0 0 var(--vr-gold);
38431
+ }
38432
+ .vr-model-menu button[aria-selected="true"] strong,
38433
+ .vr-model-menu button:has(> b) strong { color: var(--vr-gold-bright); }
38434
+ .vr-model-menu button b,
38435
+ .vr-scope-menu button b { border-color: var(--vr-gold-bright); }
38436
+ /* themed menu scrollbars */
38437
+ .vr-model-menu::-webkit-scrollbar,
38438
+ .vr-scope-menu::-webkit-scrollbar,
38439
+ .vr-chat-command-popover::-webkit-scrollbar { width: 8px; }
38440
+ .vr-model-menu::-webkit-scrollbar-thumb,
38441
+ .vr-scope-menu::-webkit-scrollbar-thumb,
38442
+ .vr-chat-command-popover::-webkit-scrollbar-thumb { background: var(--vr-border-strong); border: 2px solid rgba(13, 12, 16, 0.97); }
38443
+
38444
+ .vr-chat-inline-actions > button,
38445
+ .vr-chat-command-trigger > button {
38446
+ border: 1px solid var(--vr-border);
38447
+ border-radius: 5px;
38448
+ background: rgba(13, 12, 16, 0.7);
38449
+ color: rgba(244, 241, 232, 0.82);
38450
+ }
38451
+ .vr-chat-inline-actions > button:hover,
38452
+ .vr-chat-command-trigger > button:hover {
38453
+ border-color: var(--vr-border-strong);
38454
+ background: rgba(20, 18, 12, 0.85);
38455
+ color: var(--vr-gold-bright);
38456
+ }
38457
+
38458
+ /* ----- Area 2: readability ----- */
38459
+ .vr-chat-command-bar .vr-chat-input { color: var(--vr-text); }
38460
+ .vr-chat-command-bar .vr-chat-input::placeholder { color: var(--vr-muted); }
38461
+ .vr-chat-inline-actions button.is-primary {
38462
+ background: var(--vr-gold);
38463
+ color: #241207;
38464
+ border-color: var(--vr-gold-bright);
38465
+ font-weight: 720;
38466
+ }
38467
+ .vr-chat-inline-actions button.is-muted { opacity: 1; color: var(--vr-muted); font-weight: 650; }
38468
+ .vr-cli-os-fallback { color: var(--vr-muted); text-decoration-color: var(--vr-border-strong); }
38469
+ .vr-cli-os-fallback:hover { color: var(--vr-gold-bright); }
38470
+
38471
+ /* ----- Area 4b: primary CTAs -> gold (after Area 1 so filled-gold wins) ----- */
38472
+ .vr-agent-followups button.is-primary,
38473
+ .vr-agent-inline-action.is-primary,
38474
+ .vr-chat-command-popover button.is-primary,
38475
+ .vr-cli-action-button.is-primary,
38476
+ .vr-studio-architecture > footer button.is-primary,
38477
+ .vr-arch-drawer footer button.is-primary,
38478
+ .vr-wt-actions button.is-primary,
38479
+ .vr-approve-all,
38480
+ .vr-new-chat-button,
38481
+ .vr-provider-ask-agent {
38482
+ background: var(--vr-gold);
38483
+ border-color: var(--vr-gold-bright);
38484
+ color: #0a0b0e;
38485
+ font-family: var(--mg-display);
38486
+ text-transform: uppercase;
38487
+ letter-spacing: 0.04em;
38488
+ }
38489
+ .vr-agent-followups button.is-primary:hover,
38490
+ .vr-agent-inline-action.is-primary:hover,
38491
+ .vr-chat-command-popover button.is-primary:hover,
38492
+ .vr-approve-all:hover,
38493
+ .vr-new-chat-button:hover,
38494
+ .vr-provider-ask-agent:hover { background: var(--vr-gold-bright); border-color: var(--vr-gold-bright); color: #0a0b0e; }
38495
+ .vr-composer-controls .vr-chat-send:not(.is-stop),
38496
+ .vr-chat-command-bar > .vr-chat-composer > .vr-chat-send:not(.is-stop) {
38497
+ background: var(--vr-gold);
38498
+ border-color: var(--vr-gold-bright);
38499
+ color: #0a0b0e;
38500
+ }
38501
+ .vr-composer-controls .vr-chat-send:not(.is-stop):hover,
38502
+ .vr-chat-command-bar > .vr-chat-composer > .vr-chat-send:not(.is-stop):hover { background: var(--vr-gold-bright); border-color: var(--vr-gold-bright); }
38503
+
38504
+ /* ----- Area 3: world background (inked drafting table, lit from above) ----- */
38505
+ body {
38506
+ background-color: #08080a;
38507
+ background-image:
38508
+ radial-gradient(rgba(232, 193, 90, 0.05) 1px, transparent 1.6px),
38509
+ repeating-linear-gradient(52deg, rgba(232, 193, 90, 0.045) 0 1px, transparent 1px 8px),
38510
+ repeating-linear-gradient(-52deg, rgba(232, 193, 90, 0.04) 0 1px, transparent 1px 9px),
38511
+ radial-gradient(ellipse 70% 42% at 50% -8%, rgba(232, 193, 90, 0.07), transparent 60%),
38512
+ radial-gradient(120% 120% at 50% 40%, transparent 58%, rgba(0, 0, 0, 0.55) 100%),
38513
+ linear-gradient(180deg, #101015 0%, #0b0b0e 46%, #08080a 100%);
38514
+ background-size: 9px 9px, 900px 900px, 900px 900px, 100% 100%, 100% 100%, 100% 100%;
38515
+ background-position: 0 0, top right, bottom left, center top, center, center;
38516
+ background-repeat: repeat, no-repeat, no-repeat, no-repeat, no-repeat, no-repeat;
38517
+ background-attachment: scroll;
38518
+ }
38519
+ .vr-composed-shell {
38520
+ background:
38521
+ radial-gradient(ellipse 60% 30% at 50% -4%, rgba(232, 193, 90, 0.06), transparent 60%),
38522
+ radial-gradient(circle at 45% 9%, rgba(232, 193, 90, 0.035), transparent 30%),
38523
+ rgba(11, 12, 15, 0.965);
38524
+ box-shadow:
38525
+ inset 0 1px 0 rgba(255, 233, 173, 0.06),
38526
+ inset 0 0 0 1px rgba(232, 193, 90, 0.05),
38527
+ 0 24px 60px rgba(0, 0, 0, 0.55);
38528
+ }
38529
+ .vr-studio-topbar {
38530
+ background:
38531
+ linear-gradient(180deg, rgba(232, 193, 90, 0.05), transparent 70%),
38532
+ rgba(13, 12, 16, 0.62);
38533
+ }
38534
+
38535
+ /* ----- Area 4a: top tab bar (CHAT/ARCHITECTURE/TERMINAL/DIFF) ----- */
38536
+ .vr-studio-tabs {
38537
+ border: 1px solid var(--vr-border);
38538
+ border-radius: 4px;
38539
+ clip-path: polygon(0 0, calc(100% - 9px) 0, 100% 9px, 100% 100%, 9px 100%, 0 calc(100% - 9px));
38540
+ background: rgba(13, 12, 16, 0.85);
38541
+ overflow: hidden;
38542
+ }
38543
+ .vr-studio-tabs button {
38544
+ font-family: var(--mg-display);
38545
+ text-transform: uppercase;
38546
+ letter-spacing: 0.05em;
38547
+ color: rgba(244, 241, 232, 0.7);
38548
+ }
38549
+ .vr-studio-tabs button:hover { color: var(--vr-gold-bright); background: rgba(232, 193, 90, 0.08); }
38550
+ .vr-studio-tabs button.is-active {
38551
+ background: var(--vr-gold);
38552
+ color: #0a0b0e;
38553
+ border-radius: 3px;
38554
+ box-shadow: inset 0 0 0 1px rgba(255, 233, 173, 0.5);
38555
+ }
38556
+
38557
+ /* ----- Area 4c: modal (ink+gold, not navy/cyan) ----- */
38558
+ .vr-modal-card {
38559
+ border-radius: 4px;
38560
+ clip-path: polygon(0 0, calc(100% - 20px) 0, 100% 20px, 100% 100%, 20px 100%, 0 calc(100% - 20px));
38561
+ background:
38562
+ radial-gradient(ellipse 70% 40% at 50% -4%, rgba(232, 193, 90, 0.06), transparent 60%),
38563
+ radial-gradient(rgba(232, 193, 90, 0.04) 1px, transparent 1.4px),
38564
+ linear-gradient(180deg, rgba(16, 15, 12, 0.99), rgba(13, 12, 16, 0.995));
38565
+ background-size: 100% 100%, 11px 11px, 100%;
38566
+ }
38567
+ .vr-modal-card::before { background: linear-gradient(135deg, rgba(232, 193, 90, 0.08), transparent 28%); }
38568
+ .vr-modal-card::after { background: linear-gradient(90deg, transparent, rgba(232, 193, 90, 0.05), rgba(232, 193, 90, 0.2), transparent); opacity: 0.3; }
38569
+ .vr-modal-backdrop { background: radial-gradient(ellipse 60% 40% at 50% 8%, rgba(232, 193, 90, 0.07), transparent 45%), rgba(4, 5, 8, 0.8); }
38570
+ .vr-modal-card::-webkit-scrollbar-thumb { border: 3px solid rgba(13, 12, 16, 0.92); background: rgba(232, 193, 90, 0.4); }
38571
+
38572
+ /* ----- Area 4d: chrome buttons, terminal input, connect rows ----- */
38573
+ .vr-studio-add-chat,
38574
+ .vr-studio-actions > button:not(.vr-glow-button) {
38575
+ border: 1px solid var(--vr-border);
38576
+ border-radius: 5px;
38577
+ background: rgba(13, 12, 16, 0.7);
38578
+ color: rgba(244, 241, 232, 0.88);
38579
+ }
38580
+ .vr-studio-add-chat:hover,
38581
+ .vr-studio-actions > button:not(.vr-glow-button):hover { border-color: var(--vr-border-strong); background: rgba(232, 193, 90, 0.08); color: var(--vr-gold-bright); }
38582
+ .vr-terminal-compose textarea {
38583
+ border: 1px solid var(--vr-border);
38584
+ border-radius: 4px;
38585
+ clip-path: polygon(0 0, calc(100% - 8px) 0, 100% 8px, 100% 100%, 8px 100%, 0 calc(100% - 8px));
38586
+ background: rgba(13, 12, 16, 0.72);
38587
+ color: rgba(244, 241, 232, 0.92);
38588
+ font-family: var(--vr-hud-mono);
38589
+ }
38590
+ .vr-terminal-compose textarea::placeholder { color: var(--vr-muted); }
38591
+ .vr-terminal-compose textarea:focus { border-color: var(--vr-gold); box-shadow: 0 0 18px rgba(232, 193, 90, 0.14); }
38592
+ .vr-env-connect-option {
38593
+ border: 1px solid var(--vr-border);
38594
+ border-radius: 4px;
38595
+ clip-path: polygon(0 0, calc(100% - 10px) 0, 100% 10px, 100% 100%, 10px 100%, 0 calc(100% - 10px));
38596
+ background: rgba(13, 12, 16, 0.6);
38597
+ color: rgba(244, 241, 232, 0.9);
38598
+ }
38599
+ .vr-env-connect-option:hover { border-color: var(--vr-border-strong); background: rgba(232, 193, 90, 0.07); }
38600
+ .vr-env-connect-option b {
38601
+ border-radius: 3px;
38602
+ background: rgba(232, 193, 90, 0.1);
38603
+ color: var(--vr-gold-bright);
38604
+ font-family: var(--vr-hud-mono);
38605
+ text-transform: uppercase;
38606
+ letter-spacing: 0.04em;
38607
+ }
38608
+
38609
+ /* ----- Area 4e: focus ring ----- */
38610
+ button:focus-visible,
38611
+ a:focus-visible {
38612
+ outline: 2px solid var(--vr-gold);
38613
+ outline-offset: 2px;
38614
+ box-shadow: 0 0 0 4px rgba(232, 193, 90, 0.16);
38615
+ }
38616
+
38617
+ /* ----- Area 5: raven watermarks fade away from text (readability) ----- */
38618
+ .vr-chat-agent-response::before {
38619
+ opacity: 0.05;
38620
+ -webkit-mask-image: linear-gradient(90deg, transparent 0%, #000 62%);
38621
+ mask-image: linear-gradient(90deg, transparent 0%, #000 62%);
38622
+ }
38623
+ .vr-table-view > *,
38624
+ .vr-studio-architecture.is-region-map > * { position: relative; z-index: 1; }
38625
+ .vr-table-view::after,
38626
+ .vr-studio-architecture.is-region-map::after {
38627
+ opacity: 0.05;
38628
+ -webkit-mask-image: radial-gradient(circle at right bottom, #000 28%, transparent 72%);
38629
+ mask-image: radial-gradient(circle at right bottom, #000 28%, transparent 72%);
38630
+ }
38631
+
38632
+ /* ----- placed cards get an always-visible X to clear them off the table.
38633
+ NB: has-art cards have overflow:hidden, so the X must sit INSIDE the card
38634
+ corner (top-left, away from the grade coin) or it gets clipped. ----- */
38635
+ .vr-tcard.is-placed .vr-tcard-unplace,
38636
+ .vr-vcard.is-mini.is-placed .vr-tcard-unplace {
38637
+ opacity: 1;
38638
+ width: 21px;
38639
+ height: 21px;
38640
+ top: 5px;
38641
+ left: 5px;
38642
+ right: auto;
38643
+ z-index: 6;
38644
+ background: rgba(6, 6, 8, 0.92);
38645
+ border: 1.5px solid var(--vr-gold);
38646
+ color: var(--vr-gold-bright);
38647
+ font-size: 11px;
38648
+ box-shadow: 0 2px 6px rgba(0, 0, 0, 0.7);
38649
+ transition: transform 0.1s ease, background 0.1s ease, color 0.1s ease;
38650
+ }
38651
+ .vr-tcard.is-placed:hover .vr-tcard-unplace,
38652
+ .vr-vcard.is-mini.is-placed:hover .vr-tcard-unplace { opacity: 1; }
38653
+ .vr-tcard.is-placed .vr-tcard-unplace:hover,
38654
+ .vr-vcard.is-mini.is-placed .vr-tcard-unplace:hover {
38655
+ background: var(--vr-red);
38656
+ border-color: var(--vr-red);
38657
+ color: #fff;
38658
+ transform: scale(1.12);
38659
+ }
38660
+
38661
+
38662
+ /* ==========================================================================
38663
+ MANGA B&W CONTRAST PASS \u2014 appended AFTER "MANGA THEME PASS" so it wins by
38664
+ source order. Direction: blacker blacks, whiter/cream ink, crisp near-white
38665
+ hairlines, screentone dots for grey; gold pulled back to an ACCENT only
38666
+ (spines, selected rails, small marks). Semantic red kept for errors.
38667
+ De-duplicated + conflict-resolved (higher contrast wins). No !important
38668
+ except where an existing same-file !important rule must be overridden.
38669
+ ========================================================================== */
38670
+
38671
+ /* ---------- (1) B&W CONTRAST BASE ---------- */
38672
+
38673
+ /* Retone root tokens: fixes the majority of muted labels + gold-alpha edges
38674
+ in one place. --vr-muted lifted off the dim tan; borders become near-white
38675
+ ink hairlines; gold moves to an explicit accent hook. */
38676
+ :root {
38677
+ --vr-bg: #050506;
38678
+ --vr-bg-2: #0e0e11;
38679
+ --vr-text: #f7f4ec;
38680
+ --vr-muted: #c4bcaf;
38681
+ --vr-border: rgba(238, 232, 220, 0.16);
38682
+ --vr-border-strong: rgba(238, 232, 220, 0.34);
38683
+ --vr-panel: rgba(9, 9, 11, 0.92);
38684
+ --vr-panel-2: rgba(14, 14, 17, 0.9);
38685
+ --vr-hud-surface: rgba(6, 6, 8, 0.94);
38686
+ --vr-hud-line: rgba(238, 232, 220, 0.14);
38687
+ --vr-accent-line: rgba(232, 193, 90, 0.55);
38688
+ }
38689
+
38690
+ /* World background: drop the gold cross-hatch haze for black paper + faint
38691
+ neutral screentone + vignette. */
38692
+ body {
38693
+ background-color: #050506;
38694
+ background-image:
38695
+ radial-gradient(rgba(244, 241, 232, 0.028) 1px, transparent 1.5px),
38696
+ radial-gradient(ellipse 68% 40% at 50% -8%, rgba(255, 255, 255, 0.035), transparent 62%),
38697
+ radial-gradient(130% 120% at 50% 42%, transparent 56%, rgba(0, 0, 0, 0.72) 100%),
38698
+ linear-gradient(180deg, #0c0c0f 0%, #08080a 48%, #050506 100%);
38699
+ background-size: 8px 8px, 100% 100%, 100% 100%, 100% 100%;
38700
+ background-position: 0 0, center top, center, center;
38701
+ background-repeat: repeat, no-repeat, no-repeat, no-repeat;
38702
+ }
38703
+
38704
+ /* Composed shell: near-flat true-black with one white top hairline. */
38705
+ .vr-composed-shell {
38706
+ background:
38707
+ radial-gradient(ellipse 62% 26% at 50% -3%, rgba(255, 255, 255, 0.04), transparent 60%),
38708
+ rgba(7, 7, 9, 0.975);
38709
+ box-shadow:
38710
+ inset 0 1px 0 rgba(255, 255, 255, 0.07),
38711
+ inset 0 0 0 1px rgba(238, 232, 220, 0.05),
38712
+ 0 24px 60px rgba(0, 0, 0, 0.62);
38713
+ }
38714
+
38715
+ /* Studio topbar: neutral top-light + single thin gold underline accent. */
38716
+ .vr-studio-topbar {
38717
+ background:
38718
+ linear-gradient(180deg, rgba(255, 255, 255, 0.045), transparent 70%),
38719
+ rgba(6, 6, 9, 0.72);
38720
+ border-bottom: 1px solid var(--vr-border);
38721
+ box-shadow: inset 0 -1px 0 var(--vr-accent-line);
38722
+ }
38723
+
38724
+ /* Persistent top-bar readout (was #64748b cold slate, ~3.4:1). */
38725
+ .vr-topbar-spacer { color: var(--vr-muted); font-weight: 780; }
38726
+
38727
+ /* Studio tabs: active tab reads as inked-white paper with a gold underline
38728
+ mark, not a gold slab; idle-tab contrast raised. */
38729
+ .vr-studio-tabs button { color: rgba(244, 241, 232, 0.82); }
38730
+ .vr-studio-tabs button.is-active {
38731
+ background: rgba(244, 241, 232, 0.1);
38732
+ color: #f7f4ec;
38733
+ box-shadow: inset 0 -2px 0 var(--vr-gold), inset 0 0 0 1px rgba(238, 232, 220, 0.14);
38734
+ }
38735
+ .vr-studio-tabs button:hover { color: #ffe9ad; background: rgba(255, 255, 255, 0.05); }
38736
+
38737
+ /* Dropdown / popup menus: clean black sheet (kills the gold halftone),
38738
+ gold only on the left spine + hover/selected rail; item + label contrast up. */
38739
+ .vr-model-menu,
38740
+ .vr-scope-menu,
38741
+ .vr-chat-command-popover {
38742
+ background-color: rgba(5, 5, 7, 0.99);
38743
+ background-image: none;
38744
+ border-color: var(--vr-border-strong);
38745
+ border-left: 3px solid var(--vr-gold);
38746
+ }
38747
+ .vr-model-menu button,
38748
+ .vr-scope-menu button,
38749
+ .vr-chat-command-popover button { color: #f0ece2; }
38750
+ .vr-model-menu button em { color: #c4bcae; }
38751
+ .vr-model-menu button:hover,
38752
+ .vr-scope-menu button:hover,
38753
+ .vr-chat-command-popover button:hover {
38754
+ background: rgba(255, 255, 255, 0.06);
38755
+ box-shadow: inset 3px 0 0 var(--vr-gold);
38756
+ color: #ffe9ad;
38757
+ }
38758
+
38759
+ /* HUD strip (telemetry line): raise the faded cool-grey meta to readable ink. */
38760
+ .vr-hud-strip { color: #c9c1b4; border-bottom-color: rgba(238, 232, 220, 0.12); }
38761
+ .vr-hud-strip strong { color: #f7f4ec; }
38762
+ .vr-hud-strip em { color: #c9c1b4; }
38763
+ .vr-hud-strip b { color: var(--vr-muted); font-weight: 600; } /* merged: was #b0a89b / rgba(156,163,175,.72) */
38764
+
38765
+ /* HUD card chassis: white-ruled black panel; the ONE spot of color is the
38766
+ left rail (semantic status). */
38767
+ .vr-hud-card {
38768
+ background: rgba(6, 6, 8, 0.94);
38769
+ border-color: rgba(238, 232, 220, 0.16);
38770
+ border-left-color: rgba(238, 232, 220, 0.45);
38771
+ }
38772
+ .vr-hud-card.is-nominal { border-left-color: var(--vr-green); }
38773
+ .vr-hud-card.is-blocked { border-left-color: var(--vr-red); }
38774
+ .vr-hud-card.is-working { border-left-color: var(--vr-orange); }
38775
+ .vr-hud-card.is-mission { border-left-color: var(--vr-gold); }
38776
+
38777
+ /* HUD gate row + severity: lift the primary line to full-opacity ink,
38778
+ metadata to solid grey. */
38779
+ .vr-hud-gate { background: rgba(255, 255, 255, 0.02); border-color: rgba(238, 232, 220, 0.14); }
38780
+ .vr-hud-gate header { border-bottom-color: rgba(238, 232, 220, 0.12); }
38781
+ .vr-hud-gate-row { border-bottom-color: rgba(238, 232, 220, 0.09); }
38782
+ .vr-hud-gate-row em { color: #f3efe6; }
38783
+ .vr-hud-gate-row em small { color: #c4bcae; }
38784
+ .vr-hud-gate-row b { color: #b6ae9f; }
38785
+ .vr-hud-sev.is-info { color: #b6ae9f; }
38786
+ .vr-hud-gate-foot {
38787
+ border-top-color: rgba(238, 232, 220, 0.12);
38788
+ background: rgba(255, 255, 255, 0.025);
38789
+ color: #f4f1e8;
38790
+ }
38791
+ .vr-hud-gate-foot b { color: #bcb4a6; }
38792
+ .vr-hud-gate-foot:hover { background: rgba(255, 255, 255, 0.05); }
38793
+ .vr-hud-raw { color: #bcb4a6; }
38794
+ .vr-agent-raw-sub { color: #c4bcae; }
38795
+
38796
+ /* ---------- (2) VERSIONS / WORKTREE / SHUFFLE ---------- */
38797
+
38798
+ /* Table view container + header: solid ink base so cream text pops; header
38799
+ is cream Anton with a hairline rule, gold reserved for the spine accents. */
38800
+ .vr-table-view { background: #0b0b0d !important; }
38801
+ .vr-table-view-head { border-bottom: 1.5px solid var(--vr-border-strong) !important; }
38802
+ .vr-table-view-head strong { color: var(--vr-text) !important; -webkit-text-stroke: 0.4px rgba(0, 0, 0, 0.35); }
38803
+ .vr-table-view-head span { color: var(--vr-muted) !important; }
38804
+
38805
+ /* Big version card: ink plate + white hairline + comic notch (large panel \u2014
38806
+ notch is safe here); gold only as border tint on current. NOTE: base rules
38807
+ at ~13450-13492 use !important, so these must too. */
38808
+ .vr-vcard {
38809
+ border: 1.5px solid var(--vr-border-strong) !important;
38810
+ border-radius: 4px !important;
38811
+ background: rgba(10, 10, 12, 0.96) !important;
38812
+ box-shadow: 0 10px 24px rgba(0, 0, 0, 0.55) !important;
38813
+ clip-path: polygon(0 0, calc(100% - 16px) 0, 100% 16px, 100% 100%, 16px 100%, 0 calc(100% - 16px));
38814
+ }
38815
+ .vr-vcard::before {
38816
+ inset: 4px !important;
38817
+ border-radius: 3px !important;
38818
+ background-color: rgba(11, 11, 13, 0.9) !important;
38819
+ background-image: radial-gradient(rgba(232, 193, 90, 0.05) 1px, transparent 1.4px) !important;
38820
+ background-size: 9px 9px !important;
38821
+ }
38822
+ .vr-vcard:not(.is-current) { filter: none !important; opacity: 0.9; }
38823
+ .vr-vcard.is-current {
38824
+ border-color: var(--vr-gold) !important;
38825
+ border-left: 3px solid var(--vr-gold) !important; /* thin gold spine = "chosen" */
38826
+ box-shadow: 0 10px 24px rgba(0, 0, 0, 0.55), 0 0 16px rgba(232, 193, 90, 0.16) !important;
38827
+ }
38828
+ .vr-vcard-top b,
38829
+ .vr-vpile-card b { color: var(--vr-text) !important; font-family: var(--vr-display) !important; letter-spacing: 0.5px; }
38830
+ /* merged .vr-vcard-meta: prefer --vr-muted (brightest) + the larger 8.5px */
38831
+ .vr-vcard-meta { color: var(--vr-muted) !important; font-size: 8.5px !important; letter-spacing: 0.8px; opacity: 0.95; }
38832
+ .vr-vcard-branch { color: #cfc6b6 !important; font-size: 8.5px !important; }
38833
+
38834
+ /* Version pile (bottom-left floating pile): thin hairline + gold spine, not a
38835
+ thick foil frame. */
38836
+ .vr-vpile-card {
38837
+ border: 1px solid var(--vr-border-strong) !important;
38838
+ border-left: 3px solid var(--vr-gold) !important;
38839
+ background: rgba(11, 11, 13, 0.96) !important;
38840
+ box-shadow: 0 6px 14px rgba(0, 0, 0, 0.5) !important;
38841
+ }
38842
+ .vr-vpile-card.is-top {
38843
+ border-color: var(--vr-gold) !important;
38844
+ box-shadow: 0 6px 14px rgba(0, 0, 0, 0.5), 0 0 12px rgba(232, 193, 90, 0.15) !important;
38845
+ }
38846
+ .vr-vpile-card:not(.is-top) { opacity: 0.8; }
38847
+ .vr-vpile-card em { color: #cfc6b6 !important; }
38848
+ .vr-version-pile-label { color: var(--vr-muted) !important; letter-spacing: 1.8px; }
38849
+
38850
+ /* SHUFFLE button: crisp ink pill + gold hairline; hover = thin gold-tint, not
38851
+ a solid gold slab. */
38852
+ .vr-pile-shuffle {
38853
+ border: 1.5px solid var(--vr-border-strong) !important;
38854
+ border-radius: 4px !important;
38855
+ background: rgba(11, 11, 13, 0.95) !important;
38856
+ color: var(--vr-text) !important;
38857
+ font-family: var(--vr-hud-mono) !important;
38858
+ font-size: 8.5px !important;
38859
+ font-weight: 700;
38860
+ letter-spacing: 1.4px;
38861
+ padding: 4px 10px !important;
38862
+ }
38863
+ .vr-pile-shuffle:hover {
38864
+ background: rgba(232, 193, 90, 0.12) !important;
38865
+ border-color: var(--vr-gold) !important;
38866
+ color: var(--vr-gold-bright) !important;
38867
+ }
38868
+
38869
+ /* Worktree branch card (bcard): gold spine + readable title/sub. */
38870
+ .vr-bcard { border-left: 3px solid var(--vr-gold) !important; }
38871
+ .vr-bcard b { color: var(--vr-text) !important; font-size: 13.5px !important; }
38872
+ .vr-bcard em { color: #cfc6b6 !important; font-size: 9px !important; }
38873
+ .vr-bcard-actions button {
38874
+ border: 1.5px solid var(--vr-border-strong) !important;
38875
+ color: var(--vr-text) !important;
38876
+ background: rgba(11, 11, 13, 0.6) !important;
38877
+ letter-spacing: 1px;
38878
+ }
38879
+ .vr-bcard-actions button:hover {
38880
+ background: rgba(232, 193, 90, 0.12) !important;
38881
+ border-color: var(--vr-gold) !important;
38882
+ color: var(--vr-gold-bright) !important;
38883
+ }
38884
+
38885
+ /* Worktree uncommitted-changes warning: readable body, orange COMMIT button. */
38886
+ .vr-table-warning { color: var(--vr-text) !important; font-size: 13px !important; }
38887
+ .vr-table-warning button { color: var(--vr-orange) !important; font-size: 9.5px !important; letter-spacing: 1.2px; }
38888
+ .vr-table-warning button:hover { background: var(--vr-orange) !important; color: #0b0b0d !important; }
38889
+
38890
+ /* Empty-state + add-version footer button. */
38891
+ .vr-table-empty { color: #cfc6b6 !important; font-size: 11.5px !important; }
38892
+ .vr-table-foot button { color: var(--vr-text) !important; border-color: var(--vr-border-strong) !important; }
38893
+ .vr-table-foot button:hover { background: rgba(232, 193, 90, 0.1) !important; color: var(--vr-gold-bright) !important; }
38894
+
38895
+ /* ---------- (3) CLEAR STAMP FIX (region-map node badge clipped) ---------- */
38896
+ /* The comic-notch clip-path on .vr-arch-node:not(.is-card) clips its
38897
+ corner-mounted .vr-arch-node-badge, cutting "CLEAR" -> "CLEA.". Per the
38898
+ manga rule (never notch anything whose badge reaches a corner), drop the
38899
+ notch on these nodes and keep the ink/halftone look via a hairline. */
38900
+ .vr-arch-node:not(.is-card) {
38901
+ clip-path: none;
38902
+ overflow: visible;
38903
+ border: 1.5px solid var(--vr-border-strong);
38904
+ background-image: radial-gradient(rgba(232, 193, 90, 0.05) 1px, transparent 1.4px);
38905
+ background-size: 8px 8px;
38906
+ }
38907
+ .vr-studio-architecture.is-region-map .vr-arch-node:not(.is-card) .vr-arch-node-badge.vr-stamp.is-mini {
38908
+ overflow: visible;
38909
+ z-index: 3;
38910
+ background: var(--vr-bg);
38911
+ }
38912
+
38913
+ /* ---------- (4) WELCOME MODAL QUICKSTART CARDS (flip bleed-through) ---------- */
38914
+ /* On a DONE card (is-flipped, e.g. SCAN "SETUP \xB7 WON") the back-face mascot +
38915
+ FLIP label bled through because both faces trusted only backface-visibility.
38916
+ Hide the inactive face explicitly (opacity+visibility) and give the flip a
38917
+ fixed height so the four cards stay aligned. Make the back opaque. */
38918
+ .vr-qs-card.is-hero { position: relative; }
38919
+ .vr-qs-card.is-hero [data-flip-card],
38920
+ .vr-qs-card.is-hero .vr-flip-inner { height: 100%; }
38921
+ .vr-qs-card.is-hero .vr-flip-inner { min-height: 318px; }
38922
+ .vr-qs-card .vr-flip-face,
38923
+ .vr-qs-card .vr-flip-back {
38924
+ position: absolute;
38925
+ inset: 0;
38926
+ height: 100%;
38927
+ transition: opacity 200ms ease, visibility 200ms ease;
38928
+ }
38929
+ .vr-qs-card:not(.is-flipped) .vr-flip-face { opacity: 0; visibility: hidden; }
38930
+ .vr-qs-card.is-flipped .vr-flip-face { opacity: 1; visibility: visible; }
38931
+ .vr-qs-card.is-flipped .vr-flip-back { opacity: 0; visibility: hidden; }
38932
+ .vr-qs-card:not(.is-flipped) .vr-flip-back { opacity: 1; visibility: visible; }
38933
+ .vr-qs-card .vr-qs-back { background-color: #0e0e12; }
38934
+ .vr-qs-card.is-hero .vr-qs-card-inner { position: relative; z-index: 1; }
38935
+
38936
+ /* ---------- (5) READABILITY (retone off-palette / low-contrast text) ---------- */
38937
+
38938
+ /* Agent answer body / structured sections: warm near-white cream on the
38939
+ highest-traffic reading surface (was cool blue-greys). */
38940
+ .vr-agent-answer-body,
38941
+ .vr-agent-answer-text { color: #f2efe6; }
38942
+ .vr-agent-answer-body p { color: #eae6db; }
38943
+ .vr-agent-list li,
38944
+ .vr-chat-message p { color: var(--vr-text); } /* merged: cream token wins over #eae6db/#e7e7e9 */
38945
+ .vr-agent-answer-body .vr-agent-empty { color: #bcb4a6; }
38946
+
38947
+ /* Inline code / pill chips: near-white on true-black chip + white hairline,
38948
+ gold only as the faint left tick (was gold body text). */
38949
+ .vr-agent-answer-body code {
38950
+ background: rgba(4, 4, 6, 0.85);
38951
+ border-color: rgba(238, 232, 220, 0.16);
38952
+ border-left: 2px solid var(--vr-gold);
38953
+ color: #f4f1e8;
38954
+ }
38955
+ .vr-chat-message b {
38956
+ border-color: rgba(238, 232, 220, 0.24);
38957
+ background: rgba(255, 255, 255, 0.05);
38958
+ color: #f4f1e8;
38959
+ }
38960
+
38961
+ /* Chat sub-caption / notes / inline em: readable neutral cream-grey. */
38962
+ .vr-chat-message em,
38963
+ .vr-chat-copy { color: #c6bfb2; }
38964
+ .vr-chat-note { color: #cfc8ba; }
38965
+
38966
+ /* Chat bubbles (agent/system telemetry): true-black glass + white hairline +
38967
+ warm-white text (was #1d1e22 grey slab w/ blue-grey borders). */
38968
+ .vr-chat-bubble {
38969
+ background: rgba(7, 7, 9, 0.92);
38970
+ border-color: rgba(238, 232, 220, 0.16);
38971
+ }
38972
+ .vr-chat-bubble.is-system { background: rgba(4, 4, 6, 0.9); border-color: rgba(238, 232, 220, 0.14); }
38973
+ .vr-chat-bubble span { color: #e9e2d5; }
38974
+ .vr-chat-bubble p,
38975
+ .vr-chat-bubble ul { color: #f5f1e8; }
38976
+
38977
+ /* Terminal output: warm near-white body color (was cold #cbd5e1). */
38978
+ .vr-terminal-line { color: #ece7db; }
38979
+
38980
+ /* Region-map / architecture captions + hints (cold slate -> warm muted). */
38981
+ .vr-arch-zone { color: rgba(232, 214, 176, 0.82); font-weight: 800; }
38982
+ .vr-arch-hint { color: var(--vr-muted); }
38983
+ .vr-inspector-body .vr-studio-architecture.is-inspector-map .vr-arch-zone { color: rgba(244, 241, 232, 0.86); }
38984
+
38985
+ /* Card / hero TYPE eyebrows (were 6-7.5px at 0.5-0.55 alpha, illegible). */
38986
+ .vr-tcard-type { color: rgba(244, 241, 232, 0.9); font-size: 8px; letter-spacing: 1px; }
38987
+ .vr-qs-type {
38988
+ color: rgba(244, 241, 232, 0.9);
38989
+ font-size: 9px;
38990
+ position: relative; /* merged w/ Bug-2 stacking so SETUP\xB7WON sits above art */
38991
+ z-index: 2;
38992
+ margin-top: 2px;
38993
+ white-space: nowrap;
38994
+ }
38995
+
38996
+ /* Misc muted secondary lines / captions (cold slate -> warm muted). */
38997
+ .vr-context-chip-label .muted { color: var(--vr-muted); font-weight: 550; }
38998
+ .vr-recent-chat em,
38999
+ .vr-recent-chat small { color: var(--vr-muted); }
39000
+ .vr-provider-drop-zone { color: var(--vr-muted); }
39001
+ .vr-chat-agent-task header em { color: var(--vr-muted); }
39002
+ .vr-provider-mcp-panel em { color: var(--vr-muted); }
39003
+
39004
+ /* Icon-only close/secondary button: readable at rest, gold on hover. */
39005
+ .vr-context-chip-remove { color: var(--vr-muted); }
39006
+ .vr-context-chip-remove:hover { color: var(--vr-gold-bright); }
39007
+
39008
+ /* ==========================================================================
39009
+ LIVE TABLE STATS strip \u2014 real repo/provider metrics on the table (the
39010
+ "Active Users" KPI idea, sourced from real data). Manga stat cards: ink
39011
+ plate, white hairline, gold top-tick accent, big Anton number, mono label.
39012
+ ========================================================================== */
39013
+ .vr-stat-strip {
39014
+ display: grid;
39015
+ grid-template-columns: repeat(6, minmax(0, 1fr));
39016
+ gap: 10px;
39017
+ margin: 0 0 16px;
39018
+ }
39019
+ @media (max-width: 1080px) { .vr-stat-strip { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
39020
+ .vr-stat-card {
39021
+ position: relative;
39022
+ padding: 11px 13px 10px;
39023
+ border: 1px solid var(--vr-border-strong);
39024
+ border-top: 2px solid rgba(238, 232, 220, 0.28);
39025
+ border-radius: 4px;
39026
+ background:
39027
+ radial-gradient(rgba(232, 193, 90, 0.045) 1px, transparent 1.4px),
39028
+ rgba(9, 9, 11, 0.94);
39029
+ background-size: 9px 9px, auto;
39030
+ overflow: hidden;
39031
+ }
39032
+ .vr-stat-card::after {
39033
+ content: '';
39034
+ position: absolute;
39035
+ left: 0; top: 0;
39036
+ width: 26px; height: 2px;
39037
+ background: var(--vr-gold);
39038
+ }
39039
+ .vr-stat-card b {
39040
+ display: block;
39041
+ font-family: var(--mg-display);
39042
+ font-weight: 400;
39043
+ font-size: 27px;
39044
+ line-height: 1;
39045
+ letter-spacing: 0.01em;
39046
+ color: var(--vr-text);
39047
+ }
39048
+ .vr-stat-card.is-word b { font-size: 18px; padding: 4px 0 3px; }
39049
+ .vr-stat-card span {
39050
+ display: block;
39051
+ margin-top: 5px;
39052
+ font-family: var(--vr-hud-mono);
39053
+ font-size: 8.5px;
39054
+ font-weight: 650;
39055
+ letter-spacing: 0.14em;
39056
+ text-transform: uppercase;
39057
+ color: var(--vr-muted);
39058
+ }
39059
+ .vr-stat-card.is-gold b { color: var(--vr-gold-bright); }
39060
+ .vr-stat-card.is-green b { color: var(--vr-green); }
39061
+ .vr-stat-card.is-orange b { color: var(--vr-orange); }
39062
+ .vr-stat-card.is-muted b { color: rgba(244, 241, 232, 0.6); }
39063
+ .vr-stat-card.is-green::after { background: var(--vr-green); }
39064
+ .vr-stat-card.is-orange::after { background: var(--vr-orange); }
39065
+
39066
+ /* ==========================================================================
39067
+ FINAL FIXES \u2014 hand spread + hover, worktree tag clip, placed-card readability
39068
+ ========================================================================== */
39069
+ /* Hand: cards stick together more (was -34 = too spread) so the fan reads as a
39070
+ held hand, with a clean hover lift on a high z so it never hides behind a
39071
+ neighbour. */
39072
+ .vr-hand-fan .vr-tcard { margin-left: -46px; }
39073
+ .vr-hand-fan .vr-tcard:first-child { margin-left: 0; }
39074
+ /* Stable hover: lift STRAIGHT up with almost no scale. A big scale grows the
39075
+ card footprint and flickers against the overlapping neighbours (the "buggy"
39076
+ hover). Keep the same width, just rise + straighten; smooth + GPU-hinted. */
39077
+ .vr-hand-fan .vr-tcard {
39078
+ transition: transform 130ms ease, box-shadow 130ms ease;
39079
+ will-change: transform;
39080
+ }
39081
+ .vr-hand-fan .vr-tcard:hover {
39082
+ transform: rotate(0deg) translateY(-26px) scale(1.02);
39083
+ z-index: 30;
39084
+ }
39085
+
39086
+ /* Worktree branch cards: drop the comic-notch that was slicing the
39087
+ "YOU ARE HERE" tag on the current branch (same clip bug as the region-map
39088
+ stamp). Keep the ink+gold-spine look via border only. */
39089
+ .vr-bcard { clip-path: none; overflow: visible; }
39090
+ .vr-bcard .vr-stamp.is-mini { z-index: 4; }
39091
+
39092
+ /* Placed-card X sits above the holo foil so it is always clickable. */
39093
+ .vr-tcard.is-placed .vr-tcard-holofoil,
39094
+ .vr-tcard.is-placed .vr-tcard-glare { pointer-events: none; }
39095
+
39096
+ /* Placed PROVIDER card X: top-left (grade coin is top-right), small + clean. */
39097
+ .vr-tcard.is-placed .vr-tcard-unplace {
39098
+ width: 17px;
39099
+ height: 17px;
39100
+ top: 5px;
39101
+ left: 5px;
39102
+ right: auto;
39103
+ font-size: 9px;
39104
+ }
39105
+ /* Placed VERSION card X: top-RIGHT (the V1.x label sits top-left), small. */
39106
+ .vr-vcard.is-mini.is-placed .vr-tcard-unplace {
39107
+ opacity: 1;
39108
+ width: 17px;
39109
+ height: 17px;
39110
+ top: 5px;
39111
+ right: 5px;
39112
+ left: auto;
39113
+ font-size: 9px;
39114
+ background: rgba(6, 6, 8, 0.92);
39115
+ border: 1.5px solid var(--vr-gold);
39116
+ color: var(--vr-gold-bright);
39117
+ box-shadow: 0 2px 6px rgba(0, 0, 0, 0.7);
39118
+ }
39119
+ .vr-vcard.is-mini.is-placed .vr-tcard-unplace:hover { background: var(--vr-red); border-color: var(--vr-red); color: #fff; }
39120
+
39121
+ /* Placed-card HOVER must KEEP the translate(-50%,-50%) centering \u2014 the has-art
39122
+ hover rule was replacing it, so the card JUMPED off-position (the "buggy
39123
+ hover"). Gentle lift, keep centered, no snap. */
39124
+ .vr-tcard.is-placed:hover,
39125
+ .vr-tcard.is-placed.has-art:hover {
39126
+ transform: translate(-50%, -50%) rotate(0deg) scale(1.05) !important;
39127
+ z-index: 40;
39128
+ }
39129
+
39130
+ /* ==========================================================================
39131
+ MANGA READABILITY + CARD-VIBE PASS
39132
+ Appended after the FINAL FIXES block (>16354) so it wins by source order.
39133
+ (1) Typography: 3-tier hierarchy \u2014 Anton=headline only, mono=legible floor,
39134
+ sans=running/secondary UI. (2) Manga card-vibe: faint margin-only ink,
39135
+ gold halftone + card/raven/poster watermarks that NEVER sit behind text.
39136
+ !important is used ONLY where an existing same-file !important forces it.
39137
+ ========================================================================== */
39138
+
39139
+ /* -------------------------------------------------------------------------
39140
+ (1) TYPOGRAPHY
39141
+ ------------------------------------------------------------------------- */
39142
+
39143
+ /* 1a. Small mono-telemetry label FLOOR \u2014 lift every sub-9.5px cap to legible.
39144
+ (.vr-tcard-type/-grade base 7-8px; .vr-bcard em / .vr-vpile-card em carry
39145
+ an existing 9px !important, so those need !important to reach the floor.) */
39146
+ .vr-tcard-type,
39147
+ .vr-tcard-grade,
39148
+ .vr-tcard.is-back .vr-tcard-backname,
39149
+ .vr-qs-local,
39150
+ .vr-bcard em,
39151
+ .vr-vpile-card em,
39152
+ .vr-addpick-list button em {
39153
+ font-size: 9.5px !important; /* was 6\u20139px; beats 16104/16079 !important */
39154
+ line-height: 1.25;
39155
+ font-weight: 620; /* thin mono needs weight on black paper */
39156
+ }
39157
+ .vr-tcard.is-dealt .vr-tcard-type,
39158
+ .vr-tcard.is-dealt .vr-tcard-grade { font-size: 10px !important; }
39159
+
39160
+ /* 1b. Letter-spacing CAP on tiny caps \u2014 extreme 1.4\u20132px tracking smears words.
39161
+ (Cap to ~0.07em. .vr-hud-strip / chip .muted already retoned at 15992+,
39162
+ so only their tracking is nudged, not restated.) */
39163
+ .vr-tcard-type,
39164
+ .vr-tcard-grade,
39165
+ .vr-tcard.is-back .vr-tcard-backname,
39166
+ .vr-qs-local,
39167
+ .vr-pile-shuffle,
39168
+ .vr-bcard-actions button,
39169
+ .vr-table-warning button {
39170
+ letter-spacing: 0.07em;
39171
+ }
39172
+ .vr-hud-strip { letter-spacing: 0.04em; }
39173
+
39174
+ /* 1c. Anton DEMOTION \u2014 pull the condensed poster face off running UI text.
39175
+ Menu labels + action-button captions move to the readable sans stack. */
39176
+ .vr-scope-menu p,
39177
+ .vr-studio-add-chat,
39178
+ .vr-provider-ask-agent,
39179
+ .vr-new-chat-button,
39180
+ .vr-agent-followups button.is-primary,
39181
+ .vr-chat-command-popover button.is-primary,
39182
+ .vr-cli-action-button.is-primary {
39183
+ font-family: var(--vr-hud-sans, 'Geist', 'Inter', system-ui, sans-serif);
39184
+ font-weight: 700;
39185
+ letter-spacing: 0.03em;
39186
+ text-transform: uppercase;
39187
+ }
39188
+ .vr-scope-menu p { font-size: 10px; }
39189
+
39190
+ /* 1d. Studio tab bar \u2014 Anton stays (display scale) but sized up + opened for
39191
+ scannable CHAT / ARCHITECTURE / TERMINAL / DIFF. */
39192
+ .vr-studio-tabs button {
39193
+ font-size: 13px;
39194
+ letter-spacing: 0.06em;
39195
+ font-weight: 400; /* Anton is already heavy */
39196
+ }
39197
+
39198
+ /* 1e. HUD telemetry strip \u2014 bigger, weightier instrument line. */
39199
+ .vr-hud-strip { font-size: 10px; color: #d2cabc; }
39200
+ .vr-hud-strip strong { font-weight: 720; color: #f7f4ec; }
39201
+ .vr-hud-strip em,
39202
+ .vr-hud-strip b { font-weight: 600; }
39203
+
39204
+ /* 1f. Context chips \u2014 cream sans label, drop the cold-grey muted qualifier. */
39205
+ .vr-context-chip-label {
39206
+ font-size: 12px;
39207
+ font-weight: 620;
39208
+ letter-spacing: 0.01em;
39209
+ }
39210
+ .vr-context-chip-label .muted {
39211
+ color: var(--vr-muted);
39212
+ font-weight: 560;
39213
+ }
39214
+
39215
+ /* 1g. Region-map / architecture node badges + zone/hint \u2014 weight up, cap track. */
39216
+ .vr-arch-node-badge {
39217
+ font-size: 10.5px;
39218
+ font-weight: 780;
39219
+ letter-spacing: 0.02em;
39220
+ }
39221
+ .vr-arch-zone { font-size: 10px; letter-spacing: 0.06em; }
39222
+ .vr-arch-hint { font-size: 11px; letter-spacing: 0.01em; line-height: 1.4; }
39223
+
39224
+ /* 1h. Model/scope picker button \u2014 selected model reads clean, non-condensed. */
39225
+ .vr-model-picker > button strong {
39226
+ font-family: var(--vr-hud-sans, 'Geist', 'Inter', system-ui, sans-serif);
39227
+ font-size: 12px;
39228
+ font-weight: 680;
39229
+ letter-spacing: 0.01em;
39230
+ }
39231
+ .vr-model-picker > button span {
39232
+ font-size: 10px;
39233
+ letter-spacing: 0.02em;
39234
+ color: var(--vr-muted);
39235
+ }
39236
+
39237
+ /* 1i. Dropdown menu rows \u2014 readable item body + sub-caption, sans not condensed. */
39238
+ .vr-model-menu button,
39239
+ .vr-scope-menu button,
39240
+ .vr-chat-command-popover button {
39241
+ font-size: 12.5px;
39242
+ letter-spacing: 0.005em;
39243
+ }
39244
+ .vr-model-menu button strong { font-weight: 660; }
39245
+ .vr-model-menu button em { font-size: 11px; font-style: normal; color: var(--vr-muted); }
39246
+
39247
+ /* 1j. Stat-card label under the big Anton KPI number \u2014 size up the smear. */
39248
+ .vr-stat-card span {
39249
+ font-size: 9.5px;
39250
+ letter-spacing: 0.08em;
39251
+ font-weight: 620;
39252
+ color: #ccc4b6;
39253
+ }
39254
+
39255
+ /* 1k. Version card meta / branch / pile label \u2014 floor size + cap tracking.
39256
+ (Overrides the contrast-pass 8.5px !important at 16063-64 and the pile
39257
+ letter-spacing at 16080, so font-size/letter-spacing need !important.) */
39258
+ .vr-vcard-meta,
39259
+ .vr-vcard-branch {
39260
+ font-size: 9.5px !important; /* was 8.5px !important */
39261
+ letter-spacing: 0.05em !important;
39262
+ }
39263
+ .vr-version-pile-label {
39264
+ font-size: 9.5px !important;
39265
+ letter-spacing: 0.08em !important; /* was 1.8px */
39266
+ font-weight: 640;
39267
+ }
39268
+
39269
+ /* 1l. SHUFFLE / worktree / table-foot action captions \u2014 legible click labels. */
39270
+ .vr-pile-shuffle,
39271
+ .vr-bcard-actions button,
39272
+ .vr-table-warning button,
39273
+ .vr-table-foot button {
39274
+ font-size: 10px;
39275
+ letter-spacing: 0.07em;
39276
+ font-weight: 680;
39277
+ }
39278
+
39279
+ /* 1m. Highest-traffic reading surfaces \u2014 comfortable body size + leading. */
39280
+ .vr-agent-answer-body,
39281
+ .vr-agent-answer-body p,
39282
+ .vr-chat-message p,
39283
+ .vr-chat-bubble p {
39284
+ font-size: 13.5px;
39285
+ line-height: 1.55;
39286
+ letter-spacing: 0.003em;
39287
+ }
39288
+ .vr-terminal-line { font-size: 12.5px; line-height: 1.5; }
39289
+
39290
+ /* 1n. Empty-state / drop-zone / note / recent-chat meta \u2014 lift learn-the-UI text. */
39291
+ .vr-table-empty { font-size: 12.5px; line-height: 1.45; }
39292
+ .vr-provider-drop-zone { font-size: 12px; letter-spacing: 0.01em; }
39293
+ .vr-chat-note { font-size: 12px; line-height: 1.45; }
39294
+ .vr-recent-chat em,
39295
+ .vr-recent-chat small { font-size: 11px; font-style: normal; }
39296
+
39297
+ /* 1o. Env-connect code chip + terminal compose \u2014 legible mono command hints. */
39298
+ .vr-env-connect-option b {
39299
+ font-size: 10px;
39300
+ letter-spacing: 0.04em;
39301
+ font-weight: 640;
39302
+ }
39303
+ .vr-terminal-compose textarea {
39304
+ font-size: 13px;
39305
+ line-height: 1.5;
39306
+ letter-spacing: 0;
39307
+ }
39308
+
39309
+ /* -------------------------------------------------------------------------
39310
+ (2) MANGA CARD-VIBE BACKGROUND \u2014 all margin/corner-masked, never behind text
39311
+ ------------------------------------------------------------------------- */
39312
+
39313
+ /* Keep app chrome above the fixed texture layers so opaque panels occlude them. */
39314
+ .vr-app { position: relative; z-index: 1; }
39315
+ .vr-composed-shell { position: relative; z-index: 1; }
39316
+
39317
+ /* 2a. Fixed full-viewport ink-splatter, painted ONLY in the outer page gutter.
39318
+ The .vr-composed-shell (opaque near-black, margin:10px 12px) sits on top and
39319
+ blocks it wherever real content lives; screen-blend keeps it light ink. */
39320
+ body::before {
39321
+ content: '';
39322
+ position: fixed;
39323
+ inset: 0;
39324
+ z-index: 0;
39325
+ pointer-events: none;
39326
+ background: url('/manga/bg.webp') no-repeat center center / cover;
39327
+ /* opacity + mask only \u2014 no mix-blend/filter (those are GPU-heavy on a
39328
+ fixed full-viewport layer and stall headless capture). The art is already
39329
+ ink-on-black so it reads as faint ink in the gutter at this opacity. */
39330
+ opacity: 0.09;
39331
+ -webkit-mask-image: radial-gradient(ellipse 74% 70% at 50% 46%, transparent 60%, #000 92%);
39332
+ mask-image: radial-gradient(ellipse 74% 70% at 50% 46%, transparent 60%, #000 92%);
39333
+ }
39334
+
39335
+ /* 2b. Corner-weighted fine GOLD halftone screentone (manga-card dot vibe),
39336
+ masked to two opposite corners only so the chat/panel mid-screen stays clean. */
39337
+ body::after {
39338
+ content: '';
39339
+ position: fixed;
39340
+ inset: 0;
39341
+ z-index: 0;
39342
+ pointer-events: none;
39343
+ background-image: radial-gradient(rgba(232, 193, 90, 0.10) 0.9px, transparent 1.3px);
39344
+ background-size: 5px 5px;
39345
+ opacity: 0.5;
39346
+ -webkit-mask-image:
39347
+ radial-gradient(circle at 100% 0%, #000 0%, transparent 34%),
39348
+ radial-gradient(circle at 0% 100%, #000 0%, transparent 34%);
39349
+ mask-image:
39350
+ radial-gradient(circle at 100% 0%, #000 0%, transparent 34%),
39351
+ radial-gradient(circle at 0% 100%, #000 0%, transparent 34%);
39352
+ -webkit-mask-composite: source-over;
39353
+ mask-composite: add;
39354
+ }
39355
+
39356
+ /* 2c. Ghost provider card tucked into the shell's top-left gutter, behind the
39357
+ translucent topbar/rail only. Lives inside overflow:hidden (clipped to shell),
39358
+ blurred + rotated like a card left on the table, faded before the chat column.
39359
+ RISK NOTE: .vr-composed-shell::after is reused as the drop overlay while
39360
+ dragging (.vr-app.is-dragging-*), which harmlessly hides this watermark
39361
+ during an active drag; it returns on drop. Idle state is unaffected. */
39362
+ .vr-composed-shell .vr-studio-body::after {
39363
+ content: '';
39364
+ position: absolute;
39365
+ left: -70px;
39366
+ top: -40px;
39367
+ width: 300px;
39368
+ height: 420px;
39369
+ z-index: 0;
39370
+ pointer-events: none;
39371
+ background: url('/manga/cards/supabase.webp') no-repeat center / contain;
39372
+ opacity: 0.05;
39373
+ transform: rotate(-13deg);
39374
+ filter: grayscale(0.6) contrast(0.95) blur(0.4px);
39375
+ -webkit-mask-image: linear-gradient(135deg, #000 12%, transparent 62%);
39376
+ mask-image: linear-gradient(135deg, #000 12%, transparent 62%);
39377
+ }
39378
+
39379
+ /* 2d. Existing raven emblem watermark (base at 15577, opacity 0.06, no mask):
39380
+ add a corner-fade mask + push below content so it never creeps under rows.
39381
+ !important here re-targets the already-textured pseudo across states. */
39382
+ .vr-table-view::after,
39383
+ .vr-studio-architecture.is-region-map::after {
39384
+ z-index: 0 !important;
39385
+ opacity: 0.07 !important;
39386
+ -webkit-mask-image: radial-gradient(circle at 100% 100%, #000 0%, transparent 58%) !important;
39387
+ mask-image: radial-gradient(circle at 100% 100%, #000 0%, transparent 58%) !important;
39388
+ }
39389
+ .vr-table-view > *,
39390
+ .vr-studio-architecture.is-region-map > * { position: relative; z-index: 1; }
39391
+
39392
+ /* 2e. Poster ghost low in the left gutter of the studio body \u2014 far-corner masked
39393
+ + blurred so it never reaches the readable center/right columns. */
39394
+ .vr-composed-shell .vr-studio-body { position: relative; }
39395
+ .vr-composed-shell .vr-studio-body::before {
39396
+ content: '';
39397
+ position: absolute;
39398
+ left: -30px;
39399
+ bottom: -30px;
39400
+ width: 280px;
39401
+ height: 360px;
39402
+ z-index: 0;
39403
+ pointer-events: none;
39404
+ background: url('/manga/poster.webp') no-repeat left bottom / contain;
39405
+ opacity: 0.045;
39406
+ filter: grayscale(0.7) contrast(1.05) blur(0.6px);
39407
+ -webkit-mask-image: radial-gradient(circle at 0% 100%, #000 0%, transparent 55%);
39408
+ mask-image: radial-gradient(circle at 0% 100%, #000 0%, transparent 55%);
39409
+ }
39410
+
39411
+ /* 2f. CONTRAST GUARD \u2014 the three translucent panels are rgba(18,20,24,.78),
39412
+ 22% transparent, so texture could faintly show under text. Bump the ink base
39413
+ more opaque; margins keep the vibe, text stays crisp. (background-color only,
39414
+ later source order beats the shorthand at 1737 without !important.) */
39415
+ .vr-studio-chat,
39416
+ .vr-studio-terminal,
39417
+ .vr-studio-diff { background-color: rgba(14, 15, 19, 0.9); }
39418
+
39419
+ /* ============================================================================
39420
+ MANGA READABILITY \u2014 FINAL ENFORCEMENT PASS (appended after all prior
39421
+ override chains: MANGA THEME PASS / B&W CONTRAST / FINAL FIXES / READABILITY).
39422
+ Wins by source order. Two groups:
39423
+ (1) READABLE FONTS \u2014 strict 3-tier type system, no coloured/gradient
39424
+ button text, condensed Anton pulled off all running/clickable UI.
39425
+ (2) MENTION + WELCOME/HANDOFF MANGA THEME \u2014 re-skin the old blue-slate
39426
+ PromptModal + quickstart to ink/gold manga.
39427
+ !important is used ONLY where an existing same-file !important forces it
39428
+ (documented inline). Readability always wins over theme where they overlap.
39429
+ RISK NOTES: clip-path notches added to modal/menu panels can clip focus
39430
+ rings / drop-shadows on the notched corner \u2014 acceptable (decorative corner
39431
+ only, ~12-18px, no interactive target sits there). No overflow:hidden added,
39432
+ so popovers/tooltips are unaffected.
39433
+ ========================================================================= */
39434
+
39435
+ /* ---------------------------------------------------------------------------
39436
+ GROUP 1 \u2014 READABLE FONTS
39437
+ --------------------------------------------------------------------------- */
39438
+
39439
+ /* 1.0 \u2014 Foundation: guarantee one clean non-condensed sans stack for TIER 2.
39440
+ Every rule below that says var(--vr-hud-sans) resolves to this. */
39441
+ :root {
39442
+ --vr-hud-sans: 'Geist', 'Inter', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
39443
+ }
39444
+
39445
+ /* 1.1 \u2014 Composer action bar (ADD CONTEXT / MENTION / COMMANDS / WORST CARD /
39446
+ ADD CARD): the exact pills the user screenshotted. Clean sans, normal-case,
39447
+ comfortable size, single cream colour. Neutralise any inherited condensed
39448
+ face and any -webkit-text-fill on the label span. */
39449
+ .vr-chat-inline-actions > button,
39450
+ .vr-chat-command-trigger > button,
39451
+ .vr-chat-inline-actions button {
39452
+ font-family: var(--vr-hud-sans);
39453
+ font-size: 12.5px;
39454
+ font-weight: 650;
39455
+ letter-spacing: 0.02em;
39456
+ text-transform: none;
39457
+ color: var(--vr-text);
39458
+ }
39459
+ .vr-chat-inline-actions > button span,
39460
+ .vr-chat-command-trigger > button span {
39461
+ font-family: var(--vr-hud-sans);
39462
+ font-weight: 650;
39463
+ letter-spacing: 0.02em;
39464
+ color: var(--vr-text);
39465
+ -webkit-text-fill-color: currentColor;
39466
+ background: none;
39467
+ }
39468
+
39469
+ /* 1.2 \u2014 Composer action icons: kill MENTION multicolour / ADD CARD purple.
39470
+ Pin all glyphs to the same muted cream as the label so each pill is one
39471
+ legible colour. (Worst-card icon keeps a gold accent \u2014 see 1.3.) */
39472
+ .vr-chat-inline-actions > button i[data-icon],
39473
+ .vr-chat-inline-actions > button svg,
39474
+ .vr-chat-command-trigger > button i[data-icon],
39475
+ .vr-chat-command-trigger > button svg {
39476
+ color: var(--vr-muted);
39477
+ stroke: var(--vr-muted);
39478
+ }
39479
+
39480
+ /* 1.3 \u2014 WORST CARD pill: remove the gold TEXT (overrides 14663-14664). Label
39481
+ goes cream like its siblings; gold survives only as border + icon accent. */
39482
+ .vr-chat-inline-actions > button.is-worst-card,
39483
+ .vr-chat-inline-actions > button.is-worst-card span {
39484
+ color: var(--vr-text);
39485
+ }
39486
+ .vr-chat-inline-actions > button.is-worst-card {
39487
+ border-color: rgba(232, 193, 90, 0.42);
39488
+ }
39489
+ .vr-chat-inline-actions > button.is-worst-card i[data-icon],
39490
+ .vr-chat-inline-actions > button.is-worst-card svg {
39491
+ color: var(--vr-gold);
39492
+ stroke: var(--vr-gold);
39493
+ }
39494
+
39495
+ /* 1.4 \u2014 is-primary composer pills (gold fill): dark ink text, not gold text. */
39496
+ .vr-chat-inline-actions button.is-primary,
39497
+ .vr-chat-inline-actions button.is-primary span {
39498
+ color: #1a1206;
39499
+ }
39500
+
39501
+ /* 1.5 \u2014 Hover on composer buttons: emphasis via border + bright ink, not by
39502
+ flipping the whole label. Gold-bright label on hover is legible on the
39503
+ darkened background and matches the menu hover language elsewhere. */
39504
+ .vr-chat-inline-actions button:hover,
39505
+ .vr-chat-inline-actions > button:hover,
39506
+ .vr-chat-command-trigger > button:hover {
39507
+ color: var(--vr-gold-bright);
39508
+ border-color: var(--vr-gold);
39509
+ background: rgba(20, 18, 12, 0.85);
39510
+ }
39511
+ .vr-chat-inline-actions > button:hover span { color: var(--vr-gold-bright); }
39512
+ .vr-chat-inline-actions > button:hover i[data-icon],
39513
+ .vr-chat-inline-actions > button:hover svg {
39514
+ color: var(--vr-gold-bright);
39515
+ stroke: var(--vr-gold-bright);
39516
+ }
39517
+
39518
+ /* 1.6 \u2014 ADD CARD shelf chip + is-gold variant. Base 8.5px/1.4px mono (14503)
39519
+ is NOT !important, so plain declarations win by source order \u2014 no
39520
+ !important needed. Readable sans, >=11px, tight tracking; gold stays as the
39521
+ FILL with dark ink text. */
39522
+ .vr-shelf-chip {
39523
+ font-family: var(--vr-hud-sans);
39524
+ font-size: 11px;
39525
+ font-weight: 650;
39526
+ letter-spacing: 0.03em;
39527
+ text-transform: none;
39528
+ color: var(--vr-text);
39529
+ }
39530
+ .vr-shelf-chip.is-gold { color: #1a1206; }
39531
+
39532
+ /* 1.7 \u2014 Quick-action tiles (left rail). Sans, readable weight/size, single
39533
+ cream colour; icon a quiet accent. */
39534
+ .vr-quick-action,
39535
+ .vr-quick-action strong,
39536
+ .vr-quick-action em {
39537
+ font-family: var(--vr-hud-sans);
39538
+ letter-spacing: 0.01em;
39539
+ text-transform: none;
39540
+ color: var(--vr-text);
39541
+ }
39542
+ .vr-quick-action strong { font-size: 12.5px; font-weight: 660; }
39543
+ .vr-quick-action em { font-size: 11px; font-weight: 500; color: var(--vr-muted); }
39544
+ .vr-quick-action i, .vr-quick-action svg { color: var(--vr-muted); stroke: var(--vr-muted); }
39545
+
39546
+ /* 1.8 \u2014 Dropdown menu rows (model / scope / command popover). Sans, comfortable
39547
+ size, cream text. Section HEADERS (.vr-model-menu p / .vr-scope-menu p) stay
39548
+ Anton gold \u2014 that is a valid TIER-1 eyebrow, restyled in 1.9 / GROUP 2. */
39549
+ .vr-model-menu button,
39550
+ .vr-scope-menu button,
39551
+ .vr-chat-command-popover button {
39552
+ font-family: var(--vr-hud-sans);
39553
+ font-size: 12.5px;
39554
+ font-weight: 600;
39555
+ letter-spacing: 0.005em;
39556
+ text-transform: none;
39557
+ color: var(--vr-text);
39558
+ }
39559
+ .vr-model-menu button strong,
39560
+ .vr-scope-menu button strong { font-family: var(--vr-hud-sans); font-weight: 660; }
39561
+ .vr-model-menu button em,
39562
+ .vr-scope-menu button em {
39563
+ font-family: var(--vr-hud-sans);
39564
+ font-style: normal;
39565
+ font-weight: 500;
39566
+ color: var(--vr-muted);
39567
+ }
39568
+
39569
+ /* 1.9 \u2014 Quickstrike command menu: clickable rows go sans/cream; header stays a
39570
+ small tracked eyebrow. */
39571
+ .vr-quickstrike button {
39572
+ font-family: var(--vr-hud-sans);
39573
+ font-size: 12.5px;
39574
+ font-weight: 600;
39575
+ letter-spacing: 0.01em;
39576
+ text-transform: none;
39577
+ color: var(--vr-text);
39578
+ }
39579
+ .vr-quickstrike header {
39580
+ font-size: 11px;
39581
+ letter-spacing: 0.06em;
39582
+ }
39583
+
39584
+ /* 1.10 \u2014 Model/scope picker trigger (selected-model label). */
39585
+ .vr-model-picker > button strong {
39586
+ font-family: var(--vr-hud-sans);
39587
+ font-size: 12px;
39588
+ font-weight: 660;
39589
+ letter-spacing: 0.01em;
39590
+ color: var(--vr-text);
39591
+ }
39592
+ .vr-model-picker > button span {
39593
+ font-family: var(--vr-hud-sans);
39594
+ font-size: 10.5px;
39595
+ letter-spacing: 0.02em;
39596
+ color: var(--vr-muted);
39597
+ }
39598
+
39599
+ /* 1.11 \u2014 Primary gold CTAs that were switched to Anton (15704+): pull them to
39600
+ bold sans, dark ink on gold. Gold is fill/accent, never text colour. */
39601
+ .vr-new-chat-button,
39602
+ .vr-provider-ask-agent,
39603
+ .vr-approve-all,
39604
+ .vr-chat-command-popover button.is-primary,
39605
+ .vr-agent-followups button.is-primary,
39606
+ .vr-agent-inline-action.is-primary,
39607
+ .vr-cli-action-button.is-primary,
39608
+ .vr-studio-add-chat,
39609
+ .vr-studio-architecture > footer button.is-primary,
39610
+ .vr-arch-drawer footer button.is-primary,
39611
+ .vr-wt-actions button.is-primary {
39612
+ font-family: var(--vr-hud-sans);
39613
+ font-weight: 700;
39614
+ letter-spacing: 0.02em;
39615
+ text-transform: none;
39616
+ color: #1a1206;
39617
+ }
39618
+
39619
+ /* 1.12 \u2014 Studio tab bar (CHAT / ARCHITECTURE / TERMINAL / DIFF): legitimate
39620
+ TIER-1 marquee. Keep Anton, but lock size/tracking legible; active tab keeps
39621
+ dark ink on gold. */
39622
+ .vr-studio-tabs button {
39623
+ font-family: var(--mg-display);
39624
+ font-size: 13.5px;
39625
+ letter-spacing: 0.06em;
39626
+ font-weight: 400;
39627
+ }
39628
+ .vr-studio-tabs button.is-active { color: #0a0b0e; }
39629
+
39630
+ /* 1.13 \u2014 Context chips + provider drop-label in the composer. */
39631
+ .vr-context-chip-label,
39632
+ .vr-provider-drop-label {
39633
+ font-family: var(--vr-hud-sans);
39634
+ font-size: 12px;
39635
+ font-weight: 620;
39636
+ letter-spacing: 0.01em;
39637
+ text-transform: none;
39638
+ color: var(--vr-text);
39639
+ }
39640
+ .vr-context-chip-label .muted { font-weight: 520; color: var(--vr-muted); }
39641
+
39642
+ /* 1.14 \u2014 Stat-card LABEL beneath the Anton KPI number. The number
39643
+ (.vr-stat-card b) keeps Anton untouched; only the micro-label is lifted. */
39644
+ .vr-stat-card span {
39645
+ font-family: var(--vr-hud-sans);
39646
+ font-size: 10.5px;
39647
+ font-weight: 600;
39648
+ letter-spacing: 0.04em;
39649
+ text-transform: uppercase;
39650
+ color: #ccc4b6;
39651
+ }
39652
+
39653
+ /* 1.15 \u2014 Ribbon stat qualifier + table-ribbon brand value. */
39654
+ .vr-ribbon-stat i {
39655
+ font-family: var(--vr-hud-sans);
39656
+ font-size: 10px;
39657
+ font-weight: 600;
39658
+ letter-spacing: 0.03em;
39659
+ color: var(--vr-muted);
39660
+ }
39661
+ .vr-table-ribbon > b {
39662
+ font-family: var(--vr-hud-sans);
39663
+ font-size: 12.5px;
39664
+ font-weight: 700;
39665
+ color: var(--vr-gold-bright);
39666
+ }
39667
+
39668
+ /* 1.16 \u2014 Version card meta / branch / pile labels. These carry same-file
39669
+ !important (16062/16064/16065/16081) on color/size/family, so !important is
39670
+ required here to win on family + tracking. Size floors from 16386 are left
39671
+ intact (readability floor already >=9.5px). */
39672
+ .vr-vcard-meta,
39673
+ .vr-vcard-branch,
39674
+ .vr-version-pile-label {
39675
+ font-family: var(--vr-hud-sans) !important;
39676
+ letter-spacing: 0.03em !important;
39677
+ text-transform: none;
39678
+ }
39679
+ .vr-vpile-card b {
39680
+ font-family: var(--vr-hud-sans) !important; /* beats 16062 !important */
39681
+ letter-spacing: 0.01em;
39682
+ }
39683
+
39684
+ /* 1.17 \u2014 Shuffle / worktree / table-foot / back-card / addpick action captions:
39685
+ every click label a plain readable word, not a condensed cap. */
39686
+ .vr-pile-shuffle,
39687
+ .vr-bcard-actions button,
39688
+ .vr-table-warning button,
39689
+ .vr-table-foot button,
39690
+ .vr-addpick-list button strong {
39691
+ font-family: var(--vr-hud-sans);
39692
+ font-size: 11px;
39693
+ font-weight: 640;
39694
+ letter-spacing: 0.03em;
39695
+ text-transform: none;
39696
+ color: var(--vr-text);
39697
+ }
39698
+
39699
+ /* 1.18 \u2014 Global guard: no gradient/clipped text may re-enter any button/label.
39700
+ (No same-file !important on -webkit-text-fill-color, so none needed here.) */
39701
+ .vr-chat-inline-actions button,
39702
+ .vr-chat-command-trigger button,
39703
+ .vr-quick-action,
39704
+ .vr-quick-action strong,
39705
+ .vr-quick-action em,
39706
+ .vr-shelf-chip,
39707
+ .vr-studio-tabs button,
39708
+ .vr-model-menu button,
39709
+ .vr-scope-menu button,
39710
+ .vr-chat-command-popover button {
39711
+ -webkit-text-fill-color: currentColor;
39712
+ -webkit-background-clip: border-box;
39713
+ background-clip: border-box;
39714
+ }
39715
+
39716
+ /* ---------------------------------------------------------------------------
39717
+ GROUP 2 \u2014 MENTION + WELCOME/HANDOFF MANGA THEME
39718
+ The composer "Mention" button (data-action="prompt") opens the PromptModal,
39719
+ so re-theming the PromptModal fixes both the "mention screen" and the Scoped
39720
+ Handoff / Agent Mission modal. Also sweeps the leftover blue-slate theme off
39721
+ Guide/Plan/Notifications and finishes the quickstart cards.
39722
+ RISK NOTE: clip-path notch on modal panels clips the far corner only; no
39723
+ text or control sits there.
39724
+ --------------------------------------------------------------------------- */
39725
+
39726
+ /* 2.1 \u2014 Modal header shell (Agent Mission / Guide): navy gradient + slate
39727
+ border -> ink panel, gold left spine, gold halftone. */
39728
+ .vr-modal-prompt-head,
39729
+ .vr-modal-guide-head {
39730
+ border: 1px solid var(--vr-border-strong);
39731
+ border-left: 3px solid var(--vr-gold);
39732
+ border-radius: 0;
39733
+ clip-path: polygon(0 0, calc(100% - 18px) 0, 100% 18px, 100% 100%, 18px 100%, 0 calc(100% - 18px));
39734
+ background-color: rgba(9, 9, 11, 0.94);
39735
+ background-image:
39736
+ radial-gradient(circle at 12% 30%, rgba(232, 193, 90, 0.07), transparent 40%),
39737
+ radial-gradient(rgba(232, 193, 90, 0.05) 1px, transparent 1.4px);
39738
+ background-size: auto, 9px 9px;
39739
+ box-shadow: 5px 6px 0 rgba(0, 0, 0, 0.55);
39740
+ }
39741
+
39742
+ /* 2.2 \u2014 Header eyebrow: gold token, small Anton poster eyebrow. */
39743
+ .vr-modal-prompt-head p,
39744
+ .vr-modal-guide-head p {
39745
+ color: var(--vr-gold);
39746
+ font-family: var(--mg-display);
39747
+ font-weight: 400;
39748
+ font-size: 12px;
39749
+ letter-spacing: 0.14em;
39750
+ }
39751
+
39752
+ /* 2.3 \u2014 Header title: Anton poster caps, cream ink. */
39753
+ .vr-modal-prompt-head h2,
39754
+ .vr-modal-guide-head h2 {
39755
+ font-family: var(--mg-display);
39756
+ font-weight: 400;
39757
+ font-size: 40px;
39758
+ letter-spacing: 0.01em;
39759
+ text-transform: uppercase;
39760
+ color: var(--vr-text);
39761
+ }
39762
+
39763
+ /* 2.4 \u2014 Header sub-copy: ice-blue -> readable cream sans. */
39764
+ .vr-modal-prompt-head em,
39765
+ .vr-modal-guide-head em {
39766
+ font-family: var(--vr-hud-sans);
39767
+ color: var(--vr-muted);
39768
+ font-size: 14px;
39769
+ font-style: normal;
39770
+ line-height: 1.5;
39771
+ }
39772
+
39773
+ /* 2.5 \u2014 Header side aside (Scope / One gap): slate glass -> ink + gold hairline. */
39774
+ .vr-modal-prompt-head aside,
39775
+ .vr-modal-guide-head aside {
39776
+ border: 1px solid var(--vr-border-strong);
39777
+ border-radius: 0;
39778
+ background: rgba(0, 0, 0, 0.28);
39779
+ }
39780
+ .vr-modal-prompt-head aside > i,
39781
+ .vr-modal-guide-head aside > i {
39782
+ background: rgba(232, 193, 90, 0.10);
39783
+ color: var(--vr-gold-bright);
39784
+ border-radius: 0;
39785
+ }
39786
+ .vr-modal-prompt-head aside > span,
39787
+ .vr-modal-guide-head aside > span {
39788
+ color: var(--vr-gold);
39789
+ font-family: var(--vr-hud-sans);
39790
+ letter-spacing: 0.06em;
39791
+ }
39792
+ .vr-modal-prompt-head aside > strong,
39793
+ .vr-modal-guide-head aside > strong { color: var(--vr-text); }
39794
+
39795
+ /* 2.6 \u2014 Scoped-handoff mission <pre> card: navy border + ice-blue mono ->
39796
+ ink card, gold spine, cream mono. */
39797
+ .vr-modal-prompt-card {
39798
+ border: 1px solid var(--vr-border);
39799
+ border-left: 3px solid var(--vr-gold);
39800
+ border-radius: 0;
39801
+ clip-path: polygon(0 0, calc(100% - 14px) 0, 100% 14px, 100% 100%, 14px 100%, 0 calc(100% - 14px));
39802
+ background:
39803
+ radial-gradient(circle at 88% 0%, rgba(232, 193, 90, 0.05), transparent 34%),
39804
+ rgba(9, 9, 11, 0.94);
39805
+ }
39806
+ .vr-modal-prompt-card pre {
39807
+ color: var(--vr-text);
39808
+ font-family: var(--vr-hud-mono);
39809
+ font-size: 12.5px;
39810
+ line-height: 1.7;
39811
+ }
39812
+
39813
+ /* 2.7 \u2014 Flow/signal list panels: navy -> ink + gold halftone. */
39814
+ .vr-modal-flow-list,
39815
+ .vr-modal-signal-list {
39816
+ border: 1px solid var(--vr-border-strong);
39817
+ border-radius: 0;
39818
+ clip-path: polygon(0 0, calc(100% - 16px) 0, 100% 16px, 100% 100%, 16px 100%, 0 calc(100% - 16px));
39819
+ background:
39820
+ radial-gradient(rgba(232, 193, 90, 0.04) 1px, transparent 1.4px) 0 0 / 9px 9px,
39821
+ rgba(9, 9, 11, 0.94);
39822
+ }
39823
+
39824
+ /* 2.8 \u2014 Flow-list step rows: slate borders + purple glow + cold-blue caption
39825
+ -> ink rows, gold-tinted hover spine, readable sans. */
39826
+ .vr-modal-flow-list button {
39827
+ border: 1px solid var(--vr-border);
39828
+ border-radius: 0;
39829
+ background: rgba(0, 0, 0, 0.25);
39830
+ }
39831
+ .vr-modal-flow-list button:hover {
39832
+ border-color: var(--vr-gold);
39833
+ box-shadow: inset 3px 0 0 var(--vr-gold);
39834
+ background: rgba(232, 193, 90, 0.10);
39835
+ }
39836
+ .vr-modal-flow-list button > span {
39837
+ border: 2px solid var(--vr-gold);
39838
+ color: var(--vr-gold-bright);
39839
+ }
39840
+ .vr-modal-flow-list strong { font-family: var(--vr-hud-sans); color: var(--vr-text); }
39841
+ .vr-modal-flow-list em {
39842
+ font-family: var(--vr-hud-sans);
39843
+ color: var(--vr-muted);
39844
+ font-size: 12px;
39845
+ line-height: 1.45;
39846
+ }
39847
+
39848
+ /* 2.9 \u2014 Composer scope/mention popup panel: reinforce ink + gold spine +
39849
+ comic-notch so it reads as part of the manga card system. */
39850
+ .vr-scope-menu {
39851
+ border-radius: 0;
39852
+ border-left: 3px solid var(--vr-gold);
39853
+ clip-path: polygon(0 0, calc(100% - 12px) 0, 100% 12px, 100% 100%, 12px 100%, 0 calc(100% - 12px));
39854
+ background-color: rgba(9, 9, 11, 0.96);
39855
+ box-shadow: 4px 6px 0 rgba(0, 0, 0, 0.55);
39856
+ }
39857
+
39858
+ /* 2.10 \u2014 Scope/mention popup section label: Anton gold eyebrow, legible size.
39859
+ (Group 1 already forced the ROWS to sans; this only styles the <p> header.) */
39860
+ .vr-scope-menu p {
39861
+ font-family: var(--mg-display);
39862
+ font-size: 11px;
39863
+ letter-spacing: 0.14em;
39864
+ color: var(--vr-gold);
39865
+ }
39866
+
39867
+ /* 2.11 \u2014 Scope/mention popup rows: gold-tinted hover + selected state.
39868
+ (Font-family/size/colour already set sans in Group 1 \xA71.8 \u2014 here we only add
39869
+ the hover/selected theming so the two passes don't fight.) */
39870
+ .vr-scope-menu button:hover {
39871
+ background: rgba(232, 193, 90, 0.14);
39872
+ box-shadow: inset 3px 0 0 var(--vr-gold);
39873
+ color: var(--vr-gold-bright);
39874
+ }
39875
+ .vr-scope-menu button:has(> b) {
39876
+ background: rgba(232, 193, 90, 0.18);
39877
+ box-shadow: inset 3px 0 0 var(--vr-gold-bright);
39878
+ color: var(--vr-gold-bright);
39879
+ }
39880
+
39881
+ /* 2.12 \u2014 Welcome/quickstart title: condensed --vr-display -> Anton poster. */
39882
+ .vr-qs-head h2 {
39883
+ font-family: var(--mg-display);
39884
+ font-weight: 400;
39885
+ font-size: 32px;
39886
+ letter-spacing: 0.02em;
39887
+ color: var(--vr-text);
39888
+ }
39889
+
39890
+ /* 2.13 \u2014 Welcome sub-copy + LOCAL badge. The 9.5px floor at 16386 keeps
39891
+ .vr-qs-local legible; here we only recolor to gold (no size restated, so we
39892
+ don't fight that !important). */
39893
+ .vr-qs-head p {
39894
+ font-family: var(--vr-hud-sans);
39895
+ font-size: 13px;
39896
+ color: var(--vr-muted);
39897
+ }
39898
+ .vr-qs-local {
39899
+ color: var(--vr-gold);
39900
+ font-weight: 660;
39901
+ }
39902
+
39903
+ /* 2.14 \u2014 Welcome hero cards: rounded flat -> gold clipped-frame collector card. */
39904
+ .vr-qs-card.is-hero {
39905
+ border-radius: 0;
39906
+ clip-path: polygon(0 0, calc(100% - 14px) 0, 100% 14px, 100% 100%, 14px 100%, 0 calc(100% - 14px));
39907
+ background: linear-gradient(160deg, var(--vr-gold), rgba(232, 193, 90, 0.35));
39908
+ padding: 2px;
39909
+ box-shadow: 0 14px 30px rgba(0, 0, 0, 0.6);
39910
+ }
39911
+ .vr-qs-card.is-hero .vr-qs-card-inner {
39912
+ border-radius: 0;
39913
+ clip-path: polygon(0 0, calc(100% - 12px) 0, 100% 12px, 100% 100%, 12px 100%, 0 calc(100% - 12px));
39914
+ background:
39915
+ radial-gradient(rgba(232, 193, 90, 0.05) 1px, transparent 1.4px) 0 0 / 8px 8px,
39916
+ rgba(9, 9, 11, 0.96);
39917
+ }
39918
+
39919
+ /* 2.15 \u2014 Welcome hero card title: Anton only on the title; body/labels go sans. */
39920
+ .vr-qs-card.is-hero .vr-qs-card-top strong {
39921
+ font-family: var(--mg-display);
39922
+ font-weight: 400;
39923
+ font-size: 15px;
39924
+ letter-spacing: 0.02em;
39925
+ text-transform: uppercase;
39926
+ color: var(--vr-text);
39927
+ }
39928
+ .vr-qs-card.is-hero .vr-qs-card em,
39929
+ .vr-qs-card.is-hero .vr-qs-type {
39930
+ font-family: var(--vr-hud-sans);
39931
+ color: var(--vr-muted);
39932
+ }
39933
+ .vr-qs-card.is-hero button {
39934
+ font-family: var(--vr-hud-sans);
39935
+ font-weight: 700;
39936
+ letter-spacing: 0.02em;
39937
+ text-transform: none;
39938
+ color: var(--vr-gold-bright);
39939
+ border-color: var(--vr-gold);
39940
+ }
39941
+
39942
+ /* ==========================================================================
39943
+ FINAL POLISH \u2014 drag ghost, active tab, low-quality chat cards
39944
+ ========================================================================== */
39945
+ /* Drag ghost: a clean floating card preview. It was a clone that KEPT the
39946
+ placed/hand card's position:absolute + translate(-50%,-50%) / rotation /
39947
+ negative margin, so it rendered as a corrupted half-card. Reset to a plain
39948
+ fixed, upright (slightly tilted) card sized to the source. */
39949
+ .vr-drag-ghost {
39950
+ position: fixed !important;
39951
+ margin: 0 !important;
39952
+ transform: rotate(-4deg) !important;
39953
+ transform-origin: top left;
39954
+ z-index: 100000 !important;
39955
+ pointer-events: none !important;
39956
+ opacity: 0.96;
39957
+ box-shadow: 0 24px 46px rgba(0, 0, 0, 0.78);
39958
+ transition: none !important;
39959
+ }
39960
+ .vr-drag-ghost .vr-tcard-unplace { display: none !important; }
39961
+ .vr-drag-ghost .vr-tcard-holofoil,
39962
+ .vr-drag-ghost .vr-tcard-glare { animation: none !important; opacity: 0.2 !important; }
39963
+
39964
+ /* Active tab: was dark #0a0b0e text on a near-transparent wash = an unreadable
39965
+ "black mark". Gold-bright text on a subtle wash + gold underline reads clean. */
39966
+ .vr-studio-tabs button.is-active {
39967
+ color: var(--vr-gold-bright) !important;
39968
+ background: rgba(244, 241, 232, 0.05);
39969
+ box-shadow: inset 0 -2px 0 var(--vr-gold), inset 0 0 0 1px rgba(232, 193, 90, 0.26);
39970
+ border-radius: 4px;
39971
+ }
39972
+
39973
+ /* ==========================================================================
39974
+ VERSION CARDS \u2014 richer "release card" (was small/plain crimson-inner).
39975
+ Gold ornate frame + comic notch + halftone + BIG Anton version number,
39976
+ matching the premium provider-card language. No full-art (releases have no
39977
+ art), so the version number IS the hero.
39978
+ ========================================================================== */
39979
+ .vr-vcard {
39980
+ border: 1.5px solid var(--vr-border-strong) !important;
39981
+ border-radius: 5px !important;
39982
+ clip-path: polygon(0 0, calc(100% - 16px) 0, 100% 16px, 100% 100%, 16px 100%, 0 calc(100% - 16px));
39983
+ background: rgba(9, 9, 11, 0.97) !important;
39984
+ box-shadow: 0 12px 28px rgba(0, 0, 0, 0.6), inset 0 0 0 1px rgba(232, 193, 90, 0.16) !important;
39985
+ overflow: hidden !important;
39986
+ }
39987
+ .vr-vcard::before {
39988
+ inset: 0 !important;
39989
+ border-radius: 0 !important;
39990
+ background-color: transparent !important;
39991
+ background-image:
39992
+ radial-gradient(rgba(232, 193, 90, 0.05) 1px, transparent 1.4px),
39993
+ radial-gradient(ellipse 78% 46% at 50% 0%, rgba(232, 193, 90, 0.07), transparent 60%) !important;
39994
+ background-size: 9px 9px, 100% 100% !important;
39995
+ background-repeat: repeat, no-repeat !important;
39996
+ opacity: 1 !important;
39997
+ }
39998
+ .vr-vcard:not(.is-current) { filter: none !important; opacity: 0.92; }
39999
+ .vr-vcard.is-current {
40000
+ border-color: var(--vr-gold) !important;
40001
+ border-left: 3px solid var(--vr-gold) !important;
40002
+ box-shadow: 0 12px 28px rgba(0, 0, 0, 0.6), 0 0 22px rgba(232, 193, 90, 0.24), inset 0 0 0 1px rgba(232, 193, 90, 0.2) !important;
40003
+ }
40004
+ .vr-vcard-top b {
40005
+ font-family: var(--mg-display) !important;
40006
+ font-size: 26px !important;
40007
+ font-weight: 400 !important;
40008
+ color: var(--vr-gold-bright) !important;
40009
+ letter-spacing: 0.02em !important;
40010
+ }
40011
+ .vr-vcard.is-mini .vr-vcard-top b { font-size: 22px !important; }
40012
+ .vr-vcard-branch { color: var(--vr-muted) !important; }
40013
+ /* four gold corner ticks like the provider cards */
40014
+ .vr-vcard .vr-vcard-foot::after {
40015
+ content: '';
40016
+ position: absolute;
40017
+ left: 8px; bottom: 8px; right: 8px;
40018
+ height: 1px;
40019
+ background: linear-gradient(90deg, var(--vr-gold), transparent 70%);
40020
+ opacity: 0.5;
40021
+ }
40022
+
40023
+ /* ==========================================================================
40024
+ WELCOME FLIP-CARD BACK \u2014 flat manga halftone (was a gold spiral). Ink plate,
40025
+ gold halftone screentone, a soft gold top-glow, the fierce raven mark, gold
40026
+ FLIP label. Reads as a face-down manga card, not a casino spiral.
40027
+ ========================================================================== */
40028
+ .vr-qs-back,
40029
+ .vr-tcard.is-back:not(.has-art) {
40030
+ border: 1.5px solid var(--vr-border-strong) !important;
40031
+ border-radius: 5px !important;
40032
+ background:
40033
+ radial-gradient(rgba(232, 193, 90, 0.06) 1px, transparent 1.4px) 0 0 / 9px 9px,
40034
+ radial-gradient(ellipse 72% 52% at 50% 40%, rgba(232, 193, 90, 0.08), transparent 62%),
40035
+ #0a0a0d !important;
40036
+ }
40037
+ .vr-qs-back::after,
40038
+ .vr-tcard.is-back:not(.has-art)::after {
40039
+ content: '';
40040
+ position: absolute;
40041
+ inset: 5px;
40042
+ border: 1px solid rgba(232, 193, 90, 0.22);
40043
+ border-radius: 3px;
40044
+ pointer-events: none;
40045
+ }
40046
+ .vr-qs-back img,
40047
+ .vr-tcard.is-back:not(.has-art) .vr-tcard-backart img {
40048
+ width: 62px !important;
40049
+ height: 62px !important;
40050
+ opacity: 0.96 !important;
40051
+ filter: drop-shadow(0 0 10px rgba(232, 193, 90, 0.22));
40052
+ }
40053
+ .vr-qs-back b,
40054
+ .vr-tcard.is-back:not(.has-art) .vr-tcard-backname {
40055
+ color: var(--vr-gold-bright) !important;
40056
+ font-family: var(--vr-hud-mono) !important;
40057
+ letter-spacing: 0.16em !important;
40058
+ }
40059
+
40060
+ /* Region map: keep the grade medallion in the top-right corner like the binder,
40061
+ just scaled down to fit the small map cards so it stays tucked in the corner
40062
+ instead of covering the provider name. */
40063
+ .vr-studio-architecture.is-region-map .vr-arch-node.is-card .vr-tcard-hp.is-onart {
40064
+ top: 5px;
40065
+ right: 5px;
40066
+ bottom: auto;
40067
+ transform: none;
40068
+ width: 16px;
40069
+ height: 16px;
40070
+ }
40071
+ .vr-studio-architecture.is-region-map .vr-arch-node.is-card .vr-tcard-hp.is-onart i {
40072
+ font-size: 9px;
40073
+ }
40074
+ `;
40075
+ }
40076
+ });
40077
+
40078
+ // src/local-ui/static/appHtml.ts
40079
+ function escapeJsonForHtml(value) {
40080
+ return JSON.stringify(value).replace(/</g, "\\u003c");
40081
+ }
40082
+ function renderLocalUiHtml(state) {
40083
+ return `<!doctype html>
40084
+ <html lang="en">
40085
+ <head>
40086
+ <meta charset="utf-8" />
40087
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
40088
+ <meta name="theme-color" content="#0b0b0d" />
40089
+ <title>VibeRaven</title>
40090
+ <link rel="icon" type="image/png" href="/manga/favicon.png?v=manga1" />
40091
+ <link rel="shortcut icon" type="image/png" href="/manga/favicon.png?v=manga1" />
40092
+ <link rel="apple-touch-icon" href="/manga/favicon-180.png?v=manga1" />
40093
+ <link rel="preload" as="image" href="/report/assets/viberaven-mascot.png?v=raven-cards-2" />
40094
+ <style>${localUiCss}</style>
40095
+ </head>
40096
+ <body>
40097
+ <div id="vr-app" class="vr-app" aria-label="VibeRaven"></div>
40098
+ <script id="vr-state" type="application/json">${escapeJsonForHtml(state)}</script>
40099
+ <script src="/vendor/anime.umd.min.js"></script>
40100
+ <script>${localUiClientJs}</script>
40101
+ </body>
40102
+ </html>`;
40103
+ }
40104
+ var init_appHtml = __esm({
40105
+ "src/local-ui/static/appHtml.ts"() {
40106
+ "use strict";
40107
+ init_appClient();
40108
+ init_appCss();
40109
+ }
40110
+ });
40111
+
40112
+ // src/local-ui/server.ts
40113
+ function normalizeShellCommand(value) {
40114
+ return value.trim().replace(/\s+/g, " ");
40115
+ }
40116
+ function resolveApprovedShellCommand(command, requestedCliId) {
40117
+ const normalized = normalizeShellCommand(command);
40118
+ const candidateIds = requestedCliId ? [requestedCliId] : ["codex", "claude", "gemini"];
40119
+ for (const cliId of candidateIds) {
40120
+ const setup = CLI_AGENT_SETUP[cliId];
40121
+ const install = normalizeShellCommand(setup.installCommand);
40122
+ if (normalized === install) {
40123
+ return { cliId, step: "install", command: setup.installCommand };
40124
+ }
40125
+ const signIn = normalizeShellCommand(setup.signInCommand);
40126
+ if (normalized === signIn) {
40127
+ return { cliId, step: "sign-in", command: setup.signInCommand };
40128
+ }
40129
+ }
40130
+ return null;
40131
+ }
40132
+ async function connectProviderMcp(cwd, providerId) {
40133
+ const recipe = MCP_SERVER_RECIPES[providerId];
40134
+ if (!recipe) {
40135
+ return { ok: false, message: `No known MCP server for ${providerId} yet. Add one manually to .mcp.json.` };
40136
+ }
40137
+ const configPath = (0, import_node_path30.join)(cwd, ".mcp.json");
40138
+ let config = {};
40139
+ try {
40140
+ const raw = await (0, import_promises23.readFile)(configPath, "utf8");
40141
+ const parsed = JSON.parse(raw.replace(/^\uFEFF/, ""));
40142
+ if (isRecord4(parsed)) config = parsed;
40143
+ } catch {
40144
+ }
40145
+ const servers = isRecord4(config.mcpServers) ? { ...config.mcpServers } : {};
40146
+ if (servers[providerId]) {
40147
+ return { ok: true, message: `${providerId} MCP server already configured in .mcp.json.`, path: ".mcp.json" };
40148
+ }
40149
+ servers[providerId] = recipe.entry;
40150
+ const next = { ...config, mcpServers: servers };
40151
+ await (0, import_promises23.writeFile)(configPath, `${JSON.stringify(next, null, 2)}
40152
+ `, "utf8");
40153
+ const suffix = recipe.note ? ` ${recipe.note}` : "";
40154
+ return { ok: true, message: `Added ${providerId} MCP server to .mcp.json.${suffix}`, path: ".mcp.json" };
40155
+ }
40156
+ async function defaultScanRunner(flags, positional) {
40157
+ const cli = await Promise.resolve().then(() => (init_cli(), cli_exports));
40158
+ return cli.runScanCommand(flags, positional);
40159
+ }
40160
+ async function readPackageJson(cwd) {
40161
+ try {
40162
+ const raw = await (0, import_promises23.readFile)((0, import_node_path30.join)(cwd, "package.json"), "utf8");
40163
+ const parsed = JSON.parse(raw.replace(/^\uFEFF/, ""));
40164
+ return isRecord4(parsed) ? parsed : {};
40165
+ } catch {
40166
+ return {};
40167
+ }
40168
+ }
40169
+ function dependencyNames(packageJson) {
40170
+ const names = /* @__PURE__ */ new Set();
40171
+ for (const key of ["dependencies", "devDependencies", "peerDependencies", "optionalDependencies"]) {
40172
+ const value = packageJson[key];
40173
+ if (!isRecord4(value)) continue;
40174
+ Object.keys(value).forEach((name) => names.add(name.toLowerCase()));
40175
+ }
40176
+ return names;
40177
+ }
40178
+ async function listProjectFiles(cwd, maxFiles = 1800) {
40179
+ const skipped = /* @__PURE__ */ new Set([".git", "node_modules", ".next", "dist", "build", "coverage", ".turbo", ".viberaven"]);
40180
+ const files = [];
40181
+ async function walk(dir, prefix = "") {
40182
+ if (files.length >= maxFiles) return;
40183
+ let entries;
40184
+ try {
40185
+ entries = await (0, import_promises23.readdir)(dir, { withFileTypes: true });
40186
+ } catch {
40187
+ return;
40188
+ }
40189
+ for (const entry of entries) {
40190
+ if (files.length >= maxFiles) return;
37691
40191
  const rel = prefix ? `${prefix}/${entry.name}` : entry.name;
37692
40192
  if (entry.isDirectory()) {
37693
40193
  if (!skipped.has(entry.name)) await walk((0, import_node_path30.join)(dir, entry.name), rel);
@@ -38316,13 +40816,13 @@ function parseAgentChatRequest(value) {
38316
40816
  function taskKindExecutionBlock(taskKind) {
38317
40817
  switch (taskKind) {
38318
40818
  case "analyze":
38319
- return "Execution mode: analyze launch readiness. Read repo files and .viberaven artifacts; return concise findings with paths. Do not dump CLI install manuals or numbered manual runbooks.";
40819
+ return "Execution mode: analyze launch readiness. Inspect the real application code \u2014 auth, protected routes, middleware, database/RLS, migrations, webhooks, and env usage. Return a short prose summary plus up to 3 concrete blockers, each with the real code file path. Do not read, grep, or cite the .viberaven/ folder; those are stale internal artifacts.";
38320
40820
  case "plan":
38321
40821
  return "Execution mode: build an ordered fix plan from repo evidence. If the top blocker is repo-code, apply that fix in the project before answering. Do not reply with only manual instructions when code edits are possible.";
38322
40822
  case "fix":
38323
40823
  return "Execution mode: apply one minimal scoped repo fix IN CODE NOW. Read existing files, edit the project, save changes, then summarize. Forbidden: telling the user what to do manually when you can patch the repo yourself.";
38324
40824
  case "verify":
38325
- return "Execution mode: verify launch readiness. Prefer `npx -y viberaven --verify` or read .viberaven/gate-result.json and agent-tasklist.md.";
40825
+ return "Execution mode: verify launch readiness against the real code. Re-check the specific files you changed and run the project tests or build when available. Do not rely on .viberaven/ scan artifacts.";
38326
40826
  case "provider":
38327
40827
  return "Execution mode: provider proof or connect mission. Edit repo wiring when missing; separate dashboard-only steps from code changes; never claim live connection without proof.";
38328
40828
  case "diff":
@@ -38347,9 +40847,18 @@ function missionCardsContextBlock(state) {
38347
40847
  });
38348
40848
  return lines.length > 1 ? lines.join("\n") : void 0;
38349
40849
  }
40850
+ async function hasPriorReleases(cwd) {
40851
+ try {
40852
+ const tags = await runGit(cwd, ["tag", "--list"]);
40853
+ return Boolean(tags.trim());
40854
+ } catch {
40855
+ return false;
40856
+ }
40857
+ }
38350
40858
  async function buildAgentChatPrompt(request, cwd) {
38351
40859
  const state = await readState(cwd);
38352
40860
  const executionBlock = taskKindExecutionBlock(request.taskKind);
40861
+ const priorReleases = await hasPriorReleases(cwd);
38353
40862
  const cardsBlock = missionCardsContextBlock(state);
38354
40863
  const providerState2 = request.provider?.id ? state.providers.find((provider2) => provider2.id === request.provider?.id) : state.providers.find((provider2) => provider2.name === request.provider?.name);
38355
40864
  const mcp = providerState2?.mcp;
@@ -38367,6 +40876,7 @@ async function buildAgentChatPrompt(request, cwd) {
38367
40876
  releaseDiffBlock,
38368
40877
  state.gate?.label ? `Project status: ${state.gate.label} (${state.gate.status ?? "unknown"})` : void 0,
38369
40878
  state.missionControl?.mainBlocker ? `Main blocker: ${state.missionControl.mainBlocker}` : void 0,
40879
+ priorReleases ? "Deployment state: this project already has release tags, so it may already be in production. When asked about production readiness, prioritize what changed since the last release and the risk in the NEXT change over first-launch blockers." : void 0,
38370
40880
  executionBlock,
38371
40881
  cardsBlock,
38372
40882
  "",
@@ -38378,9 +40888,11 @@ async function buildAgentChatPrompt(request, cwd) {
38378
40888
  "- Return only the final user-facing answer. Do not echo this prompt, session metadata, logs, or command transcripts.",
38379
40889
  "- For fix/plan/provider repo gaps: edit files in this project before answering. Manual dashboard steps are only for gaps that cannot be solved in repo code.",
38380
40890
  "- Never paste long VibeRaven operator manuals, heal loops, or PowerShell command blocks unless the user explicitly asked how to run verify.",
40891
+ "- Do not read, grep, cite, or paste the contents of the .viberaven/ folder. Those are stale internal scan artifacts, not source of truth. Reason from the real application code (auth, routes, middleware, migrations, webhooks, env usage).",
40892
+ "- Keep the whole answer under ~15 lines. Never paste file contents, grep output, JSON, or command transcripts; summarize in prose and short bullets.",
38381
40893
  "",
38382
40894
  "Response format (markdown):",
38383
- "1. **Findings** \u2014 bullets with file paths or artifact names",
40895
+ "1. **Findings** \u2014 up to 3 bullets, each naming the real code file and the risk",
38384
40896
  "2. **Changes made** \u2014 what you edited, or say none only if the blocker is dashboard-only",
38385
40897
  '3. **Files touched** \u2014 list paths (or "none")',
38386
40898
  "4. **Next step** \u2014 one verify action or one dashboard action (do not paste raw npx commands; the UI renders Verify buttons)",
@@ -38786,7 +41298,7 @@ async function sendReportArtifact(cwd, response) {
38786
41298
  <body>
38787
41299
  <main>
38788
41300
  <div class="brand">
38789
- <img src="/report/assets/viberaven-agent-icon.png" alt="" />
41301
+ <img src="/report/assets/viberaven-mascot.png" alt="" />
38790
41302
  <div>
38791
41303
  <h1>Report preview is ready</h1>
38792
41304
  <p>${projectName} does not have a generated report artifact yet. The launch screen still gives you the provider map, agent handoff, and verification preview.</p>
@@ -38815,6 +41327,30 @@ async function sendExtensionIcon(cwd, response) {
38815
41327
  sendText(response, 404, "Icon not found");
38816
41328
  }
38817
41329
  }
41330
+ async function sendMangaAsset(pathname, response) {
41331
+ const name = decodeURIComponent(pathname.replace(/^\/manga\//, ""));
41332
+ if (name.includes("..") || !/^(cards\/)?[a-z0-9._-]+\.(png|webp|svg|jpg|jpeg)$/i.test(name)) {
41333
+ sendText(response, 404, "Asset not found");
41334
+ return;
41335
+ }
41336
+ const candidates = [
41337
+ (0, import_node_path30.join)(__dirname, "manga", name),
41338
+ // installed + dev: <pkg>/dist/manga
41339
+ (0, import_node_path30.join)(__dirname, "..", "assets", "manga", name),
41340
+ // fallback: <pkg>/assets/manga (source)
41341
+ (0, import_node_path30.join)(__dirname, "..", "..", "assets", "manga", name)
41342
+ ];
41343
+ for (const candidate of candidates) {
41344
+ try {
41345
+ const body = await (0, import_promises23.readFile)(candidate);
41346
+ response.setHeader("cache-control", "no-cache");
41347
+ sendBinary(response, 200, contentTypeForAsset(candidate), body);
41348
+ return;
41349
+ } catch {
41350
+ }
41351
+ }
41352
+ sendText(response, 404, "Asset not found");
41353
+ }
38818
41354
  function contentTypeForAsset(pathname) {
38819
41355
  switch ((0, import_node_path30.extname)(pathname).toLowerCase()) {
38820
41356
  case ".svg":
@@ -38866,7 +41402,12 @@ async function handleRequest(request, response, options) {
38866
41402
  }
38867
41403
  if (request.method === "GET" && url.pathname === "/") {
38868
41404
  const state = await readState(options.cwd);
38869
- response.writeHead(200, { "content-type": "text/html; charset=utf-8" });
41405
+ response.writeHead(200, {
41406
+ "content-type": "text/html; charset=utf-8",
41407
+ "cache-control": "no-store, no-cache, must-revalidate",
41408
+ pragma: "no-cache",
41409
+ expires: "0"
41410
+ });
38870
41411
  response.end(renderLocalUiHtml(state));
38871
41412
  return;
38872
41413
  }
@@ -39199,6 +41740,27 @@ async function handleRequest(request, response, options) {
39199
41740
  await sendBundledReportAsset(url.pathname, response);
39200
41741
  return;
39201
41742
  }
41743
+ if (request.method === "GET" && url.pathname.startsWith("/manga/")) {
41744
+ await sendMangaAsset(url.pathname, response);
41745
+ return;
41746
+ }
41747
+ if (request.method === "GET" && url.pathname === "/vendor/anime.umd.min.js") {
41748
+ for (const candidate of [
41749
+ (0, import_node_path30.join)(__dirname, "vendor", "anime.umd.min.js"),
41750
+ (0, import_node_path30.join)(__dirname, "..", "assets", "vendor", "anime.umd.min.js"),
41751
+ (0, import_node_path30.join)(__dirname, "..", "..", "assets", "vendor", "anime.umd.min.js")
41752
+ ]) {
41753
+ try {
41754
+ const body = await (0, import_promises23.readFile)(candidate);
41755
+ response.setHeader("cache-control", "public, max-age=604800, immutable");
41756
+ sendBinary(response, 200, "text/javascript; charset=utf-8", body);
41757
+ return;
41758
+ } catch {
41759
+ }
41760
+ }
41761
+ sendText(response, 404, "Asset not found");
41762
+ return;
41763
+ }
39202
41764
  if (request.method === "POST" && url.pathname === "/api/scan") {
39203
41765
  await options.scanRunner({ "agent-mode": true }, [options.cwd]);
39204
41766
  sendJson(response, await readState(options.cwd));