/* ai-chat-studio-markup.css - the markup round: boxing a sketch and saying what to change.
   One of the Studio stylesheet's 7 parts (part 2). 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). */

/* ── Markup round: box what should change, say what to change ── */
.studio-markup-modal .project-settings-modal-content { max-width: 720px; }   /* image-first dialog: the 480px settings width is for forms (owner 2026-08-27: let the picture be wide) */
/* The stage is a full-width centring rail; the figure inside shrink-wraps the picture, so the frame IS the
   picture (a tall image drags no dead side margins) and the drawing layer anchors to the figure = the image
   box exactly. */
.studio-markup-stage { display: flex; justify-content: center; line-height: 0; }
.studio-markup-figure { position: relative; display: block; width: fit-content; max-width: 100%; line-height: 0; }
.studio-markup-img { display: block; box-sizing: border-box; max-width: 100%; max-height: 64vh; width: auto; object-fit: contain; }   /* square and frameless: a working image never rounds or outlines itself (owner 2026-08-27) */
/* The drawing surface must sit exactly over the picture, so boxes stored as 0..1 land where they were drawn. */
.studio-markup-layer { position: absolute; inset: 0; cursor: crosshair; touch-action: pan-y pinch-zoom; user-select: none; -webkit-user-select: none; -webkit-touch-callout: none; }
.studio-markup-box { position: absolute; border: 2px solid var(--color-text); border-radius: calc(8px * var(--corner-k)); box-shadow: 0 0 0 1px var(--color-bg-page); }   /* 8 = the site's small-control radius (menus/buttons); was 4 (owner 2026-08-28: larger) */
.studio-markup-box.is-ghost { border-style: dashed; }
.studio-markup-box.is-ellipse { border-radius: 50%; corner-shape: round; }
/* Lasso: the bbox div is only a frame; the outline is an SVG polygon scaled to it. non-scaling-stroke keeps 2px under the non-uniform viewBox. */
.studio-markup-box.is-lasso { border-color: transparent; box-shadow: none; }
.studio-markup-box.is-lasso svg { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; display: block; }
.studio-markup-box.is-lasso polygon { fill: none; vector-effect: non-scaling-stroke; stroke-linejoin: round; }
.studio-markup-box.is-lasso polygon.halo { stroke: var(--color-bg-page); stroke-width: 4; }
.studio-markup-box.is-lasso polygon.line { stroke: var(--color-text); stroke-width: 2; }
.studio-markup-box.is-ghost polygon.line { stroke-dasharray: 4 3; }
/* Arrow: same ink and halo as the lasso; the shaft is an SVG line in the bbox's space, the head a fixed-size triangle
   seated on the tip and turned by the engine (its angle only exists in pixels, so CSS cannot know it). */
.studio-markup-box.is-arrow { border-color: transparent; box-shadow: none; overflow: visible; }
.studio-markup-box.is-arrow svg { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; display: block; }
.studio-markup-box.is-arrow line { fill: none; vector-effect: non-scaling-stroke; stroke-linecap: round; }
.studio-markup-box.is-arrow line.halo { stroke: var(--color-bg-page); stroke-width: 4; }
.studio-markup-box.is-arrow line.line { stroke: var(--color-text); stroke-width: 2; }
.studio-markup-box.is-ghost line.line { stroke-dasharray: 4 3; }
.studio-markup-head { position: absolute; width: 0; height: 0; border-left: 12px solid var(--color-text); border-top: 6px solid transparent; border-bottom: 6px solid transparent; transform-origin: 100% 50%; filter: drop-shadow(0 0 1px var(--color-bg-page)); pointer-events: none; }
/* Tool group (rect / ellipse / lasso): the buttons ARE the file previewer's tool buttons (.duo-preview-tool: 32 box,
   18 icon, 0.6 colour, is-active) and sit next to the close exactly as the previewer's do (.duo-viewer-actions gap 4).
   Close: 28 box at top/right 16 -> centre line y 30; a 32 box centres on it at top 14; right = 16 + 28 + 4 = 48. */
