Spaces:
Running
Running
| <template> | |
| <div class="arena-page"> | |
| <!-- ============== ASSETS ============== --> | |
| <section class="section"> | |
| <header class="section-head"> | |
| <h2 class="section-title"> | |
| <span class="ama-gradient">Assets in the Arena</span> | |
| </h2> | |
| </header> | |
| <!-- Time Window Slider --> | |
| <div class="time-window-control"> | |
| <div class="time-window-header"> | |
| <span class="time-window-label">Time Window:</span> | |
| <span class="time-window-range">{{ formatDate(dateRange[0]) }} - {{ formatDate(dateRange[1]) }}</span> | |
| </div> | |
| <Slider v-model="dateRange" :min="0" :max="maxDateIndex" :range="true" class="time-slider" /> | |
| </div> | |
| <div class="grid grid-assets-4"> | |
| <article v-for="a in assets" :key="a.code" class="card asset-card"> | |
| <div class="asset-head"> | |
| <div class="asset-logo-wrap"> | |
| <img :src="a.icon" :alt="a.code" class="asset-logo" /> | |
| </div> | |
| <div class="asset-name"> | |
| <div class="asset-code truncate">{{ a.code }}</div> | |
| <div class="asset-full truncate" :title="a.name">{{ a.name }}</div> | |
| </div> | |
| <span class="type-badge" :class="'type-' + a.type.toLowerCase()">{{ a.type }}</span> | |
| </div> | |
| <div class="asset-body"> | |
| <div class="spark-wrap"> | |
| <MiniEchart :data="a.month" :color="a.sparkColor" /> | |
| </div> | |
| <div class="asset-stats"> | |
| <div class="stat"> | |
| <div class="stat-label">Return</div> | |
| <div class="stat-value mono" :class="a.change1m >= 0 ? 'pos' : 'neg'"> | |
| {{ signedPct(a.change1m) }} | |
| </div> | |
| </div> | |
| <div class="stat"> | |
| <div class="stat-label">Volatility</div> | |
| <div class="stat-value mono">{{ formatPct(a.volatility) }}</div> | |
| </div> | |
| <div class="stat"> | |
| <div class="stat-label">Runs</div> | |
| <div class="stat-value mono">{{ a.runs }}</div> | |
| </div> | |
| <div class="stat"> | |
| <div class="stat-label">Agents</div> | |
| <div class="stat-value mono">{{ a.agents }}</div> | |
| </div> | |
| <div class="stat"> | |
| <div class="stat-label">Days</div> | |
| <div class="stat-value mono">{{ a.days }}</div> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="asset-foot"> | |
| <div class="asset-foot-row"> | |
| <span class="muted">EOD</span> | |
| <span class="mono">{{ a.eod || '—' }}</span> | |
| </div> | |
| </div> | |
| </article> | |
| </div> | |
| <div class="cta-row"> | |
| <a :href="assetFormUrl" target="_blank" rel="noopener" class="btn-cta link-btn"> | |
| <i class="pi pi-plus mr-2"></i> Vote to Add Asset | |
| </a> | |
| </div> | |
| </section> | |
| <!-- ============== AGENTS (no performance) ============== --> | |
| <section class="section"> | |
| <header class="section-head"> | |
| <h2 class="section-title"> | |
| <span class="ama-gradient">Agents in the Arena</span> | |
| </h2> | |
| <p class="section-sub">Tournament cards • GitHub & arXiv links (no performance)</p> | |
| </header> | |
| <div class="grid grid-agents"> | |
| <article v-for="g in agents" :key="g.name" class="card agent-card"> | |
| <div class="agent-top"> | |
| <div class="agent-badge" :style="{ borderColor: g.color }"> | |
| <div class="agent-ring" :style="{ background: g.color }"></div> | |
| <img :src="g.logo" :alt="g.name" class="agent-logo" /> | |
| </div> | |
| <div class="agent-title"> | |
| <div class="agent-name truncate" :title="g.name">{{ g.name }}</div> | |
| <div class="agent-links"> | |
| <a :href="g.github" target="_blank" rel="noopener" class="link">GitHub</a> | |
| <span class="dot">•</span> | |
| <a :href="g.arxiv" target="_blank" rel="noopener" class="link">arXiv</a> | |
| </div> | |
| </div> | |
| </div> | |
| </article> | |
| </div> | |
| <!-- Info-only integration guide --> | |
| <div class="card integration-guide"> | |
| <h3>Agent Integration Guide</h3> | |
| <div class="guide-section"> | |
| <h4>Quick Summary</h4> | |
| <p><strong>What you need:</strong> Create an API that receives market data and returns trading decisions.</p> | |
| </div> | |
| <div class="guide-section"> | |
| <h4>Input (What we send to your agent)</h4> | |
| <pre class="code-block">{ | |
| "date": "2025-10-24", | |
| "price": {"BTC": 67890.50}, | |
| "news": {"BTC": ["Bitcoin news 1...", "Bitcoin news 2...", "Bitcoin news 3..."]}, | |
| "model": "gpt-4o", | |
| "history_price": { | |
| "BTC": [ | |
| {"date": "2025-10-14", "price": 65000.00}, | |
| {"date": "2025-10-15", "price": 65500.00}, | |
| {"date": "2025-10-16", "price": 66000.00}, | |
| {"date": "2025-10-17", "price": 66200.00}, | |
| {"date": "2025-10-18", "price": 66500.00}, | |
| {"date": "2025-10-21", "price": 66800.00}, | |
| {"date": "2025-10-22", "price": 67000.00}, | |
| {"date": "2025-10-23", "price": 67500.00} | |
| ] | |
| } | |
| }</pre> | |
| <p class="note"><strong>Note:</strong> <code>history_price</code> contains the last 10 days of price data (if available).</p> | |
| </div> | |
| <div class="guide-section"> | |
| <h4>Output (What we expect from your agent)</h4> | |
| <pre class="code-block">{ | |
| "recommended_action": "BUY" | |
| }</pre> | |
| <p class="note"><strong>Valid actions:</strong> <code>"BUY"</code>, <code>"SELL"</code>, or <code>"HOLD"</code> (uppercase)</p> | |
| </div> | |
| </div> | |
| <!-- Submit Agent button (Google Form link) --> | |
| <div class="cta-row"> | |
| <a :href="agentFormUrl" target="_blank" rel="noopener" class="btn-cta link-btn"> | |
| <i class="pi pi-send mr-2"></i> Submit Agent (Google Form) | |
| </a> | |
| </div> | |
| </section> | |
| <!-- ============== PAPER & CITATION ============== --> | |
| <section class="section paper-section"> | |
| <header class="section-head"> | |
| <h2 class="section-title"> | |
| <span class="ama-gradient">Paper & Citation</span> | |
| </h2> | |
| </header> | |
| <div class="paper-card"> | |
| <div class="paper-content"> | |
| <div class="paper-description"> | |
| For more information, please check our released paper in arXiv. And remember to cite us if you find it useful! | |
| </div> | |
| <div class="paper-links"> | |
| <a href="https://arxiv.org/pdf/2510.11695" target="_blank" rel="noopener" class="paper-link"> | |
| <i class="pi pi-file-pdf"></i> Read Paper on arXiv | |
| </a> | |
| </div> | |
| <div class="citation-box"> | |
| <div class="citation-header"> | |
| <span class="citation-label">BibTeX Citation</span> | |
| <button @click="copyCitation" class="copy-btn" :class="{ 'copied': citationCopied }"> | |
| <i :class="citationCopied ? 'pi pi-check' : 'pi pi-copy'"></i> | |
| {{ citationCopied ? 'Copied!' : 'Copy' }} | |
| </button> | |
| </div> | |
| <pre class="citation-text">@article{qian2025agents, | |
| title={When Agents Trade: Live Multi-Market Trading Benchmark for LLM Agents}, | |
| author={Qian, Lingfei and Peng, Xueqing and Wang, Yan and Zhang, Vincent Jim and He, Huan and Smith, Hanley and Han, Yi and He, Yueru and Li, Haohang and Cao, Yupeng and others}, | |
| journal={arXiv preprint arXiv:2510.11695}, | |
| year={2025} | |
| }</pre> | |
| </div> | |
| </div> | |
| </div> | |
| </section> | |
| </div> | |
| </template> | |
| <script> | |
| import { dataService } from '../lib/dataService' | |
| import { getAllDecisions } from '../lib/dataCache' | |
| import { readAllRawDecisions } from '../lib/idb' | |
| import { filterRowsToNyseTradingDays } from '../lib/marketCalendar' | |
| import { computeBuyHoldEquity } from '../lib/perf' | |
| import MiniEchart from '../components/MiniEchart.vue' | |
| export default { | |
| name: 'RequestView', | |
| components: { MiniEchart }, | |
| data() { | |
| return { | |
| assetFormUrl: 'https://docs.google.com/forms/d/e/1FAIpQLSdA4bZau8X2gEWjxRY3oFolcGa4-s4_D4F5Ky1uh48hnwMkVA/viewform?usp=dialog', | |
| agentFormUrl: 'https://docs.google.com/forms/d/e/1FAIpQLSdwaheaiVYcBQVNOEyUD6ERTLXD6fKrOgGPKkjTrG9zi_feqw/viewform?usp=publish-editor', | |
| // dynamic from dataService | |
| assets: [], | |
| // time window control | |
| allDates: [], | |
| dateRange: [0, 0], | |
| maxDateIndex: 0, | |
| // agents (no performance fields shown) | |
| agents: [ | |
| { | |
| name: 'InvestorAgent', | |
| github: 'https://github.com/felis33/INVESTOR-BENCH', | |
| arxiv: 'https://arxiv.org/abs/2412.18174', | |
| strategy: 'Aggressive Long Only', | |
| focus: 'BTC, MSFT', | |
| color: 'linear-gradient(90deg,#ffd700,#eab308)', | |
| logo: new URL('../assets/images/agents_images/investor.png', import.meta.url).href | |
| }, | |
| { | |
| name: 'TradeAgent', | |
| github: 'https://github.com/TauricResearch/TradingAgents', | |
| arxiv: 'https://arxiv.org/abs/2412.20138', | |
| strategy: 'Aggressive Long Only', | |
| focus: 'BTC, ETH', | |
| color: 'linear-gradient(90deg,#4338ca,#6d28d9)', | |
| logo: new URL('../assets/images/agents_images/tradeagent.png', import.meta.url).href | |
| }, | |
| { | |
| name: 'DeepFundAgent', | |
| github: 'https://github.com/HKUSTDial/DeepFund', | |
| arxiv: 'https://arxiv.org/abs/2505.11065', | |
| strategy: 'Aggressive Long Only', | |
| focus: '—', | |
| color: 'linear-gradient(90deg,#0ea5e9,#22d3ee)', | |
| logo: new URL('../assets/images/agents_images/deepfund.png', import.meta.url).href | |
| }, | |
| { | |
| name: 'HedgeFundAgent', | |
| github: 'https://github.com/virattt/ai-hedge-fund', | |
| arxiv: 'https://github.com/virattt/ai-hedge-fund', | |
| strategy: 'Aggressive Long Only', | |
| focus: 'BMRN, TSLA', | |
| color: 'linear-gradient(90deg,#f43f5e,#ef4444)', | |
| logo: new URL('../assets/images/agents_images/hedgefund.png', import.meta.url).href | |
| } | |
| ], | |
| // live state | |
| rowsRef: [], | |
| allDecisions: [], | |
| unsubscribe: null, | |
| ASSET_ICONS: { | |
| BTC: new URL('../assets/images/assets_images/BTC.png', import.meta.url).href, | |
| ETH: new URL('../assets/images/assets_images/ETH.png', import.meta.url).href, | |
| MSFT: new URL('../assets/images/assets_images/MSFT.png', import.meta.url).href, | |
| BMRN: new URL('../assets/images/assets_images/BMRN.png', import.meta.url).href, | |
| TSLA: new URL('../assets/images/assets_images/TSLA.png', import.meta.url).href | |
| }, | |
| // Paper & Citation | |
| citationCopied: false | |
| } | |
| }, | |
| mounted() { | |
| this.unsubscribe = dataService.subscribe((state) => { | |
| this.rowsRef = Array.isArray(state.tableRows) ? state.tableRows : [] | |
| this.rebuildAssets().catch(() => {}) | |
| }) | |
| this.rowsRef = Array.isArray(dataService.tableRows) ? dataService.tableRows : [] | |
| if (!dataService.loaded && !dataService.loading) { | |
| dataService.load(false).catch(e => console.error('RequestView: load failed', e)) | |
| } | |
| // warm decision cache | |
| this.allDecisions = getAllDecisions() || [] | |
| if (!this.allDecisions.length) { | |
| readAllRawDecisions().then(cached => { if (cached?.length) this.allDecisions = cached }) | |
| } | |
| this.rebuildAssets().catch(() => {}) | |
| }, | |
| beforeUnmount() { | |
| if (this.unsubscribe) { this.unsubscribe(); this.unsubscribe = null } | |
| }, | |
| methods: { | |
| signedPct(p) { | |
| const v = Number(p || 0) * 100 | |
| const s = v >= 0 ? '+' : '−' | |
| return `${s}${Math.abs(v).toFixed(2)}%` | |
| }, | |
| fmtUSD(n) { | |
| return (n ?? 0).toLocaleString(undefined, { style: 'currency', currency: 'USD', maximumFractionDigits: 2 }) | |
| }, | |
| formatPct(p) { | |
| const v = Number(p || 0) * 100 | |
| return `${Math.abs(v).toFixed(2)}%` | |
| }, | |
| formatDate(index) { | |
| if (!this.allDates || index < 0 || index >= this.allDates.length) return '—' | |
| return this.allDates[index] | |
| }, | |
| calculateVolatility(values) { | |
| if (!Array.isArray(values) || values.length < 2) return 0 | |
| // Calculate returns | |
| const returns = [] | |
| for (let i = 1; i < values.length; i++) { | |
| if (values[i-1] > 0) { | |
| returns.push((values[i] - values[i-1]) / values[i-1]) | |
| } | |
| } | |
| if (returns.length === 0) return 0 | |
| // Calculate mean | |
| const mean = returns.reduce((sum, r) => sum + r, 0) / returns.length | |
| // Calculate variance | |
| const variance = returns.reduce((sum, r) => sum + Math.pow(r - mean, 2), 0) / returns.length | |
| // Return standard deviation (volatility) | |
| return Math.sqrt(variance) | |
| }, | |
| /* ===== Dynamic assets built from B&H equity ===== */ | |
| async buildAssetSeq(assetCode) { | |
| let seq = (this.allDecisions || []).filter(r => r.asset === assetCode) | |
| seq.sort((a,b) => (a.date > b.date ? 1 : -1)) | |
| const isCrypto = assetCode === 'BTC' || assetCode === 'ETH' | |
| if (!isCrypto) { | |
| try { seq = await filterRowsToNyseTradingDays(seq) } catch {} | |
| } | |
| return seq | |
| }, | |
| sampleSeries(values, n = 12) { | |
| if (!Array.isArray(values) || !values.length) return [] | |
| if (values.length <= n) return values.slice(-n) | |
| const step = (values.length - 1) / (n - 1) | |
| const out = [] | |
| for (let i = 0; i < n; i++) out.push(values[Math.round(i * step)]) | |
| return out | |
| }, | |
| pctChange(a, b) { | |
| if (a == null || b == null || a === 0) return 0 | |
| return (b / a) - 1 | |
| }, | |
| async rebuildAssets() { | |
| console.log('[RequestView] rebuildAssets called, allDecisions:', this.allDecisions?.length || 0, 'rowsRef:', this.rowsRef?.length || 0) | |
| const assetsInRows = Array.from(new Set((this.rowsRef || []).map(r => r.asset))).filter(Boolean) | |
| console.log('[RequestView] Assets in rows:', assetsInRows) | |
| if (!assetsInRows.length) { this.assets = []; return } | |
| // Initialize all dates from first asset to set up slider | |
| if (this.allDates.length === 0 && assetsInRows.length > 0) { | |
| const firstSeq = await this.buildAssetSeq(assetsInRows[0]) | |
| if (firstSeq.length > 0) { | |
| this.allDates = firstSeq.map(r => r.date).filter(d => d >= '2025-08-01') | |
| this.maxDateIndex = this.allDates.length - 1 | |
| this.dateRange = [0, this.maxDateIndex] | |
| } | |
| } | |
| const cards = [] | |
| for (const code of assetsInRows) { | |
| const fullSeq = await this.buildAssetSeq(code) | |
| console.log('[RequestView] Asset', code, 'full seq length:', fullSeq?.length || 0) | |
| if (!fullSeq.length) continue | |
| // Filter sequence based on date range | |
| const startDate = this.allDates[this.dateRange[0]] || '2025-08-01' | |
| const endDate = this.allDates[this.dateRange[1]] || this.allDates[this.allDates.length - 1] | |
| const seq = fullSeq.filter(r => r.date >= startDate && r.date <= endDate) | |
| console.log('[RequestView] Asset', code, 'filtered seq length:', seq?.length || 0, 'from', startDate, 'to', endDate) | |
| if (!seq.length) continue | |
| // B&H equity as normalized series for spark and change | |
| let bh = [] | |
| try { bh = computeBuyHoldEquity(seq, 100000) || [] } catch { bh = [] } | |
| const lastIdx = bh.length - 1 | |
| console.log('[RequestView] Asset', code, 'bh length:', bh?.length || 0) | |
| if (lastIdx < 0) continue | |
| // Get end_date from actual sequence data (most accurate) | |
| const rowsForAsset = (this.rowsRef || []).filter(r => r.asset === code) | |
| const lastDate = seq[lastIdx]?.date || null | |
| const spark = this.sampleSeries(bh.map(v => v), 12) | |
| console.log('[RequestView] Asset', code, 'spark data:', spark) | |
| const change1m = this.pctChange(spark[0], spark[spark.length - 1]) | |
| // Calculate volatility | |
| const volatility = this.calculateVolatility(bh) | |
| // trading day span | |
| let days = 0 | |
| try { | |
| const start = new Date(seq[0].date) | |
| const end = new Date(seq[lastIdx].date) | |
| days = Math.max(1, Math.round((end - start) / 86400000) + 1) | |
| } catch {} | |
| // runs & agents from rows table | |
| const runs = rowsForAsset.length | |
| const agents = new Set(rowsForAsset.map(r => r.agent_name)).size | |
| cards.push({ | |
| code, | |
| name: code, | |
| type: (code === 'BTC' || code === 'ETH') ? 'Crypto' : 'Stock', | |
| icon: this.ASSET_ICONS[code] || null, | |
| month: spark, | |
| sparkColor: (code === 'BTC') ? '#f59e0b' : (code === 'ETH' ? '#6366f1' : '#22c55e'), | |
| change1m, | |
| volatility, | |
| runs, agents, days, | |
| eod: lastDate | |
| }) | |
| } | |
| // Stable display order | |
| const order = ['BTC','ETH','MSFT','BMRN','TSLA'] | |
| cards.sort((a,b) => (order.indexOf(a.code) - order.indexOf(b.code))) | |
| this.assets = cards | |
| }, | |
| async copyCitation() { | |
| const citation = `@article{qian2025agents, | |
| title={When Agents Trade: Live Multi-Market Trading Benchmark for LLM Agents}, | |
| author={Qian, Lingfei and Peng, Xueqing and Wang, Yan and Zhang, Vincent Jim and He, Huan and Smith, Hanley and Han, Yi and He, Yueru and Li, Haohang and Cao, Yupeng and others}, | |
| journal={arXiv preprint arXiv:2510.11695}, | |
| year={2025} | |
| }` | |
| try { | |
| await navigator.clipboard.writeText(citation) | |
| this.citationCopied = true | |
| setTimeout(() => { | |
| this.citationCopied = false | |
| }, 2000) | |
| } catch (err) { | |
| console.error('Failed to copy citation:', err) | |
| } | |
| } | |
| }, | |
| watch: { | |
| dateRange() { | |
| // Rebuild assets when date range changes | |
| this.rebuildAssets().catch(() => {}) | |
| } | |
| } | |
| } | |
| </script> | |
| <style scoped> | |
| /* ===== AMA Core ===== */ | |
| .ama-gradient{ | |
| background: linear-gradient(90deg, rgb(0,0,185), #7b2cbf, #d946ef, rgb(240,0,15)); | |
| -webkit-background-clip: text; background-clip: text; color: transparent; | |
| } | |
| .arena-page{ | |
| max-width:1280px; margin:0 auto; | |
| padding:16px 20px 120px; background:#fff; | |
| } | |
| /* ===== Helpers ===== */ | |
| .truncate{ overflow:hidden; text-overflow:ellipsis; white-space:nowrap; } | |
| .clamp-2{ display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; overflow:hidden; } | |
| .w-full{ width:100%; } | |
| /* ===== Time Window Control ===== */ | |
| .time-window-control{ margin:12px 0 16px; padding:16px; background:#F6F8FB; border:1px solid #E7ECF3; border-radius:12px; } | |
| .time-window-header{ display:flex; justify-content:space-between; align-items:center; margin-bottom:12px; } | |
| .time-window-label{ font-weight:700; font-size:14px; color:#0f172a; } | |
| .time-window-range{ font-size:13px; color:#64748b; font-family: ui-monospace, monospace; } | |
| .time-slider{ margin-top:8px; } | |
| /* ===== Sections ===== */ | |
| .section{ margin-top:18px; } | |
| .section-head{ margin-bottom:8px; } | |
| .section-title{ font-size:clamp(1.2rem, 1.2rem + 0.4vw, 1.6rem); font-weight:800; letter-spacing:-0.02em; display:inline-block; } | |
| .section-sub{ margin-top:4px; color:#6b7280; } | |
| /* ===== Grids ===== */ | |
| .grid{ display:grid; gap:14px; min-width:0; } | |
| .grid-assets-4{ grid-template-columns: repeat(4, minmax(0,1fr)); } | |
| .grid-agents{ grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); } | |
| @media (max-width: 1200px){ .grid-assets-4{ grid-template-columns: repeat(3, minmax(0,1fr)); } } | |
| @media (max-width: 900px){ .grid-assets-4{ grid-template-columns: repeat(2, minmax(0,1fr)); } } | |
| @media (max-width: 620px){ .grid-assets-4{ grid-template-columns: 1fr; } } | |
| /* ===== Card base ===== */ | |
| .card{ | |
| background:#ffffff; border:1px solid #E7ECF3; border-radius:14px; | |
| box-shadow:0 1px 2px rgba(16,24,40,.03), 0 4px 12px rgba(16,24,40,.04); | |
| transition: transform .18s ease, box-shadow .2s ease, border-color .2s ease; | |
| min-width:0; | |
| } | |
| .card:hover{ transform:translateY(-2px); box-shadow:0 10px 26px rgba(16,24,40,.08); border-color:#D9E2EF; } | |
| /* ===== Assets ===== */ | |
| .asset-card{ padding:12px; display:flex; flex-direction:column; gap:10px; } | |
| .asset-head{ display:grid; grid-template-columns:44px 1fr auto; gap:10px; align-items:center; min-width:0; } | |
| .asset-logo-wrap{ width:44px; height:44px; border-radius:12px; background:#f3f4f6; display:grid; place-items:center; border:1px solid #E7ECF3; overflow:hidden; } | |
| .asset-logo{ width:70%; height:70%; object-fit:contain; } | |
| .asset-name{ min-width:0; } | |
| .asset-code{ font-weight:900; color:#0f172a; letter-spacing:.02em; } | |
| .asset-full{ font-size:12px; color:#64748b; } | |
| .type-badge{ | |
| border-radius:999px; padding:4px 8px; font-size:12px; font-weight:700; | |
| border:1px solid #E7ECF3; background:#F6F8FB; color:#0f172a; white-space:nowrap; | |
| } | |
| .type-crypto{ background:#f8fbff; border-color:#e0e7ff; color:#1e3a8a; } | |
| .type-stock { background:#f6fffb; border-color:#d9fbe6; color:#064e3b; } | |
| .asset-body{ display:flex; flex-direction:column; gap:10px; } | |
| .spark-wrap{ width:100%; height:90px; overflow:hidden; border-radius:8px; background:#F6F8FB; border:1px solid #E7ECF3; position:relative; } | |
| .asset-stats{ display:grid; grid-template-columns:repeat(5, minmax(0, 1fr)); gap:6px; } | |
| .stat{ background:#F6F8FB; border:1px solid #E7ECF3; border-radius:8px; padding:4px 6px; min-width:0; display:flex; flex-direction:column; align-items:center; text-align:center; } | |
| .stat-label{ font-size:9px; color:#6b7280; margin-bottom:2px; white-space:nowrap; } | |
| .stat-value{ font-weight:700; font-size:11.5px; color:#0f172a; line-height:1.2; } | |
| .mono{ font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace; } | |
| .muted{ color:#6b7280; } | |
| .asset-desc{ margin-top:2px; font-size:12.5px; line-height:1.45; } | |
| .asset-foot{ margin-top:4px; } | |
| .asset-bar{ height:6px; border-radius:999px; border:1px solid #E7ECF3; background:#F2F4F8; position:relative; overflow:hidden; } | |
| .asset-bar::after{ content:''; position:absolute; left:0; top:0; height:100%; width:var(--pct,0%); background:linear-gradient(90deg,#16a34a,#22c55e); } | |
| .asset-foot-row{ margin-top:6px; display:flex; align-items:center; justify-content:space-between; color:#6b7280; } | |
| /* ===== Agents (perf-free) ===== */ | |
| .agent-card{ padding:12px; display:flex; flex-direction:column; gap:8px; } | |
| .agent-top{ display:grid; grid-template-columns:52px 1fr; gap:10px; align-items:center; min-width:0; } | |
| .agent-badge{ width:52px; height:52px; border-radius:16px; border:2px solid #e5e7eb; position:relative; display:grid; place-items:center; overflow:hidden; } | |
| .agent-ring{ position:absolute; inset:0; opacity:.18; } | |
| .agent-logo{ width:70%; height:70%; object-fit:contain; position:relative; z-index:1; } | |
| .agent-title{ min-width:0; } | |
| .agent-name{ font-weight:900; letter-spacing:.02em; color:#0f172a; text-transform:uppercase; } | |
| .agent-links{ display:flex; align-items:center; gap:8px; color:#64748b; flex-wrap:wrap; min-width:0; } | |
| .link{ color:#334155; font-weight:600; text-decoration:none; } | |
| .link:hover{ color:#0f172a; text-decoration:underline; } | |
| .dot{ color:#94a3b8; } | |
| .agent-foot{ display:flex; align-items:center; gap:8px; flex-wrap:wrap; margin-top:2px; } | |
| .chip{ display:inline-flex; align-items:center; gap:6px; padding:4px 8px; border-radius:999px; font-size:12px; font-weight:700; background:#F6F8FB; color:#0f172a; border:1px solid #E7ECF3; white-space:nowrap; } | |
| .chip-outline{ background:#fff; } | |
| /* ===== Integration Guide ===== */ | |
| .integration-guide{ margin-top:12px; padding:16px; } | |
| .integration-guide h3{ font-weight:800; font-size:1.1rem; color:#0f172a; margin-bottom:8px; } | |
| .guide-section{ margin:10px 0; } | |
| .code-block{ background:#0b1020; color:#E6EDF3; border-radius:10px; padding:12px; overflow:auto; font-size:12.5px; line-height:1.5; } | |
| .note{ color:#334155; margin-top:6px; } | |
| /* ===== Buttons ===== */ | |
| .cta-row{ margin-top:12px; display:flex; justify-content:center; } | |
| .link-btn{ display:inline-flex; align-items:center; justify-content:center; gap:.5rem; text-decoration:none; } | |
| .btn-cta{ background:linear-gradient(90deg, rgb(0,0,185), #7b2cbf, #d946ef, rgb(240,0,15)); border:none; color:#fff; font-weight:800; letter-spacing:.02em; padding:10px 16px; border-radius:12px; cursor:pointer; } | |
| .mr-2{ margin-right:.5rem; } | |
| .pos{ color:#0e7a3a; } .neg{ color:#B91C1C; } | |
| /* ===== Paper & Citation ===== */ | |
| .paper-section{ margin-top:40px; margin-bottom:40px; } | |
| .paper-card{ | |
| background:#ffffff; border:1px solid #E7ECF3; border-radius:14px; | |
| box-shadow:0 1px 2px rgba(16,24,40,.03), 0 4px 12px rgba(16,24,40,.04); | |
| padding:24px; | |
| } | |
| .paper-content{ display:flex; flex-direction:column; gap:20px; } | |
| .paper-description{ | |
| font-size:1.05rem; font-weight:500; color:#334155; | |
| line-height:1.6; margin:0; | |
| } | |
| .paper-links{ display:flex; gap:12px; align-items:center; } | |
| .paper-link{ | |
| display:inline-flex; align-items:center; gap:8px; | |
| padding:10px 18px; background:#f3f4f6; border:1px solid #E7ECF3; | |
| border-radius:10px; text-decoration:none; color:#0f172a; | |
| font-weight:700; font-size:14px; transition:all .2s ease; | |
| } | |
| .paper-link:hover{ | |
| background:#e5e7eb; border-color:#cbd5e1; transform:translateY(-1px); | |
| box-shadow:0 4px 12px rgba(16,24,40,.08); | |
| } | |
| .paper-link i{ font-size:16px; } | |
| .citation-box{ | |
| background:#f8fafc; border:1px solid #e2e8f0; | |
| border-radius:12px; padding:16px; | |
| } | |
| .citation-header{ | |
| display:flex; justify-content:space-between; align-items:center; | |
| margin-bottom:12px; | |
| } | |
| .citation-label{ | |
| font-weight:700; font-size:14px; color:#0f172a; | |
| } | |
| .copy-btn{ | |
| display:inline-flex; align-items:center; gap:6px; | |
| padding:6px 12px; background:#ffffff; border:1px solid #cbd5e1; | |
| border-radius:8px; cursor:pointer; font-weight:600; | |
| font-size:13px; color:#334155; transition:all .2s ease; | |
| } | |
| .copy-btn:hover{ | |
| background:#f1f5f9; border-color:#94a3b8; | |
| } | |
| .copy-btn.copied{ | |
| background:#dcfce7; border-color:#86efac; color:#166534; | |
| } | |
| .copy-btn i{ font-size:12px; } | |
| .citation-text{ | |
| background:#0b1020; color:#E6EDF3; border-radius:8px; | |
| padding:14px; overflow:auto; font-family: ui-monospace, monospace; | |
| font-size:13px; line-height:1.6; margin:0; white-space:pre; | |
| } | |
| @media (max-width: 620px){ | |
| .paper-card{ padding:16px; } | |
| .paper-description{ font-size:0.95rem; } | |
| .citation-header{ flex-direction:column; align-items:flex-start; gap:8px; } | |
| } | |
| </style> | |