一、④段整套同步(用户选候选 A)
- 新增 `references/stage-proto-doc/assets/产品文档模板.html`(16 KB/247 行,从备份拷、转 LF)。
- ④段 SKILL.md 换成备份 15:28 那版,并按模板口径改齐:
· 「产品文档框架」从「四块+一附录」改成**六节** —— 页面结构/功能清单/使用场景/操作流程/
跨页与界面状态/待确认与偏离;与模板同构,⛔ 不从空白页重写结构。
· 编号从 R/S/F 改成 **P 页面/S 场景/F 功能/J 二级功能**,各自唯一、按展示顺序通排。
· 保留旧版有价值的要求:每项功能补齐 做什么/入口在哪/输入边界/异常与恢复;重点功能三处标红;
横向流程图每步写清页面/功能/操作/完成判据。
· 完成标准「七节」→「六节」,自查清单同步换掉「需求」那批条目,补模板与配图两条。
· 路径 `designs/<项目>/` → `docs/pm/<项目>/ui/`(2 处)。
· 补回本机那条「每份产出文档开头都要有一张图」的引用(指向主 skill,段内不复述)。
- 主 skill 同步:四段结构表④行、④段沿革、画图小节(④段画页面结构图 + 用户路径)。
二、open-design 移到技能根(用户选候选 A)
- `git mv product-planning/references/stage-delivery/assets/open-design → open-design/`(4178 文件)。
- `.gitignore` 白名单加 `!/open-design/` —— 否则移出去就不再纳入版本控制。
- 引用改 11 处:路径形态 `assets/open-design/...` → `<技能根>/open-design/...`;
「随段携带的自带件」改称「同根技能」;「路径怎么找」那条改成自带件(段内相对路径)与
同根技能(`<技能根>/<技能名>/`)两类分开写。
- open-design 自己的 ATTRIBUTION.md 两处同步(它现在与 oil-ui-pro 同层)。
- 副作用(有益):命名闸门读数由 2063 降到 225 —— 此前它把 open-design 里 919 个第三方 md
当成规则一起扫了;现在只扫本包 23 个 md。已确认④段的 H1 与 4a/4b/4c 子步仍在检查范围内。
三、验收
- check_naming.py --ws E:/ProgramData/AIProject/contentm_agent = 通过 225 / 失败 0 / 提示 27。
- 活文件里 `assets/open-design` 残留 0;工作区 CRLF 文件 0。
2317 lines
119 KiB
HTML
2317 lines
119 KiB
HTML
<!doctype html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="utf-8" />
|
||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||
<title>Quiver · Crypto Live Portfolio</title>
|
||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||
<link href="https://fonts.googleapis.com/css2?family=Geist:wght@400;500;600;700&family=Geist+Mono:wght@400;500;600&family=JetBrains+Mono:wght@400;500;600;700&family=Instrument+Serif&display=swap" rel="stylesheet">
|
||
<style>
|
||
:root {
|
||
/* DeFi terminal — deeper, warmer black with crypto-native accents */
|
||
--bg: #060309; /* almost-black with magenta undertone */
|
||
--bg-grid: #0a050f;
|
||
--surface: #110815; /* card warm-dark */
|
||
--surface-2: #170a1c;
|
||
--surface-3: #1f0d28;
|
||
--surface-glow: #261034;
|
||
--line: #28163a; /* purple-tinted lines */
|
||
--line-strong: #3b1f55;
|
||
--hairline: rgba(255,0,170,0.06);
|
||
|
||
/* Ink */
|
||
--ink: #f4ecff; /* slightly warmer/violet white */
|
||
--ink-soft: #cdb8e3;
|
||
--muted: #8a7099;
|
||
--muted-2: #5b3f6e;
|
||
--dim: #2c1c3a;
|
||
|
||
/* Semantic */
|
||
--up: #00ff9d; /* matrix-neon green — terminal feel */
|
||
--up-soft: rgba(0,255,157,0.14);
|
||
--up-line: rgba(0,255,157,0.55);
|
||
--down: #ff2e6b; /* hot pink-red */
|
||
--down-soft: rgba(255,46,107,0.14);
|
||
--down-line: rgba(255,46,107,0.55);
|
||
|
||
/* Crypto-native accents */
|
||
--accent: #f7931a; /* Bitcoin orange — data highlight */
|
||
--accent-soft: rgba(247,147,26,0.14);
|
||
--magenta: #ff00aa; /* DeFi magenta — primary brand */
|
||
--magenta-soft: rgba(255,0,170,0.14);
|
||
--cyan: #00e0ff; /* electric blue — chart primary */
|
||
--cyan-soft: rgba(0,224,255,0.14);
|
||
--violet: #c084fc; /* AI / ETH band */
|
||
--violet-soft: rgba(192,132,252,0.16);
|
||
--matrix: #00ff9d; /* matrix green */
|
||
--solana: #14f195; /* SOL teal */
|
||
|
||
/* Type — heavier monospace presence for terminal/explorer feel */
|
||
--display: 'Geist', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
||
--body: 'Geist', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
||
--mono: 'JetBrains Mono', 'Geist Mono', ui-monospace, 'SFMono-Regular', Menlo, monospace;
|
||
--serif: 'Instrument Serif', 'Iowan Old Style', Georgia, serif;
|
||
|
||
--r-card: 6px; /* sharper corners — engineering / terminal */
|
||
--r-pill: 999px;
|
||
--r-chip: 4px;
|
||
}
|
||
|
||
* { box-sizing: border-box; }
|
||
html, body { margin: 0; padding: 0; }
|
||
body {
|
||
background:
|
||
/* Hex grid SVG overlay — DeFi/web3 aesthetic */
|
||
url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='56' height='48' viewBox='0 0 56 48'><path d='M28 0 L52 14 L52 38 L28 52 L4 38 L4 14 Z' fill='none' stroke='%23ff00aa' stroke-opacity='0.04' stroke-width='0.7'/></svg>"),
|
||
radial-gradient(1200px 600px at 80% -10%, rgba(255,0,170,0.10), transparent 60%),
|
||
radial-gradient(900px 500px at -10% 30%, rgba(247,147,26,0.07), transparent 60%),
|
||
radial-gradient(1200px 800px at 50% 110%, rgba(0,255,157,0.05), transparent 60%),
|
||
radial-gradient(800px 400px at 100% 60%, rgba(0,224,255,0.06), transparent 60%),
|
||
var(--bg);
|
||
color: var(--ink);
|
||
font-family: var(--body);
|
||
font-size: 13.5px;
|
||
line-height: 1.5;
|
||
letter-spacing: -0.005em;
|
||
-webkit-font-smoothing: antialiased;
|
||
text-rendering: optimizeLegibility;
|
||
min-height: 100vh;
|
||
}
|
||
a { color: inherit; text-decoration: none; }
|
||
button { font-family: inherit; cursor: pointer; }
|
||
|
||
/* ─────── On-chain ribbon (crypto-specific) ─────── */
|
||
.onchain-ribbon {
|
||
border-bottom: 1px solid var(--hairline);
|
||
background:
|
||
linear-gradient(180deg, rgba(255,0,170,0.04), rgba(247,147,26,0.02)),
|
||
var(--bg-grid);
|
||
padding: 8px 28px;
|
||
display: flex; align-items: center; gap: 18px; flex-wrap: wrap;
|
||
font-family: var(--mono); font-size: 10.5px;
|
||
color: var(--muted);
|
||
text-transform: uppercase; letter-spacing: 0.1em;
|
||
}
|
||
.onchain-ribbon .label { color: var(--muted-2); }
|
||
.onchain-ribbon .v { color: var(--ink-soft); font-weight: 500; }
|
||
.onchain-ribbon .item { display: inline-flex; gap: 6px; align-items: center; }
|
||
.onchain-ribbon .item.live::before {
|
||
content: ""; width: 6px; height: 6px; border-radius: 50%;
|
||
background: var(--matrix);
|
||
box-shadow: 0 0 8px var(--matrix);
|
||
animation: matrixBlink 1.4s ease-in-out infinite;
|
||
}
|
||
@keyframes matrixBlink {
|
||
0%, 100% { opacity: 1; }
|
||
50% { opacity: 0.4; }
|
||
}
|
||
.onchain-ribbon .block-h {
|
||
font-family: var(--mono); font-weight: 700; color: var(--matrix);
|
||
text-shadow: 0 0 12px rgba(0,255,157,0.4);
|
||
letter-spacing: 0.04em;
|
||
}
|
||
.onchain-ribbon .gas {
|
||
color: var(--accent);
|
||
font-weight: 600;
|
||
}
|
||
.onchain-ribbon .item .v.cyan { color: var(--cyan); }
|
||
.onchain-ribbon .item .v.magenta { color: var(--magenta); text-shadow: 0 0 8px rgba(255,0,170,0.4); }
|
||
|
||
/* ─────── Top ribbon: ticker tape ─────── */
|
||
.ticker-tape {
|
||
border-bottom: 1px solid var(--hairline);
|
||
background: linear-gradient(180deg, #0a0d14, #07090d);
|
||
overflow: hidden;
|
||
position: relative;
|
||
height: 32px;
|
||
}
|
||
.ticker-tape::before, .ticker-tape::after {
|
||
content: ""; position: absolute; top: 0; bottom: 0; width: 60px; z-index: 2; pointer-events: none;
|
||
}
|
||
.ticker-tape::before { left: 0; background: linear-gradient(90deg, var(--bg), transparent); }
|
||
.ticker-tape::after { right: 0; background: linear-gradient(270deg, var(--bg), transparent); }
|
||
.tape {
|
||
display: inline-flex; align-items: center; height: 32px; gap: 28px; padding-right: 28px;
|
||
animation: tape 60s linear infinite;
|
||
white-space: nowrap;
|
||
font-family: var(--mono);
|
||
font-size: 11.5px;
|
||
}
|
||
.tape-item { display: inline-flex; gap: 8px; align-items: center; color: var(--muted); }
|
||
.tape-item .sym { color: var(--ink); font-weight: 600; letter-spacing: 0.02em; }
|
||
.tape-item .px { color: var(--ink-soft); }
|
||
.tape-item .pct.up { color: var(--up); }
|
||
.tape-item .pct.down { color: var(--down); }
|
||
@keyframes tape { from { transform: translateX(0); } to { transform: translateX(-50%); } }
|
||
|
||
/* ─────── App header ─────── */
|
||
header.app {
|
||
display: flex; align-items: center; justify-content: space-between;
|
||
padding: 18px 28px;
|
||
border-bottom: 1px solid var(--hairline);
|
||
background: rgba(7,9,13,0.6);
|
||
backdrop-filter: blur(18px) saturate(140%);
|
||
position: sticky; top: 0; z-index: 50;
|
||
}
|
||
.brand { display: flex; align-items: center; gap: 12px; }
|
||
.logo {
|
||
width: 30px; height: 30px;
|
||
clip-path: polygon(50% 0%, 100% 25%, 100% 75%, 50% 100%, 0% 75%, 0% 25%);
|
||
background:
|
||
radial-gradient(circle at 30% 30%, #ffb86b, transparent 55%),
|
||
conic-gradient(from 200deg, #f7931a, #ff00aa, #00e0ff, #00ff9d, #f7931a);
|
||
box-shadow: 0 0 24px rgba(255,0,170,0.4), 0 0 12px rgba(247,147,26,0.3);
|
||
filter: saturate(1.15);
|
||
}
|
||
.brand .word { font-family: var(--display); font-weight: 700; letter-spacing: -0.02em; font-size: 17px; }
|
||
.brand .pro {
|
||
font-family: var(--mono); font-size: 10px;
|
||
color: var(--magenta); border: 1px solid rgba(255,0,170,0.55);
|
||
padding: 2px 6px; border-radius: 4px; text-transform: uppercase; letter-spacing: 0.16em;
|
||
background: var(--magenta-soft);
|
||
box-shadow: 0 0 12px rgba(255,0,170,0.2), inset 0 0 12px rgba(255,0,170,0.05);
|
||
}
|
||
|
||
.nav { display: flex; gap: 4px; margin-left: 16px; }
|
||
.nav a {
|
||
font-family: var(--mono); font-size: 12px; padding: 8px 12px; border-radius: 8px;
|
||
color: var(--muted); text-transform: uppercase; letter-spacing: 0.06em;
|
||
}
|
||
.nav a:hover { color: var(--ink); background: var(--surface); }
|
||
.nav a.active { color: var(--ink); background: var(--surface-2); border: 1px solid var(--line); }
|
||
|
||
.head-right { display: flex; align-items: center; gap: 10px; }
|
||
.market-state {
|
||
display: inline-flex; gap: 8px; align-items: center;
|
||
padding: 6px 10px; border: 1px solid var(--line); border-radius: var(--r-pill);
|
||
font-family: var(--mono); font-size: 11px; color: var(--ink-soft);
|
||
background: var(--surface);
|
||
}
|
||
.pulse { width: 8px; height: 8px; border-radius: 50%; background: var(--accent); position: relative; box-shadow: 0 0 8px var(--accent); }
|
||
.pulse::after {
|
||
content: ""; position: absolute; inset: -4px; border-radius: 50%;
|
||
box-shadow: 0 0 0 0 rgba(247,147,26,0.55);
|
||
animation: pulse 1.8s ease-out infinite;
|
||
}
|
||
@keyframes pulse {
|
||
0% { box-shadow: 0 0 0 0 rgba(247,147,26,0.55); }
|
||
70% { box-shadow: 0 0 0 12px rgba(247,147,26,0); }
|
||
100% { box-shadow: 0 0 0 0 rgba(247,147,26,0); }
|
||
}
|
||
|
||
.search {
|
||
display: inline-flex; gap: 8px; align-items: center;
|
||
padding: 6px 10px 6px 12px; border-radius: var(--r-pill);
|
||
background: var(--surface); border: 1px solid var(--line);
|
||
color: var(--muted); font-family: var(--mono); font-size: 11.5px;
|
||
}
|
||
.search .kbd {
|
||
display: inline-flex; gap: 2px;
|
||
}
|
||
.search kbd {
|
||
font-family: var(--mono); font-size: 10px; padding: 1px 5px; border: 1px solid var(--line-strong); border-radius: 4px; color: var(--muted);
|
||
}
|
||
|
||
.avatar {
|
||
width: 30px; height: 30px; border-radius: 50%;
|
||
background:
|
||
radial-gradient(circle at 30% 30%, #fde58a, #facc15 40%, #c98a00 100%);
|
||
border: 1px solid rgba(255,255,255,0.12);
|
||
display: inline-flex; align-items: center; justify-content: center;
|
||
color: #2a1a00; font-weight: 700; font-size: 12px; letter-spacing: 0.04em;
|
||
}
|
||
|
||
/* ─────── Layout ─────── */
|
||
main {
|
||
padding: 24px 28px 60px;
|
||
display: grid;
|
||
grid-template-columns: 1fr;
|
||
gap: 18px;
|
||
max-width: 1480px; margin: 0 auto;
|
||
}
|
||
|
||
/* ─────── Hero strip ─────── */
|
||
.hero {
|
||
display: grid;
|
||
grid-template-columns: 1.6fr 1fr;
|
||
gap: 18px;
|
||
}
|
||
.card {
|
||
background: linear-gradient(180deg, var(--surface), var(--surface-2));
|
||
border: 1px solid var(--line);
|
||
border-radius: var(--r-card);
|
||
padding: 22px;
|
||
position: relative;
|
||
overflow: hidden;
|
||
box-shadow:
|
||
0 1px 0 rgba(255,255,255,0.02) inset,
|
||
0 0 24px rgba(255,0,170,0.04);
|
||
}
|
||
.card::after {
|
||
content: ""; position: absolute; inset: 0; pointer-events: none;
|
||
border-radius: inherit;
|
||
box-shadow: inset 0 0 0 1px rgba(255,0,170,0.02), inset 0 1px 0 rgba(255,255,255,0.03);
|
||
}
|
||
.card.glass::before {
|
||
content: ""; position: absolute; inset: 0;
|
||
background:
|
||
radial-gradient(700px 200px at 0% 0%, rgba(255,0,170,0.06), transparent 60%),
|
||
radial-gradient(600px 200px at 100% 100%, rgba(0,224,255,0.05), transparent 60%);
|
||
pointer-events: none;
|
||
}
|
||
|
||
.pf-head { display: flex; justify-content: space-between; align-items: flex-start; gap: 16px; }
|
||
.pf-meta { font-family: var(--mono); font-size: 10.5px; color: var(--muted); text-transform: uppercase; letter-spacing: 0.12em; }
|
||
.pf-title { font-family: var(--display); font-weight: 600; font-size: 14.5px; margin-top: 6px; color: var(--ink-soft); display: flex; gap: 8px; align-items: center; }
|
||
.pf-value {
|
||
font-family: var(--display); font-weight: 600;
|
||
font-size: 56px; line-height: 1; margin-top: 10px;
|
||
letter-spacing: -0.03em;
|
||
background: linear-gradient(180deg, #fff, #b9c1d2);
|
||
-webkit-background-clip: text; background-clip: text; color: transparent;
|
||
}
|
||
.pf-value .cents { color: var(--muted); font-weight: 500; font-size: 28px; vertical-align: 0.18em; }
|
||
.pf-deltas { display: flex; gap: 22px; margin-top: 14px; align-items: center; }
|
||
.delta {
|
||
display: inline-flex; gap: 8px; align-items: baseline;
|
||
font-family: var(--mono); font-size: 13px;
|
||
}
|
||
.delta .v { font-weight: 600; }
|
||
.delta.up .v, .delta.up .arr { color: var(--up); }
|
||
.delta.down .v, .delta.down .arr { color: var(--down); }
|
||
.delta .lbl { color: var(--muted); font-size: 11.5px; text-transform: uppercase; letter-spacing: 0.08em; }
|
||
|
||
.pf-chart { margin-top: 18px; }
|
||
.pf-chart svg { width: 100%; height: 160px; display: block; }
|
||
.pf-axis { display: flex; justify-content: space-between; font-family: var(--mono); font-size: 10.5px; color: var(--muted); margin-top: 4px; padding: 0 2px; }
|
||
|
||
.range-tabs { display: inline-flex; padding: 3px; background: var(--surface); border: 1px solid var(--line); border-radius: 8px; gap: 0; }
|
||
.range-tabs button {
|
||
border: 0; background: transparent; color: var(--muted);
|
||
font-family: var(--mono); font-size: 11px; padding: 5px 9px; border-radius: 6px; letter-spacing: 0.06em;
|
||
}
|
||
.range-tabs button.active { background: var(--surface-3); color: var(--ink); box-shadow: 0 1px 0 rgba(255,255,255,0.04); }
|
||
|
||
/* KPI strip on the right */
|
||
.kpis { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
|
||
.kpi {
|
||
border: 1px solid var(--line);
|
||
background: var(--surface);
|
||
border-radius: 12px;
|
||
padding: 14px 14px 16px;
|
||
position: relative;
|
||
overflow: hidden;
|
||
}
|
||
.kpi .k-label { font-family: var(--mono); font-size: 10px; color: var(--muted); text-transform: uppercase; letter-spacing: 0.12em; }
|
||
.kpi .k-val { font-family: var(--display); font-weight: 600; font-size: 22px; letter-spacing: -0.01em; margin-top: 6px; }
|
||
.kpi .k-sub { font-family: var(--mono); font-size: 11px; margin-top: 4px; }
|
||
.kpi.spark svg { width: 100%; height: 36px; margin-top: 6px; }
|
||
.kpi.alpha .k-val { color: var(--accent); }
|
||
|
||
/* ─────── Main grid: chart + side ─────── */
|
||
.grid-2 {
|
||
display: grid;
|
||
grid-template-columns: 1.7fr 1fr;
|
||
gap: 18px;
|
||
}
|
||
|
||
/* Chart card */
|
||
.chart-card { padding: 0; }
|
||
.chart-head {
|
||
display: flex; justify-content: space-between; align-items: flex-start; gap: 16px;
|
||
padding: 20px 22px 14px;
|
||
border-bottom: 1px solid var(--hairline);
|
||
}
|
||
.ticker {
|
||
display: flex; gap: 16px; align-items: center;
|
||
}
|
||
.ticker .icon {
|
||
width: 44px; height: 44px; border-radius: 10px;
|
||
background: linear-gradient(135deg, #76b900, #4d8400);
|
||
display: inline-flex; align-items: center; justify-content: center;
|
||
color: #0a1502; font-family: var(--display); font-weight: 700; font-size: 18px;
|
||
box-shadow: inset 0 1px 0 rgba(255,255,255,0.3);
|
||
}
|
||
.ticker .meta { display: flex; flex-direction: column; gap: 2px; }
|
||
.ticker .sym-row { display: flex; align-items: baseline; gap: 10px; }
|
||
.ticker .sym { font-family: var(--display); font-weight: 700; font-size: 22px; letter-spacing: -0.01em; }
|
||
.ticker .ext {
|
||
font-family: var(--mono); font-size: 10.5px; color: var(--muted);
|
||
border: 1px solid var(--line); border-radius: 6px; padding: 2px 6px;
|
||
text-transform: uppercase; letter-spacing: 0.08em;
|
||
}
|
||
.ticker .ext:hover { color: var(--ink); border-color: var(--line-strong); }
|
||
.ticker .ext .ico { font-size: 9px; opacity: 0.7; margin-left: 2px; }
|
||
.ticker .name { color: var(--muted); font-size: 12.5px; }
|
||
|
||
.price-block { text-align: right; }
|
||
.px-now { font-family: var(--display); font-weight: 600; font-size: 30px; letter-spacing: -0.02em; }
|
||
.px-delta { font-family: var(--mono); font-size: 12.5px; margin-top: 2px; }
|
||
|
||
.chart-stats {
|
||
display: grid; grid-template-columns: repeat(6, 1fr); gap: 1px;
|
||
background: var(--hairline);
|
||
border-bottom: 1px solid var(--hairline);
|
||
}
|
||
.stat { background: var(--surface); padding: 10px 16px; }
|
||
.stat .lbl { font-family: var(--mono); font-size: 9.5px; color: var(--muted); text-transform: uppercase; letter-spacing: 0.1em; }
|
||
.stat .val { font-family: var(--mono); font-size: 12.5px; color: var(--ink); margin-top: 2px; font-weight: 500; }
|
||
|
||
.chart-body { padding: 16px 22px 18px; }
|
||
.chart-toolbar { display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px; }
|
||
.chart-tools { display: flex; gap: 6px; }
|
||
.chip {
|
||
font-family: var(--mono); font-size: 10.5px; padding: 5px 9px; border-radius: 6px;
|
||
border: 1px solid var(--line); background: var(--surface); color: var(--muted);
|
||
text-transform: uppercase; letter-spacing: 0.08em;
|
||
}
|
||
.chip.on { color: var(--cyan); border-color: rgba(34,211,238,0.4); background: var(--cyan-soft); }
|
||
|
||
.candles { width: 100%; height: 320px; display: block; }
|
||
.vol { width: 100%; height: 70px; display: block; margin-top: 2px; }
|
||
|
||
/* ─────── AI Recommendation card ─────── */
|
||
.ai-card { padding: 0; overflow: hidden; }
|
||
.ai-head {
|
||
padding: 18px 20px 14px;
|
||
background:
|
||
radial-gradient(360px 140px at 100% 0%, rgba(167,139,250,0.18), transparent 65%),
|
||
radial-gradient(280px 120px at 0% 100%, rgba(34,229,140,0.12), transparent 70%),
|
||
var(--surface-2);
|
||
border-bottom: 1px solid var(--hairline);
|
||
}
|
||
.ai-tag {
|
||
display: inline-flex; gap: 8px; align-items: center;
|
||
font-family: var(--mono); font-size: 10.5px; color: var(--violet);
|
||
text-transform: uppercase; letter-spacing: 0.14em;
|
||
}
|
||
.ai-tag::before {
|
||
content: ""; width: 6px; height: 6px; border-radius: 50%; background: var(--violet);
|
||
box-shadow: 0 0 12px var(--violet);
|
||
}
|
||
.ai-headline {
|
||
font-family: var(--display); font-weight: 600; font-size: 22px; letter-spacing: -0.015em;
|
||
margin-top: 10px; line-height: 1.25;
|
||
}
|
||
.ai-headline .accent {
|
||
background: linear-gradient(90deg, var(--magenta), var(--cyan), var(--matrix));
|
||
-webkit-background-clip: text; background-clip: text; color: transparent;
|
||
filter: drop-shadow(0 0 12px rgba(255,0,170,0.3));
|
||
}
|
||
.ai-sub { color: var(--ink-soft); font-size: 12.5px; margin-top: 6px; max-width: 46ch; }
|
||
|
||
.ai-stat-row { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 0; border-top: 1px solid var(--hairline); }
|
||
.ai-stat { padding: 14px 16px; border-right: 1px solid var(--hairline); }
|
||
.ai-stat:last-child { border-right: 0; }
|
||
.ai-stat .lbl { font-family: var(--mono); font-size: 9.5px; color: var(--muted); text-transform: uppercase; letter-spacing: 0.1em; }
|
||
.ai-stat .v { font-family: var(--display); font-weight: 600; font-size: 18px; margin-top: 4px; letter-spacing: -0.01em; }
|
||
.ai-stat .v.up { color: var(--up); }
|
||
.ai-stat.conviction .bar { height: 4px; background: var(--surface-3); border-radius: 999px; margin-top: 8px; overflow: hidden; }
|
||
.ai-stat.conviction .fill { height: 100%; background: linear-gradient(90deg, var(--up), var(--cyan)); border-radius: 999px; box-shadow: 0 0 14px rgba(34,211,238,0.5); }
|
||
|
||
.ai-thesis { padding: 16px 20px; }
|
||
.ai-thesis h4 { font-family: var(--mono); font-size: 10.5px; color: var(--muted); text-transform: uppercase; letter-spacing: 0.12em; margin: 0 0 10px; font-weight: 500; }
|
||
.thesis-list { display: flex; flex-direction: column; gap: 10px; }
|
||
.thesis-item { display: grid; grid-template-columns: 22px 1fr; gap: 10px; align-items: flex-start; }
|
||
.thesis-num {
|
||
font-family: var(--mono); font-size: 10px; color: var(--accent);
|
||
border: 1px solid rgba(250,204,21,0.4); border-radius: 6px;
|
||
padding: 1px 0; width: 22px; text-align: center;
|
||
background: var(--accent-soft);
|
||
}
|
||
.thesis-text { font-size: 12.5px; color: var(--ink-soft); line-height: 1.55; }
|
||
.thesis-text strong { color: var(--ink); font-weight: 600; }
|
||
|
||
.ai-actions { display: flex; gap: 8px; padding: 14px 20px 18px; border-top: 1px solid var(--hairline); }
|
||
.btn {
|
||
flex: 1; padding: 10px 12px; border-radius: 10px; border: 1px solid var(--line);
|
||
background: var(--surface); color: var(--ink); font-family: var(--display); font-weight: 500; font-size: 12.5px;
|
||
display: inline-flex; align-items: center; justify-content: center; gap: 6px;
|
||
transition: transform 0.06s ease, border-color 0.15s ease;
|
||
}
|
||
.btn:hover { border-color: var(--line-strong); transform: translateY(-1px); }
|
||
.btn.primary {
|
||
background: linear-gradient(180deg, #2dffa0, #18b673);
|
||
color: #00200f; border-color: rgba(34,229,140,0.5);
|
||
box-shadow: 0 8px 24px rgba(34,229,140,0.25), inset 0 1px 0 rgba(255,255,255,0.35);
|
||
font-weight: 700;
|
||
}
|
||
|
||
/* ─────── Holdings table ─────── */
|
||
.panel-head { display: flex; justify-content: space-between; align-items: center; padding: 16px 20px; border-bottom: 1px solid var(--hairline); }
|
||
.panel-title { font-family: var(--display); font-weight: 600; font-size: 14.5px; letter-spacing: -0.005em; }
|
||
.panel-sub { font-family: var(--mono); font-size: 10.5px; color: var(--muted); text-transform: uppercase; letter-spacing: 0.1em; }
|
||
|
||
table.holdings { width: 100%; border-collapse: collapse; }
|
||
table.holdings th, table.holdings td {
|
||
padding: 12px 16px; text-align: left; font-size: 12.5px;
|
||
border-bottom: 1px solid var(--hairline);
|
||
}
|
||
table.holdings th {
|
||
font-family: var(--mono); font-size: 10px; color: var(--muted);
|
||
text-transform: uppercase; letter-spacing: 0.12em; font-weight: 500;
|
||
background: var(--surface);
|
||
}
|
||
table.holdings tr:last-child td { border-bottom: 0; }
|
||
table.holdings tr:hover td { background: rgba(34,211,238,0.025); }
|
||
td.num, th.num { text-align: right; font-family: var(--mono); }
|
||
.sym-cell { display: flex; align-items: center; gap: 12px; }
|
||
.sym-mark {
|
||
width: 30px; height: 30px; border-radius: 8px;
|
||
display: inline-flex; align-items: center; justify-content: center;
|
||
font-family: var(--display); font-weight: 700; font-size: 12px;
|
||
box-shadow: inset 0 1px 0 rgba(255,255,255,0.18);
|
||
}
|
||
.sym-cell .name-block { display: flex; flex-direction: column; }
|
||
.sym-cell .sym-name { font-weight: 600; letter-spacing: -0.005em; font-size: 13px; }
|
||
.sym-cell .sym-co { font-size: 11px; color: var(--muted); }
|
||
.sym-cell a.ext-mini {
|
||
font-family: var(--mono); font-size: 10px; color: var(--muted);
|
||
margin-left: 6px;
|
||
border: 1px solid var(--line); border-radius: 5px; padding: 1px 5px;
|
||
}
|
||
.sym-cell a.ext-mini:hover { color: var(--cyan); border-color: rgba(34,211,238,0.45); }
|
||
.pl-cell { display: inline-flex; flex-direction: column; align-items: flex-end; gap: 2px; }
|
||
.pl-pct { font-size: 11px; opacity: 0.85; }
|
||
.pl-cell.up .pl-val, .pl-cell.up .pl-pct { color: var(--up); }
|
||
.pl-cell.down .pl-val, .pl-cell.down .pl-pct { color: var(--down); }
|
||
.alloc-bar { width: 80px; height: 4px; border-radius: 999px; background: var(--surface-3); overflow: hidden; display: inline-block; vertical-align: middle; }
|
||
.alloc-fill { height: 100%; background: linear-gradient(90deg, var(--cyan), var(--violet)); border-radius: 999px; }
|
||
.mini-spark svg { width: 70px; height: 24px; display: block; }
|
||
|
||
/* ─────── Watchlist grid ─────── */
|
||
.watch-grid {
|
||
display: grid; grid-template-columns: repeat(4, 1fr); gap: 14px;
|
||
}
|
||
.watch-card {
|
||
border: 1px solid var(--line); border-radius: 12px;
|
||
background: linear-gradient(180deg, var(--surface), var(--surface-2));
|
||
padding: 14px 16px;
|
||
position: relative;
|
||
overflow: hidden;
|
||
}
|
||
.watch-card::before {
|
||
content: ""; position: absolute; top: 0; left: 0; right: 0; height: 1px;
|
||
background: linear-gradient(90deg, transparent, rgba(34,211,238,0.4), transparent);
|
||
opacity: 0; transition: opacity 0.2s ease;
|
||
}
|
||
.watch-card:hover::before { opacity: 1; }
|
||
.watch-card:hover { border-color: var(--line-strong); }
|
||
.watch-top { display: flex; justify-content: space-between; align-items: flex-start; }
|
||
.watch-sym { font-family: var(--display); font-weight: 700; font-size: 15px; letter-spacing: -0.01em; }
|
||
.watch-co { font-size: 11px; color: var(--muted); margin-top: 2px; }
|
||
.watch-pct {
|
||
font-family: var(--mono); font-size: 11.5px;
|
||
padding: 3px 7px; border-radius: 6px; font-weight: 600;
|
||
}
|
||
.watch-pct.up { color: var(--up); background: var(--up-soft); }
|
||
.watch-pct.down { color: var(--down); background: var(--down-soft); }
|
||
.watch-px { font-family: var(--display); font-weight: 600; font-size: 22px; margin-top: 10px; letter-spacing: -0.015em; }
|
||
.watch-spark { margin-top: 8px; }
|
||
.watch-spark svg { width: 100%; height: 40px; display: block; }
|
||
.watch-foot { display: flex; justify-content: space-between; align-items: center; margin-top: 10px; font-family: var(--mono); font-size: 10.5px; color: var(--muted); }
|
||
.watch-foot a { color: var(--muted); }
|
||
.watch-foot a:hover { color: var(--cyan); }
|
||
|
||
/* ─────── News / signals feed ─────── */
|
||
.news-list { display: flex; flex-direction: column; }
|
||
.news-item {
|
||
padding: 14px 20px; display: grid; grid-template-columns: 56px 1fr auto; gap: 14px; align-items: flex-start;
|
||
border-bottom: 1px solid var(--hairline);
|
||
}
|
||
.news-item:last-child { border-bottom: 0; }
|
||
.news-time { font-family: var(--mono); font-size: 10.5px; color: var(--muted); padding-top: 2px; }
|
||
.news-body .src { font-family: var(--mono); font-size: 10px; color: var(--muted); text-transform: uppercase; letter-spacing: 0.1em; }
|
||
.news-body .src .tag {
|
||
color: var(--accent); border: 1px solid rgba(250,204,21,0.4); background: var(--accent-soft);
|
||
padding: 1px 5px; border-radius: 4px; margin-right: 6px;
|
||
}
|
||
.news-body .head { font-size: 13px; color: var(--ink); margin-top: 4px; line-height: 1.45; }
|
||
.news-body .head .t-up { color: var(--up); }
|
||
.news-body .head .t-down { color: var(--down); }
|
||
.news-body .syms { margin-top: 6px; display: flex; gap: 5px; flex-wrap: wrap; }
|
||
.news-body .syms .s {
|
||
font-family: var(--mono); font-size: 10px;
|
||
padding: 1px 6px; border: 1px solid var(--line); border-radius: 4px; color: var(--ink-soft);
|
||
}
|
||
.impact { font-family: var(--mono); font-size: 10.5px; padding: 2px 7px; border-radius: 5px; }
|
||
.impact.high { color: var(--down); border: 1px solid rgba(255,79,109,0.35); background: var(--down-soft); }
|
||
.impact.pos { color: var(--up); border: 1px solid rgba(34,229,140,0.35); background: var(--up-soft); }
|
||
.impact.med { color: var(--accent); border: 1px solid rgba(250,204,21,0.35); background: var(--accent-soft); }
|
||
|
||
/* ─────── Footer ─────── */
|
||
footer.app {
|
||
margin-top: 14px; padding: 18px 28px;
|
||
border-top: 1px solid var(--hairline);
|
||
display: flex; justify-content: space-between; align-items: center;
|
||
font-family: var(--mono); font-size: 11px; color: var(--muted);
|
||
text-transform: uppercase; letter-spacing: 0.1em;
|
||
}
|
||
footer .left { display: flex; gap: 14px; align-items: center; }
|
||
footer .live { display: inline-flex; gap: 6px; align-items: center; color: var(--up); }
|
||
footer .live::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: var(--up); box-shadow: 0 0 8px var(--up); }
|
||
footer .right { display: flex; gap: 16px; }
|
||
|
||
/* Responsive */
|
||
@media (max-width: 1180px) {
|
||
.grid-2 { grid-template-columns: 1fr; }
|
||
.hero { grid-template-columns: 1fr; }
|
||
.watch-grid { grid-template-columns: repeat(2, 1fr); }
|
||
}
|
||
@media (max-width: 720px) {
|
||
main { padding: 16px; }
|
||
.chart-stats { grid-template-columns: repeat(3, 1fr); }
|
||
.ai-stat-row { grid-template-columns: 1fr; }
|
||
.ai-stat { border-right: 0; border-bottom: 1px solid var(--hairline); }
|
||
.nav, .search { display: none; }
|
||
.pf-value { font-size: 40px; }
|
||
.watch-grid { grid-template-columns: 1fr; }
|
||
}
|
||
|
||
/* small icon helpers */
|
||
.ic { width: 14px; height: 14px; display: inline-block; vertical-align: -2px; }
|
||
|
||
/* number flicker on the live price */
|
||
.flicker { animation: flicker 2.6s ease-in-out infinite; }
|
||
@keyframes flicker {
|
||
0%, 96%, 100% { opacity: 1; }
|
||
97% { opacity: 0.55; }
|
||
98% { opacity: 1; }
|
||
}
|
||
|
||
/* subtle grid background under candles */
|
||
.gridlines line { stroke: rgba(255,255,255,0.04); stroke-dasharray: 2 4; }
|
||
|
||
/* ─────── Interactive layer ─────── */
|
||
/* Refresh button */
|
||
.icon-btn {
|
||
display: inline-flex; align-items: center; gap: 8px;
|
||
padding: 6px 12px; border-radius: var(--r-pill);
|
||
background: var(--surface); border: 1px solid var(--line); color: var(--ink-soft);
|
||
font-family: var(--mono); font-size: 11px; letter-spacing: 0.04em;
|
||
transition: all 0.15s ease;
|
||
}
|
||
.icon-btn:hover { color: var(--ink); border-color: var(--line-strong); transform: translateY(-1px); }
|
||
.icon-btn.primary { color: var(--cyan); border-color: rgba(34,211,238,0.45); background: var(--cyan-soft); }
|
||
.icon-btn.primary:hover { box-shadow: 0 0 16px rgba(34,211,238,0.25); }
|
||
.icon-btn .ico { width: 13px; height: 13px; transition: transform 0.6s ease; }
|
||
.icon-btn.spin .ico { animation: spin 0.8s linear; }
|
||
@keyframes spin { from { transform: rotate(0); } to { transform: rotate(360deg); } }
|
||
|
||
/* Price flash */
|
||
.flash-up { animation: flashUp 0.9s ease-out; }
|
||
.flash-down { animation: flashDown 0.9s ease-out; }
|
||
@keyframes flashUp {
|
||
0% { background: rgba(34,229,140,0.22); box-shadow: inset 0 0 0 1px rgba(34,229,140,0.4); }
|
||
100% { background: transparent; box-shadow: none; }
|
||
}
|
||
@keyframes flashDown {
|
||
0% { background: rgba(255,79,109,0.22); box-shadow: inset 0 0 0 1px rgba(255,79,109,0.4); }
|
||
100% { background: transparent; box-shadow: none; }
|
||
}
|
||
|
||
/* AI tabs */
|
||
.ai-tabs {
|
||
display: flex; gap: 0;
|
||
padding: 0 20px;
|
||
background: var(--surface);
|
||
border-bottom: 1px solid var(--hairline);
|
||
}
|
||
.ai-tab {
|
||
background: transparent; border: 0;
|
||
padding: 12px 14px;
|
||
font-family: var(--mono); font-size: 10.5px;
|
||
color: var(--muted);
|
||
text-transform: uppercase; letter-spacing: 0.1em;
|
||
border-bottom: 2px solid transparent;
|
||
margin-bottom: -1px;
|
||
transition: color 0.15s ease, border-color 0.15s ease;
|
||
}
|
||
.ai-tab:hover { color: var(--ink-soft); }
|
||
.ai-tab.active { color: var(--violet); border-bottom-color: var(--violet); }
|
||
.ai-tab .dot { display: inline-block; width: 6px; height: 6px; border-radius: 50%; margin-left: 6px; vertical-align: middle; }
|
||
.ai-tab .dot.bull { background: var(--up); }
|
||
.ai-tab .dot.risk { background: var(--down); }
|
||
.ai-tab .dot.amber { background: var(--accent); }
|
||
.ai-tab .dot.violet { background: var(--violet); }
|
||
.ai-panel { display: none; }
|
||
.ai-panel.active { display: block; animation: panelIn 0.25s ease; }
|
||
@keyframes panelIn { from { opacity: 0; transform: translateY(2px); } to { opacity: 1; transform: translateY(0); } }
|
||
|
||
/* AI regenerate */
|
||
.ai-regen {
|
||
display: inline-flex; align-items: center; gap: 6px;
|
||
padding: 4px 10px; border-radius: var(--r-pill);
|
||
background: rgba(167,139,250,0.1); border: 1px solid rgba(167,139,250,0.35); color: var(--violet);
|
||
font-family: var(--mono); font-size: 10px; letter-spacing: 0.08em; text-transform: uppercase;
|
||
margin-left: 10px; cursor: pointer;
|
||
transition: all 0.15s ease;
|
||
}
|
||
.ai-regen:hover { background: rgba(167,139,250,0.18); }
|
||
.ai-regen .ico { width: 10px; height: 10px; }
|
||
.ai-regen.thinking .ico { animation: spin 0.8s linear infinite; }
|
||
|
||
.thinking-dots { display: inline-flex; gap: 3px; align-items: center; }
|
||
.thinking-dots .d { width: 4px; height: 4px; border-radius: 50%; background: var(--violet); opacity: 0.6; animation: dotpulse 1.2s ease-in-out infinite; }
|
||
.thinking-dots .d:nth-child(2) { animation-delay: 0.15s; }
|
||
.thinking-dots .d:nth-child(3) { animation-delay: 0.3s; }
|
||
@keyframes dotpulse { 0%,100% { opacity: 0.3; transform: scale(0.85); } 50% { opacity: 1; transform: scale(1.15); } }
|
||
|
||
/* Risk / position-read / trade-plan content */
|
||
.risk-list { display: flex; flex-direction: column; gap: 10px; padding: 16px 20px; }
|
||
.risk-row {
|
||
display: grid; grid-template-columns: 56px 1fr auto; gap: 12px; align-items: flex-start;
|
||
padding: 10px 12px; border: 1px solid var(--line); border-radius: 10px; background: var(--surface);
|
||
}
|
||
.risk-row .lvl {
|
||
font-family: var(--mono); font-size: 9.5px; padding: 3px 6px; border-radius: 5px;
|
||
text-transform: uppercase; letter-spacing: 0.1em; text-align: center; font-weight: 600;
|
||
}
|
||
.risk-row .lvl.high { color: var(--down); border: 1px solid rgba(255,79,109,0.4); background: var(--down-soft); }
|
||
.risk-row .lvl.med { color: var(--accent); border: 1px solid rgba(250,204,21,0.4); background: var(--accent-soft); }
|
||
.risk-row .lvl.low { color: var(--up); border: 1px solid rgba(34,229,140,0.4); background: var(--up-soft); }
|
||
.risk-row .body { font-size: 12.5px; color: var(--ink-soft); line-height: 1.5; }
|
||
.risk-row .body strong { color: var(--ink); }
|
||
.risk-row .prob { font-family: var(--mono); font-size: 11px; color: var(--muted); white-space: nowrap; padding-top: 2px; }
|
||
|
||
/* Trade plan stepper */
|
||
.plan-grid { padding: 16px 20px; display: flex; flex-direction: column; gap: 0; }
|
||
.plan-step { display: grid; grid-template-columns: 24px 100px 1fr; gap: 12px; padding: 12px 0; border-bottom: 1px dashed var(--line); align-items: center; }
|
||
.plan-step:last-child { border-bottom: 0; }
|
||
.plan-bullet { width: 22px; height: 22px; border-radius: 50%; background: var(--surface-3); border: 1px solid var(--line); display: inline-flex; align-items: center; justify-content: center; font-family: var(--mono); font-size: 10px; color: var(--muted); }
|
||
.plan-step.entry .plan-bullet { background: var(--up-soft); border-color: rgba(34,229,140,0.4); color: var(--up); }
|
||
.plan-step.stop .plan-bullet { background: var(--down-soft); border-color: rgba(255,79,109,0.4); color: var(--down); }
|
||
.plan-step.target .plan-bullet { background: var(--accent-soft); border-color: rgba(250,204,21,0.4); color: var(--accent); }
|
||
.plan-label { font-family: var(--mono); font-size: 10.5px; color: var(--muted); text-transform: uppercase; letter-spacing: 0.1em; }
|
||
.plan-zone { font-family: var(--mono); font-size: 12px; color: var(--ink); }
|
||
.plan-zone .px { font-weight: 600; color: var(--ink); }
|
||
.plan-zone .pct.up { color: var(--up); }
|
||
.plan-zone .pct.down { color: var(--down); }
|
||
|
||
/* Position-read facts */
|
||
.pos-read { padding: 16px 20px; }
|
||
.pos-read .pos-line { display: grid; grid-template-columns: 1fr auto; padding: 8px 0; border-bottom: 1px dashed var(--line); font-size: 12.5px; }
|
||
.pos-read .pos-line:last-child { border-bottom: 0; }
|
||
.pos-read .pos-line .lbl { color: var(--muted); }
|
||
.pos-read .pos-line .val { font-family: var(--mono); color: var(--ink); }
|
||
.pos-read .pos-line .val.up { color: var(--up); }
|
||
.pos-read .pos-line .val.down { color: var(--down); }
|
||
.pos-read .pos-narr {
|
||
margin-top: 12px; padding: 12px 14px; border-radius: 8px;
|
||
background: rgba(167,139,250,0.06); border-left: 2px solid var(--violet);
|
||
color: var(--ink-soft); font-size: 12.5px; line-height: 1.55;
|
||
}
|
||
.pos-read .pos-narr .quote { font-family: var(--serif); font-style: italic; color: var(--ink); font-size: 14px; line-height: 1.4; }
|
||
|
||
/* Toast */
|
||
.toast-box {
|
||
position: fixed; bottom: 28px; right: 28px; z-index: 200;
|
||
display: flex; flex-direction: column; gap: 10px;
|
||
pointer-events: none;
|
||
}
|
||
.toast {
|
||
pointer-events: auto;
|
||
min-width: 280px; max-width: 360px;
|
||
padding: 12px 16px;
|
||
background: rgba(15,19,28,0.92);
|
||
border: 1px solid var(--line);
|
||
border-left: 3px solid var(--up);
|
||
border-radius: 10px;
|
||
backdrop-filter: blur(16px) saturate(140%);
|
||
box-shadow: 0 20px 50px rgba(0,0,0,0.45), inset 0 1px 0 rgba(255,255,255,0.04);
|
||
font-family: var(--body); font-size: 12.5px;
|
||
color: var(--ink);
|
||
animation: toastIn 0.32s cubic-bezier(0.2, 0.8, 0.2, 1);
|
||
transition: opacity 0.25s ease, transform 0.25s ease;
|
||
}
|
||
.toast.error { border-left-color: var(--down); }
|
||
.toast.warn { border-left-color: var(--accent); }
|
||
.toast.info { border-left-color: var(--cyan); }
|
||
.toast.violet { border-left-color: var(--violet); }
|
||
.toast .t-head { display: flex; justify-content: space-between; align-items: center; gap: 12px; }
|
||
.toast .t-title { font-family: var(--mono); font-size: 10px; text-transform: uppercase; letter-spacing: 0.1em; color: var(--muted); }
|
||
.toast .t-close { background: transparent; border: 0; color: var(--muted); font-size: 14px; padding: 0; line-height: 1; cursor: pointer; }
|
||
.toast .t-close:hover { color: var(--ink); }
|
||
.toast .t-body { margin-top: 4px; line-height: 1.4; color: var(--ink-soft); }
|
||
.toast .t-body strong { color: var(--ink); }
|
||
.toast.fade { opacity: 0; transform: translateX(20px); }
|
||
@keyframes toastIn {
|
||
from { opacity: 0; transform: translateX(20px) scale(0.96); }
|
||
to { opacity: 1; transform: translateX(0) scale(1); }
|
||
}
|
||
|
||
/* Selection highlight on switch */
|
||
.swap-highlight { animation: swapPulse 0.7s ease-out; }
|
||
@keyframes swapPulse {
|
||
0% { box-shadow: inset 0 0 0 1px rgba(34,211,238,0.6), 0 0 0 0 rgba(34,211,238,0.5); }
|
||
100% { box-shadow: inset 0 0 0 1px transparent, 0 0 0 12px transparent; }
|
||
}
|
||
|
||
/* clickable affordances */
|
||
table.holdings tbody tr { cursor: pointer; }
|
||
table.holdings tbody tr.active td { background: rgba(34,211,238,0.06); }
|
||
table.holdings tbody tr.active td:first-child { box-shadow: inset 3px 0 0 var(--cyan); }
|
||
.watch-card { cursor: pointer; transition: transform 0.15s ease, border-color 0.15s ease; }
|
||
.watch-card:hover { transform: translateY(-2px); }
|
||
.watch-card.active { border-color: rgba(34,211,238,0.55); box-shadow: 0 0 24px rgba(34,211,238,0.18); }
|
||
.tape-item { cursor: pointer; }
|
||
.tape-item:hover .sym { color: var(--cyan); }
|
||
|
||
.range-tabs button { cursor: pointer; }
|
||
|
||
/* Market regime mini badge inside AI head */
|
||
.regime-row { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 12px; }
|
||
.regime-pill {
|
||
font-family: var(--mono); font-size: 10px;
|
||
padding: 4px 9px; border-radius: var(--r-pill);
|
||
border: 1px solid var(--line); background: var(--surface);
|
||
color: var(--ink-soft); letter-spacing: 0.06em;
|
||
display: inline-flex; gap: 6px; align-items: center;
|
||
}
|
||
.regime-pill .lbl { color: var(--muted); }
|
||
.regime-pill.up .v { color: var(--up); }
|
||
.regime-pill.down .v { color: var(--down); }
|
||
.regime-pill.neut .v { color: var(--accent); }
|
||
</style>
|
||
</head>
|
||
<body>
|
||
|
||
<!-- ─────── Ticker tape ─────── -->
|
||
<div class="ticker-tape">
|
||
<div class="tape">
|
||
<span class="tape-item"><span class="sym">BTC</span><span class="px">68,412</span><span class="pct up">▲ 1.84%</span></span>
|
||
<span class="tape-item"><span class="sym">ETH</span><span class="px">3,612</span><span class="pct up">▲ 2.10%</span></span>
|
||
<span class="tape-item"><span class="sym">SOL</span><span class="px">186.42</span><span class="pct up">▲ 4.62%</span></span>
|
||
<span class="tape-item"><span class="sym">BNB</span><span class="px">612.40</span><span class="pct up">▲ 0.94%</span></span>
|
||
<span class="tape-item"><span class="sym">XRP</span><span class="px">0.5824</span><span class="pct down">▼ 0.42%</span></span>
|
||
<span class="tape-item"><span class="sym">ADA</span><span class="px">0.4318</span><span class="pct up">▲ 1.20%</span></span>
|
||
<span class="tape-item"><span class="sym">DOGE</span><span class="px">0.1842</span><span class="pct up">▲ 6.84%</span></span>
|
||
<span class="tape-item"><span class="sym">AVAX</span><span class="px">36.84</span><span class="pct up">▲ 2.86%</span></span>
|
||
<span class="tape-item"><span class="sym">LINK</span><span class="px">14.92</span><span class="pct up">▲ 1.40%</span></span>
|
||
<span class="tape-item"><span class="sym">DOT</span><span class="px">7.18</span><span class="pct down">▼ 0.62%</span></span>
|
||
<span class="tape-item"><span class="sym">SUI</span><span class="px">2.18</span><span class="pct up">▲ 8.40%</span></span>
|
||
<span class="tape-item"><span class="sym">NEAR</span><span class="px">5.42</span><span class="pct up">▲ 3.20%</span></span>
|
||
<span class="tape-item"><span class="sym">TIA</span><span class="px">7.84</span><span class="pct down">▼ 1.18%</span></span>
|
||
<span class="tape-item"><span class="sym">ARB</span><span class="px">0.6420</span><span class="pct up">▲ 0.95%</span></span>
|
||
<!-- duplicate for seamless scroll -->
|
||
<span class="tape-item"><span class="sym">BTC</span><span class="px">68,412</span><span class="pct up">▲ 1.84%</span></span>
|
||
<span class="tape-item"><span class="sym">ETH</span><span class="px">3,612</span><span class="pct up">▲ 2.10%</span></span>
|
||
<span class="tape-item"><span class="sym">SOL</span><span class="px">186.42</span><span class="pct up">▲ 4.62%</span></span>
|
||
<span class="tape-item"><span class="sym">BNB</span><span class="px">612.40</span><span class="pct up">▲ 0.94%</span></span>
|
||
<span class="tape-item"><span class="sym">XRP</span><span class="px">0.5824</span><span class="pct down">▼ 0.42%</span></span>
|
||
<span class="tape-item"><span class="sym">ADA</span><span class="px">0.4318</span><span class="pct up">▲ 1.20%</span></span>
|
||
<span class="tape-item"><span class="sym">DOGE</span><span class="px">0.1842</span><span class="pct up">▲ 6.84%</span></span>
|
||
<span class="tape-item"><span class="sym">AVAX</span><span class="px">36.84</span><span class="pct up">▲ 2.86%</span></span>
|
||
<span class="tape-item"><span class="sym">LINK</span><span class="px">14.92</span><span class="pct up">▲ 1.40%</span></span>
|
||
<span class="tape-item"><span class="sym">DOT</span><span class="px">7.18</span><span class="pct down">▼ 0.62%</span></span>
|
||
<span class="tape-item"><span class="sym">SUI</span><span class="px">2.18</span><span class="pct up">▲ 8.40%</span></span>
|
||
<span class="tape-item"><span class="sym">NEAR</span><span class="px">5.42</span><span class="pct up">▲ 3.20%</span></span>
|
||
<span class="tape-item"><span class="sym">TIA</span><span class="px">7.84</span><span class="pct down">▼ 1.18%</span></span>
|
||
<span class="tape-item"><span class="sym">ARB</span><span class="px">0.6420</span><span class="pct up">▲ 0.95%</span></span>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ─────── On-chain ribbon ─────── -->
|
||
<div class="onchain-ribbon">
|
||
<span class="item live"><span class="label">Block</span><span class="block-h" id="blockH">287,418,612</span></span>
|
||
<span class="item"><span class="label">Gas</span><span class="gas">12 gwei</span><span class="label">· low</span></span>
|
||
<span class="item"><span class="label">Mempool</span><span class="v">142,840 tx</span></span>
|
||
<span class="item"><span class="label">BTC hash</span><span class="v cyan">612 EH/s</span></span>
|
||
<span class="item"><span class="label">DeFi TVL</span><span class="v magenta">$84.2B</span></span>
|
||
<span class="item"><span class="label">ETH staking</span><span class="v">28.4% (34M ETH)</span></span>
|
||
<span class="item"><span class="label">Fear & Greed</span><span class="v" style="color:var(--accent)">78 · Greed</span></span>
|
||
<span class="item" style="margin-left:auto"><span class="label">Stables MC</span><span class="v">$172.8B</span></span>
|
||
</div>
|
||
|
||
<!-- ─────── Header ─────── -->
|
||
<header class="app">
|
||
<div class="brand">
|
||
<div class="logo"></div>
|
||
<div class="word">quiver</div>
|
||
<div class="pro">Crypto</div>
|
||
<nav class="nav">
|
||
<a href="#" class="active">Portfolio</a>
|
||
<a href="#">Markets</a>
|
||
<a href="#">On-chain</a>
|
||
<a href="#">DeFi</a>
|
||
<a href="#">Alerts</a>
|
||
</nav>
|
||
</div>
|
||
<div class="head-right">
|
||
<span class="market-state">
|
||
<span class="pulse"></span>
|
||
<span>24/7 markets · <span id="liveClock">14:32:18 UTC</span></span>
|
||
</span>
|
||
<span class="search">
|
||
<svg class="ic" viewBox="0 0 16 16" fill="none"><circle cx="7" cy="7" r="5" stroke="currentColor" stroke-width="1.4"/><path d="M11 11l3 3" stroke="currentColor" stroke-width="1.4" stroke-linecap="round"/></svg>
|
||
<span>Search ticker, news, analyst…</span>
|
||
<span class="kbd"><kbd>⌘</kbd><kbd>K</kbd></span>
|
||
</span>
|
||
<button class="icon-btn primary" id="refreshBtn" title="Refresh quotes & analysis">
|
||
<svg class="ico" viewBox="0 0 16 16" fill="none"><path d="M14 8a6 6 0 1 1-1.76-4.24M14 3v3.5h-3.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
||
<span>Refresh</span>
|
||
</button>
|
||
<span class="avatar">PT</span>
|
||
</div>
|
||
</header>
|
||
|
||
<main>
|
||
|
||
<!-- ─────── Hero: portfolio value + KPIs ─────── -->
|
||
<section class="hero">
|
||
<div class="card glass">
|
||
<div class="pf-head">
|
||
<div>
|
||
<div class="pf-meta">Total crypto net worth · USD</div>
|
||
<div class="pf-title">Coinbase · Ledger · MetaMask <span class="market-state" style="padding:3px 8px;font-size:10.5px;"><span class="pulse"></span> On-chain live</span></div>
|
||
</div>
|
||
<div class="range-tabs">
|
||
<button>24H</button>
|
||
<button>1W</button>
|
||
<button>1M</button>
|
||
<button>3M</button>
|
||
<button>6M</button>
|
||
<button class="active">1Y</button>
|
||
<button>ALL</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="pf-value flicker">
|
||
$186,420<span class="cents">.18</span>
|
||
</div>
|
||
<div class="pf-deltas">
|
||
<span class="delta up"><span class="arr">▲</span> <span class="v">+$5,842.16</span> <span class="lbl">today · +3.23%</span></span>
|
||
<span class="delta up"><span class="arr">▲</span> <span class="v">+$112,420.18</span> <span class="lbl">all time · +151.9%</span></span>
|
||
<span class="delta"><span class="lbl" style="color:var(--muted)">vs BTC</span> <span class="v" style="color:var(--accent);">+22.4pp</span></span>
|
||
</div>
|
||
|
||
<div class="pf-chart">
|
||
<svg viewBox="0 0 880 170" preserveAspectRatio="none">
|
||
<defs>
|
||
<linearGradient id="pfGrad" x1="0" x2="0" y1="0" y2="1">
|
||
<stop offset="0%" stop-color="#22e58c" stop-opacity="0.32"/>
|
||
<stop offset="60%" stop-color="#22e58c" stop-opacity="0.06"/>
|
||
<stop offset="100%" stop-color="#22e58c" stop-opacity="0"/>
|
||
</linearGradient>
|
||
<linearGradient id="pfStroke" x1="0" x2="1" y1="0" y2="0">
|
||
<stop offset="0%" stop-color="#ff00aa"/>
|
||
<stop offset="55%" stop-color="#00e0ff"/>
|
||
<stop offset="100%" stop-color="#00ff9d"/>
|
||
</linearGradient>
|
||
<pattern id="grid" x="0" y="0" width="60" height="34" patternUnits="userSpaceOnUse">
|
||
<path d="M 60 0 L 0 0 0 34" fill="none" stroke="rgba(255,255,255,0.035)" stroke-width="1"/>
|
||
</pattern>
|
||
</defs>
|
||
<rect width="880" height="170" fill="url(#grid)"/>
|
||
<!-- Benchmark (S&P) ghost line -->
|
||
<polyline fill="none" stroke="rgba(255,255,255,0.22)" stroke-width="1.2" stroke-dasharray="3 4"
|
||
points="10,130 60,128 110,124 160,122 210,118 260,114 310,108 360,104 410,98 460,92 510,86 560,82 610,78 660,72 710,66 760,62 810,56 860,52" />
|
||
<!-- Portfolio area -->
|
||
<polygon fill="url(#pfGrad)"
|
||
points="10,150 10,128 60,124 110,116 160,118 210,108 260,114 310,98 360,90 410,96 460,80 510,72 560,76 610,60 660,54 710,46 760,40 810,32 860,28 870,28 870,150" />
|
||
<polyline fill="none" stroke="url(#pfStroke)" stroke-width="2.4" stroke-linejoin="round" stroke-linecap="round"
|
||
points="10,128 60,124 110,116 160,118 210,108 260,114 310,98 360,90 410,96 460,80 510,72 560,76 610,60 660,54 710,46 760,40 810,32 860,28" />
|
||
<!-- "Now" marker -->
|
||
<circle cx="860" cy="28" r="4" fill="#22e58c"/>
|
||
<circle cx="860" cy="28" r="9" fill="none" stroke="#22e58c" stroke-opacity="0.5"/>
|
||
<line x1="860" y1="28" x2="860" y2="160" stroke="rgba(34,229,140,0.35)" stroke-dasharray="2 3"/>
|
||
<!-- Buy markers -->
|
||
<g>
|
||
<circle cx="210" cy="108" r="3.5" fill="#facc15"/>
|
||
<circle cx="460" cy="80" r="3.5" fill="#facc15"/>
|
||
<circle cx="660" cy="54" r="3.5" fill="#facc15"/>
|
||
</g>
|
||
</svg>
|
||
<div class="pf-axis">
|
||
<span>May '24</span><span>Jul</span><span>Sep</span><span>Nov</span><span>Jan '25</span><span>Mar</span><span>Today</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="kpis">
|
||
<div class="kpi">
|
||
<div class="k-label">24h P&L</div>
|
||
<div class="k-val" style="color:var(--up)">+$5,842.16</div>
|
||
<div class="k-sub" style="color:var(--up)">+3.23% · 9 green / 2 red</div>
|
||
</div>
|
||
<div class="kpi">
|
||
<div class="k-label">Stables · dry powder</div>
|
||
<div class="k-val">$24,180</div>
|
||
<div class="k-sub" style="color:var(--muted)">USDC 18.4k · USDT 5.78k · 4.92% APY</div>
|
||
</div>
|
||
<div class="kpi spark">
|
||
<div class="k-label">Best holding · SOL</div>
|
||
<div class="k-val" style="color:var(--up)">+812.4%</div>
|
||
<svg viewBox="0 0 200 36" preserveAspectRatio="none">
|
||
<defs><linearGradient id="sg1" x1="0" x2="0" y1="0" y2="1"><stop offset="0%" stop-color="#22e58c" stop-opacity="0.4"/><stop offset="100%" stop-color="#22e58c" stop-opacity="0"/></linearGradient></defs>
|
||
<polygon fill="url(#sg1)" points="0,32 0,30 20,28 40,26 60,22 80,20 100,18 120,14 140,12 160,8 180,5 200,3 200,36 0,36"/>
|
||
<polyline fill="none" stroke="#22e58c" stroke-width="1.6" points="0,30 20,28 40,26 60,22 80,20 100,18 120,14 140,12 160,8 180,5 200,3"/>
|
||
</svg>
|
||
</div>
|
||
<div class="kpi alpha">
|
||
<div class="k-label">Alpha vs BTC · YTD</div>
|
||
<div class="k-val">+22.4pp</div>
|
||
<div class="k-sub" style="color:var(--muted)">Sharpe 1.86 · Vol 64% · Max DD −34%</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- ─────── Main grid: chart + AI rec ─────── -->
|
||
<section class="grid-2">
|
||
<!-- Chart -->
|
||
<div class="card chart-card" id="chartCard">
|
||
<div class="chart-head">
|
||
<div class="ticker">
|
||
<div class="icon" id="tkIcon" style="background:linear-gradient(135deg,#f7931a,#a35e00);color:#1a0e00;">₿</div>
|
||
<div class="meta">
|
||
<div class="sym-row">
|
||
<span class="sym" id="tkSym">BTC</span>
|
||
<a class="ext" id="tkLinkExch" href="https://www.coinbase.com/price/bitcoin" target="_blank" rel="noopener">Coinbase <span class="ico">↗</span></a>
|
||
<a class="ext" id="tkLinkYahoo" href="https://www.binance.com/en/price/bitcoin" target="_blank" rel="noopener">Binance <span class="ico">↗</span></a>
|
||
<a class="ext" id="tkLinkTV" href="https://coinmarketcap.com/currencies/bitcoin/" target="_blank" rel="noopener">CoinMarketCap <span class="ico">↗</span></a>
|
||
</div>
|
||
<div class="name" id="tkName">Bitcoin · L1 · Mkt cap $1.35T · Dominance 54.2%</div>
|
||
</div>
|
||
</div>
|
||
<div class="price-block">
|
||
<div class="px-now flicker" id="tkPx" style="color:var(--up)">$68,412</div>
|
||
<div class="px-delta" id="tkDelta" style="color:var(--up)">▲ +$1,236.40 +1.84% 24h</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="chart-stats" id="tkStats">
|
||
<div class="stat"><div class="lbl">24h open</div><div class="val" data-stat="open">67,176</div></div>
|
||
<div class="stat"><div class="lbl">24h high</div><div class="val" data-stat="high">68,920</div></div>
|
||
<div class="stat"><div class="lbl">24h low</div><div class="val" data-stat="low">66,840</div></div>
|
||
<div class="stat"><div class="lbl">24h volume</div><div class="val" data-stat="vol">$28.4B</div></div>
|
||
<div class="stat"><div class="lbl">Circ supply</div><div class="val" data-stat="pe">19.74M / 21M</div></div>
|
||
<div class="stat"><div class="lbl">ATH</div><div class="val" data-stat="range52">$73,750 (Mar)</div></div>
|
||
</div>
|
||
|
||
<div class="chart-body">
|
||
<div class="chart-toolbar">
|
||
<div class="chart-tools">
|
||
<span class="chip on">Candles</span>
|
||
<span class="chip">Line</span>
|
||
<span class="chip">Volume</span>
|
||
<span class="chip">SMA 50</span>
|
||
<span class="chip">RSI</span>
|
||
</div>
|
||
<div class="range-tabs">
|
||
<button>1D</button>
|
||
<button>5D</button>
|
||
<button class="active">1M</button>
|
||
<button>3M</button>
|
||
<button>1Y</button>
|
||
<button>5Y</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Candlestick -->
|
||
<svg class="candles" id="candleSvg" viewBox="0 0 880 320" preserveAspectRatio="none">
|
||
<defs>
|
||
<pattern id="grid2" x="0" y="0" width="80" height="40" patternUnits="userSpaceOnUse">
|
||
<path d="M 80 0 L 0 0 0 40" fill="none" stroke="rgba(255,255,255,0.035)" stroke-width="1"/>
|
||
</pattern>
|
||
</defs>
|
||
<rect width="880" height="320" fill="url(#grid2)"/>
|
||
|
||
<!-- Y axis labels -->
|
||
<g font-family="Geist Mono,monospace" font-size="10" fill="#6b7689">
|
||
<text x="6" y="36">940</text>
|
||
<text x="6" y="96">900</text>
|
||
<text x="6" y="156">860</text>
|
||
<text x="6" y="216">820</text>
|
||
<text x="6" y="276">780</text>
|
||
</g>
|
||
|
||
<!-- SMA50 trend line -->
|
||
<polyline fill="none" stroke="#facc15" stroke-opacity="0.55" stroke-width="1.4" stroke-dasharray="3 3"
|
||
points="40,220 80,214 120,208 160,200 200,196 240,188 280,180 320,170 360,160 400,150 440,140 480,128 520,118 560,108 600,96 640,84 680,76 720,70 760,62 800,54 840,46"/>
|
||
|
||
<!-- Candles: x ranges from 40 to 840 (stride 40), 21 candles -->
|
||
<!-- format: wick line + body rect; up = green, down = red -->
|
||
<g>
|
||
<!-- 1 -->
|
||
<line x1="44" y1="248" x2="44" y2="222" stroke="#22e58c" stroke-width="1.2"/><rect x="38" y="230" width="12" height="14" fill="#22e58c"/>
|
||
<!-- 2 -->
|
||
<line x1="84" y1="240" x2="84" y2="208" stroke="#22e58c" stroke-width="1.2"/><rect x="78" y="216" width="12" height="20" fill="#22e58c"/>
|
||
<!-- 3 down -->
|
||
<line x1="124" y1="222" x2="124" y2="246" stroke="#ff4f6d" stroke-width="1.2"/><rect x="118" y="224" width="12" height="14" fill="#ff4f6d"/>
|
||
<!-- 4 -->
|
||
<line x1="164" y1="232" x2="164" y2="200" stroke="#22e58c" stroke-width="1.2"/><rect x="158" y="206" width="12" height="22" fill="#22e58c"/>
|
||
<!-- 5 -->
|
||
<line x1="204" y1="218" x2="204" y2="190" stroke="#22e58c" stroke-width="1.2"/><rect x="198" y="196" width="12" height="18" fill="#22e58c"/>
|
||
<!-- 6 down -->
|
||
<line x1="244" y1="200" x2="244" y2="226" stroke="#ff4f6d" stroke-width="1.2"/><rect x="238" y="204" width="12" height="16" fill="#ff4f6d"/>
|
||
<!-- 7 -->
|
||
<line x1="284" y1="220" x2="284" y2="184" stroke="#22e58c" stroke-width="1.2"/><rect x="278" y="190" width="12" height="22" fill="#22e58c"/>
|
||
<!-- 8 -->
|
||
<line x1="324" y1="200" x2="324" y2="170" stroke="#22e58c" stroke-width="1.2"/><rect x="318" y="178" width="12" height="18" fill="#22e58c"/>
|
||
<!-- 9 down -->
|
||
<line x1="364" y1="180" x2="364" y2="208" stroke="#ff4f6d" stroke-width="1.2"/><rect x="358" y="184" width="12" height="18" fill="#ff4f6d"/>
|
||
<!-- 10 -->
|
||
<line x1="404" y1="206" x2="404" y2="158" stroke="#22e58c" stroke-width="1.2"/><rect x="398" y="166" width="12" height="34" fill="#22e58c"/>
|
||
<!-- 11 -->
|
||
<line x1="444" y1="170" x2="444" y2="146" stroke="#22e58c" stroke-width="1.2"/><rect x="438" y="152" width="12" height="14" fill="#22e58c"/>
|
||
<!-- 12 down -->
|
||
<line x1="484" y1="148" x2="484" y2="174" stroke="#ff4f6d" stroke-width="1.2"/><rect x="478" y="152" width="12" height="16" fill="#ff4f6d"/>
|
||
<!-- 13 -->
|
||
<line x1="524" y1="166" x2="524" y2="128" stroke="#22e58c" stroke-width="1.2"/><rect x="518" y="136" width="12" height="24" fill="#22e58c"/>
|
||
<!-- 14 -->
|
||
<line x1="564" y1="142" x2="564" y2="116" stroke="#22e58c" stroke-width="1.2"/><rect x="558" y="124" width="12" height="14" fill="#22e58c"/>
|
||
<!-- 15 down -->
|
||
<line x1="604" y1="120" x2="604" y2="146" stroke="#ff4f6d" stroke-width="1.2"/><rect x="598" y="124" width="12" height="16" fill="#ff4f6d"/>
|
||
<!-- 16 -->
|
||
<line x1="644" y1="142" x2="644" y2="100" stroke="#22e58c" stroke-width="1.2"/><rect x="638" y="106" width="12" height="30" fill="#22e58c"/>
|
||
<!-- 17 -->
|
||
<line x1="684" y1="112" x2="684" y2="84" stroke="#22e58c" stroke-width="1.2"/><rect x="678" y="92" width="12" height="14" fill="#22e58c"/>
|
||
<!-- 18 down -->
|
||
<line x1="724" y1="84" x2="724" y2="106" stroke="#ff4f6d" stroke-width="1.2"/><rect x="718" y="86" width="12" height="14" fill="#ff4f6d"/>
|
||
<!-- 19 -->
|
||
<line x1="764" y1="106" x2="764" y2="68" stroke="#22e58c" stroke-width="1.2"/><rect x="758" y="74" width="12" height="26" fill="#22e58c"/>
|
||
<!-- 20 -->
|
||
<line x1="804" y1="80" x2="804" y2="52" stroke="#22e58c" stroke-width="1.2"/><rect x="798" y="58" width="12" height="20" fill="#22e58c"/>
|
||
<!-- 21 (today) -->
|
||
<line x1="844" y1="68" x2="844" y2="38" stroke="#22e58c" stroke-width="1.2"/><rect x="838" y="44" width="12" height="22" fill="#22e58c"/>
|
||
</g>
|
||
|
||
<!-- Buy/sell markers from journal -->
|
||
<g font-family="Geist Mono,monospace" font-size="10">
|
||
<circle cx="284" cy="200" r="5" fill="#facc15"/>
|
||
<text x="296" y="204" fill="#facc15">BUY · 60 @ 812</text>
|
||
<circle cx="524" cy="166" r="5" fill="#facc15"/>
|
||
<text x="536" y="170" fill="#facc15">BUY · 40 @ 845</text>
|
||
</g>
|
||
|
||
<!-- Current price marker -->
|
||
<g id="pxMarker">
|
||
<line x1="40" y1="44" x2="844" y2="44" stroke="rgba(34,229,140,0.25)" stroke-dasharray="2 3"/>
|
||
<rect x="836" y="36" width="40" height="18" fill="#22e58c" rx="3"/>
|
||
<text id="pxMarkerText" x="856" y="49" font-family="Geist Mono,monospace" font-size="10" fill="#00200f" text-anchor="middle" font-weight="700">924.31</text>
|
||
</g>
|
||
</svg>
|
||
|
||
<!-- Volume -->
|
||
<svg class="vol" id="volSvg" viewBox="0 0 880 70" preserveAspectRatio="none">
|
||
<g>
|
||
<rect x="38" y="50" width="12" height="20" fill="#22e58c" opacity="0.5"/>
|
||
<rect x="78" y="44" width="12" height="26" fill="#22e58c" opacity="0.5"/>
|
||
<rect x="118" y="48" width="12" height="22" fill="#ff4f6d" opacity="0.55"/>
|
||
<rect x="158" y="40" width="12" height="30" fill="#22e58c" opacity="0.5"/>
|
||
<rect x="198" y="46" width="12" height="24" fill="#22e58c" opacity="0.5"/>
|
||
<rect x="238" y="50" width="12" height="20" fill="#ff4f6d" opacity="0.55"/>
|
||
<rect x="278" y="36" width="12" height="34" fill="#22e58c" opacity="0.5"/>
|
||
<rect x="318" y="42" width="12" height="28" fill="#22e58c" opacity="0.5"/>
|
||
<rect x="358" y="44" width="12" height="26" fill="#ff4f6d" opacity="0.55"/>
|
||
<rect x="398" y="22" width="12" height="48" fill="#22e58c" opacity="0.65"/>
|
||
<rect x="438" y="46" width="12" height="24" fill="#22e58c" opacity="0.5"/>
|
||
<rect x="478" y="44" width="12" height="26" fill="#ff4f6d" opacity="0.55"/>
|
||
<rect x="518" y="30" width="12" height="40" fill="#22e58c" opacity="0.6"/>
|
||
<rect x="558" y="44" width="12" height="26" fill="#22e58c" opacity="0.5"/>
|
||
<rect x="598" y="46" width="12" height="24" fill="#ff4f6d" opacity="0.55"/>
|
||
<rect x="638" y="20" width="12" height="50" fill="#22e58c" opacity="0.7"/>
|
||
<rect x="678" y="42" width="12" height="28" fill="#22e58c" opacity="0.5"/>
|
||
<rect x="718" y="48" width="12" height="22" fill="#ff4f6d" opacity="0.55"/>
|
||
<rect x="758" y="26" width="12" height="44" fill="#22e58c" opacity="0.6"/>
|
||
<rect x="798" y="34" width="12" height="36" fill="#22e58c" opacity="0.5"/>
|
||
<rect x="838" y="14" width="12" height="56" fill="#22e58c" opacity="0.85"/>
|
||
</g>
|
||
<text x="6" y="14" font-family="Geist Mono,monospace" font-size="10" fill="#6b7689">VOL</text>
|
||
<text x="6" y="64" font-family="Geist Mono,monospace" font-size="10" fill="#6b7689">38.4M</text>
|
||
</svg>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- AI Recommendation -->
|
||
<div class="card ai-card">
|
||
<div class="ai-head">
|
||
<div style="display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap;">
|
||
<div class="ai-tag">Quiver AI · Signal #2417</div>
|
||
<button class="ai-regen" id="regenBtn" title="Regenerate analysis">
|
||
<svg class="ico" viewBox="0 0 16 16" fill="none"><path d="M14 8a6 6 0 1 1-1.76-4.24M14 3v3.5h-3.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
||
<span id="regenLabel">Regenerate</span>
|
||
</button>
|
||
</div>
|
||
<div class="ai-headline" id="aiHeadline">
|
||
<span class="accent">Rotate stables → SOL</span> on DePIN + Firedancer accelerator.
|
||
</div>
|
||
<div class="ai-sub" id="aiSub">
|
||
On-chain activity is breaking out while ETF spot demand is steady. Risk-reward favors adding SOL while keeping BTC core untouched.
|
||
</div>
|
||
<div class="regime-row">
|
||
<span class="regime-pill up"><span class="lbl">Trend</span><span class="v">▲ Bullish</span></span>
|
||
<span class="regime-pill neut"><span class="lbl">Funding</span><span class="v">+0.012% 8h</span></span>
|
||
<span class="regime-pill up"><span class="lbl">ETF flow</span><span class="v">+$418M / 5d</span></span>
|
||
<span class="regime-pill down"><span class="lbl">Sentiment</span><span class="v">Greed 78</span></span>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="ai-stat-row">
|
||
<div class="ai-stat conviction">
|
||
<div class="lbl">Conviction</div>
|
||
<div class="v" style="color:var(--cyan)" id="convVal">82%</div>
|
||
<div class="bar"><div class="fill" id="convFill" style="width:82%"></div></div>
|
||
</div>
|
||
<div class="ai-stat">
|
||
<div class="lbl">3-mo target</div>
|
||
<div class="v up">$240</div>
|
||
<div class="lbl" style="margin-top:2px;color:var(--up)">+28.7% upside</div>
|
||
</div>
|
||
<div class="ai-stat">
|
||
<div class="lbl">Suggested size</div>
|
||
<div class="v">+45 SOL</div>
|
||
<div class="lbl" style="margin-top:2px;color:var(--muted)">≈ $8,389 · 4.5% port</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Tabs -->
|
||
<div class="ai-tabs" role="tablist">
|
||
<button class="ai-tab active" data-tab="catalysts">Catalysts <span class="dot bull"></span></button>
|
||
<button class="ai-tab" data-tab="risks">Risks <span class="dot risk"></span></button>
|
||
<button class="ai-tab" data-tab="position">Position read <span class="dot violet"></span></button>
|
||
<button class="ai-tab" data-tab="plan">Trade plan <span class="dot amber"></span></button>
|
||
</div>
|
||
|
||
<!-- Catalysts panel -->
|
||
<div class="ai-panel active" data-panel="catalysts">
|
||
<div class="ai-thesis">
|
||
<h4>Why now · 3 catalysts</h4>
|
||
<div class="thesis-list">
|
||
<div class="thesis-item">
|
||
<div class="thesis-num">01</div>
|
||
<div class="thesis-text"><strong>Firedancer mainnet imminent</strong> — Jump's validator client is in canary on testnet. Once live, expect a step-change in throughput narrative. Historical analogs (Sui mainnet, Sept '23) priced 28% in 14 days.</div>
|
||
</div>
|
||
<div class="thesis-item">
|
||
<div class="thesis-num">02</div>
|
||
<div class="thesis-text"><strong>DePIN flywheel printing real revenue</strong> — Helium, Render, Hivemapper combined paid <span style="color:var(--up)">$32M+</span> in last quarter. Settlement is on Solana; fee accrual is sticky.</div>
|
||
</div>
|
||
<div class="thesis-item">
|
||
<div class="thesis-num">03</div>
|
||
<div class="thesis-text"><strong>BTC ETF flows holding</strong> — net <span style="color:var(--up)">+$418M</span> over the last 5 sessions. Macro tape is risk-on; BTC dominance compressing — usually a green light for L1 rotation.</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Risks panel -->
|
||
<div class="ai-panel" data-panel="risks">
|
||
<div class="risk-list">
|
||
<div class="risk-row">
|
||
<div class="lvl high">High</div>
|
||
<div class="body"><strong>Mainnet outage.</strong> Solana has had 6 partial halts in 2 years. A Firedancer rollout could either harden or briefly fracture the network.</div>
|
||
<div class="prob">P 24%</div>
|
||
</div>
|
||
<div class="risk-row">
|
||
<div class="lvl med">Med</div>
|
||
<div class="body"><strong>FTX estate distributions.</strong> ~14M SOL still unlocked to creditors over 18 months. Recent prints have been absorbed; a fast-paced sale would pressure spot 8–12%.</div>
|
||
<div class="prob">P 32%</div>
|
||
</div>
|
||
<div class="risk-row">
|
||
<div class="lvl med">Med</div>
|
||
<div class="body"><strong>L2 vs L1 narrative whipsaw.</strong> If Base/Arbitrum capture re-flips dominance, SOL beta to ETH could turn negative for a quarter.</div>
|
||
<div class="prob">P 22%</div>
|
||
</div>
|
||
<div class="risk-row">
|
||
<div class="lvl low">Low</div>
|
||
<div class="body"><strong>Macro risk-off.</strong> Crypto beta to NDX is ~2.4. A 5% NDX correction would mark down crypto book ~12% even on unchanged thesis. Use stables ladder as buffer.</div>
|
||
<div class="prob">P 18%</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Position read panel -->
|
||
<div class="ai-panel" data-panel="position">
|
||
<div class="pos-read">
|
||
<div class="pos-line"><span class="lbl">Avg cost basis · SOL</span><span class="val">$20.46 · 240 SOL</span></div>
|
||
<div class="pos-line"><span class="lbl">Mark-to-market</span><span class="val up">$44,740 (+812.4%)</span></div>
|
||
<div class="pos-line"><span class="lbl">Holding period</span><span class="val">692 days · earned 18.4 SOL staking</span></div>
|
||
<div class="pos-line"><span class="lbl">Position vs target</span><span class="val">24.0% / 30% target — <span style="color:var(--up)">room to add</span></span></div>
|
||
<div class="pos-line"><span class="lbl">Wallet</span><span class="val">Phantom · Ledger Nano X</span></div>
|
||
<div class="pos-line"><span class="lbl">Beta to BTC</span><span class="val">1.84</span></div>
|
||
<div class="pos-narr">
|
||
<div class="quote">"Conviction trade in a portfolio that's already worked. Don't let recency bias trim a thesis that's strengthening on every datapoint."</div>
|
||
Quiver AI flags <strong>+$418M ETF inflows + Firedancer testnet</strong> as the recent regime change. Funding is calm (+0.012%) — no froth — and on-chain DAU just hit a 90-day high. The cleanest add is on a <strong>−5% wick into $176</strong>; at-mkt is acceptable.
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Trade plan panel -->
|
||
<div class="ai-panel" data-panel="plan">
|
||
<div class="plan-grid">
|
||
<div class="plan-step entry">
|
||
<div class="plan-bullet">▲</div>
|
||
<div class="plan-label">Entry zone</div>
|
||
<div class="plan-zone"><span class="px">$182 — $190</span> <span class="pct up">at-mkt OK</span></div>
|
||
</div>
|
||
<div class="plan-step entry">
|
||
<div class="plan-bullet">+</div>
|
||
<div class="plan-label">Add lot 2</div>
|
||
<div class="plan-zone"><span class="px">$176</span> on -5% wick <span class="pct">+30 SOL</span></div>
|
||
</div>
|
||
<div class="plan-step stop">
|
||
<div class="plan-bullet">▼</div>
|
||
<div class="plan-label">Invalidation</div>
|
||
<div class="plan-zone"><span class="px">$162</span> daily close <span class="pct down">−13.0% from spot</span></div>
|
||
</div>
|
||
<div class="plan-step target">
|
||
<div class="plan-bullet">①</div>
|
||
<div class="plan-label">Target 1</div>
|
||
<div class="plan-zone"><span class="px">$220</span> trim 25% <span class="pct up">+18.0%</span></div>
|
||
</div>
|
||
<div class="plan-step target">
|
||
<div class="plan-bullet">②</div>
|
||
<div class="plan-label">Target 2</div>
|
||
<div class="plan-zone"><span class="px">$240</span> trim 25% <span class="pct up">+28.7%</span></div>
|
||
</div>
|
||
<div class="plan-step target">
|
||
<div class="plan-bullet">③</div>
|
||
<div class="plan-label">Cycle target</div>
|
||
<div class="plan-zone"><span class="px">$295</span> trim 50% <span class="pct up">+58.2%</span></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="ai-actions">
|
||
<button class="btn primary" data-action="add">+ Buy 45 SOL</button>
|
||
<button class="btn" data-action="alert">Set price alert</button>
|
||
<button class="btn" data-action="thesis">View on-chain thesis</button>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- ─────── Holdings ─────── -->
|
||
<div class="card" style="padding:0;">
|
||
<div class="panel-head">
|
||
<div class="panel-title">Holdings · 9 wallets</div>
|
||
<div class="panel-sub">On-chain · last sync 14:32:18 UTC</div>
|
||
</div>
|
||
<table class="holdings" id="holdingsTable">
|
||
<thead>
|
||
<tr>
|
||
<th>Asset</th>
|
||
<th class="num">Amount</th>
|
||
<th class="num">Avg cost</th>
|
||
<th class="num">Last</th>
|
||
<th>Trend · 30d</th>
|
||
<th class="num">Market value</th>
|
||
<th class="num">Unrealized P&L</th>
|
||
<th>Allocation</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr data-symbol="BTC" class="active">
|
||
<td>
|
||
<div class="sym-cell">
|
||
<span class="sym-mark" style="background:linear-gradient(135deg,#f7931a,#a35e00);color:#1a0e00;">₿</span>
|
||
<span class="name-block">
|
||
<span class="sym-name">BTC <a class="ext-mini" href="https://www.coinbase.com/price/bitcoin" target="_blank" rel="noopener" onclick="event.stopPropagation()">Coinbase ↗</a></span>
|
||
<span class="sym-co">Bitcoin · Ledger Nano X</span>
|
||
</span>
|
||
</div>
|
||
</td>
|
||
<td class="num">1.0842</td>
|
||
<td class="num">28,440</td>
|
||
<td class="num" style="color:var(--up);font-weight:600;">68,412</td>
|
||
<td class="mini-spark">
|
||
<svg viewBox="0 0 70 24" preserveAspectRatio="none"><polyline fill="none" stroke="#22e58c" stroke-width="1.4" points="0,18 6,17 12,16 18,15 24,14 30,15 36,13 42,11 48,10 54,8 60,7 66,6 70,5"/></svg>
|
||
</td>
|
||
<td class="num">$74,170</td>
|
||
<td class="num"><span class="pl-cell up"><span class="pl-val">+$43,738</span><span class="pl-pct">+143.7%</span></span></td>
|
||
<td><span class="alloc-bar"><span class="alloc-fill" style="width:40%"></span></span> <span style="font-family:var(--mono);font-size:11px;color:var(--muted)">39.8%</span></td>
|
||
</tr>
|
||
<tr data-symbol="SOL">
|
||
<td>
|
||
<div class="sym-cell">
|
||
<span class="sym-mark" style="background:linear-gradient(135deg,#14f195,#9945ff);color:#0a0020;">◎</span>
|
||
<span class="name-block">
|
||
<span class="sym-name">SOL <a class="ext-mini" href="https://www.coinbase.com/price/solana" target="_blank" rel="noopener" onclick="event.stopPropagation()">Coinbase ↗</a></span>
|
||
<span class="sym-co">Solana · Phantom (staked 18.4)</span>
|
||
</span>
|
||
</div>
|
||
</td>
|
||
<td class="num">240.00</td>
|
||
<td class="num">20.46</td>
|
||
<td class="num" style="color:var(--up);font-weight:600;">186.42</td>
|
||
<td class="mini-spark">
|
||
<svg viewBox="0 0 70 24" preserveAspectRatio="none"><polyline fill="none" stroke="#22e58c" stroke-width="1.4" points="0,20 6,18 12,17 18,15 24,14 30,12 36,10 42,8 48,9 54,6 60,5 66,3 70,2"/></svg>
|
||
</td>
|
||
<td class="num">$44,740</td>
|
||
<td class="num"><span class="pl-cell up"><span class="pl-val">+$39,830</span><span class="pl-pct">+812.4%</span></span></td>
|
||
<td><span class="alloc-bar"><span class="alloc-fill" style="width:24%"></span></span> <span style="font-family:var(--mono);font-size:11px;color:var(--muted)">24.0%</span></td>
|
||
</tr>
|
||
<tr data-symbol="ETH">
|
||
<td>
|
||
<div class="sym-cell">
|
||
<span class="sym-mark" style="background:linear-gradient(135deg,#627eea,#3c5ed7);color:#fff;">Ξ</span>
|
||
<span class="name-block">
|
||
<span class="sym-name">ETH <a class="ext-mini" href="https://www.coinbase.com/price/ethereum" target="_blank" rel="noopener" onclick="event.stopPropagation()">Coinbase ↗</a></span>
|
||
<span class="sym-co">Ethereum · MetaMask · 8.4 staked</span>
|
||
</span>
|
||
</div>
|
||
</td>
|
||
<td class="num">10.62</td>
|
||
<td class="num">1,820</td>
|
||
<td class="num" style="color:var(--up);font-weight:600;">3,612</td>
|
||
<td class="mini-spark">
|
||
<svg viewBox="0 0 70 24" preserveAspectRatio="none"><polyline fill="none" stroke="#22e58c" stroke-width="1.4" points="0,17 6,16 12,17 18,15 24,14 30,15 36,12 42,11 48,12 54,9 60,8 66,7 70,6"/></svg>
|
||
</td>
|
||
<td class="num">$38,360</td>
|
||
<td class="num"><span class="pl-cell up"><span class="pl-val">+$19,032</span><span class="pl-pct">+98.5%</span></span></td>
|
||
<td><span class="alloc-bar"><span class="alloc-fill" style="width:21%"></span></span> <span style="font-family:var(--mono);font-size:11px;color:var(--muted)">20.6%</span></td>
|
||
</tr>
|
||
<tr data-symbol="LINK">
|
||
<td>
|
||
<div class="sym-cell">
|
||
<span class="sym-mark" style="background:linear-gradient(135deg,#2a5ada,#1d3ea0);color:#fff;">L</span>
|
||
<span class="name-block">
|
||
<span class="sym-name">LINK <a class="ext-mini" href="https://www.coinbase.com/price/chainlink" target="_blank" rel="noopener" onclick="event.stopPropagation()">Coinbase ↗</a></span>
|
||
<span class="sym-co">Chainlink · MetaMask</span>
|
||
</span>
|
||
</div>
|
||
</td>
|
||
<td class="num">680</td>
|
||
<td class="num">8.42</td>
|
||
<td class="num" style="color:var(--up);font-weight:600;">14.92</td>
|
||
<td class="mini-spark">
|
||
<svg viewBox="0 0 70 24" preserveAspectRatio="none"><polyline fill="none" stroke="#22e58c" stroke-width="1.4" points="0,18 6,17 12,15 18,14 24,15 30,12 36,11 42,12 48,10 54,8 60,7 66,6 70,5"/></svg>
|
||
</td>
|
||
<td class="num">$10,146</td>
|
||
<td class="num"><span class="pl-cell up"><span class="pl-val">+$4,420</span><span class="pl-pct">+77.2%</span></span></td>
|
||
<td><span class="alloc-bar"><span class="alloc-fill" style="width:6%"></span></span> <span style="font-family:var(--mono);font-size:11px;color:var(--muted)">5.4%</span></td>
|
||
</tr>
|
||
<tr data-symbol="AVAX">
|
||
<td>
|
||
<div class="sym-cell">
|
||
<span class="sym-mark" style="background:linear-gradient(135deg,#e84142,#7a0d0e);color:#fff;">A</span>
|
||
<span class="name-block">
|
||
<span class="sym-name">AVAX <a class="ext-mini" href="https://www.coinbase.com/price/avalanche" target="_blank" rel="noopener" onclick="event.stopPropagation()">Coinbase ↗</a></span>
|
||
<span class="sym-co">Avalanche · Core wallet</span>
|
||
</span>
|
||
</div>
|
||
</td>
|
||
<td class="num">220</td>
|
||
<td class="num">28.40</td>
|
||
<td class="num" style="color:var(--up);font-weight:600;">36.84</td>
|
||
<td class="mini-spark">
|
||
<svg viewBox="0 0 70 24" preserveAspectRatio="none"><polyline fill="none" stroke="#22e58c" stroke-width="1.4" points="0,16 6,15 12,17 18,14 24,12 30,13 36,11 42,10 48,12 54,9 60,8 66,7 70,7"/></svg>
|
||
</td>
|
||
<td class="num">$8,105</td>
|
||
<td class="num"><span class="pl-cell up"><span class="pl-val">+$1,857</span><span class="pl-pct">+29.7%</span></span></td>
|
||
<td><span class="alloc-bar"><span class="alloc-fill" style="width:5%"></span></span> <span style="font-family:var(--mono);font-size:11px;color:var(--muted)">4.4%</span></td>
|
||
</tr>
|
||
<tr data-symbol="DOT">
|
||
<td>
|
||
<div class="sym-cell">
|
||
<span class="sym-mark" style="background:linear-gradient(135deg,#e6007a,#860146);color:#fff;">●</span>
|
||
<span class="name-block">
|
||
<span class="sym-name">DOT <a class="ext-mini" href="https://www.coinbase.com/price/polkadot" target="_blank" rel="noopener" onclick="event.stopPropagation()">Coinbase ↗</a></span>
|
||
<span class="sym-co">Polkadot · Polkadot.js</span>
|
||
</span>
|
||
</div>
|
||
</td>
|
||
<td class="num">600</td>
|
||
<td class="num">9.10</td>
|
||
<td class="num" style="color:var(--down);font-weight:600;">7.18</td>
|
||
<td class="mini-spark">
|
||
<svg viewBox="0 0 70 24" preserveAspectRatio="none"><polyline fill="none" stroke="#ff4f6d" stroke-width="1.4" points="0,10 6,11 12,9 18,12 24,13 30,11 36,14 42,15 48,13 54,16 60,17 66,18 70,18"/></svg>
|
||
</td>
|
||
<td class="num">$4,308</td>
|
||
<td class="num"><span class="pl-cell down"><span class="pl-val">−$1,152</span><span class="pl-pct">−21.1%</span></span></td>
|
||
<td><span class="alloc-bar"><span class="alloc-fill" style="width:3%"></span></span> <span style="font-family:var(--mono);font-size:11px;color:var(--muted)">2.3%</span></td>
|
||
</tr>
|
||
<tr data-symbol="DOGE">
|
||
<td>
|
||
<div class="sym-cell">
|
||
<span class="sym-mark" style="background:linear-gradient(135deg,#c2a633,#8a7321);color:#1a1100;">Ð</span>
|
||
<span class="name-block">
|
||
<span class="sym-name">DOGE <a class="ext-mini" href="https://www.coinbase.com/price/dogecoin" target="_blank" rel="noopener" onclick="event.stopPropagation()">Coinbase ↗</a></span>
|
||
<span class="sym-co">Dogecoin · Hot wallet</span>
|
||
</span>
|
||
</div>
|
||
</td>
|
||
<td class="num">12,400</td>
|
||
<td class="num">0.082</td>
|
||
<td class="num" style="color:var(--up);font-weight:600;">0.1842</td>
|
||
<td class="mini-spark">
|
||
<svg viewBox="0 0 70 24" preserveAspectRatio="none"><polyline fill="none" stroke="#22e58c" stroke-width="1.4" points="0,20 6,18 12,17 18,15 24,18 30,14 36,12 42,15 48,10 54,8 60,7 66,5 70,4"/></svg>
|
||
</td>
|
||
<td class="num">$2,284</td>
|
||
<td class="num"><span class="pl-cell up"><span class="pl-val">+$1,267</span><span class="pl-pct">+124.6%</span></span></td>
|
||
<td><span class="alloc-bar"><span class="alloc-fill" style="width:1%"></span></span> <span style="font-family:var(--mono);font-size:11px;color:var(--muted)">1.2%</span></td>
|
||
</tr>
|
||
<tr>
|
||
<td>
|
||
<div class="sym-cell">
|
||
<span class="sym-mark" style="background:linear-gradient(135deg,#2775ca,#1a4f8a);color:#fff;">$</span>
|
||
<span class="name-block">
|
||
<span class="sym-name">USDC stables</span>
|
||
<span class="sym-co">Coinbase + Aave (4.92% APY)</span>
|
||
</span>
|
||
</div>
|
||
</td>
|
||
<td class="num">18,420</td>
|
||
<td class="num">1.00</td>
|
||
<td class="num">1.00</td>
|
||
<td>—</td>
|
||
<td class="num">$18,420</td>
|
||
<td class="num"><span style="color:var(--up);font-family:var(--mono);">+$842 yield</span></td>
|
||
<td><span class="alloc-bar"><span class="alloc-fill" style="width:9%"></span></span> <span style="font-family:var(--mono);font-size:11px;color:var(--muted)">9.9%</span></td>
|
||
</tr>
|
||
<tr>
|
||
<td>
|
||
<div class="sym-cell">
|
||
<span class="sym-mark" style="background:linear-gradient(135deg,#26a17b,#136f50);color:#fff;">T</span>
|
||
<span class="name-block">
|
||
<span class="sym-name">USDT stables</span>
|
||
<span class="sym-co">Tron · cold storage</span>
|
||
</span>
|
||
</div>
|
||
</td>
|
||
<td class="num">5,780</td>
|
||
<td class="num">1.00</td>
|
||
<td class="num">1.00</td>
|
||
<td>—</td>
|
||
<td class="num">$5,780</td>
|
||
<td class="num"><span style="color:var(--muted);font-family:var(--mono);">—</span></td>
|
||
<td><span class="alloc-bar"><span class="alloc-fill" style="width:3%"></span></span> <span style="font-family:var(--mono);font-size:11px;color:var(--muted)">3.1%</span></td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
|
||
<!-- ─────── Watchlist + News two-column ─────── -->
|
||
<section class="grid-2">
|
||
<div class="card" style="padding:0;">
|
||
<div class="panel-head">
|
||
<div class="panel-title">Watchlist · L1 + L2 + DePIN</div>
|
||
<div class="panel-sub">14 tickers · 14:32 UTC</div>
|
||
</div>
|
||
<div class="watch-grid" style="padding:14px;">
|
||
<div class="watch-card">
|
||
<div class="watch-top">
|
||
<div><div class="watch-sym">SUI</div><div class="watch-co">Sui Network</div></div>
|
||
<span class="watch-pct up">▲ 8.40%</span>
|
||
</div>
|
||
<div class="watch-px">$2.18</div>
|
||
<div class="watch-spark">
|
||
<svg viewBox="0 0 200 40" preserveAspectRatio="none">
|
||
<defs><linearGradient id="ws1" x1="0" x2="0" y1="0" y2="1"><stop offset="0%" stop-color="#22e58c" stop-opacity="0.4"/><stop offset="100%" stop-color="#22e58c" stop-opacity="0"/></linearGradient></defs>
|
||
<polygon fill="url(#ws1)" points="0,32 0,28 20,30 40,26 60,24 80,28 100,22 120,18 140,20 160,14 180,10 200,6 200,40 0,40"/>
|
||
<polyline fill="none" stroke="#22e58c" stroke-width="1.6" points="0,28 20,30 40,26 60,24 80,28 100,22 120,18 140,20 160,14 180,10 200,6"/>
|
||
</svg>
|
||
</div>
|
||
<div class="watch-foot">
|
||
<span>Vol $812M</span>
|
||
<a href="https://www.coinbase.com/price/sui" target="_blank" rel="noopener">Coinbase ↗</a>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="watch-card">
|
||
<div class="watch-top">
|
||
<div><div class="watch-sym">DOGE</div><div class="watch-co">Dogecoin</div></div>
|
||
<span class="watch-pct up">▲ 6.84%</span>
|
||
</div>
|
||
<div class="watch-px">$0.1842</div>
|
||
<div class="watch-spark">
|
||
<svg viewBox="0 0 200 40" preserveAspectRatio="none">
|
||
<polygon fill="url(#ws1)" points="0,30 0,26 20,28 40,22 60,24 80,20 100,22 120,18 140,16 160,12 180,14 200,10 200,40 0,40"/>
|
||
<polyline fill="none" stroke="#22e58c" stroke-width="1.6" points="0,26 20,28 40,22 60,24 80,20 100,22 120,18 140,16 160,12 180,14 200,10"/>
|
||
</svg>
|
||
</div>
|
||
<div class="watch-foot">
|
||
<span>Vol $1.4B</span>
|
||
<a href="https://www.coinbase.com/price/dogecoin" target="_blank" rel="noopener">Coinbase ↗</a>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="watch-card">
|
||
<div class="watch-top">
|
||
<div><div class="watch-sym">AVAX</div><div class="watch-co">Avalanche</div></div>
|
||
<span class="watch-pct up">▲ 2.86%</span>
|
||
</div>
|
||
<div class="watch-px">$36.84</div>
|
||
<div class="watch-spark">
|
||
<svg viewBox="0 0 200 40" preserveAspectRatio="none">
|
||
<polygon fill="url(#ws1)" points="0,28 0,24 20,22 40,26 60,20 80,18 100,20 120,16 140,14 160,12 180,10 200,8 200,40 0,40"/>
|
||
<polyline fill="none" stroke="#22e58c" stroke-width="1.6" points="0,24 20,22 40,26 60,20 80,18 100,20 120,16 140,14 160,12 180,10 200,8"/>
|
||
</svg>
|
||
</div>
|
||
<div class="watch-foot">
|
||
<span>Vol $640M</span>
|
||
<a href="https://www.coinbase.com/price/avalanche" target="_blank" rel="noopener">Coinbase ↗</a>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="watch-card">
|
||
<div class="watch-top">
|
||
<div><div class="watch-sym">NEAR</div><div class="watch-co">NEAR Protocol</div></div>
|
||
<span class="watch-pct up">▲ 3.20%</span>
|
||
</div>
|
||
<div class="watch-px">$5.42</div>
|
||
<div class="watch-spark">
|
||
<svg viewBox="0 0 200 40" preserveAspectRatio="none">
|
||
<polygon fill="url(#ws1)" points="0,32 0,28 20,30 40,26 60,28 80,22 100,18 120,20 140,14 160,10 180,12 200,6 200,40 0,40"/>
|
||
<polyline fill="none" stroke="#22e58c" stroke-width="1.6" points="0,28 20,30 40,26 60,28 80,22 100,18 120,20 140,14 160,10 180,12 200,6"/>
|
||
</svg>
|
||
</div>
|
||
<div class="watch-foot">
|
||
<span>Vol $312M</span>
|
||
<a href="https://www.coinbase.com/price/near-protocol" target="_blank" rel="noopener">Coinbase ↗</a>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="watch-card">
|
||
<div class="watch-top">
|
||
<div><div class="watch-sym">TIA</div><div class="watch-co">Celestia</div></div>
|
||
<span class="watch-pct down">▼ 1.18%</span>
|
||
</div>
|
||
<div class="watch-px">$7.84</div>
|
||
<div class="watch-spark">
|
||
<svg viewBox="0 0 200 40" preserveAspectRatio="none">
|
||
<defs><linearGradient id="ws2" x1="0" x2="0" y1="0" y2="1"><stop offset="0%" stop-color="#ff4f6d" stop-opacity="0.4"/><stop offset="100%" stop-color="#ff4f6d" stop-opacity="0"/></linearGradient></defs>
|
||
<polygon fill="url(#ws2)" points="0,10 0,14 20,12 40,16 60,14 80,18 100,20 120,18 140,22 160,24 180,28 200,30 200,40 0,40"/>
|
||
<polyline fill="none" stroke="#ff4f6d" stroke-width="1.6" points="0,14 20,12 40,16 60,14 80,18 100,20 120,18 140,22 160,24 180,28 200,30"/>
|
||
</svg>
|
||
</div>
|
||
<div class="watch-foot">
|
||
<span>Vol $84M</span>
|
||
<a href="https://www.coinbase.com/price/celestia" target="_blank" rel="noopener">Coinbase ↗</a>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="watch-card">
|
||
<div class="watch-top">
|
||
<div><div class="watch-sym">ARB</div><div class="watch-co">Arbitrum</div></div>
|
||
<span class="watch-pct up">▲ 0.95%</span>
|
||
</div>
|
||
<div class="watch-px">$0.6420</div>
|
||
<div class="watch-spark">
|
||
<svg viewBox="0 0 200 40" preserveAspectRatio="none">
|
||
<polygon fill="url(#ws1)" points="0,34 0,30 20,32 40,28 60,30 80,26 100,20 120,16 140,18 160,12 180,8 200,4 200,40 0,40"/>
|
||
<polyline fill="none" stroke="#22e58c" stroke-width="1.6" points="0,30 20,32 40,28 60,30 80,26 100,20 120,16 140,18 160,12 180,8 200,4"/>
|
||
</svg>
|
||
</div>
|
||
<div class="watch-foot">
|
||
<span>Vol $186M</span>
|
||
<a href="https://www.coinbase.com/price/arbitrum" target="_blank" rel="noopener">Coinbase ↗</a>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="watch-card">
|
||
<div class="watch-top">
|
||
<div><div class="watch-sym">BNB</div><div class="watch-co">BNB Chain</div></div>
|
||
<span class="watch-pct up">▲ 0.94%</span>
|
||
</div>
|
||
<div class="watch-px">$612.40</div>
|
||
<div class="watch-spark">
|
||
<svg viewBox="0 0 200 40" preserveAspectRatio="none">
|
||
<polygon fill="url(#ws1)" points="0,28 0,24 20,26 40,22 60,20 80,22 100,18 120,16 140,18 160,14 180,12 200,12 200,40 0,40"/>
|
||
<polyline fill="none" stroke="#22e58c" stroke-width="1.6" points="0,24 20,26 40,22 60,20 80,22 100,18 120,16 140,18 160,14 180,12 200,12"/>
|
||
</svg>
|
||
</div>
|
||
<div class="watch-foot">
|
||
<span>Vol $920M</span>
|
||
<a href="https://www.coinbase.com/price/bnb" target="_blank" rel="noopener">Coinbase ↗</a>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="watch-card">
|
||
<div class="watch-top">
|
||
<div><div class="watch-sym">XRP</div><div class="watch-co">Ripple</div></div>
|
||
<span class="watch-pct down">▼ 0.42%</span>
|
||
</div>
|
||
<div class="watch-px">$0.5824</div>
|
||
<div class="watch-spark">
|
||
<svg viewBox="0 0 200 40" preserveAspectRatio="none">
|
||
<polygon fill="url(#ws2)" points="0,12 0,16 20,14 40,18 60,16 80,18 100,16 120,20 140,18 160,22 180,20 200,24 200,40 0,40"/>
|
||
<polyline fill="none" stroke="#ff4f6d" stroke-width="1.6" points="0,16 20,14 40,18 60,16 80,18 100,16 120,20 140,18 160,22 180,20 200,24"/>
|
||
</svg>
|
||
</div>
|
||
<div class="watch-foot">
|
||
<span>Vol $1.2B</span>
|
||
<a href="https://www.coinbase.com/price/xrp" target="_blank" rel="noopener">Coinbase ↗</a>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- News / signals feed -->
|
||
<div class="card" style="padding:0;">
|
||
<div class="panel-head">
|
||
<div class="panel-title">Live signals & news</div>
|
||
<div class="panel-sub">Filtered to your book</div>
|
||
</div>
|
||
<div class="news-list">
|
||
<div class="news-item">
|
||
<div class="news-time">14:31</div>
|
||
<div class="news-body">
|
||
<div class="src"><span class="tag">AI</span> On-chain anomaly</div>
|
||
<div class="head"><span class="t-up">▲</span> Solana DEX volume rolling 7d up <strong>+62%</strong> — Jupiter + Raydium printing record fee days. Settlement layer thesis intact.</div>
|
||
<div class="syms"><span class="s">SOL</span><span class="s">JUP</span></div>
|
||
</div>
|
||
<span class="impact pos">+ Bull</span>
|
||
</div>
|
||
<div class="news-item">
|
||
<div class="news-time">14:18</div>
|
||
<div class="news-body">
|
||
<div class="src">CoinDesk</div>
|
||
<div class="head">Spot BTC ETFs see <strong>+$418M</strong> net inflows over 5 sessions; IBIT alone takes 70%, FBTC 22%.</div>
|
||
<div class="syms"><span class="s">BTC</span><span class="s">IBIT</span></div>
|
||
</div>
|
||
<span class="impact pos">+ Bull</span>
|
||
</div>
|
||
<div class="news-item">
|
||
<div class="news-time">13:54</div>
|
||
<div class="news-body">
|
||
<div class="src">The Block</div>
|
||
<div class="head">Firedancer canary deployment hits <strong>1.2M TPS</strong> in stress test — full mainnet rollout targeted Q4.</div>
|
||
<div class="syms"><span class="s">SOL</span></div>
|
||
</div>
|
||
<span class="impact pos">+ Bull</span>
|
||
</div>
|
||
<div class="news-item">
|
||
<div class="news-time">13:30</div>
|
||
<div class="news-body">
|
||
<div class="src"><span class="tag">AI</span> Funding tracker</div>
|
||
<div class="head">Perp funding turning slightly positive across L1s; ETH 8h funding now <strong>+0.014%</strong>. Watch for crowding on next 5% leg.</div>
|
||
<div class="syms"><span class="s">ETH</span><span class="s">SOL</span></div>
|
||
</div>
|
||
<span class="impact med">Watch</span>
|
||
</div>
|
||
<div class="news-item">
|
||
<div class="news-time">12:48</div>
|
||
<div class="news-body">
|
||
<div class="src">Bloomberg</div>
|
||
<div class="head">SEC granted certificate of effectiveness on <strong>spot ETH staking ETF</strong> filing — issuer confirmation expected within 7 days.</div>
|
||
<div class="syms"><span class="s">ETH</span></div>
|
||
</div>
|
||
<span class="impact pos">+ Bull</span>
|
||
</div>
|
||
<div class="news-item">
|
||
<div class="news-time">11:22</div>
|
||
<div class="news-body">
|
||
<div class="src"><span class="tag">AI</span> Whale flow</div>
|
||
<div class="head"><span class="t-down">▼</span> 12,400 ETH moved from Genesis estate cold wallet to OKX — 6h. Possible distribution; watch order books.</div>
|
||
<div class="syms"><span class="s">ETH</span></div>
|
||
</div>
|
||
<span class="impact high">High risk</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
</main>
|
||
|
||
<!-- Toast container -->
|
||
<div class="toast-box" id="toastBox" aria-live="polite"></div>
|
||
|
||
<footer class="app">
|
||
<div class="left">
|
||
<span class="live">On-chain feed — block 287,418,612</span>
|
||
<span>Quotes via Coinbase + CoinGecko · Glassnode for on-chain</span>
|
||
</div>
|
||
<div class="right">
|
||
<span>Quiver Crypto · v1.4.0</span>
|
||
<span>Source: Coinbase + Phantom + Ledger linked</span>
|
||
<span>Last sync <span id="lastSync">14:32:18 UTC</span></span>
|
||
</div>
|
||
</footer>
|
||
|
||
<script>
|
||
(() => {
|
||
// ─────── Ticker registry (crypto) ───────
|
||
// Each asset carries display data + archetype for candle rendering.
|
||
// Three external links per asset: primary exchange, secondary exchange, market data site.
|
||
const TICKERS = {
|
||
BTC: {
|
||
name: 'Bitcoin', sector: 'L1 · Mkt cap $1.35T · Dominance 54.2%',
|
||
icon: '₿', bg: 'linear-gradient(135deg,#f7931a,#a35e00)', fg: '#1a0e00',
|
||
exch: 'Coinbase',
|
||
links: { exch: 'https://www.coinbase.com/price/bitcoin',
|
||
yahoo: 'https://www.binance.com/en/price/bitcoin',
|
||
tv: 'https://coinmarketcap.com/currencies/bitcoin/' },
|
||
px: 68412, dayChg: 1236.40, dayPct: 1.84, dir: 'up',
|
||
open: 67176, high: 68920, low: 66840, vol: '$28.4B', pe: '19.74M / 21M', range52: '$73,750 (Mar)',
|
||
arch: 'bull-strong'
|
||
},
|
||
ETH: {
|
||
name: 'Ethereum', sector: 'L1 · Mkt cap $435B · Gas 12 gwei',
|
||
icon: 'Ξ', bg: 'linear-gradient(135deg,#627eea,#3c5ed7)', fg: '#ffffff',
|
||
exch: 'Coinbase',
|
||
links: { exch: 'https://www.coinbase.com/price/ethereum',
|
||
yahoo: 'https://www.binance.com/en/price/ethereum',
|
||
tv: 'https://coinmarketcap.com/currencies/ethereum/' },
|
||
px: 3612, dayChg: 74.20, dayPct: 2.10, dir: 'up',
|
||
open: 3540, high: 3624, low: 3522, vol: '$14.2B', pe: '120.4M', range52: '$4,890 (Mar)',
|
||
arch: 'bull-mild'
|
||
},
|
||
SOL: {
|
||
name: 'Solana', sector: 'L1 · Mkt cap $87B · DePIN settlement',
|
||
icon: '◎', bg: 'linear-gradient(135deg,#14f195,#9945ff)', fg: '#0a0020',
|
||
exch: 'Coinbase',
|
||
links: { exch: 'https://www.coinbase.com/price/solana',
|
||
yahoo: 'https://www.binance.com/en/price/solana',
|
||
tv: 'https://coinmarketcap.com/currencies/solana/' },
|
||
px: 186.42, dayChg: 8.22, dayPct: 4.62, dir: 'up',
|
||
open: 178.20, high: 188.40, low: 177.30, vol: '$3.8B', pe: '466.4M', range52: '$259 (Mar)',
|
||
arch: 'bull-strong'
|
||
},
|
||
BNB: {
|
||
name: 'BNB', sector: 'L1 · Mkt cap $89B · Native to BNB Chain',
|
||
icon: 'B', bg: 'linear-gradient(135deg,#f3ba2f,#a17500)', fg: '#1a0f00',
|
||
exch: 'Binance',
|
||
links: { exch: 'https://www.binance.com/en/price/bnb',
|
||
yahoo: 'https://www.coinbase.com/price/bnb',
|
||
tv: 'https://coinmarketcap.com/currencies/bnb/' },
|
||
px: 612.40, dayChg: 5.72, dayPct: 0.94, dir: 'up',
|
||
open: 606.80, high: 614.20, low: 604.40, vol: '$920M', pe: '147.6M', range52: '$721 (Jun)',
|
||
arch: 'bull-mild'
|
||
},
|
||
XRP: {
|
||
name: 'XRP', sector: 'Payments · Mkt cap $33B',
|
||
icon: 'X', bg: 'linear-gradient(135deg,#23292f,#000)', fg: '#ffffff',
|
||
exch: 'Coinbase',
|
||
links: { exch: 'https://www.coinbase.com/price/xrp',
|
||
yahoo: 'https://www.binance.com/en/price/xrp',
|
||
tv: 'https://coinmarketcap.com/currencies/xrp/' },
|
||
px: 0.5824, dayChg: -0.0024, dayPct: -0.42, dir: 'down',
|
||
open: 0.5848, high: 0.5896, low: 0.5780, vol: '$1.2B', pe: '56.6B', range52: '$0.74 (Jul)',
|
||
arch: 'choppy'
|
||
},
|
||
ADA: {
|
||
name: 'Cardano', sector: 'L1 · Mkt cap $15.4B',
|
||
icon: 'A', bg: 'linear-gradient(135deg,#0033ad,#001a5e)', fg: '#ffffff',
|
||
exch: 'Coinbase',
|
||
links: { exch: 'https://www.coinbase.com/price/cardano',
|
||
yahoo: 'https://www.binance.com/en/price/cardano',
|
||
tv: 'https://coinmarketcap.com/currencies/cardano/' },
|
||
px: 0.4318, dayChg: 0.0051, dayPct: 1.20, dir: 'up',
|
||
open: 0.4267, high: 0.4332, low: 0.4240, vol: '$284M', pe: '35.7B', range52: '$0.81 (Mar)',
|
||
arch: 'bull-mild'
|
||
},
|
||
DOGE: {
|
||
name: 'Dogecoin', sector: 'Memecoin · Mkt cap $27B',
|
||
icon: 'Ð', bg: 'linear-gradient(135deg,#c2a633,#8a7321)', fg: '#1a1100',
|
||
exch: 'Coinbase',
|
||
links: { exch: 'https://www.coinbase.com/price/dogecoin',
|
||
yahoo: 'https://www.binance.com/en/price/dogecoin',
|
||
tv: 'https://coinmarketcap.com/currencies/dogecoin/' },
|
||
px: 0.1842, dayChg: 0.0118, dayPct: 6.84, dir: 'up',
|
||
open: 0.1724, high: 0.1872, low: 0.1716, vol: '$1.4B', pe: '146.6B', range52: '$0.225 (Mar)',
|
||
arch: 'bull-strong'
|
||
},
|
||
AVAX: {
|
||
name: 'Avalanche', sector: 'L1 · Mkt cap $14.4B',
|
||
icon: 'A', bg: 'linear-gradient(135deg,#e84142,#7a0d0e)', fg: '#ffffff',
|
||
exch: 'Coinbase',
|
||
links: { exch: 'https://www.coinbase.com/price/avalanche',
|
||
yahoo: 'https://www.binance.com/en/price/avalanche',
|
||
tv: 'https://coinmarketcap.com/currencies/avalanche/' },
|
||
px: 36.84, dayChg: 1.02, dayPct: 2.86, dir: 'up',
|
||
open: 35.82, high: 37.20, low: 35.40, vol: '$640M', pe: '391.2M', range52: '$58.20 (Mar)',
|
||
arch: 'bull-mild'
|
||
},
|
||
LINK: {
|
||
name: 'Chainlink', sector: 'Oracle · Mkt cap $9.0B',
|
||
icon: 'L', bg: 'linear-gradient(135deg,#2a5ada,#1d3ea0)', fg: '#ffffff',
|
||
exch: 'Coinbase',
|
||
links: { exch: 'https://www.coinbase.com/price/chainlink',
|
||
yahoo: 'https://www.binance.com/en/price/chainlink',
|
||
tv: 'https://coinmarketcap.com/currencies/chainlink/' },
|
||
px: 14.92, dayChg: 0.21, dayPct: 1.40, dir: 'up',
|
||
open: 14.71, high: 14.96, low: 14.62, vol: '$248M', pe: '604.7M', range52: '$22.30 (Mar)',
|
||
arch: 'bull-mild'
|
||
},
|
||
DOT: {
|
||
name: 'Polkadot', sector: 'L0 · Mkt cap $9.6B',
|
||
icon: '●', bg: 'linear-gradient(135deg,#e6007a,#860146)', fg: '#ffffff',
|
||
exch: 'Coinbase',
|
||
links: { exch: 'https://www.coinbase.com/price/polkadot',
|
||
yahoo: 'https://www.binance.com/en/price/polkadot',
|
||
tv: 'https://coinmarketcap.com/currencies/polkadot/' },
|
||
px: 7.18, dayChg: -0.045, dayPct: -0.62, dir: 'down',
|
||
open: 7.225, high: 7.26, low: 7.14, vol: '$192M', pe: '1.34B', range52: '$11.84 (Mar)',
|
||
arch: 'bear'
|
||
},
|
||
SUI: {
|
||
name: 'Sui Network', sector: 'L1 · Mkt cap $5.8B',
|
||
icon: 'S', bg: 'linear-gradient(135deg,#4ca3df,#1a4f8a)', fg: '#ffffff',
|
||
exch: 'Coinbase',
|
||
links: { exch: 'https://www.coinbase.com/price/sui',
|
||
yahoo: 'https://www.binance.com/en/price/sui',
|
||
tv: 'https://coinmarketcap.com/currencies/sui/' },
|
||
px: 2.18, dayChg: 0.169, dayPct: 8.40, dir: 'up',
|
||
open: 2.011, high: 2.21, low: 1.992, vol: '$812M', pe: '2.66B', range52: '$2.21 (today)',
|
||
arch: 'bull-strong'
|
||
},
|
||
NEAR: {
|
||
name: 'NEAR Protocol', sector: 'L1 · Mkt cap $5.9B',
|
||
icon: 'N', bg: 'linear-gradient(135deg,#000,#5f6368)', fg: '#ffffff',
|
||
exch: 'Coinbase',
|
||
links: { exch: 'https://www.coinbase.com/price/near-protocol',
|
||
yahoo: 'https://www.binance.com/en/price/near-protocol',
|
||
tv: 'https://coinmarketcap.com/currencies/near-protocol/' },
|
||
px: 5.42, dayChg: 0.168, dayPct: 3.20, dir: 'up',
|
||
open: 5.252, high: 5.46, low: 5.21, vol: '$312M', pe: '1.09B', range52: '$8.46 (Mar)',
|
||
arch: 'bull-strong'
|
||
},
|
||
TIA: {
|
||
name: 'Celestia', sector: 'Modular DA · Mkt cap $1.6B',
|
||
icon: 'T', bg: 'linear-gradient(135deg,#7b2bf9,#3d0e8c)', fg: '#ffffff',
|
||
exch: 'Binance',
|
||
links: { exch: 'https://www.binance.com/en/price/celestia',
|
||
yahoo: 'https://www.coinbase.com/price/celestia',
|
||
tv: 'https://coinmarketcap.com/currencies/celestia/' },
|
||
px: 7.84, dayChg: -0.094, dayPct: -1.18, dir: 'down',
|
||
open: 7.934, high: 7.96, low: 7.74, vol: '$84M', pe: '204M', range52: '$20.80 (Feb)',
|
||
arch: 'bear'
|
||
},
|
||
ARB: {
|
||
name: 'Arbitrum', sector: 'L2 · Mkt cap $2.7B',
|
||
icon: 'A', bg: 'linear-gradient(135deg,#28a0f0,#1565a8)', fg: '#ffffff',
|
||
exch: 'Coinbase',
|
||
links: { exch: 'https://www.coinbase.com/price/arbitrum',
|
||
yahoo: 'https://www.binance.com/en/price/arbitrum',
|
||
tv: 'https://coinmarketcap.com/currencies/arbitrum/' },
|
||
px: 0.6420, dayChg: 0.006, dayPct: 0.95, dir: 'up',
|
||
open: 0.636, high: 0.648, low: 0.632, vol: '$186M', pe: '4.21B', range52: '$2.39 (Jan)',
|
||
arch: 'bull-mild'
|
||
}
|
||
};
|
||
|
||
// ─────── Candle templates ───────
|
||
// Each template is a series of SVG strings rendering 21 candles + SMA line.
|
||
// Coordinates fit the existing chart viewBox 880×320, padding-left 40.
|
||
// Generate procedurally based on archetype for variety without bloating HTML.
|
||
function generateCandles(arch) {
|
||
// closes[] is 21 normalised closes (0=top of band, 1=bottom)
|
||
let closes;
|
||
switch (arch) {
|
||
case 'bull-strong':
|
||
closes = [0.78, 0.72, 0.74, 0.66, 0.62, 0.66, 0.58, 0.54, 0.58, 0.46, 0.40, 0.44, 0.36, 0.30, 0.34, 0.26, 0.22, 0.26, 0.18, 0.14, 0.10];
|
||
break;
|
||
case 'bull-mild':
|
||
closes = [0.66, 0.64, 0.68, 0.60, 0.58, 0.62, 0.54, 0.52, 0.54, 0.48, 0.46, 0.50, 0.42, 0.40, 0.44, 0.36, 0.34, 0.38, 0.30, 0.28, 0.24];
|
||
break;
|
||
case 'choppy':
|
||
closes = [0.42, 0.50, 0.40, 0.46, 0.54, 0.48, 0.58, 0.50, 0.56, 0.46, 0.52, 0.42, 0.50, 0.58, 0.50, 0.56, 0.48, 0.54, 0.44, 0.50, 0.46];
|
||
break;
|
||
case 'bear':
|
||
closes = [0.22, 0.26, 0.30, 0.28, 0.34, 0.38, 0.36, 0.42, 0.46, 0.44, 0.50, 0.54, 0.52, 0.58, 0.62, 0.60, 0.66, 0.70, 0.68, 0.74, 0.78];
|
||
break;
|
||
default:
|
||
closes = Array.from({length:21}, (_,i)=>0.5);
|
||
}
|
||
// map normalised position to y in [38, 248] (top=high price area)
|
||
const yMap = (n) => 38 + n * 210;
|
||
let parts = [];
|
||
let prev = 0.5; // open of first bar
|
||
let smaPts = [];
|
||
for (let i = 0; i < closes.length; i++) {
|
||
const x = 44 + i * 40;
|
||
const close = closes[i];
|
||
const open = prev;
|
||
const yOpen = yMap(open);
|
||
const yClose = yMap(close);
|
||
const isUp = close < open; // close higher (lower y) = up
|
||
const color = isUp ? '#22e58c' : '#ff4f6d';
|
||
// wick: tiny extension above and below body
|
||
const wickHi = Math.min(yOpen, yClose) - 6 - Math.random()*4;
|
||
const wickLo = Math.max(yOpen, yClose) + 6 + Math.random()*4;
|
||
const top = Math.min(yOpen, yClose);
|
||
const bot = Math.max(yOpen, yClose);
|
||
parts.push(`<line x1="${x}" y1="${wickHi}" x2="${x}" y2="${wickLo}" stroke="${color}" stroke-width="1.2"/>`);
|
||
parts.push(`<rect x="${x-6}" y="${top}" width="12" height="${Math.max(2, bot-top)}" fill="${color}"/>`);
|
||
smaPts.push(`${x},${yMap(close)+8}`);
|
||
prev = close;
|
||
}
|
||
const smaLine = `<polyline fill="none" stroke="#facc15" stroke-opacity="0.55" stroke-width="1.4" stroke-dasharray="3 3" points="${smaPts.join(' ')}"/>`;
|
||
// last close y for current px marker
|
||
const lastY = yMap(closes[closes.length - 1]);
|
||
return { candles: parts.join(''), sma: smaLine, lastY };
|
||
}
|
||
|
||
function renderCandleSvg(arch, displayPx) {
|
||
const { candles, sma, lastY } = generateCandles(arch);
|
||
const inner = `
|
||
<defs>
|
||
<pattern id="grid2" x="0" y="0" width="80" height="40" patternUnits="userSpaceOnUse">
|
||
<path d="M 80 0 L 0 0 0 40" fill="none" stroke="rgba(255,255,255,0.035)" stroke-width="1"/>
|
||
</pattern>
|
||
</defs>
|
||
<rect width="880" height="320" fill="url(#grid2)"/>
|
||
<g font-family="Geist Mono,monospace" font-size="10" fill="#6b7689">
|
||
<text x="6" y="36">High</text>
|
||
<text x="6" y="96">·</text>
|
||
<text x="6" y="156">Mid</text>
|
||
<text x="6" y="216">·</text>
|
||
<text x="6" y="276">Low</text>
|
||
</g>
|
||
${sma}
|
||
${candles}
|
||
<g font-family="Geist Mono,monospace" font-size="10">
|
||
<circle cx="284" cy="200" r="5" fill="#facc15"/>
|
||
<text x="296" y="204" fill="#facc15">BUY · journal entry</text>
|
||
</g>
|
||
<g id="pxMarker">
|
||
<line x1="40" y1="${lastY}" x2="844" y2="${lastY}" stroke="rgba(34,229,140,0.25)" stroke-dasharray="2 3"/>
|
||
<rect x="836" y="${lastY-9}" width="40" height="18" fill="#22e58c" rx="3"/>
|
||
<text id="pxMarkerText" x="856" y="${lastY+4}" font-family="Geist Mono,monospace" font-size="10" fill="#00200f" text-anchor="middle" font-weight="700">${displayPx}</text>
|
||
</g>
|
||
`;
|
||
return inner;
|
||
}
|
||
|
||
function renderVolumeSvg(arch) {
|
||
// Rough volume pattern: amplify with archetype.
|
||
let heights;
|
||
if (arch === 'bull-strong') heights = [20, 26, 22, 30, 24, 20, 34, 28, 26, 48, 24, 26, 40, 26, 24, 50, 28, 22, 44, 36, 56];
|
||
else if (arch === 'bull-mild') heights = [18, 22, 26, 24, 20, 28, 22, 24, 18, 32, 28, 24, 22, 30, 26, 20, 28, 24, 30, 26, 32];
|
||
else if (arch === 'choppy') heights = [22, 36, 18, 42, 28, 32, 24, 38, 22, 30, 36, 22, 30, 26, 38, 22, 32, 28, 36, 22, 30];
|
||
else if (arch === 'bear') heights = [40, 32, 36, 28, 30, 26, 24, 30, 28, 26, 32, 24, 30, 26, 22, 28, 22, 24, 22, 26, 28];
|
||
else heights = Array.from({length: 21}, () => 24);
|
||
const bars = heights.map((h, i) => {
|
||
const x = 38 + i * 40;
|
||
const y = 70 - h;
|
||
const upish = (i % 3) !== 1; // arbitrary
|
||
const c = upish ? '#22e58c' : '#ff4f6d';
|
||
const op = upish ? 0.55 : 0.55;
|
||
return `<rect x="${x}" y="${y}" width="12" height="${h}" fill="${c}" opacity="${op}"/>`;
|
||
}).join('');
|
||
return `<g>${bars}</g>
|
||
<text x="6" y="14" font-family="Geist Mono,monospace" font-size="10" fill="#6b7689">VOL</text>
|
||
<text x="6" y="64" font-family="Geist Mono,monospace" font-size="10" fill="#6b7689">avg</text>`;
|
||
}
|
||
|
||
// ─────── Toast ───────
|
||
const toastBox = document.getElementById('toastBox');
|
||
function toast(opts) {
|
||
const { kind = 'info', title = 'Notice', body = '' } = (typeof opts === 'string') ? { body: opts } : opts;
|
||
const node = document.createElement('div');
|
||
node.className = `toast ${kind}`;
|
||
node.innerHTML = `
|
||
<div class="t-head">
|
||
<span class="t-title">${title}</span>
|
||
<button class="t-close" aria-label="Dismiss">×</button>
|
||
</div>
|
||
<div class="t-body">${body}</div>
|
||
`;
|
||
toastBox.appendChild(node);
|
||
const close = () => {
|
||
node.classList.add('fade');
|
||
setTimeout(() => node.remove(), 280);
|
||
};
|
||
node.querySelector('.t-close').addEventListener('click', close);
|
||
setTimeout(close, 4200);
|
||
}
|
||
|
||
// ─────── Live clock + block ───────
|
||
const clockEl = document.getElementById('liveClock');
|
||
const lastSyncEl = document.getElementById('lastSync');
|
||
const blockHEl = document.getElementById('blockH');
|
||
let blockH = 287418612;
|
||
// Block ticks roughly every 12 seconds (Ethereum-ish cadence).
|
||
setInterval(() => {
|
||
blockH += 1;
|
||
if (blockHEl) blockHEl.textContent = blockH.toLocaleString('en-US');
|
||
}, 12000);
|
||
// Anchor to a fake market-day start so it ticks like a real session.
|
||
let clockSeconds = 14 * 3600 + 32 * 60 + 18;
|
||
function fmtClock(s) {
|
||
const h = Math.floor(s / 3600);
|
||
const m = Math.floor((s % 3600) / 60);
|
||
const sec = s % 60;
|
||
return `${String(h).padStart(2,'0')}:${String(m).padStart(2,'0')}:${String(sec).padStart(2,'0')} UTC`;
|
||
}
|
||
setInterval(() => {
|
||
clockSeconds++;
|
||
if (clockEl) clockEl.textContent = fmtClock(clockSeconds);
|
||
}, 1000);
|
||
|
||
// ─────── Chart switcher ───────
|
||
let activeSym = 'BTC';
|
||
const tkIcon = document.getElementById('tkIcon');
|
||
const tkSym = document.getElementById('tkSym');
|
||
const tkName = document.getElementById('tkName');
|
||
const tkPx = document.getElementById('tkPx');
|
||
const tkDelta = document.getElementById('tkDelta');
|
||
const tkLinkExch = document.getElementById('tkLinkExch');
|
||
const tkLinkYahoo = document.getElementById('tkLinkYahoo');
|
||
const tkLinkTV = document.getElementById('tkLinkTV');
|
||
const tkStats = document.getElementById('tkStats');
|
||
const candleSvg = document.getElementById('candleSvg');
|
||
const volSvg = document.getElementById('volSvg');
|
||
const chartCard = document.getElementById('chartCard');
|
||
|
||
// Smart precision: high-priced coins (BTC, ETH) round to whole dollars;
|
||
// mid-priced (e.g. SOL) keep 2 decimals; sub-dollar (XRP, ADA, DOGE) use 4.
|
||
function fmtPx(p) {
|
||
if (p == null) return '$0.00';
|
||
const a = Math.abs(p);
|
||
if (a >= 1000) return '$' + p.toLocaleString('en-US', { maximumFractionDigits: 0 });
|
||
if (a >= 1) return '$' + p.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
|
||
return '$' + p.toLocaleString('en-US', { minimumFractionDigits: 4, maximumFractionDigits: 4 });
|
||
}
|
||
function fmtDelta(d) {
|
||
const sign = d >= 0 ? '+' : '−';
|
||
const v = Math.abs(d);
|
||
let s;
|
||
if (v >= 1000) s = v.toLocaleString('en-US', { maximumFractionDigits: 0 });
|
||
else if (v >= 1) s = v.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
|
||
else s = v.toLocaleString('en-US', { minimumFractionDigits: 4, maximumFractionDigits: 4 });
|
||
return `${sign}$${s}`;
|
||
}
|
||
function fmtPct(p) {
|
||
const sign = p >= 0 ? '+' : '−';
|
||
return `${sign}${Math.abs(p).toFixed(2)}%`;
|
||
}
|
||
|
||
function applyTicker(sym) {
|
||
const t = TICKERS[sym];
|
||
if (!t) return;
|
||
activeSym = sym;
|
||
tkIcon.textContent = t.icon;
|
||
tkIcon.style.background = t.bg;
|
||
tkIcon.style.color = t.fg;
|
||
tkSym.textContent = sym;
|
||
tkName.textContent = `${t.name} · ${t.sector}`;
|
||
const upish = t.dir === 'up';
|
||
tkPx.style.color = upish ? 'var(--up)' : 'var(--down)';
|
||
tkPx.textContent = fmtPx(t.px);
|
||
tkDelta.style.color = upish ? 'var(--up)' : 'var(--down)';
|
||
tkDelta.innerHTML = `${upish ? '▲' : '▼'} ${fmtDelta(t.dayChg)} ${fmtPct(t.dayPct)} today`;
|
||
tkLinkExch.href = t.links.exch;
|
||
tkLinkExch.firstChild.textContent = `${t.exch} `;
|
||
tkLinkYahoo.href = t.links.yahoo;
|
||
tkLinkTV.href = t.links.tv;
|
||
// Stats
|
||
const setStat = (k, v) => {
|
||
const el = tkStats.querySelector(`[data-stat="${k}"]`);
|
||
if (el) el.textContent = v;
|
||
};
|
||
const fmtBare = (n) => {
|
||
const a = Math.abs(n);
|
||
if (a >= 1000) return n.toLocaleString('en-US', { maximumFractionDigits: 0 });
|
||
if (a >= 1) return n.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
|
||
return n.toLocaleString('en-US', { minimumFractionDigits: 4, maximumFractionDigits: 4 });
|
||
};
|
||
setStat('open', fmtBare(t.open));
|
||
setStat('high', fmtBare(t.high));
|
||
setStat('low', fmtBare(t.low));
|
||
setStat('vol', t.vol);
|
||
setStat('pe', t.pe);
|
||
setStat('range52', t.range52);
|
||
// Repaint candles and volume
|
||
candleSvg.innerHTML = renderCandleSvg(t.arch, t.px.toFixed(2));
|
||
volSvg.innerHTML = renderVolumeSvg(t.arch);
|
||
// Highlight pulse
|
||
chartCard.classList.remove('swap-highlight');
|
||
void chartCard.offsetWidth;
|
||
chartCard.classList.add('swap-highlight');
|
||
// Sync holdings table active row + watchlist
|
||
document.querySelectorAll('table.holdings tbody tr').forEach(tr => {
|
||
const s = tr.querySelector('.sym-name');
|
||
const m = s ? s.firstChild.textContent.trim() : '';
|
||
tr.classList.toggle('active', m.split(' ')[0] === sym);
|
||
});
|
||
document.querySelectorAll('.watch-card').forEach(card => {
|
||
const s = card.querySelector('.watch-sym');
|
||
card.classList.toggle('active', s && s.textContent.trim() === sym);
|
||
});
|
||
}
|
||
|
||
// First-paint candle render so the svg is JS-controlled (allows variation later)
|
||
applyTicker('BTC');
|
||
|
||
// ─────── Wire holdings rows ───────
|
||
document.querySelectorAll('table.holdings tbody tr').forEach(tr => {
|
||
const link = tr.querySelector('.sym-name a');
|
||
if (link) link.addEventListener('click', e => e.stopPropagation());
|
||
const symEl = tr.querySelector('.sym-name');
|
||
if (!symEl) return;
|
||
const sym = symEl.firstChild.textContent.trim().split(/\s+/)[0];
|
||
if (!TICKERS[sym]) return;
|
||
tr.addEventListener('click', () => {
|
||
applyTicker(sym);
|
||
toast({ kind: 'info', title: 'Switched chart', body: `Now viewing <strong>${sym}</strong> — ${TICKERS[sym].name}` });
|
||
});
|
||
});
|
||
|
||
// ─────── Wire watchlist cards ───────
|
||
document.querySelectorAll('.watch-card').forEach(card => {
|
||
const link = card.querySelector('.watch-foot a');
|
||
if (link) link.addEventListener('click', e => e.stopPropagation());
|
||
const symEl = card.querySelector('.watch-sym');
|
||
if (!symEl) return;
|
||
const sym = symEl.textContent.trim();
|
||
if (!TICKERS[sym]) return;
|
||
card.addEventListener('click', () => {
|
||
applyTicker(sym);
|
||
toast({ kind: 'info', title: 'Switched chart', body: `Now viewing <strong>${sym}</strong> from watchlist` });
|
||
});
|
||
});
|
||
|
||
// ─────── Wire ticker tape ───────
|
||
document.querySelectorAll('.tape-item').forEach(item => {
|
||
const symEl = item.querySelector('.sym');
|
||
if (!symEl) return;
|
||
const sym = symEl.textContent.trim();
|
||
if (!TICKERS[sym]) return;
|
||
item.addEventListener('click', () => {
|
||
applyTicker(sym);
|
||
toast({ kind: 'info', title: 'Switched chart', body: `Pinned <strong>${sym}</strong> from market tape` });
|
||
});
|
||
});
|
||
|
||
// ─────── AI tabs ───────
|
||
document.querySelectorAll('.ai-tab').forEach(tab => {
|
||
tab.addEventListener('click', () => {
|
||
const target = tab.dataset.tab;
|
||
document.querySelectorAll('.ai-tab').forEach(t => t.classList.toggle('active', t === tab));
|
||
document.querySelectorAll('.ai-panel').forEach(p => p.classList.toggle('active', p.dataset.panel === target));
|
||
});
|
||
});
|
||
|
||
// ─────── Range tabs (visual) ───────
|
||
document.querySelectorAll('.range-tabs').forEach(group => {
|
||
group.addEventListener('click', e => {
|
||
const btn = e.target.closest('button');
|
||
if (!btn) return;
|
||
group.querySelectorAll('button').forEach(b => b.classList.toggle('active', b === btn));
|
||
});
|
||
});
|
||
|
||
// ─────── Action buttons ───────
|
||
document.querySelectorAll('[data-action]').forEach(btn => {
|
||
btn.addEventListener('click', () => {
|
||
const a = btn.dataset.action;
|
||
if (a === 'add') {
|
||
toast({ kind: 'info', title: 'Swap ticket', body: '<strong>SWAP 8,389 USDC → 45 SOL</strong> on Jupiter — slippage 0.3%, ready to sign.' });
|
||
} else if (a === 'alert') {
|
||
toast({ kind: 'warn', title: 'Alert set', body: 'On-chain alert created — <strong>SOL above $200</strong> or DEX vol > $1B/day. We\'ll ping you in real time.' });
|
||
} else if (a === 'thesis') {
|
||
toast({ kind: 'violet', title: 'On-chain thesis', body: 'Opening full thesis with Glassnode, Artemis, and Dune dashboards.' });
|
||
}
|
||
});
|
||
});
|
||
|
||
// ─────── Refresh ───────
|
||
const refreshBtn = document.getElementById('refreshBtn');
|
||
function jitter(p, magPct = 0.6) {
|
||
// ±magPct% jitter, with up-bias on bull tickers
|
||
const jp = (Math.random() * 2 - 1) * (magPct / 100);
|
||
return Math.max(0.01, p * (1 + jp));
|
||
}
|
||
function refreshQuotes() {
|
||
refreshBtn.classList.add('spin');
|
||
setTimeout(() => refreshBtn.classList.remove('spin'), 900);
|
||
// Update each ticker's price with small jitter + flash main price if ticker is active
|
||
Object.entries(TICKERS).forEach(([sym, t]) => {
|
||
const newPx = +(jitter(t.px, 0.45)).toFixed(2);
|
||
const dPct = ((newPx - (t.px - t.dayChg)) / (t.px - t.dayChg)) * 100;
|
||
const dChg = newPx - (t.px - t.dayChg);
|
||
t.px = newPx;
|
||
t.dayChg = +dChg.toFixed(2);
|
||
t.dayPct = +dPct.toFixed(2);
|
||
t.dir = t.dayChg >= 0 ? 'up' : 'down';
|
||
});
|
||
// Update tape
|
||
document.querySelectorAll('.tape-item').forEach(item => {
|
||
const symEl = item.querySelector('.sym');
|
||
const pxEl = item.querySelector('.px');
|
||
const pctEl = item.querySelector('.pct');
|
||
if (!symEl || !pxEl || !pctEl) return;
|
||
const sym = symEl.textContent.trim();
|
||
const t = TICKERS[sym];
|
||
if (!t) return;
|
||
pxEl.textContent = t.px.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
|
||
pctEl.className = 'pct ' + (t.dir === 'up' ? 'up' : 'down');
|
||
pctEl.textContent = `${t.dir === 'up' ? '▲' : '▼'} ${Math.abs(t.dayPct).toFixed(2)}%`;
|
||
});
|
||
// Re-apply active ticker (refresh main chart price + delta) with flash
|
||
const oldFlashClass = tkPx.classList.contains('flash-up') ? 'flash-up' : (tkPx.classList.contains('flash-down') ? 'flash-down' : null);
|
||
if (oldFlashClass) tkPx.classList.remove(oldFlashClass);
|
||
applyTicker(activeSym);
|
||
void tkPx.offsetWidth;
|
||
tkPx.classList.add(TICKERS[activeSym].dir === 'up' ? 'flash-up' : 'flash-down');
|
||
// Update watchlist sparkline cards' price + pct
|
||
document.querySelectorAll('.watch-card').forEach(card => {
|
||
const symEl = card.querySelector('.watch-sym');
|
||
if (!symEl) return;
|
||
const sym = symEl.textContent.trim();
|
||
const t = TICKERS[sym];
|
||
if (!t) return;
|
||
const pxEl = card.querySelector('.watch-px');
|
||
const pctEl = card.querySelector('.watch-pct');
|
||
if (pxEl) pxEl.textContent = fmtPx(t.px);
|
||
if (pctEl) {
|
||
pctEl.className = 'watch-pct ' + (t.dir === 'up' ? 'up' : 'down');
|
||
pctEl.textContent = `${t.dir === 'up' ? '▲' : '▼'} ${Math.abs(t.dayPct).toFixed(2)}%`;
|
||
const old = pctEl.classList.contains('flash-up') ? 'flash-up' : (pctEl.classList.contains('flash-down') ? 'flash-down' : null);
|
||
if (old) pctEl.classList.remove(old);
|
||
void pctEl.offsetWidth;
|
||
pctEl.classList.add(t.dir === 'up' ? 'flash-up' : 'flash-down');
|
||
}
|
||
});
|
||
// Update sync timestamps
|
||
if (lastSyncEl) lastSyncEl.textContent = fmtClock(clockSeconds);
|
||
toast({ kind: 'info', title: 'Quotes refreshed', body: `${Object.keys(TICKERS).length} assets updated · Coinbase + CoinGecko · ${fmtClock(clockSeconds)}` });
|
||
}
|
||
refreshBtn.addEventListener('click', refreshQuotes);
|
||
|
||
// ─────── AI regenerate ───────
|
||
const regenBtn = document.getElementById('regenBtn');
|
||
const regenLabel = document.getElementById('regenLabel');
|
||
const aiHeadline = document.getElementById('aiHeadline');
|
||
const aiSub = document.getElementById('aiSub');
|
||
const convVal = document.getElementById('convVal');
|
||
const convFill = document.getElementById('convFill');
|
||
|
||
const AI_VARIANTS = [
|
||
{
|
||
headline: '<span class="accent">Rotate stables → SOL</span> on DePIN + Firedancer accelerator.',
|
||
sub: 'On-chain activity is breaking out while ETF spot demand is steady. Risk-reward favors adding SOL while keeping BTC core untouched.',
|
||
conv: 82
|
||
},
|
||
{
|
||
headline: '<span class="accent">Hold SOL</span> · target $220 first scale-out.',
|
||
sub: 'Backlog of catalysts intact, but the move has tightened R/R. Trim 25% on a $220 print; preserve cycle exposure.',
|
||
conv: 71
|
||
},
|
||
{
|
||
headline: '<span class="accent">Add to ETH</span> ahead of staking ETF effective date.',
|
||
sub: 'SEC certificate already issued. Issuer confirmation expected in 7 days. Spot setup mirrors BTC ETF Jan \'24 base.',
|
||
conv: 78
|
||
},
|
||
{
|
||
headline: '<span class="accent">Reduce DOGE risk</span> — funding overheated.',
|
||
sub: '8h funding hit +0.082% — historically a reliable late-stage signal on memecoin runs. Trim 30%, redeploy to majors.',
|
||
conv: 66
|
||
}
|
||
];
|
||
let aiIdx = 0;
|
||
regenBtn.addEventListener('click', () => {
|
||
if (regenBtn.classList.contains('thinking')) return;
|
||
regenBtn.classList.add('thinking');
|
||
regenLabel.innerHTML = `Analyzing <span class="thinking-dots"><span class="d"></span><span class="d"></span><span class="d"></span></span>`;
|
||
setTimeout(() => {
|
||
aiIdx = (aiIdx + 1) % AI_VARIANTS.length;
|
||
const v = AI_VARIANTS[aiIdx];
|
||
aiHeadline.innerHTML = v.headline;
|
||
aiSub.textContent = v.sub;
|
||
convVal.textContent = `${v.conv}%`;
|
||
convFill.style.width = `${v.conv}%`;
|
||
regenBtn.classList.remove('thinking');
|
||
regenLabel.textContent = 'Regenerate';
|
||
toast({ kind: 'violet', title: 'AI updated', body: 'New analysis run · Glassnode + Artemis + Dune · 412 tokens · 1.4s' });
|
||
}, 1400);
|
||
});
|
||
|
||
})();
|
||
</script>
|
||
|
||
</body>
|
||
</html>
|