/* ==========================================================================
   Nullius 设计系统 v4 · 组件基线
   --------------------------------------------------------------------------
   规范站与产品的同一份组件样式。只引用 tokens.css 的语义层变量。
   类名为规范名；产品迁移时按"类名映射表"对齐（见 README.md）。
   ========================================================================== */

*,*::before,*::after{box-sizing:border-box}
[hidden]{display:none!important}
body{
  font:var(--type-body)/var(--leading-body) var(--sans);
  background:var(--canvas); color:var(--foreground);
  -webkit-font-smoothing:antialiased; font-synthesis:none;
}
.num{font-variant-numeric:tabular-nums lining-nums}
button{font:inherit;color:inherit;cursor:pointer;border:0;background:none}
input,select,textarea{font:inherit;color:inherit}
h1,h2,h3,h4{font-weight:600}
a{color:var(--accent)}
::selection{background:var(--accent-soft)}

.muted{color:var(--foreground-muted)}
.secondary{color:var(--foreground-secondary)}

/* ---------------------------------------------------------------- 文字辅助 */

.kicker{
  font-size:11px; letter-spacing:2px; font-weight:600;
  color:var(--accent); text-transform:uppercase;
  display:inline-flex; align-items:center; gap:10px;
}
.kicker::before{content:''; width:24px; height:1.5px; background:var(--accent)}

/* ---------------------------------------------------------------- 按钮
   家族：primary / ghost / soft / danger / icon
   高度：sm 32（工具区、表格内）、md 40（默认）
   busy 用 aria-busy 表达：锁定宽度、禁用点击、文字不换
   ------------------------------------------------------------------ */

.btn{
  display:inline-flex; align-items:center; justify-content:center; gap:8px;
  min-height:var(--control-md); padding:8px 18px;
  border-radius:var(--radius-md); border:1px solid var(--border);
  background:var(--surface); color:var(--foreground);
  font-size:var(--type-body); line-height:var(--leading-body); font-weight:500; letter-spacing:-.1px;
  transition:background var(--motion-feedback), border-color var(--motion-feedback),
             color var(--motion-feedback), transform var(--motion) var(--ease-arch),
             box-shadow var(--motion);
}
.btn:hover{border-color:var(--petrol-300); color:var(--accent-hover); background:var(--surface-subtle)}
.btn:active{transform:translateY(0)}
.btn:focus-visible{outline:2px solid var(--focus); outline-offset:2px}
.btn:disabled,.btn[aria-disabled=true]{opacity:.45; cursor:not-allowed; transform:none}
.btn[aria-busy=true]{cursor:progress; pointer-events:none}

.btn-sm{min-height:var(--control-sm); padding:5px 12px; font-size:var(--type-table); line-height:var(--leading-table)}

.btn-primary{background:var(--accent); border-color:transparent; color:var(--on-accent);
  box-shadow:0 1px 2px rgba(20,107,112,.2)}
.btn-primary:hover{background:var(--accent-hover); color:var(--on-accent);
  transform:translateY(-1px); box-shadow:0 4px 12px rgba(20,107,112,.28)}
.btn-primary:active{background:var(--accent-pressed)}
[data-theme=dark] .btn-primary{box-shadow:none}

.btn-soft{background:var(--accent-soft); border-color:transparent; color:var(--accent)}
.btn-soft:hover{background:var(--accent-soft); border-color:transparent; color:var(--accent-hover)}