.studio-markup-tools { display: flex; gap: 4px; }   /* the shared group; where it sits is the host's business */
.studio-markup-modal .studio-markup-tools { justify-content: center; margin: 10px 0 0; }   /* picture -> tools = tools -> rows = 10, one spacing (owner 2026-08-29: it was 8 above / 12 below) */
/* a drawn box is a handle: press and carry it (owner 2026-08-28); the browser must not pan while it is held */
.studio-markup-box:not(.is-ghost) { cursor: move; touch-action: none; }
/* Number circles = the corner-badge source (.studio-pf-stack-count) worn elsewhere: these rules only
   reposition (box corner / static in the note row) and, for the tag, resize via --badge-size - the
   proportions (font/padding/ring vs size) live in the source alone. */
.studio-pf-stack-count.studio-markup-boxnum { top: -9px; left: -9px; right: auto; }
/* Note rows are settings-row rows (padding/separators/dark from the source) but stay OUT of the grey
   card - the add row below is the one card in this area (owner 2026-08-27: the carded version read wrong,
   and on the phone the extra card picked up the cards-mode side margin and mis-aligned). */
/* Note pills stack at the pill's own 8px rhythm; the settings-row padding (16 top + 16 bottom = 32 between
   pills) belongs to labelled option rows, not to this list (owner 2026-08-28: rows sat far too far apart). */
.studio-markup-notes { margin: 10px 0 20px; display: flex; flex-direction: column; gap: 10px; }   /* 20 below = the settings modal's own section spacing before the action row (the add card used to hold that room) */   /* rows = composer pills (shared with the images page); 10 = the composer's own spacing */
/* Composer-styled markup rows (both hosts): the number / picture icon sits in .plus-icon (its colour lives there) and needs its size
   back (the base .plus-icon collapses text to 0 for the icon swap); the x is the same circle, seated on the send arrow's inset -
   34 at bottom 7 in the 48 row = 7 above, 7 below (measured). */
