/* ai-chat-studio-dna.css - brand DNA and the ability rows: chart, dimension rows, entry lists.
   One of the Studio stylesheet's 7 parts (part 6). They are contiguous slices of what used to be one
   file, and ai-chat-styles.liquid loads them in this order - the cascade depends on it, so a new part goes at the
   end of its own section, never between two others (gate: scripts/ui/studio-css-parts.test.mjs). */

/* ── 品牌 DNA:三层(六维图 → 一维 → 一条)。壳/卡/五格全借 agent 能力卡那一套 class,
   这里只补"行是入口不是开关"和"条目收起只给一行"这两件它没有的事。 ── */
/* 维度行:右端一个 chevron 说明"点进去" —— 与 agent 那边的开关行区分开(品牌不是开关) */
/* 行本身不是点击目标(owner 2026-09-06 "空白处不让它开"):能点开的只有名字和箭头,手型也只落在那两处 ——
   整行手型会让人以为哪里都能点,点了空白没反应就像坏了 */
.studio-dna-dim-row { cursor: default; text-align: left; width: 100%; border: 0; background: none; font-family: inherit; }
.studio-dna-dim-row:not(.is-static) .settings-row-label,
.studio-dna-dim-row:not(.is-static) .settings-memory-entry-chevron { cursor: pointer; }
/* the training hub's Market sense entry row: the same two targets, the same hand */
.studio-market-entry .settings-row-label, .studio-market-entry .settings-memory-entry-chevron { cursor: pointer; }
.studio-dna-dim-row.is-empty .settings-row-label { color: var(--color-text-secondary); }
/* ★行 = 四列定位:标题 | 五格 | 数字 | 箭头。后三列【定宽】,所以五格在自己那一列里左对齐,
   每行的格子左缘落在同一个 x —— 数字是 1 位还是 3 位、箭头在不在,都顶不动它。
   (基准行 agent 是"格子贴右缘"的两列布局,那边格子后面没有东西;这里多了箭头,照抄就会被顶歪。)
   五格总宽 = 5×14 + 4×3(gap) = 82px。 */
.settings-row.studio-dna-dim-row { display: grid; grid-template-columns: minmax(0, 1fr) 82px 32px 24px 18px; align-items: center; gap: 8px; }
/* 只读品牌页:纯展示行 —— 没有箭头列,也不长得像能点(cursor 跟着诚实) */
.studio-dna-dim-row.is-static { cursor: default; }
.settings-row.studio-dna-dim-row.is-static { grid-template-columns: minmax(0, 1fr) 82px 32px; }
/* 只读行混在能开的行里(agent 训练页的市场嗅觉)时,列要跟着这张【列表】走:五格和数字的位置是列表的属性,
   不是行自己的 —— 否则那一行的五格和数字跑到最右边,和上下行对不齐(owner 2026-09-05 实拍)。
   整张都只读的品牌卡页不受影响:它没有能开的行,仍是三列到边。 */
.tp-object-dna:has(.studio-dna-dim-row:not(.is-static)) .settings-row.studio-dna-dim-row.is-static { grid-template-columns: minmax(0, 1fr) 82px 32px 24px 18px; }
/* One rule for the name on both pages (the roster editor's flex row and the training page's grid row): it ellipsises,
   never wraps - so a badge after a long name (Italian, phone width) cannot make the row taller or move the meter. */