.btn-danger{background:var(--negative); border-color:transparent; color:#fff}
.btn-danger:hover{filter:brightness(.94); color:#fff; transform:none}

.btn-icon{width:var(--control-md); padding:0; flex-shrink:0}
.btn-icon.btn-sm{width:var(--control-sm)}
.btn svg{width:18px; height:18px;flex-shrink:0}
.btn>span:has(>svg){display:flex}

/* ---------------------------------------------------------------- 徽章 */

.pill{
  display:inline-flex; align-items:center; gap:6px;
  padding:3px 10px; border-radius:var(--radius-pill);
  font-size:11px; font-weight:600; letter-spacing:.4px;
  background:var(--surface-group); color:var(--foreground-secondary);
}
.pill-up{background:color-mix(in srgb,var(--positive) 12%,transparent); color:var(--positive)}
.pill-down{background:color-mix(in srgb,var(--negative) 12%,transparent); color:var(--negative)}
.pill-accent{background:var(--accent-soft); color:var(--accent)}
.pill-warn{background:color-mix(in srgb,var(--warning) 14%,transparent); color:var(--warning)}

/* 涨跌三角：提取 logo 拱的斜率 */
.tick{display:inline-block; width:0; height:0; margin-right:2px;
  border-left:4px solid transparent; border-right:4px solid transparent}
.tick-up{border-bottom:6px solid var(--positive)}
.tick-down{border-top:6px solid var(--negative)}

/* ---------------------------------------------------------------- 面板三档
   panel   —— 默认白面板，拱角出现于 hover 语境的轻反馈
   feature —— petrol 渐变底 + 拱形顶角（收益/流水三张同级摘要可成组使用）
   solid   —— petrol 实底反白（仅用于关键指标强调）
   ------------------------------------------------------------------ */

.panel{
  background:var(--surface); border:1px solid var(--border);
  border-radius:var(--radius-panel); padding:var(--space-6);
  transition:border-color var(--motion), box-shadow var(--motion);
}
.panel-hoverable{transition:transform var(--motion-panel) var(--ease-arch),
  box-shadow var(--motion-panel), border-color var(--motion-panel)}
.panel-hoverable:hover{
  transform:translateY(-2px);
  box-shadow:var(--arch-glow);
  border-color:var(--petrol-300);
}

.panel-feature{
  background:var(--surface-feature); border-color:transparent;
  border-radius:var(--radius-feature); padding:var(--space-6);
  color:var(--foreground-feature);
}

.panel-solid{
  background:var(--surface-inverse); border-color:transparent;
  border-radius:var(--radius-feature); padding:var(--space-6);
  color:#fff;
}
.panel-solid .muted{color:var(--on-inverse-secondary)}

/* hero 卡：品牌水印（logo 拱形 clip-path，低透明度，深色自动反相） */
.panel-hero{
  position:relative; overflow:hidden;
  background:var(--surface-feature); border:1px solid transparent;
  border-radius:var(--radius-hero); padding:var(--space-8);
}
.panel-hero::before{
  content:''; position:absolute; right:-40px; top:50%; transform:translateY(-50%);
  width:260px; height:216px; background:var(--accent); opacity:.06;
  clip-path:path('M20 211V89C20 43 56 8 102 8c40 0 75 25 85 61-45 6-78 40-78 86v56c0 12-8 20-20 20H40c-12 0-20-8-20-20Z M143 108c19-18 43-28 66-27 41 1 72 34 72 75v55c0 12-8 20-20 20h-49c-12 0-20-8-20-20v-52c0-23-19-43-49-51Z');
}
[data-theme=dark] .panel-hero::before{opacity:.10}
.panel-hero>*{position:relative; z-index:1}

/* 面板内的标签/指标/辅文 */
.plabel{font-size:var(--type-caption); color:var(--foreground-muted);
  display:flex; justify-content:space-between; align-items:center; gap:12px}
.panel-feature .plabel,.panel-hero .plabel{color:var(--accent); opacity:.9}
.metric{
  font-size:var(--type-amount); line-height:var(--leading-amount);
  letter-spacing:-1px; font-weight:600;
  font-variant-numeric:tabular-nums lining-nums;
}
.metric-hero{
  font-size:var(--type-display); line-height:var(--leading-display);
  letter-spacing:-.8px; font-weight:600; color:var(--foreground-feature);
}
.metric-hero-compact{font-size:var(--type-display-compact);line-height:var(--leading-display-compact)}
.metric .cur{font-size:.42em; font-weight:500; color:var(--accent); margin-right:6px; letter-spacing:0}
/* hero 金额的法币单位后缀（交易所惯例：小号、弱化、紧随数字） */
.metric-hero .unit{font-size:var(--type-display-unit);line-height:var(--leading-caption);font-weight:500;letter-spacing:0;
  color:var(--foreground-muted);margin-left:var(--space-2);vertical-align:baseline}
.metric-hero-compact .unit{font-size:var(--type-display-unit-compact)}
.psub{font-size:var(--type-caption); color:var(--foreground-muted); margin-top:8px;
  display:flex; align-items:center; gap:6px; flex-wrap:wrap}

/* ---------------------------------------------------------------- Tabs 与 Segmented
   tabs      —— 任务/页面级切换（下划线）
   segmented —— 同视图的 2–4 种模式/期间切换（滑块）
   ------------------------------------------------------------------ */

.tabs{display:flex; gap:2px; border-bottom:1px solid var(--border)}
.tabs button{
  padding:9px 14px; font-size:var(--type-body); color:var(--foreground-secondary);
  border-bottom:2px solid transparent; margin-bottom:-1px;
  transition:color var(--motion-feedback), border-color var(--motion-feedback);
}
.tabs button:hover{color:var(--foreground)}
.tabs button[aria-selected=true]{color:var(--accent); border-bottom-color:var(--accent); font-weight:600}

.segmented{
  display:inline-flex; gap:2px; padding:3px;
  background:var(--surface-group); border-radius:var(--radius-md);
}
.segmented button{
  min-height:28px; padding:4px 12px; border-radius:var(--radius-sm);
  font-size:var(--type-caption); color:var(--foreground-secondary);
  transition:all var(--motion); white-space:nowrap;
}
.segmented button:hover{color:var(--foreground)}
.segmented button[aria-pressed=true]{
  background:var(--surface); color:var(--accent); font-weight:600;
  box-shadow:0 1px 3px rgba(16,39,42,.08);
}
[data-theme=dark] .segmented button[aria-pressed=true]{box-shadow:0 1px 3px rgba(0,0,0,.3)}

/* ---------------------------------------------------------------- 表单 */

.field{display:grid; gap:6px}
.field label{font-size:var(--type-caption); font-weight:500; color:var(--foreground-secondary)}
.field .helper{font-size:var(--type-caption); color:var(--foreground-muted)}
.field .error-text{font-size:var(--type-caption); color:var(--negative)}

.input,textarea.input,select.input{
  width:100%; min-height:var(--control-md);
  padding:8px 12px; border-radius:var(--radius-md);
  border:1px solid var(--border); background:var(--surface);
  transition:border-color var(--motion-feedback), box-shadow var(--motion-feedback);
}
.input::placeholder{color:var(--foreground-muted)}
.input:hover{border-color:var(--fog-300)}
[data-theme=dark] .input:hover{border-color:var(--pine-400)}
.input:focus{outline:none; border-color:var(--focus); box-shadow:0 0 0 3px var(--accent-soft)}
.input[aria-invalid=true]{border-color:var(--negative)}
.input:disabled{background:var(--surface-group); cursor:not-allowed}
textarea.input{min-height:96px; resize:vertical; line-height:var(--leading-body)}
select.input{appearance:none; padding-right:36px;
  background-image:linear-gradient(45deg,transparent 50%,var(--foreground-muted) 50%),linear-gradient(135deg,var(--foreground-muted) 50%,transparent 50%);
  background-position:calc(100% - 18px) 50%,calc(100% - 13px) 50%;
  background-size:5px 5px; background-repeat:no-repeat;
}

/* Compact search and multi-select fields use the same control/focus roles. */
.input-sm{min-height:var(--control-sm);padding:5px 12px;
  font:400 var(--type-table)/var(--leading-table) var(--sans)}
.search-field{position:relative;min-width:0}
.search-field .input{padding-left:36px;padding-right:36px}
.search-field .input::-webkit-search-cancel-button{appearance:none;display:none}
.search-field-icon{position:absolute;left:12px;top:50%;transform:translateY(-50%);
  display:flex;color:var(--foreground-muted);pointer-events:none}
.search-field-icon svg{width:16px;height:16px}
.search-field .search-clear{position:absolute;right:0;top:0;
  width:var(--control-sm);min-height:var(--control-sm);height:var(--control-sm);border:0;background:transparent;color:var(--foreground-secondary)}
.search-field .search-clear:hover{background:var(--surface-subtle);color:var(--accent-hover)}
.check-option{position:relative;display:flex;align-items:center;gap:var(--space-2);
  min-height:var(--control-sm);padding:6px 8px;border-radius:var(--radius-sm);
  font:400 var(--type-table)/var(--leading-table) var(--sans);cursor:pointer;
  transition:background var(--motion-feedback)}
.check-option:hover{background:var(--surface-subtle)}
.check-option-input{appearance:none;width:16px;height:16px;margin:0;flex:none;
  border:1px solid var(--control-border);border-radius:var(--radius-sm);background:var(--surface);
  transition:background var(--motion-feedback),border-color var(--motion-feedback)}
.check-option:hover .check-option-input{border-color:var(--accent-hover)}
.check-option-input:checked{background:var(--accent);border-color:var(--accent)}
.check-option-input:focus-visible{outline:2px solid var(--focus);outline-offset:2px}
.check-option-mark{position:absolute;left:8px;top:50%;transform:translateY(-50%);
  display:flex;width:16px;height:16px;color:var(--on-accent);opacity:0;pointer-events:none}
.check-option-mark svg{width:16px;height:16px}
.check-option-input:checked+.check-option-mark{opacity:1}
.check-option:has(input:disabled){opacity:.45;cursor:not-allowed}

/* ---------------------------------------------------------------- 表格 */

.table{width:100%; border-collapse:collapse; font-size:var(--type-table); line-height:var(--leading-table)}
.table th{
  text-align:left; font-weight:500; color:var(--foreground-secondary);
  padding:8px 12px; border-bottom:1px solid var(--border);
  font-size:var(--type-caption); white-space:nowrap;
}
.table td{padding:12px; border-bottom:1px solid var(--divider); vertical-align:middle; font-weight:500}
.table-identity{display:inline-flex; align-items:center; gap:10px; white-space:nowrap}
.table-name{font-weight:600}
.table-full-name{color:var(--foreground-secondary); font-weight:400}
.table tbody tr{transition:background var(--motion-feedback)}
.table tbody tr:hover{background:var(--surface-subtle)}
.table .right{text-align:right; font-variant-numeric:tabular-nums lining-nums}
.table tfoot td{border-top:1px solid var(--border); border-bottom:0; font-weight:600}

/* Column roles define a stable grid; cell content does not move its tracks. */
/* Strategy's compact table sizes tracks by content and distributes spare space. */
@container (min-width:701px){
  .table-spaced{display:grid;grid-template-columns:repeat(var(--table-column-count,5),minmax(0,max-content));justify-content:space-between}
  .table-spaced>[role=row]{display:grid;grid-template-columns:subgrid;grid-column:1/-1}
  .table-spaced [role=cell],.table-spaced [role=columnheader]{min-width:0}
}
@container (min-width:701px){
  .table-grid{
    display:grid;
    grid-template-columns:var(--table-columns,minmax(0,2fr) repeat(4,minmax(0,1fr)));
    column-gap:0;
  }
  .table-grid>:is(thead,tbody),.table-grid>:is(thead,tbody)>tr{
    display:grid;grid-template-columns:subgrid;grid-column:1/-1;
  }
  .table-grid :is(th,td){min-width:0;align-content:center;border-bottom:0}
  .table-grid [colspan]{grid-column:1/-1}
  .table-grid thead tr{border-bottom:1px solid var(--border)}
  .table-grid tbody tr{border-bottom:1px solid var(--divider)}
}

/* Contribution legend and bar share a fixed 0–100% sequential color scale. */
.contribution-gain{--contribution-color:color-mix(in srgb,var(--chart-contribution-gain-soft),var(--chart-contribution-gain-deep) var(--contribution-share,0%))}
.contribution-loss{--contribution-color:color-mix(in srgb,var(--chart-contribution-loss-soft),var(--chart-contribution-loss-deep) var(--contribution-share,0%))}
.contribution-hidden{--contribution-color:var(--foreground-muted)}
.contribution-dot{flex:none; background:var(--contribution-color)}
.contribution-fill{background:var(--contribution-color)}
.contribution-list{display:grid; gap:16px}
.contribution-line{display:grid; grid-template-columns:minmax(0,1fr) auto 48px; align-items:baseline; gap:8px; margin-bottom:8px; font:500 var(--type-table)/var(--leading-table) var(--number-font); font-variant-numeric:tabular-nums}
.contribution-identity{display:inline-flex; align-items:center; gap:8px; font-family:var(--sans)}
.contribution-identity strong{font-weight:600}
.contribution-share{color:var(--foreground-secondary); text-align:right; font-size:var(--type-caption)}

/* 列表行（非表格的键值/持仓行） */
.rowline{
  display:flex; justify-content:space-between; align-items:center; gap:12px;
  padding:12px 6px; font-size:var(--type-table);
  border-radius:var(--radius-sm); transition:background var(--motion-feedback);
}
.rowline:hover{background:var(--surface-subtle)}
.rowline+.rowline{border-top:1px solid var(--divider)}
.rowline .num{font-weight:600; letter-spacing:-.2px}

/* ---------------------------------------------------------------- 头像/标识 */

.avatar{
  width:26px; height:26px; flex-shrink:0;
  border-radius:8px 8px 3px 3px;   /* 小拱形 */
  display:inline-flex; align-items:center; justify-content:center;
  font-size:10px; font-weight:700; color:#fff;
}
.avatar.asset-avatar{
  overflow:hidden;
  background:var(--asset-logo-background,var(--surface-group));
  color:var(--foreground-secondary);
  font:500 10px/1 var(--sans);
}
.asset-avatar img{display:block;width:100%;height:100%;object-fit:contain}
.asset-avatar.is-fallback{background:var(--surface-group)}
.asset-avatar img[hidden],.asset-avatar-fallback[hidden]{display:none}
.asset-avatar-fallback{border:0;background:none;border-radius:0}

/* ---------------------------------------------------------------- 迷你趋势条 */

.spark{display:flex; align-items:flex-end; gap:3px; height:32px}
.spark i{flex:1; background:var(--accent-soft); border-radius:2px 2px 0 0;
  transition:background var(--motion-panel), height var(--motion-panel) var(--ease-arch)}
.spark i.hot{background:var(--accent)}
.spark-host:hover .spark i{background:var(--petrol-300)}
.spark-host:hover .spark i.hot{background:var(--accent)}

/* ---------------------------------------------------------------- 宏观指标卡
   异动顶栏脉搏层与状态层共用。结构固定：标题左 + pill 右、主数值、副标、
   底通栏图。静态读数，不整卡点击，不展开。情绪只用面积走势，不用仪表。
   缺数保留空槽，不补示意序列。
   ------------------------------------------------------------------ */

.macro-board{
  display:flex; flex-direction:column; gap:var(--space-6);
  margin:0 0 var(--space-8); min-width:0;
}
.macro-layer{display:flex; flex-direction:column; gap:var(--space-3); min-width:0}
.macro-kicker{
  display:flex; align-items:center; gap:10px; margin:0;
  font-size:11px; font-weight:600; letter-spacing:2px;
  text-transform:uppercase; color:var(--accent);
}
.macro-kicker::after{content:''; flex:1; height:1px; background:var(--divider)}
.macro-grid{
  display:grid; grid-template-columns:repeat(3,minmax(0,1fr));
  gap:var(--space-4); align-items:stretch;
}
.macro-card{
  display:flex; flex-direction:column; gap:var(--space-2); min-width:0;
  margin:0; padding:var(--space-5) var(--space-5) var(--space-4);
  background:var(--surface); border:1px solid var(--border);
  border-radius:var(--radius-panel); color:var(--foreground);
}
.macro-head{
  display:flex; align-items:flex-start; justify-content:space-between;
  gap:var(--space-2); min-width:0;
}
.macro-title{
  margin:0; min-width:0; flex:1 1 auto;
  font:500 var(--type-caption)/var(--leading-caption) var(--sans);
  color:var(--foreground-muted); overflow-wrap:anywhere;
}
.macro-head .pill{
  flex:0 1 auto; max-width:46%; min-width:0;
  letter-spacing:0; font-variant-numeric:tabular-nums lining-nums;
}
.macro-pill-text{min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap}
.macro-card .metric{margin:0; white-space:nowrap}
.macro-sub{
  display:flex; flex-wrap:wrap; align-items:center; gap:4px;
  min-width:0; min-height:var(--leading-table); margin:0;
  font:500 var(--type-table)/var(--leading-table) var(--sans);
  color:var(--foreground-muted); font-variant-numeric:tabular-nums lining-nums;
}
.macro-long,.macro-delta-up{color:var(--positive)}
.macro-short,.macro-delta-down{color:var(--negative)}
.macro-chart{
  width:100%; height:48px; margin-top:auto; flex:0 0 48px; min-width:0;
}
.macro-chart svg{display:block; width:100%; height:100%; overflow:visible}
.macro-spark-line{
  fill:none; stroke:var(--accent); stroke-width:1.75;
  stroke-linecap:round; stroke-linejoin:round; vector-effect:non-scaling-stroke;
}
.macro-spark-point{
  fill:var(--surface); stroke:var(--accent); stroke-width:1.5;
  vector-effect:non-scaling-stroke;
}
/* Bars need more contrast than accent-soft (petrol-100 on light surfaces). */
.macro-bar{fill:color-mix(in srgb, var(--accent) 38%, var(--surface))}
.macro-bar.is-latest{fill:var(--accent)}
.macro-bar.is-up{fill:var(--positive)}
.macro-bar.is-down{fill:var(--negative)}
.macro-axis{stroke:var(--divider); stroke-width:1; vector-effect:non-scaling-stroke}
.macro-skeleton-metric{display:block; height:var(--leading-amount); width:42%}
.macro-skeleton-chart{display:block; width:100%; height:48px}
.macro-skeleton-pill{display:block; width:56px; height:18px; border-radius:var(--radius-pill); flex:0 0 auto}
.macro-note{
  display:flex; align-items:center; flex-wrap:wrap; gap:var(--space-3); margin:0;
  color:var(--foreground-secondary);
  font:400 var(--type-caption)/var(--leading-caption) var(--sans);
}
@media (max-width:720px){
  .macro-grid{grid-template-columns:1fr}
}
@container workspace (max-width:720px){
  .macro-grid{grid-template-columns:1fr}
}

/* ---------------------------------------------------------------- 浮层 */

.dialog{
  border:1px solid var(--border); border-radius:var(--radius-panel);
  background:var(--surface-overlay); padding:0; color:var(--foreground);
  box-shadow:var(--shadow-overlay); max-width:min(560px,calc(100vw - 48px));
}
.dialog::backdrop{background:var(--backdrop)}
.dialog-head{display:flex; align-items:center; justify-content:space-between;
  padding:var(--space-5) var(--space-6) 0}
.dialog-body{padding:var(--space-4) var(--space-6) var(--space-6)}
.dialog-foot{display:flex; justify-content:flex-end; gap:10px;
  padding:0 var(--space-6) var(--space-6)}

.tooltip{
  position:absolute; z-index:var(--z-overlay); pointer-events:none;
  /* Read-only overlays have no selected state; use the ordinary border. */
  background:var(--surface-overlay); border:1px solid var(--border);
  border-radius:var(--radius-md); padding:10px 12px;
  box-shadow:var(--shadow-overlay); font-size:var(--type-caption); min-width:132px;
}

.menu{
  background:var(--surface-overlay); border:1px solid var(--border);
  border-radius:var(--radius-lg); padding:6px; box-shadow:var(--shadow-overlay);
  min-width:200px;
}
.menu button{
  display:flex; align-items:center; gap:10px; width:100%;
  padding:9px 10px; border-radius:var(--radius-sm);
  font-size:var(--type-table); color:var(--foreground); text-align:left;
  transition:background var(--motion-feedback);
}
.menu button:hover{background:var(--surface-subtle)}
.menu .menu-sep{height:1px; background:var(--divider); margin:6px 4px}

/* ---------------------------------------------------------------- 状态件 */

.empty,.error-box{
  padding:var(--space-10) var(--space-6); text-align:center;
  color:var(--foreground-muted); font:400 var(--type-body)/var(--leading-body) var(--sans);
}
.empty strong{display:block; color:var(--foreground-secondary);
  font:600 var(--type-section)/var(--leading-section) var(--sans);margin-bottom:6px}
.error-box{color:var(--negative)}
.error-box strong{display:block;font:600 var(--type-section)/var(--leading-section) var(--sans);margin-bottom:6px}

/* 骨架加载 */
.skeleton{
  border-radius:var(--radius-sm);
  background:linear-gradient(90deg,var(--surface-group) 25%,var(--surface-subtle) 50%,var(--surface-group) 75%);
  background-size:200% 100%;
  animation:skeleton 1.2s linear infinite;
}
@keyframes skeleton{to{background-position:-200% 0}}
@media (prefers-reduced-motion:reduce){.skeleton{animation:none}}

/*  spinner */
.spinner{
  width:18px; height:18px; border-radius:50%;
  border:2px solid var(--accent-soft); border-top-color:var(--accent);
  animation:spin .8s linear infinite;
}
@keyframes spin{to{transform:rotate(360deg)}}
@media (prefers-reduced-motion:reduce){.spinner{animation-duration:2s}}

/* ---------------------------------------------------------------- 图表辅助 */

.legend{display:flex; gap:8px 16px; flex-wrap:wrap}
.legend button{display:inline-flex; align-items:center; gap:7px;
  border-radius:var(--radius-sm); padding:5px 8px;
  font-size:var(--type-caption); color:var(--foreground-secondary);
  transition:background var(--motion-feedback), opacity var(--motion-feedback)}
.legend button:hover{background:var(--surface-subtle)}
.legend button[aria-pressed=false]{opacity:.4; text-decoration:line-through}
.dot{width:9px; height:9px; border-radius:3px; display:inline-block; flex-shrink:0}

/* 占比条（总额=100%）；排名条（最大项=100%）同结构，由数据决定 */
.share-track{height:6px; border-radius:3px; background:var(--surface-group); overflow:hidden}
.share-track i{display:block; height:100%; border-radius:3px}

/* ---------------------------------------------------------------- 焦点 */

:focus-visible{outline:2px solid var(--focus); outline-offset:2px}

/* ================================================================ 实体组件
   同一实体在任何页面渲染同源：identity 槽位固定、主数值右对齐等宽、
   涨跌一律 tick+pill、时间戳必备。四种密度：chip / row / card / detail。
   用法：.ent + 密度（.ent-chip/.ent-row/.ent-card）+ 类型 data-ent="asset|account|position|flow"
   ========================================================================== */

.ent{display:inline-flex; align-items:center; gap:10px; min-width:0}
.ent .avatar{flex-shrink:0}
.ent-id{display:flex; align-items:baseline; gap:6px; min-width:0}
.ent-name{font-weight:500; white-space:nowrap; overflow:hidden; text-overflow:ellipsis}
.ent-code{color:var(--foreground-muted); font-weight:400; font-size:.86em; white-space:nowrap}
.ent-num{font-variant-numeric:tabular-nums lining-nums; font-weight:600; letter-spacing:-.2px; white-space:nowrap}
.ent-chg{font-size:var(--type-caption); white-space:nowrap}

/* chip —— 行内引用（Nulli 引用、研究报价、表格次级提及） */
.ent-chip{
  gap:7px; padding:4px 10px 4px 5px; border-radius:var(--radius-pill);
  background:var(--surface-subtle); border:1px solid transparent;
  font-size:var(--type-caption); line-height:18px;
  transition:border-color var(--motion-feedback), background var(--motion-feedback);
}
.ent-chip .avatar{width:18px; height:18px; border-radius:5px 5px 2px 2px; font-size:8px}
.ent-chip .ent-num{font-weight:600}
button.ent-chip:hover,a.ent-chip:hover{border-color:var(--petrol-300); background:var(--accent-soft)}

/* row —— 列表行（持仓表、账户列表、流水列表共用骨架） */
.ent-row{
  display:grid; grid-template-columns:minmax(0,1.6fr) repeat(var(--ent-cols,2),auto);
  align-items:center; gap:16px; width:100%;
  padding:12px 6px; font-size:var(--type-table);
  border-radius:var(--radius-sm); transition:background var(--motion-feedback);
}
.ent-row:hover{background:var(--surface-subtle)}
.ent-row+.ent-row{border-top:1px solid var(--divider)}
.ent-row .ent-num{text-align:right}

/* card —— 摘要卡（实体自己的重点呈现） */
.ent-card{
  display:block; background:var(--surface); border:1px solid var(--border);
  border-radius:var(--radius-panel); padding:var(--space-5);
  transition:border-color var(--motion), box-shadow var(--motion);
}
.ent-card .ent-head{display:flex; align-items:center; gap:10px; margin-bottom:12px}
.ent-card .ent-metric{font-size:var(--type-attribution); line-height:var(--leading-attribution);
  font-weight:600; letter-spacing:-.8px; font-variant-numeric:tabular-nums lining-nums}

/* detail —— 详情（dialog 内的完整属性集），复用 .rowline + dl 结构 */
.ent-detail .rowline{font-size:var(--type-body)}

/* 实体的时效戳：任何 card/detail 必须带；stale 时着 warning 色 */
.ent-time{font-size:var(--type-caption); color:var(--foreground-muted)}
.ent-time[data-stale=true]{color:var(--warning); font-weight:500}

/* ================================================================ 通知（toast）
   瞬时告知：一句话事实 + 至多一个动作。同时最多一条，队列排队。
   可核对的数据不进 toast（那是监控页的职责）。
   ========================================================================== */

.toast-host{
  position:fixed; right:24px; bottom:24px; z-index:var(--z-toast);
  display:grid; gap:10px; justify-items:end; pointer-events:none;
}
.toast{
  pointer-events:auto;
  display:flex; align-items:center; gap:12px;
  background:var(--surface-overlay); border:1px solid var(--border);
  border-radius:var(--radius-md); padding:11px 14px;
  box-shadow:var(--shadow-overlay); font-size:var(--type-table);
  animation:toast-in var(--motion-panel) var(--ease-arch);
}
.toast .dot{flex-shrink:0}
.toast-ok .dot{background:var(--positive)}
.toast-info .dot{background:var(--accent)}
.toast-error .dot{background:var(--negative)}
.toast .btn{margin:-4px -6px -4px 0}
@keyframes toast-in{from{opacity:0; transform:translateY(8px)} to{opacity:1; transform:none}}
@media (prefers-reduced-motion:reduce){.toast{animation:none}}

/* ================================================================ Nulli 组件语法
   ========================================================================== */

/* 证据卡：AI 消息内嵌的数据切片，由实体行构成，带来源与期间 */
.evidence{
  margin-top:10px; border:1px solid var(--border); border-radius:var(--radius-md);
  background:var(--surface); overflow:hidden;
}
.evidence .evidence-head{
  display:flex; justify-content:space-between; align-items:center; gap:10px;
  padding:8px 12px; background:var(--surface-subtle);
  font-size:var(--type-caption); color:var(--foreground-muted);
}
.evidence .ent-row{padding:10px 12px}
.evidence .ent-row:hover{background:var(--surface-subtle)}

/* 行动回执：Nulli 执行动作后的事实确认（状态点 + 事实 + 可撤销） */
.receipt{
  display:flex; align-items:center; gap:10px; margin-top:10px;
  padding:10px 12px; border-radius:var(--radius-md);
  background:var(--surface-subtle); font-size:var(--type-caption); color:var(--foreground-secondary);
}
.receipt .dot{background:var(--positive)}
.receipt .btn{margin-left:auto}

/* ⌘K 命令面板：与 Nulli 输入同一语法（输入即提问，/ 前缀为命令） */
.palette{
  background:var(--surface-overlay); border:1px solid var(--border);
  border-radius:var(--radius-lg); box-shadow:var(--shadow-overlay);
  overflow:hidden; max-width:520px;
}
.palette-input{
  display:flex; align-items:center; gap:10px; padding:13px 16px;
  border-bottom:1px solid var(--border); font-size:var(--type-body);
}
.palette-input .muted{flex-shrink:0}
.palette-list{padding:6px; display:grid}
.palette-list .ent-row{border-radius:var(--radius-sm); padding:9px 10px}
.palette-list .ent-row+.ent-row{border-top:0}
.palette-hint{
  display:flex; gap:14px; padding:8px 16px; border-top:1px solid var(--border);
  font-size:11px; color:var(--foreground-muted);
}
.palette-hint kbd{
  font-family:var(--code-font); font-size:10.5px; background:var(--surface-group);
  border:1px solid var(--border); border-radius:4px; padding:1px 5px;
}

/* Nulli 输入框（composer）：引用 chip 槽 + 自适应输入区 + 发送/停止同键位
   静止边用 --divider，弱于对话壳的 --border；聚焦仍回到 accent。
   空闲下限是 1 行：calc(var(--leading-body) + 8px)。+8px 是字段已有的上下各 4px 内边距。
   上限仍是 6 行。首页 .wb-home-composer 与聊天 .wb-composer 共用这一下限。 */
.composer{
  background:var(--surface); border:1px solid var(--divider);
  border-radius:var(--radius-panel); padding:10px 12px;
  display:grid; gap:8px;
  transition:border-color var(--motion), box-shadow var(--motion);
}
.composer:focus-within{border-color:var(--accent); box-shadow:0 0 0 3px var(--accent-soft)}
.composer-chips{display:flex; gap:6px; flex-wrap:wrap}
.composer-chips:empty{display:none}
.composer-main{display:flex; align-items:flex-end; gap:10px}
.composer-input{
  flex:1; border:0; background:transparent; resize:none; outline:none; padding:4px 2px;
  font:inherit; color:var(--foreground); line-height:var(--leading-body);
  min-height:calc(var(--leading-body) + 8px); max-height:calc(var(--leading-body)*6 + 8px);
}
.composer-input::placeholder{color:var(--foreground-muted)}
.composer .btn{flex-shrink:0}
