Files
workbuddy_skills/open-design/design-templates/live-artifact/examples/stock-portfolio-live/template.html
T
admin 5afeb6b78a ④段整套同步(六节 + P/S/F/J + 模板)+ open-design 移到技能根
一、④段整套同步(用户选候选 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。
2026-10-15 21:17:28 +08:00

1072 lines
51 KiB
HTML

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>{{data.summary.brand}} · 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=Instrument+Serif&display=swap" rel="stylesheet">
<style>
:root {
--bg: #07090d;
--surface: #0f131c;
--surface-2: #141a26;
--surface-3: #1b2231;
--line: #1f2738;
--line-strong: #2a3447;
--hairline: rgba(255,255,255,0.04);
--ink: #e8edf7;
--ink-soft: #c2cad8;
--muted: #6b7689;
--up: #22e58c;
--up-soft: rgba(34,229,140,0.14);
--down: #ff4f6d;
--down-soft: rgba(255,79,109,0.14);
--accent: #facc15;
--accent-soft: rgba(250,204,21,0.12);
--cyan: #22d3ee;
--cyan-soft: rgba(34,211,238,0.14);
--violet: #a78bfa;
--violet-soft: rgba(167,139,250,0.16);
--display: 'Geist', -apple-system, BlinkMacSystemFont, sans-serif;
--body: 'Geist', -apple-system, BlinkMacSystemFont, sans-serif;
--mono: 'Geist Mono', ui-monospace, 'SFMono-Regular', Menlo, monospace;
--serif: 'Instrument Serif', Georgia, serif;
}
* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
body {
background:
radial-gradient(1200px 600px at 80% -10%, rgba(34,211,238,0.07), transparent 60%),
radial-gradient(900px 500px at -10% 30%, rgba(167,139,250,0.05), transparent 60%),
radial-gradient(1200px 800px at 50% 110%, rgba(34,229,140,0.04), 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;
min-height: 100vh;
}
a { color: inherit; text-decoration: none; }
/* 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: 28px; height: 28px; border-radius: 8px;
background: conic-gradient(from 220deg, #facc15, #22d3ee, #a78bfa, #22e58c, #facc15);
box-shadow: 0 0 24px rgba(250,204,21,0.35), inset 0 1px 0 rgba(255,255,255,0.4);
}
.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(--accent); border: 1px solid rgba(250,204,21,0.45);
padding: 2px 6px; border-radius: 5px; text-transform: uppercase; letter-spacing: 0.12em;
background: var(--accent-soft);
}
.market-state {
display: inline-flex; gap: 8px; align-items: center;
padding: 6px 10px; border: 1px solid var(--line); border-radius: 999px;
font-family: var(--mono); font-size: 11px; color: var(--ink-soft);
background: var(--surface);
}
.pulse { width: 8px; height: 8px; border-radius: 50%; background: var(--up); position: relative; }
.pulse::after {
content: ""; position: absolute; inset: -4px; border-radius: 50%;
box-shadow: 0 0 0 0 rgba(34,229,140,0.6);
animation: pulse 1.8s ease-out infinite;
}
@keyframes pulse {
0% { box-shadow: 0 0 0 0 rgba(34,229,140,0.6); }
70% { box-shadow: 0 0 0 12px rgba(34,229,140,0); }
100% { box-shadow: 0 0 0 0 rgba(34,229,140,0); }
}
.head-right { display: flex; align-items: center; gap: 12px; font-family: var(--mono); font-size: 11px; color: var(--muted); }
.head-right .label { text-transform: uppercase; letter-spacing: 0.1em; }
.head-right .val { color: var(--ink-soft); }
/* Layout */
main { padding: 24px 28px 60px; max-width: 1480px; margin: 0 auto; display: grid; gap: 18px; }
.card {
background: linear-gradient(180deg, var(--surface), var(--surface-2));
border: 1px solid var(--line);
border-radius: 14px;
padding: 22px;
overflow: hidden;
}
/* Hero */
.hero { display: grid; grid-template-columns: 1.6fr 1fr; gap: 18px; }
.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); }
.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-deltas { display: flex; gap: 22px; margin-top: 14px; align-items: center; flex-wrap: wrap; }
.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; }
/* KPI grid */
.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;
}
.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-val.up { color: var(--up); }
.kpi .k-val.down { color: var(--down); }
.kpi .k-val.alpha { color: var(--accent); }
.kpi .k-sub { font-family: var(--mono); font-size: 11px; margin-top: 4px; color: var(--muted); }
.kpi .k-sub.up { color: var(--up); }
.kpi .k-sub.down { color: var(--down); }
/* Featured card */
.grid-2 { display: grid; grid-template-columns: 1.7fr 1fr; gap: 18px; }
.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;
display: inline-flex; align-items: center; justify-content: center;
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; flex-wrap: wrap; }
.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 .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-now.up { color: var(--up); }
.px-now.down { color: var(--down); }
.px-delta { font-family: var(--mono); font-size: 12.5px; margin-top: 2px; }
.px-delta.up { color: var(--up); }
.px-delta.down { color: var(--down); }
.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; }
.feat-chart svg { width: 100%; height: 280px; display: block; }
/* AI Rec */
.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(--up), var(--cyan));
-webkit-background-clip: text; background-clip: text; color: transparent;
}
.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 .v.cyan { color: var(--cyan); }
.ai-stat .bar { height: 4px; background: var(--surface-3); border-radius: 999px; margin-top: 8px; overflow: hidden; }
.ai-stat .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; }
/* Panels */
.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; }
/* Holdings */
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);
}
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;
}
.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; }
.last-cell { font-weight: 600; }
.last-cell.up { color: var(--up); }
.last-cell.down { color: var(--down); }
/* Watchlist */
.watch-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 14px; padding: 14px; }
.watch-card {
border: 1px solid var(--line); border-radius: 12px;
background: linear-gradient(180deg, var(--surface), var(--surface-2));
padding: 14px 16px;
}
.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); }
/* News */
.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-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 .head { font-size: 13px; color: var(--ink); margin-top: 4px; line-height: 1.45; }
.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.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 .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); }
@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) {
.chart-stats { grid-template-columns: repeat(3, 1fr); }
.ai-stat-row { grid-template-columns: 1fr; }
.pf-value { font-size: 40px; }
.watch-grid { grid-template-columns: 1fr; }
}
</style>
</head>
<body>
<header class="app">
<div class="brand">
<div class="logo"></div>
<div class="word">{{data.summary.brand}}</div>
<div class="pro">{{data.summary.pro}}</div>
</div>
<div class="head-right">
<span class="market-state">
<span class="pulse"></span>
<span>{{data.summary.marketState}}</span>
</span>
<span class="label">Last sync</span>
<span class="val">{{data.summary.lastSync}}</span>
</div>
</header>
<main>
<!-- ─────── Hero ─────── -->
<section class="hero">
<div class="card">
<div class="pf-head">
<div>
<div class="pf-meta">{{data.summary.totalLabel}}</div>
<div class="pf-title">{{data.summary.accountsLabel}}</div>
</div>
</div>
<div class="pf-value">{{data.summary.totalValue}}</div>
<div class="pf-deltas">
<span class="delta {{data.summary.todayDirClass}}">
<span class="arr">{{data.summary.todayArrow}}</span>
<span class="v">{{data.summary.todayPnl}}</span>
<span class="lbl">today · {{data.summary.todayPct}}</span>
</span>
<span class="delta {{data.summary.allTimeDirClass}}">
<span class="arr">{{data.summary.allTimeArrow}}</span>
<span class="v">{{data.summary.allTimePnl}}</span>
<span class="lbl">all time · {{data.summary.allTimePct}}</span>
</span>
<span class="delta">
<span class="lbl" style="color:var(--muted)">{{data.summary.alphaLabel}}</span>
<span class="v" style="color:var(--accent);">{{data.summary.alpha}}</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="#22d3ee"/>
<stop offset="100%" stop-color="#22e58c"/>
</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)"/>
<polygon fill="url(#pfGrad)" points="{{data.summary.areaPoints}}" />
<polyline fill="none" stroke="url(#pfStroke)" stroke-width="2.4" stroke-linejoin="round" stroke-linecap="round"
points="{{data.summary.linePoints}}" />
<circle cx="{{data.summary.markerX}}" cy="{{data.summary.markerY}}" r="4" fill="#22e58c"/>
<circle cx="{{data.summary.markerX}}" cy="{{data.summary.markerY}}" r="9" fill="none" stroke="#22e58c" stroke-opacity="0.5"/>
<line x1="{{data.summary.markerX}}" y1="{{data.summary.markerY}}" x2="{{data.summary.markerX}}" y2="160" stroke="rgba(34,229,140,0.35)" stroke-dasharray="2 3"/>
</svg>
<div class="pf-axis">
<span>{{data.summary.axisTicks.0.label}}</span>
<span>{{data.summary.axisTicks.1.label}}</span>
<span>{{data.summary.axisTicks.2.label}}</span>
<span>{{data.summary.axisTicks.3.label}}</span>
<span>{{data.summary.axisTicks.4.label}}</span>
<span>{{data.summary.axisTicks.5.label}}</span>
<span>{{data.summary.axisTicks.6.label}}</span>
</div>
</div>
</div>
<div class="kpis">
<div class="kpi">
<div class="k-label">{{data.kpis.0.label}}</div>
<div class="k-val {{data.kpis.0.valueClass}}">{{data.kpis.0.value}}</div>
<div class="k-sub {{data.kpis.0.subClass}}">{{data.kpis.0.sub}}</div>
</div>
<div class="kpi">
<div class="k-label">{{data.kpis.1.label}}</div>
<div class="k-val {{data.kpis.1.valueClass}}">{{data.kpis.1.value}}</div>
<div class="k-sub {{data.kpis.1.subClass}}">{{data.kpis.1.sub}}</div>
</div>
<div class="kpi">
<div class="k-label">{{data.kpis.2.label}}</div>
<div class="k-val {{data.kpis.2.valueClass}}">{{data.kpis.2.value}}</div>
<div class="k-sub {{data.kpis.2.subClass}}">{{data.kpis.2.sub}}</div>
</div>
<div class="kpi">
<div class="k-label">{{data.kpis.3.label}}</div>
<div class="k-val {{data.kpis.3.valueClass}}">{{data.kpis.3.value}}</div>
<div class="k-sub {{data.kpis.3.subClass}}">{{data.kpis.3.sub}}</div>
</div>
</div>
</section>
<!-- ─────── Featured chart + AI rec ─────── -->
<section class="grid-2">
<div class="card chart-card">
<div class="chart-head">
<div class="ticker">
<div class="icon" style="background:{{data.featured.iconBg}};color:{{data.featured.iconFg}};">{{data.featured.iconText}}</div>
<div class="meta">
<div class="sym-row">
<span class="sym">{{data.featured.symbol}}</span>
<a class="ext" href="{{data.featured.exchangeUrl}}" target="_blank" rel="noopener">{{data.featured.exchangeName}} ↗</a>
<a class="ext" href="{{data.featured.yahooUrl}}" target="_blank" rel="noopener">Yahoo ↗</a>
<a class="ext" href="{{data.featured.tradingViewUrl}}" target="_blank" rel="noopener">TradingView ↗</a>
</div>
<div class="name">{{data.featured.name}} · {{data.featured.sector}}</div>
</div>
</div>
<div class="price-block">
<div class="px-now {{data.featured.dirClass}}">{{data.featured.price}}</div>
<div class="px-delta {{data.featured.dirClass}}">{{data.featured.delta}}</div>
</div>
</div>
<div class="chart-stats">
<div class="stat">
<div class="lbl">{{data.featured.stats.0.label}}</div>
<div class="val">{{data.featured.stats.0.value}}</div>
</div>
<div class="stat">
<div class="lbl">{{data.featured.stats.1.label}}</div>
<div class="val">{{data.featured.stats.1.value}}</div>
</div>
<div class="stat">
<div class="lbl">{{data.featured.stats.2.label}}</div>
<div class="val">{{data.featured.stats.2.value}}</div>
</div>
<div class="stat">
<div class="lbl">{{data.featured.stats.3.label}}</div>
<div class="val">{{data.featured.stats.3.value}}</div>
</div>
<div class="stat">
<div class="lbl">{{data.featured.stats.4.label}}</div>
<div class="val">{{data.featured.stats.4.value}}</div>
</div>
<div class="stat">
<div class="lbl">{{data.featured.stats.5.label}}</div>
<div class="val">{{data.featured.stats.5.value}}</div>
</div>
</div>
<div class="chart-body">
<div class="feat-chart">
<svg viewBox="0 0 880 280" preserveAspectRatio="none">
<defs>
<linearGradient id="featGrad" x1="0" x2="0" y1="0" y2="1">
<stop offset="0%" stop-color="#22e58c" stop-opacity="0.34"/>
<stop offset="100%" stop-color="#22e58c" stop-opacity="0"/>
</linearGradient>
<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="280" fill="url(#grid2)"/>
<polygon fill="url(#featGrad)" points="{{data.featured.areaPoints}}"/>
<polyline fill="none" stroke="#22e58c" stroke-width="2.2" stroke-linejoin="round" stroke-linecap="round" points="{{data.featured.linePoints}}"/>
<line x1="40" y1="{{data.featured.priceY}}" x2="844" y2="{{data.featured.priceY}}" stroke="rgba(34,229,140,0.25)" stroke-dasharray="2 3"/>
<rect x="836" y="{{data.featured.priceLabelY}}" width="50" height="18" fill="#22e58c" rx="3"/>
<text x="861" y="{{data.featured.priceLabelTextY}}" font-family="Geist Mono,monospace" font-size="10" fill="#00200f" text-anchor="middle" font-weight="700">{{data.featured.priceLabelText}}</text>
</svg>
</div>
</div>
</div>
<div class="card ai-card">
<div class="ai-head">
<div class="ai-tag">{{data.ai.tag}}</div>
<div class="ai-headline">
<span class="accent">{{data.ai.headlineAccent}}</span> {{data.ai.headlineRest}}
</div>
<div class="ai-sub">{{data.ai.sub}}</div>
</div>
<div class="ai-stat-row">
<div class="ai-stat">
<div class="lbl">Conviction</div>
<div class="v cyan">{{data.ai.conviction}}</div>
<div class="bar"><div class="fill" style="width:{{data.ai.convictionWidth}}"></div></div>
</div>
<div class="ai-stat">
<div class="lbl">{{data.ai.targetLabel}}</div>
<div class="v up">{{data.ai.target}}</div>
<div class="lbl" style="margin-top:2px;color:var(--up)">{{data.ai.upside}} upside</div>
</div>
<div class="ai-stat">
<div class="lbl">Suggested size</div>
<div class="v">{{data.ai.size}}</div>
<div class="lbl" style="margin-top:2px;color:var(--muted)">{{data.ai.sizeSub}}</div>
</div>
</div>
<div class="ai-thesis">
<h4>Why now · catalysts</h4>
<div class="thesis-list">
<div class="thesis-item">
<div class="thesis-num">{{data.ai.catalysts.0.n}}</div>
<div class="thesis-text"><strong>{{data.ai.catalysts.0.title}}</strong> — {{data.ai.catalysts.0.text}}</div>
</div>
<div class="thesis-item">
<div class="thesis-num">{{data.ai.catalysts.1.n}}</div>
<div class="thesis-text"><strong>{{data.ai.catalysts.1.title}}</strong> — {{data.ai.catalysts.1.text}}</div>
</div>
<div class="thesis-item">
<div class="thesis-num">{{data.ai.catalysts.2.n}}</div>
<div class="thesis-text"><strong>{{data.ai.catalysts.2.title}}</strong> — {{data.ai.catalysts.2.text}}</div>
</div>
</div>
</div>
</div>
</section>
<!-- ─────── Holdings ─────── -->
<div class="card" style="padding:0;">
<div class="panel-head">
<div class="panel-title">{{data.holdingsPanel.title}}</div>
<div class="panel-sub">{{data.holdingsPanel.subtitle}}</div>
</div>
<table class="holdings">
<thead>
<tr>
<th>Symbol</th>
<th class="num">Shares</th>
<th class="num">Avg cost</th>
<th class="num">Last</th>
<th class="num">Market value</th>
<th class="num">Unrealized P&L</th>
<th>Allocation</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<div class="sym-cell">
<span class="sym-mark" style="background:{{data.holdings.0.iconBg}};color:{{data.holdings.0.iconFg}};">{{data.holdings.0.icon}}</span>
<span class="name-block">
<span class="sym-name">{{data.holdings.0.symbol}}
<a class="ext-mini" href="{{data.holdings.0.exchUrl}}" target="_blank" rel="noopener">{{data.holdings.0.exchName}} ↗</a>
</span>
<span class="sym-co">{{data.holdings.0.companyName}}</span>
</span>
</div>
</td>
<td class="num">{{data.holdings.0.shares}}</td>
<td class="num">{{data.holdings.0.avgCost}}</td>
<td class="num last-cell {{data.holdings.0.dirClass}}">{{data.holdings.0.last}}</td>
<td class="num">{{data.holdings.0.marketValue}}</td>
<td class="num">
<span class="pl-cell {{data.holdings.0.dirClass}}">
<span class="pl-val">{{data.holdings.0.pnl}}</span>
<span class="pl-pct">{{data.holdings.0.pnlPct}}</span>
</span>
</td>
<td>
<span class="alloc-bar"><span class="alloc-fill" style="width:{{data.holdings.0.allocWidth}}"></span></span>
<span style="font-family:var(--mono);font-size:11px;color:var(--muted);margin-left:8px;">{{data.holdings.0.alloc}}</span>
</td>
</tr>
<tr>
<td>
<div class="sym-cell">
<span class="sym-mark" style="background:{{data.holdings.1.iconBg}};color:{{data.holdings.1.iconFg}};">{{data.holdings.1.icon}}</span>
<span class="name-block">
<span class="sym-name">{{data.holdings.1.symbol}}
<a class="ext-mini" href="{{data.holdings.1.exchUrl}}" target="_blank" rel="noopener">{{data.holdings.1.exchName}} ↗</a>
</span>
<span class="sym-co">{{data.holdings.1.companyName}}</span>
</span>
</div>
</td>
<td class="num">{{data.holdings.1.shares}}</td>
<td class="num">{{data.holdings.1.avgCost}}</td>
<td class="num last-cell {{data.holdings.1.dirClass}}">{{data.holdings.1.last}}</td>
<td class="num">{{data.holdings.1.marketValue}}</td>
<td class="num">
<span class="pl-cell {{data.holdings.1.dirClass}}">
<span class="pl-val">{{data.holdings.1.pnl}}</span>
<span class="pl-pct">{{data.holdings.1.pnlPct}}</span>
</span>
</td>
<td>
<span class="alloc-bar"><span class="alloc-fill" style="width:{{data.holdings.1.allocWidth}}"></span></span>
<span style="font-family:var(--mono);font-size:11px;color:var(--muted);margin-left:8px;">{{data.holdings.1.alloc}}</span>
</td>
</tr>
<tr>
<td>
<div class="sym-cell">
<span class="sym-mark" style="background:{{data.holdings.2.iconBg}};color:{{data.holdings.2.iconFg}};">{{data.holdings.2.icon}}</span>
<span class="name-block">
<span class="sym-name">{{data.holdings.2.symbol}}
<a class="ext-mini" href="{{data.holdings.2.exchUrl}}" target="_blank" rel="noopener">{{data.holdings.2.exchName}} ↗</a>
</span>
<span class="sym-co">{{data.holdings.2.companyName}}</span>
</span>
</div>
</td>
<td class="num">{{data.holdings.2.shares}}</td>
<td class="num">{{data.holdings.2.avgCost}}</td>
<td class="num last-cell {{data.holdings.2.dirClass}}">{{data.holdings.2.last}}</td>
<td class="num">{{data.holdings.2.marketValue}}</td>
<td class="num">
<span class="pl-cell {{data.holdings.2.dirClass}}">
<span class="pl-val">{{data.holdings.2.pnl}}</span>
<span class="pl-pct">{{data.holdings.2.pnlPct}}</span>
</span>
</td>
<td>
<span class="alloc-bar"><span class="alloc-fill" style="width:{{data.holdings.2.allocWidth}}"></span></span>
<span style="font-family:var(--mono);font-size:11px;color:var(--muted);margin-left:8px;">{{data.holdings.2.alloc}}</span>
</td>
</tr>
<tr>
<td>
<div class="sym-cell">
<span class="sym-mark" style="background:{{data.holdings.3.iconBg}};color:{{data.holdings.3.iconFg}};">{{data.holdings.3.icon}}</span>
<span class="name-block">
<span class="sym-name">{{data.holdings.3.symbol}}
<a class="ext-mini" href="{{data.holdings.3.exchUrl}}" target="_blank" rel="noopener">{{data.holdings.3.exchName}} ↗</a>
</span>
<span class="sym-co">{{data.holdings.3.companyName}}</span>
</span>
</div>
</td>
<td class="num">{{data.holdings.3.shares}}</td>
<td class="num">{{data.holdings.3.avgCost}}</td>
<td class="num last-cell {{data.holdings.3.dirClass}}">{{data.holdings.3.last}}</td>
<td class="num">{{data.holdings.3.marketValue}}</td>
<td class="num">
<span class="pl-cell {{data.holdings.3.dirClass}}">
<span class="pl-val">{{data.holdings.3.pnl}}</span>
<span class="pl-pct">{{data.holdings.3.pnlPct}}</span>
</span>
</td>
<td>
<span class="alloc-bar"><span class="alloc-fill" style="width:{{data.holdings.3.allocWidth}}"></span></span>
<span style="font-family:var(--mono);font-size:11px;color:var(--muted);margin-left:8px;">{{data.holdings.3.alloc}}</span>
</td>
</tr>
<tr>
<td>
<div class="sym-cell">
<span class="sym-mark" style="background:{{data.holdings.4.iconBg}};color:{{data.holdings.4.iconFg}};">{{data.holdings.4.icon}}</span>
<span class="name-block">
<span class="sym-name">{{data.holdings.4.symbol}}
<a class="ext-mini" href="{{data.holdings.4.exchUrl}}" target="_blank" rel="noopener">{{data.holdings.4.exchName}} ↗</a>
</span>
<span class="sym-co">{{data.holdings.4.companyName}}</span>
</span>
</div>
</td>
<td class="num">{{data.holdings.4.shares}}</td>
<td class="num">{{data.holdings.4.avgCost}}</td>
<td class="num last-cell {{data.holdings.4.dirClass}}">{{data.holdings.4.last}}</td>
<td class="num">{{data.holdings.4.marketValue}}</td>
<td class="num">
<span class="pl-cell {{data.holdings.4.dirClass}}">
<span class="pl-val">{{data.holdings.4.pnl}}</span>
<span class="pl-pct">{{data.holdings.4.pnlPct}}</span>
</span>
</td>
<td>
<span class="alloc-bar"><span class="alloc-fill" style="width:{{data.holdings.4.allocWidth}}"></span></span>
<span style="font-family:var(--mono);font-size:11px;color:var(--muted);margin-left:8px;">{{data.holdings.4.alloc}}</span>
</td>
</tr>
<tr>
<td>
<div class="sym-cell">
<span class="sym-mark" style="background:{{data.holdings.5.iconBg}};color:{{data.holdings.5.iconFg}};">{{data.holdings.5.icon}}</span>
<span class="name-block">
<span class="sym-name">{{data.holdings.5.symbol}}
<a class="ext-mini" href="{{data.holdings.5.exchUrl}}" target="_blank" rel="noopener">{{data.holdings.5.exchName}} ↗</a>
</span>
<span class="sym-co">{{data.holdings.5.companyName}}</span>
</span>
</div>
</td>
<td class="num">{{data.holdings.5.shares}}</td>
<td class="num">{{data.holdings.5.avgCost}}</td>
<td class="num last-cell {{data.holdings.5.dirClass}}">{{data.holdings.5.last}}</td>
<td class="num">{{data.holdings.5.marketValue}}</td>
<td class="num">
<span class="pl-cell {{data.holdings.5.dirClass}}">
<span class="pl-val">{{data.holdings.5.pnl}}</span>
<span class="pl-pct">{{data.holdings.5.pnlPct}}</span>
</span>
</td>
<td>
<span class="alloc-bar"><span class="alloc-fill" style="width:{{data.holdings.5.allocWidth}}"></span></span>
<span style="font-family:var(--mono);font-size:11px;color:var(--muted);margin-left:8px;">{{data.holdings.5.alloc}}</span>
</td>
</tr>
<tr>
<td>
<div class="sym-cell">
<span class="sym-mark" style="background:{{data.holdings.6.iconBg}};color:{{data.holdings.6.iconFg}};">{{data.holdings.6.icon}}</span>
<span class="name-block">
<span class="sym-name">{{data.holdings.6.symbol}}
<a class="ext-mini" href="{{data.holdings.6.exchUrl}}" target="_blank" rel="noopener">{{data.holdings.6.exchName}} ↗</a>
</span>
<span class="sym-co">{{data.holdings.6.companyName}}</span>
</span>
</div>
</td>
<td class="num">{{data.holdings.6.shares}}</td>
<td class="num">{{data.holdings.6.avgCost}}</td>
<td class="num last-cell {{data.holdings.6.dirClass}}">{{data.holdings.6.last}}</td>
<td class="num">{{data.holdings.6.marketValue}}</td>
<td class="num">
<span class="pl-cell {{data.holdings.6.dirClass}}">
<span class="pl-val">{{data.holdings.6.pnl}}</span>
<span class="pl-pct">{{data.holdings.6.pnlPct}}</span>
</span>
</td>
<td>
<span class="alloc-bar"><span class="alloc-fill" style="width:{{data.holdings.6.allocWidth}}"></span></span>
<span style="font-family:var(--mono);font-size:11px;color:var(--muted);margin-left:8px;">{{data.holdings.6.alloc}}</span>
</td>
</tr>
<tr>
<td>
<div class="sym-cell">
<span class="sym-mark" style="background:{{data.holdings.7.iconBg}};color:{{data.holdings.7.iconFg}};">{{data.holdings.7.icon}}</span>
<span class="name-block">
<span class="sym-name">{{data.holdings.7.symbol}}
<a class="ext-mini" href="{{data.holdings.7.exchUrl}}" target="_blank" rel="noopener">{{data.holdings.7.exchName}} ↗</a>
</span>
<span class="sym-co">{{data.holdings.7.companyName}}</span>
</span>
</div>
</td>
<td class="num">{{data.holdings.7.shares}}</td>
<td class="num">{{data.holdings.7.avgCost}}</td>
<td class="num last-cell {{data.holdings.7.dirClass}}">{{data.holdings.7.last}}</td>
<td class="num">{{data.holdings.7.marketValue}}</td>
<td class="num">
<span class="pl-cell {{data.holdings.7.dirClass}}">
<span class="pl-val">{{data.holdings.7.pnl}}</span>
<span class="pl-pct">{{data.holdings.7.pnlPct}}</span>
</span>
</td>
<td>
<span class="alloc-bar"><span class="alloc-fill" style="width:{{data.holdings.7.allocWidth}}"></span></span>
<span style="font-family:var(--mono);font-size:11px;color:var(--muted);margin-left:8px;">{{data.holdings.7.alloc}}</span>
</td>
</tr>
<tr>
<td>
<div class="sym-cell">
<span class="sym-mark" style="background:{{data.holdings.8.iconBg}};color:{{data.holdings.8.iconFg}};">{{data.holdings.8.icon}}</span>
<span class="name-block">
<span class="sym-name">{{data.holdings.8.symbol}}
<a class="ext-mini" href="{{data.holdings.8.exchUrl}}" target="_blank" rel="noopener">{{data.holdings.8.exchName}} ↗</a>
</span>
<span class="sym-co">{{data.holdings.8.companyName}}</span>
</span>
</div>
</td>
<td class="num">{{data.holdings.8.shares}}</td>
<td class="num">{{data.holdings.8.avgCost}}</td>
<td class="num last-cell {{data.holdings.8.dirClass}}">{{data.holdings.8.last}}</td>
<td class="num">{{data.holdings.8.marketValue}}</td>
<td class="num">
<span class="pl-cell {{data.holdings.8.dirClass}}">
<span class="pl-val">{{data.holdings.8.pnl}}</span>
<span class="pl-pct">{{data.holdings.8.pnlPct}}</span>
</span>
</td>
<td>
<span class="alloc-bar"><span class="alloc-fill" style="width:{{data.holdings.8.allocWidth}}"></span></span>
<span style="font-family:var(--mono);font-size:11px;color:var(--muted);margin-left:8px;">{{data.holdings.8.alloc}}</span>
</td>
</tr>
</tbody>
</table>
</div>
<!-- ─────── Watchlist + News ─────── -->
<section class="grid-2">
<div class="card" style="padding:0;">
<div class="panel-head">
<div class="panel-title">{{data.watchlistPanel.title}}</div>
<div class="panel-sub">{{data.watchlistPanel.subtitle}}</div>
</div>
<div class="watch-grid">
<div class="watch-card">
<div class="watch-top">
<div>
<div class="watch-sym">{{data.watchlist.0.symbol}}</div>
<div class="watch-co">{{data.watchlist.0.companyName}}</div>
</div>
<span class="watch-pct {{data.watchlist.0.dirClass}}">{{data.watchlist.0.pct}}</span>
</div>
<div class="watch-px">{{data.watchlist.0.price}}</div>
<div class="watch-spark">
<svg viewBox="0 0 200 40" preserveAspectRatio="none">
<polyline fill="none" stroke="{{data.watchlist.0.sparkColor}}" stroke-width="1.6" points="{{data.watchlist.0.sparkPoints}}"/>
</svg>
</div>
<div class="watch-foot">
<span>Vol {{data.watchlist.0.volume}}</span>
<a href="{{data.watchlist.0.exchUrl}}" target="_blank" rel="noopener">{{data.watchlist.0.exchName}} ↗</a>
</div>
</div>
<div class="watch-card">
<div class="watch-top">
<div>
<div class="watch-sym">{{data.watchlist.1.symbol}}</div>
<div class="watch-co">{{data.watchlist.1.companyName}}</div>
</div>
<span class="watch-pct {{data.watchlist.1.dirClass}}">{{data.watchlist.1.pct}}</span>
</div>
<div class="watch-px">{{data.watchlist.1.price}}</div>
<div class="watch-spark">
<svg viewBox="0 0 200 40" preserveAspectRatio="none">
<polyline fill="none" stroke="{{data.watchlist.1.sparkColor}}" stroke-width="1.6" points="{{data.watchlist.1.sparkPoints}}"/>
</svg>
</div>
<div class="watch-foot">
<span>Vol {{data.watchlist.1.volume}}</span>
<a href="{{data.watchlist.1.exchUrl}}" target="_blank" rel="noopener">{{data.watchlist.1.exchName}} ↗</a>
</div>
</div>
<div class="watch-card">
<div class="watch-top">
<div>
<div class="watch-sym">{{data.watchlist.2.symbol}}</div>
<div class="watch-co">{{data.watchlist.2.companyName}}</div>
</div>
<span class="watch-pct {{data.watchlist.2.dirClass}}">{{data.watchlist.2.pct}}</span>
</div>
<div class="watch-px">{{data.watchlist.2.price}}</div>
<div class="watch-spark">
<svg viewBox="0 0 200 40" preserveAspectRatio="none">
<polyline fill="none" stroke="{{data.watchlist.2.sparkColor}}" stroke-width="1.6" points="{{data.watchlist.2.sparkPoints}}"/>
</svg>
</div>
<div class="watch-foot">
<span>Vol {{data.watchlist.2.volume}}</span>
<a href="{{data.watchlist.2.exchUrl}}" target="_blank" rel="noopener">{{data.watchlist.2.exchName}} ↗</a>
</div>
</div>
<div class="watch-card">
<div class="watch-top">
<div>
<div class="watch-sym">{{data.watchlist.3.symbol}}</div>
<div class="watch-co">{{data.watchlist.3.companyName}}</div>
</div>
<span class="watch-pct {{data.watchlist.3.dirClass}}">{{data.watchlist.3.pct}}</span>
</div>
<div class="watch-px">{{data.watchlist.3.price}}</div>
<div class="watch-spark">
<svg viewBox="0 0 200 40" preserveAspectRatio="none">
<polyline fill="none" stroke="{{data.watchlist.3.sparkColor}}" stroke-width="1.6" points="{{data.watchlist.3.sparkPoints}}"/>
</svg>
</div>
<div class="watch-foot">
<span>Vol {{data.watchlist.3.volume}}</span>
<a href="{{data.watchlist.3.exchUrl}}" target="_blank" rel="noopener">{{data.watchlist.3.exchName}} ↗</a>
</div>
</div>
<div class="watch-card">
<div class="watch-top">
<div>
<div class="watch-sym">{{data.watchlist.4.symbol}}</div>
<div class="watch-co">{{data.watchlist.4.companyName}}</div>
</div>
<span class="watch-pct {{data.watchlist.4.dirClass}}">{{data.watchlist.4.pct}}</span>
</div>
<div class="watch-px">{{data.watchlist.4.price}}</div>
<div class="watch-spark">
<svg viewBox="0 0 200 40" preserveAspectRatio="none">
<polyline fill="none" stroke="{{data.watchlist.4.sparkColor}}" stroke-width="1.6" points="{{data.watchlist.4.sparkPoints}}"/>
</svg>
</div>
<div class="watch-foot">
<span>Vol {{data.watchlist.4.volume}}</span>
<a href="{{data.watchlist.4.exchUrl}}" target="_blank" rel="noopener">{{data.watchlist.4.exchName}} ↗</a>
</div>
</div>
<div class="watch-card">
<div class="watch-top">
<div>
<div class="watch-sym">{{data.watchlist.5.symbol}}</div>
<div class="watch-co">{{data.watchlist.5.companyName}}</div>
</div>
<span class="watch-pct {{data.watchlist.5.dirClass}}">{{data.watchlist.5.pct}}</span>
</div>
<div class="watch-px">{{data.watchlist.5.price}}</div>
<div class="watch-spark">
<svg viewBox="0 0 200 40" preserveAspectRatio="none">
<polyline fill="none" stroke="{{data.watchlist.5.sparkColor}}" stroke-width="1.6" points="{{data.watchlist.5.sparkPoints}}"/>
</svg>
</div>
<div class="watch-foot">
<span>Vol {{data.watchlist.5.volume}}</span>
<a href="{{data.watchlist.5.exchUrl}}" target="_blank" rel="noopener">{{data.watchlist.5.exchName}} ↗</a>
</div>
</div>
<div class="watch-card">
<div class="watch-top">
<div>
<div class="watch-sym">{{data.watchlist.6.symbol}}</div>
<div class="watch-co">{{data.watchlist.6.companyName}}</div>
</div>
<span class="watch-pct {{data.watchlist.6.dirClass}}">{{data.watchlist.6.pct}}</span>
</div>
<div class="watch-px">{{data.watchlist.6.price}}</div>
<div class="watch-spark">
<svg viewBox="0 0 200 40" preserveAspectRatio="none">
<polyline fill="none" stroke="{{data.watchlist.6.sparkColor}}" stroke-width="1.6" points="{{data.watchlist.6.sparkPoints}}"/>
</svg>
</div>
<div class="watch-foot">
<span>Vol {{data.watchlist.6.volume}}</span>
<a href="{{data.watchlist.6.exchUrl}}" target="_blank" rel="noopener">{{data.watchlist.6.exchName}} ↗</a>
</div>
</div>
<div class="watch-card">
<div class="watch-top">
<div>
<div class="watch-sym">{{data.watchlist.7.symbol}}</div>
<div class="watch-co">{{data.watchlist.7.companyName}}</div>
</div>
<span class="watch-pct {{data.watchlist.7.dirClass}}">{{data.watchlist.7.pct}}</span>
</div>
<div class="watch-px">{{data.watchlist.7.price}}</div>
<div class="watch-spark">
<svg viewBox="0 0 200 40" preserveAspectRatio="none">
<polyline fill="none" stroke="{{data.watchlist.7.sparkColor}}" stroke-width="1.6" points="{{data.watchlist.7.sparkPoints}}"/>
</svg>
</div>
<div class="watch-foot">
<span>Vol {{data.watchlist.7.volume}}</span>
<a href="{{data.watchlist.7.exchUrl}}" target="_blank" rel="noopener">{{data.watchlist.7.exchName}} ↗</a>
</div>
</div>
</div>
</div>
<div class="card" style="padding:0;">
<div class="panel-head">
<div class="panel-title">{{data.newsPanel.title}}</div>
<div class="panel-sub">{{data.newsPanel.subtitle}}</div>
</div>
<div class="news-list">
<div class="news-item">
<div class="news-time">{{data.news.0.time}}</div>
<div class="news-body">
<div class="src">{{data.news.0.source}}</div>
<div class="head">{{data.news.0.headline}}</div>
</div>
<span class="impact {{data.news.0.impactClass}}">{{data.news.0.impactLabel}}</span>
</div>
<div class="news-item">
<div class="news-time">{{data.news.1.time}}</div>
<div class="news-body">
<div class="src">{{data.news.1.source}}</div>
<div class="head">{{data.news.1.headline}}</div>
</div>
<span class="impact {{data.news.1.impactClass}}">{{data.news.1.impactLabel}}</span>
</div>
<div class="news-item">
<div class="news-time">{{data.news.2.time}}</div>
<div class="news-body">
<div class="src">{{data.news.2.source}}</div>
<div class="head">{{data.news.2.headline}}</div>
</div>
<span class="impact {{data.news.2.impactClass}}">{{data.news.2.impactLabel}}</span>
</div>
<div class="news-item">
<div class="news-time">{{data.news.3.time}}</div>
<div class="news-body">
<div class="src">{{data.news.3.source}}</div>
<div class="head">{{data.news.3.headline}}</div>
</div>
<span class="impact {{data.news.3.impactClass}}">{{data.news.3.impactLabel}}</span>
</div>
<div class="news-item">
<div class="news-time">{{data.news.4.time}}</div>
<div class="news-body">
<div class="src">{{data.news.4.source}}</div>
<div class="head">{{data.news.4.headline}}</div>
</div>
<span class="impact {{data.news.4.impactClass}}">{{data.news.4.impactLabel}}</span>
</div>
<div class="news-item">
<div class="news-time">{{data.news.5.time}}</div>
<div class="news-body">
<div class="src">{{data.news.5.source}}</div>
<div class="head">{{data.news.5.headline}}</div>
</div>
<span class="impact {{data.news.5.impactClass}}">{{data.news.5.impactLabel}}</span>
</div>
</div>
</div>
</section>
</main>
<footer class="app">
<span class="live">{{data.footer.live}}</span>
<span>{{data.footer.source}}</span>
<span>{{data.footer.version}}</span>
<span>Last sync {{data.summary.lastSync}}</span>
</footer>
</body>
</html>