.studio-ability-bar-row .settings-row-label,
.studio-dna-dim-row .settings-row-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* 名字格:名字 + 问号并排,占的还是网格的第一列(多一个孩子会挤走数字和箭头) */
.studio-dim-name { display: flex; align-items: center; gap: 4px; min-width: 0; }
.studio-dim-name > .duo-beta { margin-left: 0; }   /* the name cell's own 4px gap spaces the badge; the badge's default 6px is for prose */
.studio-dim-name .settings-row-label { min-width: 0; }
/* 问号淡显(全站唯一那个"压下去"的值),按上去才实 —— 它是解释,不是这一行的动作 */
.studio-dim-help {
  flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center;
  width: 24px; height: 24px; padding: 0; border: 0; background: none; cursor: pointer;
  color: var(--color-text); opacity: var(--dim); border-radius: 9px;
}
/* the question mark keeps its own 15px drawing (owner 2026-09-16: back to how it was) */
.studio-dim-help svg { width: 15px; height: 15px; display: block; }
html:not([data-input="touch"]) .studio-dim-help:hover { opacity: 1; }
.studio-dim-help:active { opacity: 1; }
/* 说明浮层照全站菜单面:panel 底 + 菜单阴影/圆角,无外边线 */
.studio-dim-help-pop {
  position: fixed; z-index: var(--z-float); max-width: 260px; padding: 10px 12px;
  background: var(--color-bg-panel); border-radius: var(--menu-radius); box-shadow: var(--menu-shadow);
  font-size: 13px; line-height: 1.5; color: var(--color-text);
}
.studio-dim-help-pop[hidden] { display: none; }
.studio-dna-dim-row .studio-ability-cells { margin-left: 0; padding-right: 0; justify-content: flex-start; }
.studio-dna-dim-row .studio-ability-score { text-align: right; }
.studio-dna-dim-row .studio-ability-score { flex: none; }
.studio-dna-dim-row .settings-memory-entry-chevron,
.studio-market-entry .settings-memory-entry-chevron { display: inline-flex; justify-content: flex-end; margin-left: 0; opacity: var(--dim); }
.studio-dna-dim-row .settings-memory-entry-chevron svg,
.studio-market-entry .settings-memory-entry-chevron svg { width: var(--icon-size); height: var(--icon-size); }
/* 条目行:收起时【一行】,点开才展开正文与出处 —— 96 条摊开就是一面墙 */
.studio-dna-row { display: block; cursor: pointer; }
.studio-dna-text { font-size: 13px; line-height: 1.5; color: var(--color-text); outline: none; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.studio-dna-row.is-open .studio-dna-text { white-space: normal; }
.studio-dna-row.is-soft .studio-dna-text { color: var(--color-text-secondary); }
.studio-dna-foot { display: none; align-items: center; gap: 8px; margin-top: 6px; }
.studio-dna-row.is-open .studio-dna-foot { display: flex; }
.studio-dna-meta { font-size: 11px; color: var(--color-text-secondary); }
.studio-dna-edit { width: 24px; height: 24px; padding: 0; border: 0; background: none; color: var(--color-text-secondary); cursor: pointer; }
.studio-dna-edit svg { width: var(--icon-size-sm); height: var(--icon-size-sm); }
.studio-dna-del, .studio-dna-quickadd { margin-left: auto; width: 24px; height: 24px; padding: 0; border: 0; background: none; color: var(--color-text-secondary); cursor: pointer; display: flex; align-items: center; justify-content: center; }
.studio-dna-del svg { width: var(--icon-size-sm); height: var(--icon-size-sm); }
/* + 照 > 的配方画(owner 2026-09-02"颜色和粗细"):桌面亮色 chevron 就是继承色×--dim;
   暗色它有专门一条 —— + 并进那条真源(settings-panel 的 .dark-mode .settings-memory-entry-chevron),
   这里不写第二份色值。线宽 2(icon-plus 自带 1.8,CSS 提到 2 才和 chevron 同粗);hover = 透明度回满。 */
.studio-dna-quickadd { margin-left: 0; color: inherit; opacity: var(--dim); }
.studio-dna-quickadd svg { width: var(--icon-size); height: var(--icon-size); }
html:not([data-input="touch"]) .studio-dna-quickadd:hover, .studio-dna-quickadd:active { opacity: 1; }
.studio-dna-row.is-draft { opacity: 0.6; }
.studio-dna-add { cursor: pointer; border: 0; background: none; font-family: inherit; text-align: left; width: 100%; font-size: 12px; color: var(--color-text-secondary); }

/* Training 侧栏入口 = 主聊天 Images/Files 那条入口【逐字照抄】(同 class + 同内联尺寸,见 duohand-app-ui)。
   那两条是 <a>,这条是 <button>,所以这里只补 button 自带的那几样(边框/底/字体/对齐),几何一个数都不自定。 */
.studio-training-entry { width: 100%; border: 0; background: none; font-family: inherit; font-size: inherit; text-align: left; cursor: pointer; }

/* 训练页里的左右切换:壳照 chat-header-btn(全站那颗 36 圆钮),这里只定这一条的排布。
   位置在标题下方、居中 —— 它换的是【内容】,不是导航层级,所以不进头部那条钉死头。 */
/* 切换器【与关闭钮对称】(owner 2026-08-30):手机上它顶掉钉死头左格那 36px 占位(那本来就是留给
   返回键的位置);桌面没有那条头,× 是绝对定位在右上角 16/16,所以镜像到左上角同一个坐标。 */
.studio-training-nav { display: flex; align-items: center; gap: 4px; }
.studio-training-nav.is-corner { position: absolute; top: 16px; left: 16px; z-index: 1; }
/* 标题那一行装着切换器时:横排,标题跟在箭头后面 */
.project-settings-title.has-nav { display: flex; align-items: center; gap: 8px; }
/* 换对象 = 横向滑进来。只动 transform/opacity(不碰 clip-path/height 那类"揭开"写法) */
@keyframes duoStepInNext { from { transform: translateX(28px); opacity: 0; } to { transform: none; opacity: 1; } }
@keyframes duoStepInPrev { from { transform: translateX(-28px); opacity: 0; } to { transform: none; opacity: 1; } }
/* ★动画【绝不落在 sheet 那个元素上】:手机上 .duo-sheet 的基态是 translateY(100%)(屏幕外),
   "开着"这个状态是靠 .duo-sheet-open 那条动画的 forwards 撑住的。同特异性再后加载 = 抢走它的
   animation 槽,我这条放完(没有 forwards)元素就落回屏幕外 —— 面板消失、遮罩还在 = 卡死。
   所以只作用于滚动体(.duo-sheet-body),桌面没有 sheet 化时才作用于内容盒本身。 */
.duo-sheet-body.is-step-next { animation: duoStepInNext 0.22s cubic-bezier(0.2, 0, 0, 1); }
.duo-sheet-body.is-step-prev { animation: duoStepInPrev 0.22s cubic-bezier(0.2, 0, 0, 1); }
.project-modal-content:not(.duo-sheet).is-step-next { animation: duoStepInNext 0.22s cubic-bezier(0.2, 0, 0, 1); }
.project-modal-content:not(.duo-sheet).is-step-prev { animation: duoStepInPrev 0.22s cubic-bezier(0.2, 0, 0, 1); }
/* 换对象时不放开窗的缩放:用户眼里没有"新开一扇窗"这回事。手机 sheet 不在此列 —— 那段动画撑着它"开着"。 */
.project-modal-content:not(.duo-sheet).is-step-open { animation: none; }
/* 编辑面/列表那块内容自己滑,标题行与按钮行留在原地 */
.studio-editor-cols.is-step-next, .studio-training-grid.is-step-next { animation: duoStepInNext 0.22s cubic-bezier(0.2, 0, 0, 1); }
.studio-editor-cols.is-step-prev, .studio-training-grid.is-step-prev { animation: duoStepInPrev 0.22s cubic-bezier(0.2, 0, 0, 1); }
.studio-training-nav-btn:disabled { opacity: var(--dim); cursor: default; }
/* line-height: 1 = 数字与箭头同轴的关键:默认行盒在数字下面留着降部空间,盒子对中后字形偏高,
   两边的箭头(几何居中)就读成"下沉"。收紧行盒再交给 flex 对中 —— .project-modal-btn 同一个解法。 */
.studio-training-nav-pos { font-size: 12px; line-height: 1; color: var(--color-text-secondary); font-variant-numeric: tabular-nums; }
/* 训练中心那格卡片:与侧栏品牌格同一套卡,只是这里是弹窗内容,给它自己的外边距 */
/* 训练页借的是项目设置那个壳(480px,为一列表单行定的)。这里装的是两张方卡,480 里各只剩 ~194px。
   宽度只给训练弹窗,别把所有设置弹窗一起撑宽;数值落在设置 480 与作品集 900 之间。 */

/* 品牌页 / agent 页是【一整页】,不是快捷弹窗:桌面分左右两列 —— 左边"他是谁"(头像·名字·颜色/职务),
   右边"他会什么"(六边形那块)。手机保持竖排:那点宽度分两列只会把六边形压成指甲盖。
   壳同样是 480px 的设置壳,两列必须先给宽度,否则每列都比原来更窄。 */
.studio-editor-cols { display: grid; grid-template-columns: 1fr; }
@media (min-width: 769px) and (min-height: 501px) {
  .studio-brand-editor .project-settings-modal-content,
  .studio-agent-editor .project-settings-modal-content { max-width: min(860px, 92vw); }
  .studio-editor-cols { grid-template-columns: 1fr 1fr; column-gap: 50px; align-items: start; }
}
.studio-training-grid { margin: 0 16px 12px; }

/* (旧"岔路口两张大卡"的 fork 左右布局已删 —— 同名类的布局唯一定义在下面训练区那份【上下平分】;
   两份并存时旧 columns + 新 rows 叠成 2×2,页面就是那个"还是左右分区"。) */
/* 卡 = 【方框】+ 【框外的名字】:底色/圆角归那个方框,卡本身不再是灰块,名字因此落在框外 */
.studio-training-fork .studio-brand-card { width: auto; padding: 0; gap: 8px; background: none; }
/* 卡面 = 里面的东西:九宫格摆真实 logo / 头像。格子里的图标交出自己的固定尺寸,按格宽走 —— 它们
   在侧栏是 48px 的方块,这里是卡的 1/3 宽,同一颗图标两种尺码,不是两种画法。 */
/* 九宫格那个方框:正方 + 圆角(形状随全站开关 = 超椭圆)。三行固定,所以不满九个时下面留空,
   框仍是方的 —— 框的尺寸不该跟着"这次有几个"变,否则两张卡一高一矮。 */
.studio-training-mosaic {
  /* 比例量自 owner 的手机文件夹截图:图标 20% / 间距 11% / 内距 9%(3x20+2x11+2x9=100)。
     间距按内容盒算,内距吃掉 18%,所以写 11/82 = 13.41%,余下由 1fr 三等分回到 20%。 */
  display: grid; grid-template-columns: repeat(3, 1fr); grid-auto-rows: min-content;
  gap: 13.41%; place-content: start;
  width: 100%; aspect-ratio: 1; padding: 9%; box-sizing: border-box;
  background: var(--color-bg-card); border-radius: calc(24px * var(--corner-k));
}
/* 格子里那颗自己是正方的:行高由它给,不是反过来 —— 高度写 100% 的话行高与内容互相等对方,九宫格就塌了 */
.studio-training-mosaic > * { width: 100%; height: auto; aspect-ratio: 1; }
/* 格子里的图标按格走(高度也交给格子:三行等高,不满九个也不会把行拉扁)。
   ★圆角用【百分比】:--tile-radius 是按 48px 那档算出来的定值,搬到别的尺寸上比例就散了
   (格子比 48 大 → 显得方,小 → 显得圆)。22.4% = 手机 app 图标那个比例,跟着格子走。 */
.studio-training-mosaic .studio-brand-icon { border-radius: var(--tile-radius-pct); }
.studio-training-mosaic .studio-brand-icon,
.studio-training-mosaic .studio-agent-icon { width: 100%; height: 100%; font-size: 11px; }
.studio-training-mosaic .studio-brand-icon img,
.studio-training-mosaic .studio-agent-icon img { width: 100%; height: 100%; object-fit: cover; }
/* 作品集总览的系列瓦片 = 同一个九宫格,格子里放该系列的文件缩略图(同一颗 buildThumb)。
   总览网格把 .studio-portfolio-thumb 绝对定位铺满瓦片,格子里那颗要回到格流里按格走;
   瓦片本身(既是 thumb 又是 mosaic)也走 22.4%:外框和格子一个比例,
   --tile-radius 那 7px 在这个尺寸上只有一成,看着是方的(owner 2026-09-02:要更圆)。 */
.studio-training-mosaic .studio-portfolio-thumb { position: static; width: 100%; height: auto; aspect-ratio: 1; border-radius: var(--tile-radius-pct); }
.studio-portfolio-thumb.studio-training-mosaic { border-radius: var(--tile-radius-pct); }
.studio-training-fork .studio-brand-name { font-size: 14px; max-width: none; }
/* Brand / Agents pages inside Training: the sidebar grid classes come along (one card builder, one grid rule), but the
   sidebar's 48px is sized for a 240px rail - in a 720px modal it reads as a row of stamps (owner 2026-09-02: too small).
   Same object, the 80px tier the brand editor preview already uses (icon 80, radius by the family formula, 24px initials);
   names take the 13px tier and the whole cell, columns and gaps as the Portfolio overview grid. */
.studio-training-fork .studio-brand-grid,
.studio-training-fork .studio-agent-grid { grid-template-columns: repeat(auto-fill, minmax(80px, 1fr)); gap: 18px 14px; padding: 8px 0 0; }
.studio-training-fork .studio-brand-grid .studio-brand-icon { width: 80px; height: 80px; border-radius: var(--be-radius); font-size: 24px; }
.studio-training-fork .studio-agent-grid .studio-agent-icon { width: 80px; height: 80px; font-size: 24px; }
.studio-training-fork .studio-brand-grid .studio-brand-name,
.studio-training-fork .studio-agent-grid .studio-agent-name { font-size: 13px; max-width: 100%; }
/* 空场起跑卡(方案A):按钮维 + 无记录时聊天区中央 = 维度名 + 开跑令 + Start;有记录即退场。
   卡在场时 feed 转竖 flex 让 margin:auto 垂直居中 —— 卡退场后回到消息流的原布局 */
.studio-view-page.is-training .tc-feed:has(.tc-start-card) { display: flex; flex-direction: column; }
.tc-start-card { margin: auto; display: flex; flex-direction: column; align-items: center; gap: 10px; text-align: center; padding: 24px; }
.tc-start-card-title { font-size: 16px; font-weight: 500; color: var(--color-text); }
.tc-start-card-desc { font-size: 13px; color: var(--color-text-secondary); max-width: 360px; }
/* 开跑胶囊住在发送键【自己那个锚点】(right/bottom 7px、高 34 —— 全抄 .chat-button 那份),两颗谁露面谁占;
   露面时输入框右内距让到胶囊宽度以外,字不会钻到它底下。只作用于训练那一份 composer 实例。 */
.training-composer .tc-start {
  position: absolute; right: 7px; bottom: 7px; height: 34px;
  padding: 0 14px; z-index: 1;
}
.training-composer .composer-row.has-start-pill .chat-input { padding-right: calc(var(--tc-start-w, 96px) + 14px); }
/* 提示语是行内那个【绝对定位的元素】,不是输入框自带的 placeholder —— 它本来没有右边界,
   于是长句直接滑到按钮底下(owner 2026-09-04 手机实拍)。给它同一条留位。 */
.training-composer .composer-row.has-start-pill .typing-placeholder { right: calc(var(--tc-start-w, 96px) + 14px); }
/* 训练台是工作日志:正文不穿气泡的壳,行距按日志收紧(owner 2026-09-03)。宽度不靠气泡的 85% 卡 ——
   窗口不被撑开是每个产生点自己的责任(宽表格自己滚、窄了跟着面板收)。 */
/* 训练台不是聊天,是一份【工作记录】(owner 2026-09-04,照 Claude Code 的排版):一列到底全部左对齐、
   全场一个字号、层级只靠颜色不靠缩字。字号收在 --tc-text 一处。 */
.studio-view-page .tc-feed { --tc-text: 14px; --tc-line: 1.5; --tc-gap: 10px; --tc-para: 8px; }
/* 宽屏一行更长、每块行数更少,同一个间距读起来就更空(owner 2026-09-04:手机刚好,电脑还大)。
   两个数管整页:块与块 --tc-gap、段与段 --tc-para。 */
@media (min-width: 769px) and (min-height: 501px) { .studio-view-page .tc-feed { --tc-gap: 6px; --tc-para: 6px; } }
.studio-view-page .tc-feed .msg { margin: 0; align-items: flex-start; padding-bottom: 0; }
/* 主聊天给每行【预留】一条 footer 高度带(--footer-btn-box + 6 = 38px),好让复制/重生成那排按钮
   绝对定位落进去、挂载时行高不跳。训练台一个 footer 都不画(实测 0 处),那 38px 就是每一行底下
   一块永远不会被填的空 —— 量到的 81px 运行块里,内容只有 43px(owner 2026-09-04:"空隙太大")。 */
.studio-view-page .tc-feed .msg .bubble { margin: 0; font-size: var(--tc-text); line-height: var(--tc-line); }
.studio-view-page .tc-feed .msg.user { margin: var(--tc-gap) 0 0; width: 100%; }
/* 你说的那句 = 一行带标记的字,不是气泡:去掉底色/边框/内距,标记顶格,与回复同宽同大小 */
.studio-view-page .tc-feed .msg.user .bubble { background: none; border: 0; padding: 0; max-width: 100%; width: 100%; color: var(--color-text-secondary); }
.studio-view-page .tc-feed .msg.user .bubble > .text { position: relative; padding-left: 16px; }
.studio-view-page .tc-feed .msg.user .bubble > .text:empty { display: none; }
.studio-view-page .tc-feed .msg.user .bubble > .text::before { content: '\203A'; position: absolute; left: 0; top: 0; opacity: 0.7; }
.studio-view-page .tc-feed .msg.assistant .bubble { padding: 0; border: 0; background: none; max-width: 100%; border-radius: 0; }
.studio-view-page .tc-feed .tc-run .agent-steps { margin: 0; max-width: 100%; }
.studio-view-page.tc-hide-tokens .agent-light-foot-tok { display: none; }
.studio-view-page.tc-hide-tokens .agent-light-foot-tok { display: none; }
/* Log presentation of the same bar (owner 2026-09-03, "Claude Code, not a chat tool"): the process stays in the
   stream - every tool is a line as it lands (dot + rail), only the returned bulk is folded away (the rows carry
   the summary). The one-line summary head is for running state only; once the rows are there it goes. A bar that
   only thought keeps its "Thought for Ns" head (it has no rows). No chevron, nothing to click. */
.studio-view-page .tc-feed .tc-run .agent-light-panel { display: block; position: relative; padding-left: 14px; margin: 0 0 0 2px; }
.studio-view-page .tc-feed .tc-run .agent-seg:not(.running):has(.agent-light-row) > .agent-seg-head { display: none; }
.studio-view-page .tc-feed .tc-run .agent-light-chevron { display: none; }
.studio-view-page .tc-feed .tc-run .agent-seg-head.agent-light-toggle { cursor: default; pointer-events: none; }
.studio-view-page .tc-feed .tc-run .agent-light-row { position: relative; margin: 0 0 4px; }
/* dot 5px at -13 and a 1px rail at -11 share the centre (-10.5): integer boxes, no half-pixel blur.
   垂直位置跟着行高走:14 x 1.5 = 21px 行,(21-5)/2 = 8 —— 整数,圆点才真居中(半像素=糊) */
.studio-view-page .tc-feed .tc-run .agent-light-row::before {
  content: ""; position: absolute; left: -13px; top: 8px; width: 5px; height: 5px;
  border-radius: 50%; corner-shape: round; background: var(--color-text-secondary);
}
/* the rail joins a dot to the next row's dot (row height - dot + 4px gap + next dot's top); none after the last row */
.studio-view-page .tc-feed .tc-run .agent-light-row:has(+ .agent-light-row)::after {
  content: ""; position: absolute; left: -11px; top: 13px; width: 1px; height: calc(100% - 1px); background: var(--color-border);
}
.studio-view-page .tc-feed .tc-run .agent-seg { gap: 2px; }
.studio-view-page .tc-feed .tc-run .agent-light-row:last-child { margin-bottom: 0; }
.studio-view-page .tc-feed .tc-run .agent-light-foot { margin: 2px 0 0; }
/* !important only because the chat markdown baseline (ai-chat-bubbles.css) sets these margins !important - same
   weight is the only way this tighter rhythm can win; everything else (indent, tables, hr, code) is that baseline's */
/* 主动作按大尺寸真源(--btn-block-*,设置弹窗手机主按钮那档;pf-wide 同款吃法):
   起跑卡的 Start 与欢迎页 New Collection 同档(owner 2026-09-03) */
.studio-welcome #studioWelcomeNewBtn { padding: var(--btn-block-pad-y) 14px; font-size: var(--btn-block-font); }