.plus-button.is-region .plus-icon { font-size: 14px; font-weight: 500; transform: none; display: flex; align-items: center; justify-content: center; width: 18px; height: 18px; line-height: normal; }   /* the base .plus-icon is a 20 block nudged for the "+" glyph; a number or an 18 icon must sit dead centre */
.plus-button.is-region .plus-icon svg { width: 18px; height: 18px; display: block; }
.composer-row .plus-button.studio-markup-del-right { position: absolute; width: 34px; height: 34px; left: auto; right: 7px; bottom: 7px; }
.composer-row .plus-button.studio-markup-num { cursor: default; }
/* The badge is a name tag, not a button (owner 2026-08-29): it wears the circle's rest look under the pointer too - hover feedback on a non-action reads as a broken button. Rest values = .plus-button (#fff) / .dark-mode .plus-button (#2a2a2a). */
html:not([data-input="touch"]) .composer-row .plus-button.studio-markup-num:hover { background: #fff; }
html:not([data-input="touch"]) .dark-mode .composer-row .plus-button.studio-markup-num:hover { background: #2a2a2a; }
.studio-markup-notes:empty { display: none; }
.studio-markup-note.settings-row { gap: 8px; justify-content: flex-start; border-bottom: none; padding: 0; }
.studio-pf-stack-count.studio-markup-tag { position: static; flex: 0 0 auto; --badge-size: 34px; font-size: 14px;
  /* The badge's ring is a page-coloured halo - visible over the picture, invisible on the page. Here the same
     dark ring is made readable by a 1px line in the disc's own colour outside it (owner 2026-08-28: the row
     circle must wear the same black ring as the one on the picture). 2px matches the picture badge's halo. */
  box-shadow: 0 0 0 2px var(--color-bg-page), 0 0 0 3px var(--color-text); }   /* circle = the main-chat plus circle; the digit takes 14px (one step under the icon size, owner 2026-08-28) - the knob's 0.75 text ratio is for tiny corner badges and reads huge at 34 (owner 2026-08-28) */
/* Dark mode: --color-text is white, so the readability line outside the ring turned into a white halo (owner 2026-08-28: nothing outside the black). The page-coloured 2px is already the black ring there. */
.dark-mode .studio-pf-stack-count.studio-markup-tag { box-shadow: 0 0 0 2px var(--color-bg-page); }
/* One pill per note, main-composer style: the number circle sits inside the frame on the left, the close
   circle (the composer's .plus-button source, repositioned static) inside on the right. The pill owns the
   frame; the input inside is naked. */
.studio-markup-pill {
  flex: 1 1 auto; min-width: 0; display: flex; align-items: center; gap: 8px;
  border: 1px solid var(--color-border); border-radius: 999px; corner-shape: round; padding: 6px;   /* 1px border + 6 = the 7px gap the main pill gives its circle, on EVERY side. Integer on purpose: 6.5 laid out at 7.5 and painted 8 above / 7 below (measured, dpr 1-3) - the pixel grid does not do halves */
}
.studio-markup-text { flex: 1 1 auto; min-width: 0; border: none; outline: none; background: transparent; font-family: inherit; font-size: 14px; color: var(--color-text); padding: 0; height: 28px; }
/* one token, both themes (theme.css paints placeholders with the light-scope --text-color, unreadable in dark) */
.studio-markup-text::placeholder { color: var(--color-text-secondary); opacity: 1; }
/* Circle look/border/dark all from the .plus-button source; here only: back into flow. 34px = the SAME
   size the circle has inside the main composer pill (.composer-row .plus-button), measured equal. */
.plus-button.studio-markup-del { position: static; width: 34px; height: 34px; flex: 0 0 auto; color: var(--color-text-secondary); }
.studio-markup-del svg { width: var(--icon-size-sm); height: var(--icon-size-sm); }
/* In a composer row the mark keeps the composer's own 14px: the composer keeps its icons as they were (owner 2026-09-15) */
.composer-row .studio-markup-del svg { width: 14px; height: 14px; }

/* The overall-note entry is a Room-settings row (label + desc beside a REAL pill button) - a sentence is
   never the click target (owner 2026-08-27). Row card + label/desc + pill all come from their sources;
   this block only spaces the row group. */
.studio-markup-send[disabled] { opacity: var(--dim); cursor: default; }
/* Agent capability: hexagon radar + five-cell level bars. All ink via theme tokens (dark mode automatic). */
/* 行的左右内距只写在手机卡片那个媒体块里(14px 16px);桌面是 16px 0 → 两侧贴边、
   数字看着紧贴右缘。这里把左右同距显式写死:两种形态一致,右边距离 = 左边文字距离。 */
/* 右内距比左边大一档(owner 2026-08-25):数字右缘是齐口、文字左侧自带字形留白,
   两边同值时右边看着更紧。整行一起改 —— 数值与【对比按钮】因此始终停在同一条竖线上。 */
/* 开关行照设置页真源 16/16 —— 右边多留那一档只属于【能力卡】(数字右缘是齐口、要离边远一点),
   写成“编辑器里所有行卡”会把 Expertise 的开关也推得比设置页靠里。 */
/* 整个编辑器只有【一条右边线】= 设置页真源 16px:开关、对比按钮、五格数值全落在它上面。
   给某一张卡单独加宽就会出现第二条边线,而那两条边线必定有一条看着是错的。 */
.studio-agent-editor .settings-memory-toggle-group .settings-row { padding-left: 16px; padding-right: 16px; }
.studio-ability-box { margin-bottom: 12px; }
/* 图要对【卡片】居中,不是对行的内容框居中:行的左右内距是不对称的(右边给数值留得多),
   吃那份内距就会把六边形整体向左推半个差值。 */
.studio-ability-hexrow { justify-content: center; padding-left: 0; padding-right: 0; }
.studio-ability-foot { gap: 10px; }
/* 图的尺寸只有这一份:真图的 svg 和等图时那个空盒子都读它,所以两者永远一样大 */
.studio-ability-hex { color: var(--color-text); --hex-w: 212px; --hex-h: 180px; }
.studio-ability-hex.is-waiting { width: var(--hex-w); height: var(--hex-h); display: block; }
/* overflow:visible:画布只按六边形定,顶点旁的标签(六语宽度各异)允许伸出画布 —— 外层 svg 默认 hidden 会把
   右侧最长的标签切掉("Collectior",owner 2026-09-03)。按最长标签加宽画布是在猜字宽,不做。 */
.studio-ability-hex svg { width: var(--hex-w); height: var(--hex-h); display: block; overflow: visible; }
/* 顶点命中区:看不见、只负责接住鼠标 —— 提示里才是那些数字 */
.studio-ability-hex .hexhit { fill: transparent; cursor: help; }
/* 顶点缩写:10px = 微型字号档,比分数弱一档 —— 它是方位标注,不该和数据争重量 */
.studio-ability-hex .hexlabel { fill: var(--color-text); opacity: var(--dim); font-size: 10px; }
.studio-ability-hex .hexgrid { fill: none; stroke: var(--color-border); }
.studio-ability-hex .hexbase { fill: none; stroke: currentColor; stroke-dasharray: 2 2; }
/* The general model line: dashed outline, no fill - the gap between the two shapes is the moat */
.studio-ability-hex .hexbench { fill: none; stroke: currentColor; stroke-dasharray: 3 3; opacity: 0.45; }
.studio-ability-hex .hexpoly { fill: currentColor; fill-opacity: 0.12; stroke: currentColor; stroke-width: 1.5; }
.studio-ability-bar-row.is-off { opacity: var(--dim); }
/* the cap line under the switches, and a switch that cannot be turned on because six already are */
/* 靠右:左边标题占完剩下的,格子与数值成组贴右缘。
   尾距 8px 是光学补偿不是随手加的:文字左侧自带字形留白、数字右缘是齐口,
   同样 16px 内距下右边看着就贴到边上了。 */
/* 数值右缘与【对比按钮的右侧圆弧顶点】同线(owner 2026-08-25):两者都停在行的右内距边上,
   不再各自加尾距 —— 有一个看得见的参照物,比凭感觉调像素可靠。 */
/* 实心形状(开关/按钮)停在 16px;数字再多让 8px —— 数字右缘是齐口,没有胶囊圆边那种视觉重量,
   同样内距下就是显得贴边。这是光学对齐,不是对不齐 —— 把两者强行放到同一个数值上,数字那一列就看着坏了。 */
.studio-ability-cells { display: flex; gap: 3px; align-items: center; margin-left: auto; padding-right: 14px; }
/* 格子是刻度不是开关:里面那条按小数占宽,3.5 分就是半格 */
.studio-ability-cell { width: 14px; height: 8px; border-radius: 2px; background: var(--color-border); overflow: hidden; flex: 0 0 auto; display: flex; }
.studio-ability-cell b { display: block; height: 100%; background: var(--color-text); flex: 0 0 auto; }
/* 训练层增量接在底座后面叠;颜色 = 该品牌自己的色,由渲染处内联(动态数据,不是主题常量) */
.studio-ability-cell b.is-trained { background: none; }
/* 六边形第二个多边形 = 底座+训练的总轮廓,虚线 + 品牌色(color 由内联给),画在底座实线之下 */
.studio-ability-hex .hexpoly-trained { stroke-dasharray: 4 3; fill-opacity: 0.08; }
.studio-dna-user-dot { width: 6px; height: 6px; border-radius: 50%; corner-shape: round; flex: 0 0 auto; align-self: center; }
.studio-dna-source { font-size: 11px; color: var(--color-text-secondary); text-decoration: underline; }
/* 条目"名字: 描述"拆两行(owner 2026-09-03):名字 = 小标题档 16px/500 独占一行,描述 = 条目正文 13px 在下;
   收起时描述单行省略,展开后自然换行(同 .studio-dna-text 的开合规则) */
.studio-dna-lead { display: block; font-size: 16px; font-weight: 500; line-height: 1.4; }
/* 榜上名次:行内小数字,字号/色/等宽数字照 .studio-training-nav-pos 那档;定宽让各行标题起于同一 x */
.studio-dna-rank { display: inline-block; min-width: 22px; font-size: 12px; color: var(--color-text-secondary); font-variant-numeric: tabular-nums; }
.studio-dna-desc { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.studio-dna-row.is-open .studio-dna-desc { white-space: normal; }
/* 数值列【定宽】不是 min-width:min-width 只是下限,“1.2±0.8” 一宽就把这一行的格子往左顶,
   行与行的格子就对不上一条竖线了。定宽 = 每行这一组宽度恒等,格子永远起于同一个 x */
.studio-ability-score { font-size: 12px; color: var(--color-text); font-variant-numeric: tabular-nums; flex: 0 0 32px; text-align: right; }
/* 跑动中的等待文案 = 全站 thinking 那条文字微光的同一套参数(渐变位/周期/走位限制)。
   width 收到文字宽是必须的:段头拉满整行时光带大部分扫在空白区,字上只“闪”一下不成流动。 */
.studio-ability-foot .settings-row-desc.is-running {
  width: fit-content;
  background-image: linear-gradient(90deg, #9ca3af 42%, #dcdcdc 50%, #9ca3af 58%);
  background-size: 200% 100%;
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  animation: segHeadShimmer 1.8s linear infinite;
}
.dark-mode .studio-ability-foot .settings-row-desc.is-running,
.dark-mode-active .studio-ability-foot .settings-row-desc.is-running {
  background-image: linear-gradient(90deg, #8a8a8a 42%, #cfcfcf 50%, #8a8a8a 58%);
}
/* box-sizing 必须写:本仓没有全局 border-box,width:100% 加左右内距会让这一行比卡片宽 32px、
   向右溢出 —— 按钮就看着贴到卡片边上了。
   align-items: center 是这一行上下对齐的唯一一处 —— 手机也照用,别再在窄屏那段里改成 flex-start。 */
.studio-ability-foot { display: flex; align-items: center; gap: 10px; width: 100%; box-sizing: border-box; justify-content: space-between; margin-top: 8px; }   /* 与上方五格行拉开一点(owner 2026-09-02) */
/* 对比读条:一条通长胶囊(苹果开机那形),轨道/填充都走 token,暗色免单独覆盖。
   999px 胶囊必须自带 corner-shape: round(形状开关全站唯一,胶囊豁免);inherit 的半径要配 inherit 的形状。 */
.studio-bench-track { display: block; width: 100%; height: 5px; border-radius: 999px; corner-shape: round; background: var(--color-border); overflow: hidden; }
.studio-bench-track > b { display: block; height: 100%; width: 0; border-radius: inherit; corner-shape: inherit; background: var(--color-text); transition: width 0.25s linear; }
/* 跑动中按钮不让位:它变成"暂停",随时可停(owner 2026-09-03)。条只占剩下那一截 —— 也就不再是横贯整行的长条。 */
.studio-ability-foot.is-comparing .settings-row-desc { min-width: 0; }
/* 左边说明占剩下的、按钮不参与压缩：不写这两条，窄处文字会把按钮挤到只剩一个字宽，
   两个字的中文标签就上下排成两行。
   pre-line = 换行点由文案自己定（句与句之间），不交给浏览器在哪个字上断 ——
   否则窄屏上常常只把最后两个字挪到第二行，读起来像排版坏了 */
.studio-ability-foot .settings-row-desc { flex: 1 1 auto; min-width: 0; white-space: pre-line; }
.studio-ability-foot .project-modal-btn { flex: 0 0 auto; white-space: nowrap; }
.studio-piece-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* 名字行的竖三点:悬停才出/菜单开着保持/触屏常驻 —— 照作品集文件夹头那颗的基准 */
.studio-piece-namerow { display: flex; align-items: center; gap: 6px; padding: 0 6px 0 12px; }
.studio-piece-menu-btn { margin-left: auto; opacity: 0; transition: opacity 0.12s; }
html:not([data-input="touch"]) .studio-piece-namerow:hover .studio-piece-menu-btn,
html:not([data-input="touch"]) .studio-piece-namerow:active .studio-piece-menu-btn { opacity: 1; }
.studio-piece-namerow:has(.studio-section-menu.active) .studio-piece-menu-btn { opacity: 1; }
html[data-input="touch"] .studio-piece-menu-btn { opacity: 1; }

/* 作品详情弹窗的内联预览(安全·客户端):图片/PDF 原生;其余暂图标+提示。★弹窗 shrink-wrap 到图片大小 → 横图矮/竖图高,无留白;标题与图左缘对齐;边距收窄。 */
.project-modal-content.studio-pf-wide { width: auto; max-width: min(900px, 92vw); min-width: 300px; padding: 18px; }
.studio-pf-preview-wrap { display: flex; flex-direction: column; align-items: flex-start; gap: 10px; margin: 6px 0 14px; position: relative; }
/* ★预览舞台:【高度恒定、宽度随图片比例走】
   · 高度恒定 → 窗口不再随图片高矮忽高忽低(用户 2026-07-26 点名的就是这个)
   · 宽度自适应 → 正常比例的图【零留白】(宽高都锁死才会出现横图上下留白)
   只有极端全景图会撞到宽度上限、退化成上下留白,但高度依然恒定。 */
/* ★舞台高度对【所有】渲染器生效:图片吃 --pf-stage-h,iframe/canvas 吃 --preview-h,
   在这里把两者接上,换格式窗口高度也恒定(不是只有图片才稳)。 */
.studio-pf-preview-wrap { --pf-stage-h: min(70vh, 780px); --preview-h: var(--pf-stage-h); }
/* 单图(无画廊)同样恒定高度:连续打开不同图,窗口高度也不该变。
   ★★必须限定 img:.duo-preview 是【所有】预览的基类(图片 img / PDF 与 .ai 的 iframe / 3D 的 canvas)。
   写成 .studio-pf-preview-wrap > .duo-preview 时特异度 (0,2,0) 会压过 .duo-preview-frame (0,1,0),
   把 width:auto 灌给 iframe 和 canvas —— 而这两者的【CSS 默认固有宽度就是 300px】,
   于是 PDF/.ai/3D 全被压成 300px 宽、且【与窗口宽度无关】(拉宽窗口毫无变化),
   Chrome 的 PDF 查看器在 300px 框里还会自己收起工具条 = "浏览器功能都没了"。
   给图片写的几何规则一律带 img 限定,别用基类捞走非图片渲染器。 */
.studio-pf-preview-wrap > img.duo-preview {
  height: var(--pf-stage-h); width: auto; max-width: min(820px, 88vw); max-height: var(--pf-stage-h);
  object-fit: contain;
}
.studio-portfolio-card { display: flex; flex-direction: column; align-items: center; gap: 5px; cursor: pointer; }
.studio-portfolio-thumb {
  width: 48px; height: 48px; border-radius: var(--tile-radius);   /* 同模板/品牌图标框 */
  background: var(--color-bg-card); color: var(--color-text);
  display: flex; align-items: center; justify-content: center;
  overflow: hidden; box-sizing: border-box; border: 1px solid var(--color-border);
}
.studio-portfolio-thumb.has-filetype { border: none; color: #fff; }   /* 文档:类型彩色底 + 白图标 */
.studio-portfolio-thumb svg { width: 28px; height: 28px; }
.studio-portfolio-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }   /* 图片:缩略图铺满 */
/* 上传中的缩略图不再是空盒子:它一开始就是这个文件真正的缩略图(buildThumb),
   压暗与进度环走全站单一真源,所以这里既不用垫底色也不用再声明 position。 */
/* 缩略图自己是 overflow:hidden(裁类型色块用的),状态点放里面会被裁掉 → 套一层定位容器。
   与模板卡的 .studio-template-iconwrap 同一个道理、同一套几何。 */
.studio-pf-thumbwrap { position: relative; display: flex; }
/* 还在排的整本:它还不是一份文件,所以画得比排好的淡一档 —— 不淡的话跟真卡长得一模一样,
   人会去点它要文件。 */
.studio-portfolio-card.is-deck-job .studio-portfolio-thumb { opacity: 0.55; }
/* ★卡内文件名:提到 (0,2,0) 特异性 —— 有条来源不明(疑 theme.css 压缩)的 (0,1,0) 规则把裸 .studio-portfolio-name
   的字号/颜色盖成 14px 深色(暗色下不可见),已排除:重复规则/解析错误/CDN 陈旧/隐藏字符,均无。scoped 到卡内确保赢。 */
.studio-portfolio-card .studio-portfolio-name {
  font-size: 11px;
  color: var(--color-text);
  max-width: 60px;
  text-align: center;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* 模板编辑器:prompt textarea + 文件类预览布局 */
.studio-template-editor .studio-template-del-btn { color: var(--color-danger); }
.studio-template-editor textarea.project-modal-input { resize: vertical; min-height: 96px; line-height: 1.5; }
/* 文件类模板弹窗有表单头(标题/Name)+ 尾(按钮),预览 78vh 会把整窗撑到顶满视口 →
   弹窗【定高】90vh(上下各留 5vh 不顶格),预览 flex 吃掉表单之外的剩余高度、PDF 自身内部滚动。
   ★必须是 height 不是 max-height:flex 撑满 + 子元素 height:100% 都需要父级【确定高度】,
   max-height 给不了确定高度会让预览塌成默认小 iframe。作用域限定,Portfolio 纯预览不受影响。 */
.studio-template-editor .project-modal-content.studio-pf-wide { height: 90vh; display: flex; flex-direction: column; }
/* 预览区吃掉表单外的剩余高度;高度交给宿主用变量给(三种渲染器一起跟上) */
.studio-template-editor .studio-pf-preview-wrap { flex: 1 1 auto; min-height: 0; overflow: hidden; --preview-h: 100%; }

.studio-brand-icon {
  width: 48px; height: 48px; border-radius: var(--tile-radius);   /* app 图标圆角方块;品牌色作图标底(初始纯黑) */
  background: #000; color: #fff;
  display: flex; align-items: center; justify-content: center;
  font-size: 14px; font-weight: 500; letter-spacing: 0.3px;   /* 只用 400/500(600 违规,已归位) */
  overflow: hidden; box-sizing: border-box; border: 1px solid var(--color-border);
}
/* 有 logo 时去掉边线:--color-border 只有 10% 不透明,而 overflow:hidden 的裁切边在边线【内侧】,
   图铺不到边线底下 → 品牌色背景会从那 1px 半透明边线透出来,形成一圈色线。
   去掉边线后(box-sizing:border-box 尺寸不变)图直接铺满,边缘不再有底色;
   背景仍保留 → 透明底 logo 的内部照样透出品牌色。 */
/* 有 logo 的品牌方块 = 照 iOS 那套:【中性不透明底 + hairline 描边】。
   · hairline:白底 logo 落在亮色界面上本来没有边界(用户 2026-07-26"几乎看不到方框")——
     iOS 在 App Store/设置里就是给图标加一条极细描边解决的。
   · 中性底(不是品牌色):品牌色作底会在圆角的抗锯齿边缘渗出来(那正是"能看到底色的边");
     中性底与四周同调,渗也看不见,顺带给透明 PNG 的 logo 一个依托。
     品牌色另有表达处(collection 行的色点 + 工作台镶框),这里不必兼任色卡。 */
.studio-brand-icon.has-logo { background: var(--color-bg-panel); border: 1px solid var(--color-border); }
/* logo = 卡片封面:cover 铺满整块,四周不留品牌色边(用户要求)。
   透明底 logo 仍会从透明处透出品牌色(这正是透明底想要的);非方图会被裁 → 建议传方图。 */
.studio-brand-icon img { width: 100%; height: 100%; object-fit: cover; display: block; }
.studio-brand-name {
  font-size: 11px; color: var(--color-text); max-width: 60px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; text-align: center;
}
/* 原型 agent 卡:跟 brand 卡同宽 48px、圆形头像,可拖动(拖影只用图标) */
.studio-agent-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(56px, 1fr)); gap: 12px 8px; padding: 8px 12px 10px; }
.studio-agent-card { display: flex; flex-direction: column; align-items: center; gap: 5px; cursor: grab; }   /* 选择/长按抑制在上面 grid 级单一真源 */
.studio-agent-icon {
  width: 48px; height: 48px; border-radius: 50%;
  corner-shape: round;
  background: var(--avatar-bg); color: var(--avatar-fg);
  display: flex; align-items: center; justify-content: center;
  font-size: 14px; font-weight: 500; letter-spacing: 0.5px; box-sizing: border-box;
}
.studio-agent-name { font-size: 11px; color: var(--color-text); max-width: 60px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; text-align: center; }
/* 头像图铺满圆(overflow:hidden 已在 -icon/-circle 上或由 50% 裁切);有图不显缩写 */
.studio-agent-icon { overflow: hidden; }
/* 圆角【归图自己】,不再靠宿主 overflow:hidden 裁 —— 宿主一裁,画在它边框带上的"干活中"
   那圈线就一起被裁没了(overflow 裁到 padding box,边框带在外面)。图是正方 + cover,
   自己带 50% 圆角与宿主裁出来的形状一模一样。 */
.studio-agent-icon img, .studio-org-agent-circle img { width: 100%; height: 100%; object-fit: cover; display: block; border-radius: inherit; corner-shape: inherit; }
/* agent 编辑器预览 = 完全照 Edit profile 头像:140px 圆 + 缩写 42px。相机/× 直接用 ai-chat-settings-panel.css 的
   28px @ right:6px bottom/top:6px 基准(贴弧内侧、不探出)—— 不挂 brand 预览类,故无需覆盖任何东西 */
/* 项目名那个输入框的圆角是给左边色块留的(左方右圆);agent 这里没有色块 → 补回四角全圆 */
.studio-agent-editor .project-name-row .project-modal-input { border-radius: 8px !important; }
.studio-agent-editor-preview { display: flex; justify-content: center; margin-bottom: 16px; }
.studio-agent-editor-preview .edit-profile-avatar-wrap { width: 140px; height: 140px; }
.studio-agent-editor-preview .studio-agent-icon { width: 140px; height: 140px; font-size: 42px; }
.studio-agent-card:not(.placed) { opacity: var(--dim); }   /* 不在架构里 → 不高亮(淡显);已放的正常高亮 */
.studio-agent-card.dragging { opacity: var(--dim); }        /* 段内 reorder 拖起态:源卡淡出(拖影只留圆圈) */
/* 编辑器 = 复用项目设置 modal(.project-modal/.project-settings-modal-content);只补品牌预览 + 颜色行 */
.studio-brand-editor .project-settings-modal-content { max-width: 360px; }
.studio-brand-editor-preview { display: flex; justify-content: center; margin-bottom: 16px; padding-top: 14px; }
/* 大预览圆角单一源(下面按钮定位也按它算)。★由家族公式推,不写裸数:2026-08-28 那次"半径×1.5 +
   形状翻 squircle"的扫描扫不到裸字面量,于是它既不跟 --corner-k 走、也跟自己家族的 48px 卡对不上。
   80/48 = 大预览与卡片的边长比,保证"同一个形状放大",不是"另一个圆角"。 */
.studio-brand-editor-preview, .studio-training-fork .studio-training-grid { --be-radius: calc(var(--tile-radius) * 80 / 48); }
.studio-brand-editor-preview .studio-brand-icon { width: 80px; height: 80px; border-radius: var(--be-radius); font-size: 24px; }   /* 编辑器大预览 64→80,圆角/字号同比例跟上 */
/* 两个圆形图标照 iPhone 角标的做法:【圆心压在图标包围盒的角点上】,一半探出图标之外
   (不是落在圆角弧线内侧——那样看着偏里)。故 top/right = 0 + translate(±50%) */
.studio-brand-editor-preview .edit-profile-avatar-remove {
  top: 0; right: 0; left: auto; bottom: auto;
  transform: translate(50%, -50%);
}
.studio-brand-editor-preview .edit-profile-avatar-camera {
  bottom: 0; right: 0; left: auto; top: auto;
  transform: translate(50%, 50%);
}
/* logo 上传/移除 = 复用 Edit profile 头像那两个图标(.edit-profile-avatar-camera / -remove,样式在
   ai-chat-settings-panel.css)。那套按圆形头像定位,这里的预览是 80px 圆角方 → 只校正容器尺寸,按钮位置自然贴角 */
.studio-brand-editor-preview .edit-profile-avatar-wrap { width: 80px; height: 80px; }
/* 色卡 = 铺满竖线左侧整段(不是小圆角方块);hover/active 同形状,靠 filter 压暗、对任何品牌色都成立 */
.studio-brand-swatch {
  width: 44px; align-self: stretch; flex-shrink: 0;
  border: none; padding: 0; cursor: pointer;
  border-radius: calc(7px * var(--corner-k)) 0 0 calc(7px * var(--corner-k));   /* 内嵌在 .project-name-row 的 8px 圆角 + 1px 边线里(8-1=7) */
}
html:not([data-input="touch"]) .studio-brand-swatch:hover{ filter: brightness(var(--press-hover)); }

.studio-brand-swatch:active { filter: brightness(var(--press-active)); }
/* 颜色块直接贴住竖线(基准那 6px 左间距是留给图标的,这里不需要) */
.studio-brand-editor .project-name-row-divider { margin-left: 0; }
/* 底部动作区与上方内容拉开(基准 4px 在这个紧凑弹窗里太贴) */
/* 操作区【自成一段】:与表单各段同一个节奏(20px),不是紧贴上一行。
   归一前是同一个元素两个值 —— 基准 4px,而品牌自己调成了 12px,agent/模板还留在 4px(挤在字段下面)。 */
.studio-brand-editor .project-modal-actions,
.studio-agent-editor .project-modal-actions,
.studio-template-editor .project-modal-actions,
.studio-collection-settings .project-modal-actions,
.tp-dna-actions { margin-top: 20px; flex-shrink: 0; }
.tp-dna-actions { display: flex; justify-content: flex-end; gap: 8px; }
.tp-obj-row { display: contents; }
.tp-obj-group,
.tp-obj-bare { display: contents; }
.tp-obj-row .settings-row-label-group { display: none; }
/* Delete = 胶囊描边(延续 Cancel 的胶囊语言,不用实心红:实心红留给真正的确认删除那一步),红色用既定 danger 红 */
.studio-brand-del-btn { margin-right: auto; color: var(--color-danger); border-color: var(--color-danger); }
.dark-mode .studio-brand-del-btn { color: var(--color-danger); }   /* 盖住 .dark-mode .project-modal-btn-cancel 的浅色(同特异性,靠本表在其后加载) */
