/**
 * shell.css — 新外殼元件樣式（v2，仿 CircleKit）
 *
 * 依賴 tokens.css 先載入（吃 --bg / --fg / --s* 等變數）。這支檔案只放
 * SHELL_SPEC.md 第 3、4、7、8、9 節列出的 class，不新增 token、不寫死顏色
 * （語意色與 rgba/color-mix 疊層屬於规格允許的例外）。
 *
 * ⚠️ 表單元件（range / select / checkbox）的樣式一律「scope 在 .ctl-* 容器底下」，
 *    不動全站 input 預設外觀 —— booth.html 這一輪仍與舊的 app.css / 舊 HUD 並存，
 *    全域覆寫會波及舊面板，不歸這次任務管。
 */

/* ══════════════════════════════════════════════════════════════
   3. 外殼骨架：.shell / .rail / .shell__main / .workspace / .stage / .inspector
   ══════════════════════════════════════════════════════════════ */

.shell {
  display: flex;
  min-height: 100vh;
  background: var(--bg);
}

/* 左側細欄。預設只有 --rail-w 寬（放得下圖示），.rail--open 時展開顯示文字。
   寬度變化用 transition，且欄位本身 overflow:hidden，展開/收合才不會讓文字
   在收合狀態下把欄位撐寬。 */
.rail {
  width: var(--rail-w);
  flex-shrink: 0;
  display: flex;
  flex-direction: column;
  background: var(--bg-elev);
  border-right: 1px solid var(--line);
  overflow: hidden;
  position: sticky;
  top: 0;
  height: 100vh;
  z-index: 30;
  transition: width .22s var(--ease);
}
.rail--open { width: var(--rail-w-open); }

.rail__top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s2);
  padding: var(--s3);
  flex-shrink: 0;
}
.rail__brand {
  display: flex;
  align-items: center;
  gap: var(--s2);
  color: var(--fg);
  text-decoration: none;
  font-weight: 800;
  letter-spacing: -.02em;
  overflow: hidden;
  white-space: nowrap;
  min-width: 0;
}
.rail__toggle {
  width: 26px;
  height: 26px;
  flex-shrink: 0;
  border-radius: 50%;
  border: 1px solid var(--line);
  background: transparent;
  color: var(--fg-dim);
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: transform .22s var(--ease), background .15s var(--ease), color .15s var(--ease);
}
.rail__toggle:hover { background: var(--bg-hover); color: var(--fg); }
.rail--open .rail__toggle { transform: rotate(180deg); }

.rail__nav {
  display: flex;
  flex-direction: column;
  gap: var(--s1);
  padding: var(--s2);
  flex: 1;
  min-height: 0;
  overflow-y: auto;
}
.rail__item {
  display: flex;
  align-items: center;
  gap: var(--s3);
  padding: var(--s2) var(--s3);
  border-radius: var(--r-sm);
  color: var(--fg-dim);
  text-decoration: none;
  font-size: var(--f-sm);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  flex-shrink: 0;
  transition: background .15s var(--ease), color .15s var(--ease);
}
.rail__item:hover { background: var(--bg-hover); color: var(--fg); }
.rail__item.is-active,
.rail__item[aria-current="page"] {
  background: var(--bg-inset);
  color: var(--fg);
  font-weight: 600;
}

.rail__foot {
  display: flex;
  flex-direction: column;
  gap: var(--s1);
  padding: var(--s2);
  border-top: 1px solid var(--line);
  flex-shrink: 0;
}
.rail__foot button {
  display: flex;
  align-items: center;
  gap: var(--s3);
  width: 100%;
  padding: var(--s2) var(--s3);
  border-radius: var(--r-sm);
  border: 0;
  background: transparent;
  color: var(--fg-dim);
  font-size: var(--f-sm);
  text-align: left;
  cursor: pointer;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  transition: background .15s var(--ease), color .15s var(--ease);
}
.rail__foot button:hover { background: var(--bg-hover); color: var(--fg); }

/* 收合狀態（沒有 .rail--open）時只留圖示：假設 ui/shell.js 把圖示放第一個
   子節點、文字包成後面的 <span>（跟 .insp__tabs / .dock 的圖示+文字慣例一致）。
   就算實際結構不同，外層 width 收窄 + overflow:hidden + ellipsis 仍會把文字
   裁掉，不會爆版。 */
.rail:not(.rail--open) .rail__item,
.rail:not(.rail--open) .rail__foot button {
  justify-content: center;
}
.rail:not(.rail--open) .rail__item > span:not(:first-child),
.rail:not(.rail--open) .rail__foot button > span:not(:first-child),
.rail:not(.rail--open) .rail__brand > span:not(:first-child) {
  display: none;
}

.shell__main {
  flex: 1;
  min-width: 0;
  height: 100vh;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
}

/* ── 設計器 / 工作室的工作區（3.2） ─────────────────────────── */

.workspace {
  position: relative;
  flex: 1;
  min-height: 0;
  display: grid;
  grid-template-columns: 1fr var(--inspector-w);
  gap: var(--s3);
  padding: var(--s3);
}

.stage {
  grid-area: 1 / 1 / 2 / 2;
  position: relative;
  background: var(--stage);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  overflow: hidden;
  min-height: 320px;
}
.stage canvas { width: 100%; height: 100%; display: block; }

.stage__badges {
  position: absolute;
  top: var(--s3);
  left: var(--s3);
  z-index: 2;
  display: flex;
  flex-wrap: wrap;
  gap: var(--s2);
  pointer-events: none;
}
.stage__badges > * { pointer-events: auto; }
.stage__badges span,
.stage__badges .badge {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 4px 10px;
  border-radius: var(--r-pill);
  background: color-mix(in srgb, var(--bg-elev) 82%, transparent);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  border: 1px solid var(--line);
  font-size: var(--f-xs);
  color: var(--fg-dim);
  white-space: nowrap;
}

.stage__title {
  position: absolute;
  top: var(--s3);
  right: var(--s3);
  z-index: 2;
  padding: 4px 12px;
  border-radius: var(--r-pill);
  background: color-mix(in srgb, var(--bg-elev) 82%, transparent);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  border: 1px solid var(--line);
  font-size: var(--f-sm);
  font-weight: 600;
  color: var(--fg);
  white-space: nowrap;
  max-width: 60%;
  overflow: hidden;
  text-overflow: ellipsis;
}

.inspector {
  grid-area: 1 / 2 / 2 / 3;
  width: 100%;
  height: 100%;
  min-height: 0;
  display: flex;
  flex-direction: column;
  background: var(--bg-elev);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  overflow: hidden;
  box-shadow: var(--sh-1);
}

/* < 960px：inspector 變底部抽屜，工作區改上下堆疊 */
@media (max-width: 959px) {
  .workspace { grid-template-columns: 1fr; }
  .stage { grid-area: 1 / 1 / 2 / 2; }
  .inspector {
    grid-area: 2 / 1 / 3 / 2;
    width: 100%;
    height: 52vh;
    max-height: 52vh;
  }
}

/* ══════════════════════════════════════════════════════════════
   4. inspector.js 面板框架：.insp__tabs / .insp__body / .sect / 控制元件
   ══════════════════════════════════════════════════════════════ */

.insp__tabs {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s1);
  padding: var(--s2);
  border-bottom: 1px solid var(--line);
  flex-shrink: 0;
}
.insp__tabs button {
  display: flex;
  align-items: center;
  gap: var(--s1);
  padding: var(--s2) var(--s3);
  border-radius: var(--r-sm);
  border: 1px solid transparent;
  background: transparent;
  color: var(--fg-dim);
  font-size: var(--f-sm);
  cursor: pointer;
  transition: background .15s var(--ease), color .15s var(--ease), border-color .15s var(--ease);
}
.insp__tabs button:hover { color: var(--fg); background: var(--bg-hover); }
.insp__tabs button[aria-selected="true"],
.insp__tabs button.is-active {
  background: var(--bg-inset);
  color: var(--fg);
  border-color: var(--line);
}

.insp__body {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding: var(--s3);
  display: flex;
  flex-direction: column;
  gap: var(--s3);
}
.insp__body[hidden] { display: none; }

/* ── 可摺疊卡片 .sect ─────────────────────────────────────────
   body 的展開/收合動畫用 max-height（而不是量測實際高度再設 height），
   因為 section() 回傳的 .sect__body 就是呼叫端直接塞控制元件進去的容器，
   沒有多一層量測用的 wrapper；固定一個夠大的上限對這裡的設定面板內容
   （幾個 slider / select）綽綽有餘，換來不必在 JS 端量高度的簡單性。 */
.sect { border-bottom: 1px solid var(--line); }
.sect:last-child { border-bottom: 0; }

.sect__head {
  width: 100%;
  display: flex;
  align-items: center;
  gap: var(--s2);
  padding: var(--s3);
  background: transparent;
  border: 0;
  color: var(--fg);
  font-size: var(--f-sm);
  text-align: left;
  cursor: pointer;
}
/* collapsible:false 時 inspector.js 不會給 head 綁點擊、也不會渲染 chevron；
   用 :has() 偵測「沒有 chevron」順便把游標改回預設，讓不能點的東西看起來真的不能點。
   不支援 :has() 的舊瀏覽器就維持 pointer，屬於無害的漸進增強。 */
.sect__head:not(:has(.sect__chev)) { cursor: default; }

.sect__title { font-weight: 600; flex-shrink: 0; }
.sect__value {
  flex: 1;
  text-align: right;
  color: var(--fg-mute);
  font-size: var(--f-xs);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.sect__chev {
  flex-shrink: 0;
  display: inline-block;
  color: var(--fg-mute);
  transition: transform .18s var(--ease);
}
.sect[data-open="false"] .sect__chev { transform: rotate(-90deg); }

.sect__body {
  overflow: hidden;
  padding: 0 var(--s3);
  display: flex;
  flex-direction: column;
  gap: var(--s3);
  max-height: 800px;
  opacity: 1;
  transition: max-height .22s var(--ease), opacity .18s var(--ease), padding-bottom .22s var(--ease);
}
.sect[data-open="true"] .sect__body { padding-bottom: var(--s3); }
.sect[data-open="false"] .sect__body {
  max-height: 0;
  opacity: 0;
  padding-bottom: 0;
}

/* ── 控制元件工廠 ─────────────────────────────────────────── */

.ctl-row {
  display: flex;
  align-items: center;
  gap: var(--s2);
  flex-wrap: wrap;
}

/* ctlSlider：一行 label，下一行 range + 數字輸入框 + 單位字 */
.ctl-slider {
  display: flex;
  flex-direction: column;
  gap: var(--s1);
}
.ctl-slider > label,
.ctl-slider__label {
  display: flex;
  align-items: center;
  justify-content: space-between;
  font-size: var(--f-sm);
  color: var(--fg-dim);
}
.ctl-slider__row {
  display: flex;
  align-items: center;
  gap: var(--s2);
}
.ctl-slider input[type="range"] {
  flex: 1;
  -webkit-appearance: none;
  appearance: none;
  width: 100%;
  height: 4px;
  border-radius: var(--r-pill);
  background: var(--bg-inset);
  outline: none;
  cursor: pointer;
}
/* thumb 要在深淺色底下都看得見：實心 --accent（深色底是亮色、淺色底是深色，
   永遠跟軌道 --bg-inset 對比夠），外面再包一圈 --bg-elev 描邊當「光暈」，
   雙層對比確保軌道本身再怎麼調也不會把 thumb 吃掉。 */
.ctl-slider input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: var(--accent);
  border: 2px solid var(--bg-elev);
  box-shadow: var(--sh-1);
  cursor: pointer;
  transition: transform .12s var(--ease);
}
.ctl-slider input[type="range"]::-webkit-slider-thumb:hover { transform: scale(1.12); }
.ctl-slider input[type="range"]::-moz-range-track {
  background: var(--bg-inset);
  height: 4px;
  border-radius: var(--r-pill);
}
.ctl-slider input[type="range"]::-moz-range-thumb {
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: var(--accent);
  border: 2px solid var(--bg-elev);
  box-shadow: var(--sh-1);
  cursor: pointer;
}
.ctl-slider input[type="number"] {
  width: 64px;
  flex-shrink: 0;
  background: var(--bg-inset);
  border: 1px solid var(--line);
  border-radius: var(--r-xs);
  color: var(--fg);
  font-size: var(--f-sm);
  padding: 4px 6px;
  text-align: right;
}
.ctl-slider input[type="number"]:focus-visible { border-color: var(--line-strong); }
.ctl-slider__unit {
  flex-shrink: 0;
  font-size: var(--f-xs);
  color: var(--fg-mute);
  min-width: 1.6em;
}

/* ctlSegment：一排等寬按鈕，選中的浮出來 */
.ctl-seg {
  display: flex;
  flex-direction: column;
  gap: var(--s1);
}
.ctl-seg > label,
.ctl-seg__label {
  font-size: var(--f-sm);
  color: var(--fg-dim);
}
.ctl-seg__options {
  display: flex;
  gap: 2px;
  padding: 2px;
  background: var(--bg-inset);
  border-radius: var(--r-sm);
}
.ctl-seg button {
  flex: 1;
  border: 0;
  background: transparent;
  color: var(--fg-dim);
  font-size: var(--f-sm);
  padding: 6px 10px;
  border-radius: var(--r-xs);
  cursor: pointer;
  white-space: nowrap;
  transition: background .15s var(--ease), color .15s var(--ease);
}
.ctl-seg button:hover { color: var(--fg); }
.ctl-seg button[aria-pressed="true"],
.ctl-seg button.is-active {
  background: var(--bg-elev);
  color: var(--fg);
  box-shadow: var(--sh-1);
}

/* ctlSwitch：右側 pill 開關 */
.ctl-switch {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s3);
}
.ctl-switch__label,
.ctl-switch > label {
  font-size: var(--f-sm);
  color: var(--fg);
}
.ctl-switch input[type="checkbox"] {
  appearance: none;
  -webkit-appearance: none;
  width: 36px;
  height: 20px;
  flex-shrink: 0;
  border-radius: var(--r-pill);
  background: var(--bg-inset);
  border: 1px solid var(--line);
  position: relative;
  cursor: pointer;
  transition: background .18s var(--ease), border-color .18s var(--ease);
}
.ctl-switch input[type="checkbox"]::before {
  content: "";
  position: absolute;
  top: 1px;
  left: 1px;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: var(--fg-mute);
  transition: transform .18s var(--ease), background .18s var(--ease);
}
.ctl-switch input[type="checkbox"]:checked {
  background: var(--accent-soft);
  border-color: var(--line-strong);
}
.ctl-switch input[type="checkbox"]:checked::before {
  transform: translateX(16px);
  background: var(--accent);
}

/* ctlSelect */
.ctl-select {
  display: flex;
  flex-direction: column;
  gap: var(--s1);
}
.ctl-select > label,
.ctl-select__label {
  font-size: var(--f-sm);
  color: var(--fg-dim);
}
.ctl-select select {
  width: 100%;
  background: var(--bg-inset);
  border: 1px solid var(--line);
  color: var(--fg);
  border-radius: var(--r-sm);
  padding: 8px 10px;
  font-size: var(--f-sm);
  cursor: pointer;
}
.ctl-select select:hover { border-color: var(--line-strong); }
.ctl-select select:focus-visible { outline: 2px solid var(--teal); outline-offset: 1px; }

/* ctlColor */
.ctl-color {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s2);
}
.ctl-color > label,
.ctl-color__label {
  font-size: var(--f-sm);
  color: var(--fg-dim);
}
.ctl-color input[type="color"] {
  width: 32px;
  height: 32px;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: var(--bg-inset);
  cursor: pointer;
}
.ctl-color input[type="color"]::-webkit-color-swatch-wrapper { padding: 2px; }
.ctl-color input[type="color"]::-webkit-color-swatch { border: 0; border-radius: var(--r-xs); }
.ctl-color input[type="color"]::-moz-color-swatch { border: 0; border-radius: var(--r-xs); }

/* ctlTiles：圖示磚格，選中的有 2px 外框（用 box-shadow 疊出來，不佔版位、
   不會因為多 2px border 讓選中的磚格比其他磚格大一圈） */
.ctl-tiles {
  display: grid;
  grid-template-columns: repeat(var(--ctl-tiles-cols, 3), 1fr);
  gap: var(--s2);
}
.ctl-tiles button,
.ctl-tiles .ctl-tiles__item {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--s1);
  padding: var(--s2);
  border: 0;
  border-radius: var(--r-sm);
  background: var(--bg-inset);
  color: var(--fg-dim);
  font-size: var(--f-xs);
  text-align: center;
  cursor: pointer;
  box-shadow: 0 0 0 1px var(--line);
  transition: box-shadow .15s var(--ease), background .15s var(--ease), color .15s var(--ease);
}
.ctl-tiles button:hover,
.ctl-tiles .ctl-tiles__item:hover {
  background: var(--bg-hover);
  box-shadow: 0 0 0 1px var(--line-strong);
}
.ctl-tiles button[aria-pressed="true"],
.ctl-tiles .ctl-tiles__item.is-selected,
.ctl-tiles .is-selected {
  color: var(--fg);
  background: var(--bg-hover);
  box-shadow: 0 0 0 2px var(--teal);
}
.ctl-tiles img,
.ctl-tiles canvas,
.ctl-tiles svg,
.ctl-tiles__thumb {
  width: 100%;
  aspect-ratio: 1;
  border-radius: var(--r-xs);
  object-fit: cover;
  background: var(--bg-elev);
}

/* .btn：primary / ghost / danger 三種 variant。
   同時支援 BEM 修飾詞（.btn--primary）與裸字 class（.btn.primary）兩種寫法，
   因為 SHELL_SPEC 只定了工廠函式的 variant 字串，沒定最終 class 命名慣例。 */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s1);
  padding: 10px 18px;
  border-radius: var(--r-pill);
  border: 1px solid transparent;
  font-size: var(--f-sm);
  font-weight: 600;
  cursor: pointer;
  white-space: nowrap;
  transition: background .15s var(--ease), border-color .15s var(--ease), opacity .15s var(--ease), transform .1s var(--ease);
}
.btn:active { transform: scale(.97); }
.btn:disabled { opacity: .5; cursor: not-allowed; transform: none; }

.btn--primary, .btn.primary {
  background: var(--accent);
  color: var(--accent-fg);
}
.btn--primary:hover, .btn.primary:hover { opacity: .88; }

.btn--ghost, .btn.ghost {
  background: transparent;
  color: var(--fg);
  border-color: var(--line);
}
.btn--ghost:hover, .btn.ghost:hover { background: var(--bg-hover); border-color: var(--line-strong); }

.btn--danger, .btn.danger {
  background: transparent;
  color: var(--danger);
  border-color: var(--line);
}
.btn--danger:hover, .btn.danger:hover {
  background: rgba(224, 85, 85, .12);
  border-color: var(--danger);
}

/* ctlEmpty：空狀態 */
.ctl-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: var(--s2);
  padding: var(--s6) var(--s4);
  color: var(--fg-mute);
}
.ctl-empty__icon, .ctl-empty > :first-child {
  font-size: 28px;
  opacity: .6;
  line-height: 1;
}
.ctl-empty__lines, .ctl-empty p {
  font-size: var(--f-sm);
  color: var(--fg-dim);
  line-height: 1.6;
  margin: 0;
}
.ctl-empty .btn { margin-top: var(--s2); }

/* ══════════════════════════════════════════════════════════════
   7. .dock — 浮動藥丸工具列
   ══════════════════════════════════════════════════════════════ */

/* DOM 上 .dock 是 .stage 的親兄弟（見 3.2），不是子元素，但視覺上要「浮貼在
   .stage 底部置中」。做法：讓 .dock 跟 .stage 共用同一個 grid 儲存格
   （grid-row/column 手動指定成跟 .stage 一樣），再用 align-self:end +
   justify-self:center 貼到那格的底部置中 —— 不用另外包一層定位容器，
   也不用量測 .stage 的實際尺寸。 */
.dock {
  grid-row: 1 / 2;
  grid-column: 1 / 2;
  align-self: end;
  justify-self: center;
  margin-bottom: var(--s4);
  z-index: 5;
  display: flex;
  align-items: center;
  gap: var(--s3);
  height: var(--dock-h);
  padding: 0 var(--s3);
  background: color-mix(in srgb, var(--bg-elev) 82%, transparent);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  box-shadow: var(--sh-2);
  max-width: calc(100% - var(--s4) * 2);
  overflow-x: auto;
}
.dock__group {
  display: flex;
  align-items: center;
  gap: var(--s1);
  flex-shrink: 0;
}
.dock__sep, .dock__divider {
  width: 1px;
  align-self: stretch;
  margin: var(--s2) 0;
  background: var(--line);
  flex-shrink: 0;
}
.dock button, .dock__item {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  flex-shrink: 0;
  border-radius: 50%;
  border: 0;
  background: transparent;
  color: var(--fg-dim);
  font-size: var(--f-lg);
  cursor: pointer;
  transition: background .15s var(--ease), color .15s var(--ease);
}
.dock button:hover { background: var(--bg-hover); color: var(--fg); }
.dock button[aria-pressed="true"], .dock button.is-active {
  background: var(--accent-soft);
  color: var(--fg);
}
.dock button:disabled { opacity: .35; cursor: not-allowed; }

/* ══════════════════════════════════════════════════════════════
   8. 首頁 .home
   ══════════════════════════════════════════════════════════════ */

.home {
  max-width: 1040px;
  width: 100%;
  margin: 0 auto;
  padding: var(--s7) var(--s6) var(--s6);
  /* 極淡方格紋背景，照 CircleKit 的做法：兩組 repeating-linear-gradient
     疊出格線，線色直接用 --line（已經是低透明度的白/黑），格距 44px */
  background-image:
    repeating-linear-gradient(0deg, var(--line) 0 1px, transparent 1px 44px),
    repeating-linear-gradient(90deg, var(--line) 0 1px, transparent 1px 44px);
}
.home h1 {
  font-size: var(--f-h1);
  font-weight: 800;
  letter-spacing: -.03em;
  margin: 0 0 var(--s6);
}

.home__grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--s4);
  margin-bottom: var(--s7);
}

.card--big, .card--small {
  display: flex;
  flex-direction: column;
  gap: var(--s3);
  background: var(--bg-elev);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  color: inherit;
  text-decoration: none;
  cursor: pointer;
  transition: border-color .15s var(--ease), transform .15s var(--ease);
}
.card--big:hover, .card--small:hover { border-color: var(--line-strong); }

.card--big {
  padding: var(--s5);
  min-height: 220px;
  justify-content: space-between;
}
.card--big h2 {
  font-size: var(--f-xl);
  font-weight: 800;
  letter-spacing: -.02em;
  margin: 0;
}
.card--big p {
  font-size: var(--f-sm);
  color: var(--fg-dim);
  line-height: 1.6;
  margin: 0;
}

.card--small {
  flex-direction: row;
  align-items: center;
  gap: var(--s3);
  padding: var(--s4);
}
.card--small h3 {
  font-size: var(--f-md);
  font-weight: 700;
  margin: 0;
}
.card--small p {
  font-size: var(--f-xs);
  color: var(--fg-dim);
  margin: 0;
  line-height: 1.5;
}

.card__tag {
  display: inline-flex;
  align-self: flex-start;
  font-size: var(--f-xs);
  font-weight: 600;
  letter-spacing: .02em;
  padding: 4px 10px;
  border-radius: var(--r-pill);
  background: var(--bg-inset);
  color: var(--fg-dim);
}

.home__recent { display: flex; flex-direction: column; gap: var(--s3); margin-bottom: var(--s6); }
.home__recent-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s3);
}
.home__recent-head h2, .home__recent > h2 {
  font-size: var(--f-lg);
  font-weight: 800;
  letter-spacing: -.01em;
  margin: 0;
}
.home__recent-head a, .home__recent-head .home__recent-link {
  font-size: var(--f-sm);
  color: var(--fg-dim);
  text-decoration: none;
  flex-shrink: 0;
}
.home__recent-head a:hover, .home__recent-head .home__recent-link:hover { color: var(--fg); }

.foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--s2);
  padding-top: var(--s5);
  margin-top: var(--s6);
  border-top: 1px solid var(--line);
  font-size: var(--f-xs);
  color: var(--fg-mute);
}
.foot a {
  color: var(--fg-dim);
  text-decoration: none;
  margin-right: var(--s3);
}
.foot a:last-of-type { margin-right: 0; }
.foot a:hover { color: var(--fg); }

@media (max-width: 819px) {
  .home__grid { grid-template-columns: 1fr; }
  .home { padding: var(--s5) var(--s4) var(--s4); }
}

/* ══════════════════════════════════════════════════════════════
   9. 我的專案 .projects / .proj-grid / .proj-menu / .modal
   （.proj-card 是首頁「最近的專案」跟這頁共用的卡片，放在這一段一起管理）
   ══════════════════════════════════════════════════════════════ */

.projects {
  max-width: 1200px;
  width: 100%;
  margin: 0 auto;
  padding: var(--s6);
}
.projects__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--s3);
  margin-bottom: var(--s5);
}
.projects__head h1 {
  font-size: var(--f-h2);
  font-weight: 800;
  letter-spacing: -.02em;
  margin: 0;
}
.projects input[type="search"],
.projects input[type="text"] {
  width: 100%;
  max-width: 320px;
  background: var(--bg-inset);
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  color: var(--fg);
  font-size: var(--f-sm);
  padding: 8px 16px;
  margin-bottom: var(--s5);
}
.projects input[type="search"]:focus-visible,
.projects input[type="text"]:focus-visible { border-color: var(--line-strong); }

.proj-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  gap: var(--s4);
}

.proj-card {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: var(--s2);
  background: var(--bg-elev);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  padding: var(--s4);
  cursor: pointer;
  transition: border-color .15s var(--ease);
}
.proj-card:hover { border-color: var(--line-strong); }
.proj-card__thumb {
  aspect-ratio: 16 / 10;
  border-radius: var(--r-sm);
  background: var(--bg-inset);
  border: 1px solid var(--line);
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--fg-mute);
  font-size: 22px;
  overflow: hidden;
}
.proj-card__name {
  font-size: var(--f-md);
  font-weight: 700;
  margin: 0;
  padding-right: var(--s6); /* 留給右上角 ⋯ 選單按鈕 */
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.proj-card__meta {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s2);
  font-size: var(--f-xs);
  color: var(--fg-dim);
}
.proj-card__menu-btn {
  position: absolute;
  top: var(--s2);
  right: var(--s2);
  width: 28px;
  height: 28px;
  border-radius: 50%;
  border: 0;
  background: transparent;
  color: var(--fg-mute);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: background .15s var(--ease), color .15s var(--ease);
}
.proj-card__menu-btn:hover { background: var(--bg-hover); color: var(--fg); }

/* ⋯ 選單彈出來的小卡 */
.proj-menu {
  position: absolute;
  z-index: 20;
  min-width: 160px;
  background: var(--bg-elev);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  box-shadow: var(--sh-2);
  padding: var(--s1);
  display: flex;
  flex-direction: column;
}
.proj-menu button {
  display: flex;
  align-items: center;
  gap: var(--s2);
  padding: var(--s2) var(--s3);
  border-radius: var(--r-sm);
  border: 0;
  background: transparent;
  color: var(--fg);
  font-size: var(--f-sm);
  text-align: left;
  cursor: pointer;
}
.proj-menu button:hover { background: var(--bg-hover); }
.proj-menu button.danger, .proj-menu button--danger { color: var(--danger); }
.proj-menu button.danger:hover, .proj-menu button--danger:hover { background: rgba(224, 85, 85, .12); }

/* 自製確認對話框（刪除攤位要二次確認，不可以用瀏覽器原生 confirm()） */
.modal {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--s4);
  background: rgba(0, 0, 0, .5);
}
.modal__box {
  width: 100%;
  max-width: 380px;
  display: flex;
  flex-direction: column;
  gap: var(--s4);
  background: var(--bg-elev);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  box-shadow: var(--sh-3);
  padding: var(--s5);
}
.modal__title {
  font-size: var(--f-lg);
  font-weight: 700;
  margin: 0;
}
.modal__body {
  font-size: var(--f-sm);
  color: var(--fg-dim);
  line-height: 1.6;
  margin: 0;
}
.modal__actions {
  display: flex;
  justify-content: flex-end;
  gap: var(--s2);
}

/* ── 空狀態與載入骨架 ─────────────────────────────────────── */

.skeleton {
  position: relative;
  overflow: hidden;
  background: var(--bg-inset);
  border-radius: var(--r-sm);
  color: transparent;
}
.skeleton::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(90deg, transparent, var(--bg-hover), transparent);
  transform: translateX(-100%);
  animation: skeleton-sheen 1.4s infinite;
}
@keyframes skeleton-sheen { to { transform: translateX(100%); } }

@media (prefers-reduced-motion: reduce) {
  .skeleton::after { animation: none; }
}

/* ══════════════════════════════════════════════════════════════
   響應式：< 820px，.rail 縮成底部橫向列
   （home__grid 的斷點在第 8 節那段已處理，這裡只管 .rail 本身）
   ══════════════════════════════════════════════════════════════ */

@media (max-width: 819px) {
  .shell { flex-direction: column; }

  .rail {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    top: auto;
    width: 100%;
    height: auto;
    max-height: none;
    flex-direction: row;
    align-items: center;
    gap: var(--s2);
    padding: var(--s1) var(--s2);
    border-right: 0;
    border-top: 1px solid var(--line);
    z-index: 40;
    overflow-x: auto;
  }
  .rail--open { width: 100%; } /* 手機沒有「展開變寬」這件事，寬度恆為 100% */

  .rail__top { padding: 0; flex-shrink: 0; }
  .rail__toggle { display: none; } /* 手機的橫列本來就是展開狀態，不需要展開按鈕 */

  .rail__nav {
    flex-direction: row;
    flex: 1;
    padding: 0;
    overflow: visible;
    justify-content: space-around;
  }
  .rail__item { flex-direction: column; gap: var(--s1); padding: var(--s1) var(--s2); }

  .rail__foot {
    flex-direction: row;
    padding: 0;
    border-top: 0;
    flex-shrink: 0;
  }
  .rail__foot button { width: auto; padding: var(--s2); }

  /* 手機橫列一律只顯示圖示（跟桌面收合態同一條規則，但這裡不看 .rail--open） */
  .rail__item > span:not(:first-child),
  .rail__foot button > span:not(:first-child) {
    display: none;
  }

  /* 頁面內容底部留出橫列的高度，避免最後一段文字被蓋住 */
  .shell__main { padding-bottom: calc(56px + env(safe-area-inset-bottom, 0px)); }
}

/* ══════════════════════════════════════════════════════════════
   整合補丁（收口者新增）
   ──────────────────────────────────────────────────────────────
   上面那幾段是照 SHELL_SPEC.md 的 class 清單寫的；實際交付的
   ui/shell.js、ui/inspector.js、ui/dock.js、home.js 在規格的基礎上
   還多用了一批子元素 class（規格只定了工廠函式簽名，沒定內部 DOM）。
   這一段把「有人真的用、但上面沒有樣式」的那些補齊，缺一個畫面就會缺一塊。
   命名一律照實作端已經寫死的名字，不改 JS。
   ══════════════════════════════════════════════════════════════ */

/* ── rail：品牌 / nav 項目 / 帳號按鈕的內部結構 ─────────────────
   ui/shell.js 的慣例是「第一個子節點＝圖示、後面的 <span>＝文字」，
   上面的收合規則 (.rail:not(.rail--open) … > span:not(:first-child))
   就是靠這個結構把文字藏掉，所以這裡只補尺寸與顏色，不改排版邏輯。 */
.rail__brand-mark,
.rail__item-icon,
.rail__user-avatar {
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
}
.rail__brand-mark svg,
.rail__item-icon svg,
.rail__user-avatar svg,
.rail__toggle svg,
.rail__theme svg {
  width: 100%;
  height: 100%;
  display: block;
}
.rail__toggle svg { width: 16px; height: 16px; }
.rail__theme svg { width: 20px; height: 20px; }

.rail__brand-text,
.rail__item-label,
.rail__user-name {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.rail__brand-text { font-size: var(--f-md); }

/* 收合狀態下 rail 只有 --rail-w(70px) 寬，扣掉左右 padding 只剩 46px；
   品牌圖示(24px) + 展開鈕(26px) 橫排一定塞不下，圖示會被擠掉一半。
   → 收合時改成上下堆疊，兩顆都完整看得見。（展開時維持原本的左右分置。） */
.rail:not(.rail--open) .rail__top {
  flex-direction: column;
  justify-content: center;
  gap: var(--s2);
  padding: var(--s3) var(--s2);
}

/* 頭像可能是使用者的 Google 大頭貼（img），也可能是內建的 SVG 人形 */
img.rail__user-avatar {
  border-radius: 50%;
  object-fit: cover;
  background: var(--bg-inset);
}

/* 單位鈕上是純文字（cm / in），收合時也要看得清楚 → 給它等寬數字 */
.rail__unit {
  font-variant-numeric: tabular-nums;
  letter-spacing: .02em;
}
.rail__theme,
.rail__unit,
.rail__user { flex-shrink: 0; }

/* ── 帳號浮層選單（position:fixed，由 ui/shell.js 定位） ────── */
.rail__menu {
  z-index: 120;
  min-width: 200px;
  max-width: 280px;
  padding: var(--s1);
  display: flex;
  flex-direction: column;
  background: var(--bg-elev);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  box-shadow: var(--sh-2);
}
.rail__menu-head { padding: var(--s2) var(--s3); }
.rail__menu-name {
  font-size: var(--f-sm);
  font-weight: 600;
  color: var(--fg);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.rail__menu-email {
  font-size: var(--f-xs);
  color: var(--fg-mute);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.rail__menu-sep {
  height: 1px;
  margin: var(--s1) 0;
  background: var(--line);
}
.rail__menu-item {
  display: flex;
  align-items: center;
  gap: var(--s2);
  padding: var(--s2) var(--s3);
  border: 0;
  border-radius: var(--r-sm);
  background: transparent;
  color: var(--fg);
  font-size: var(--f-sm);
  text-align: left;
  cursor: pointer;
}
.rail__menu-item:hover { background: var(--bg-hover); }

/* ── 「關於」浮層 ─────────────────────────────────────────── */
.rail__about {
  position: fixed;
  inset: 0;
  z-index: 130;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--s4);
  background: rgba(0, 0, 0, .5);
}
.rail__about-card {
  width: 100%;
  max-width: 420px;
  display: flex;
  flex-direction: column;
  gap: var(--s3);
  padding: var(--s5);
  background: var(--bg-elev);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  box-shadow: var(--sh-3);
}
.rail__about-title {
  margin: 0;
  font-size: var(--f-xl);
  font-weight: 800;
  letter-spacing: -.02em;
}
.rail__about-body {
  margin: 0;
  font-size: var(--f-sm);
  color: var(--fg-dim);
  line-height: 1.7;
}
.rail__about-foot {
  margin: 0;
  font-size: var(--f-xs);
  color: var(--fg-mute);
}
.rail__about-close {
  align-self: flex-end;
  padding: 8px 18px;
  border-radius: var(--r-pill);
  border: 1px solid var(--line);
  background: transparent;
  color: var(--fg);
  font-size: var(--f-sm);
  font-weight: 600;
  cursor: pointer;
}
.rail__about-close:hover { background: var(--bg-hover); border-color: var(--line-strong); }

/* ── inspector：分頁容器與分頁鈕的內部結構 ──────────────────
   ⚠️ .insp__panels 這一層一定要跟著撐高：.insp__body 靠 flex:1 佔滿高度，
   若它的父層只是一個沒有 flex 設定的 div，整個面板就不會捲動、內容會被切掉。 */
.insp__panels {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
}
.insp__tab-icon {
  display: inline-flex;
  align-items: center;
  line-height: 1;
  font-size: var(--f-md);
}
.insp__tab-icon svg { width: 16px; height: 16px; }
.insp__tab-label { white-space: nowrap; }

/* ── ctlSlider 的數字輸入框 ────────────────────────────────
   inspector.js 用的是 input[type=text] + inputmode=decimal（要能顯示 "35.5" 這種
   已換算的字串，而 type=number 在不同語系下對小數點的處理不一致），
   所以上面針對 input[type="number"] 的規則抓不到它 —— 用 class 補一份同樣的外觀。 */
.ctl-slider__num {
  width: 68px;
  flex-shrink: 0;
  background: var(--bg-inset);
  border: 1px solid var(--line);
  border-radius: var(--r-xs);
  color: var(--fg);
  font-size: var(--f-sm);
  padding: 4px 6px;
  text-align: right;
  font-variant-numeric: tabular-nums;
}
.ctl-slider__num:focus-visible { border-color: var(--line-strong); outline: none; }

/* ── ctlSegment（實作端的 class 是 ctl-segment，不是 ctl-seg） ──
   兩套名字都留著：上面的 .ctl-seg 是規格用語，這裡是實際渲染出來的。 */
.ctl-segment {
  display: flex;
  flex-direction: column;
  gap: var(--s1);
}
.ctl-segment__label {
  font-size: var(--f-sm);
  color: var(--fg-dim);
}
.ctl-segment__row {
  display: flex;
  gap: 2px;
  padding: 2px;
  background: var(--bg-inset);
  border-radius: var(--r-sm);
}
.ctl-segment__btn {
  flex: 1;
  border: 0;
  background: transparent;
  color: var(--fg-dim);
  font-size: var(--f-sm);
  padding: 6px 8px;
  border-radius: var(--r-xs);
  cursor: pointer;
  white-space: nowrap;
  transition: background .15s var(--ease), color .15s var(--ease);
}
.ctl-segment__btn:hover { color: var(--fg); }
.ctl-segment__btn.is-active {
  background: var(--bg-elev);
  color: var(--fg);
  box-shadow: var(--sh-1);
}

/* ── ctlSwitch：實作端除了 checkbox 還多渲染一顆 .ctl-switch__pill ──
   開關的滑動圓點是用 checkbox 自己的 ::before 畫的（見上面），
   這顆 pill 會變成一塊多出來的空白 → 直接收掉，不要兩個東西搶同一個位置。 */
.ctl-switch__pill { display: none; }

/* ── ctlColor：色票旁邊的十六進位字 ───────────────────────── */
.ctl-color__label { flex: 1; min-width: 0; }
.ctl-color__hex {
  flex-shrink: 0;
  font-size: var(--f-xs);
  color: var(--fg-mute);
  font-variant-numeric: tabular-nums;
}

/* ── ctlTiles：實作端的選中 class 是 .is-active ─────────────── */
.ctl-tiles__tile.is-active {
  color: var(--fg);
  background: var(--bg-hover);
  box-shadow: 0 0 0 2px var(--teal);
}
.ctl-tiles__label { line-height: 1.4; }

/* ── ctlButton ─────────────────────────────────────────────
   跟 .btn 是同一套視覺語彙，但預設佔滿一整行（設定面板裡的按鈕就是這樣排的）；
   放進 .ctl-row 時才改成並排等分。 */
.ctl-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s1);
  width: 100%;
  padding: 9px 16px;
  border-radius: var(--r-pill);
  border: 1px solid transparent;
  font-size: var(--f-sm);
  font-weight: 600;
  cursor: pointer;
  white-space: nowrap;
  transition: background .15s var(--ease), border-color .15s var(--ease), opacity .15s var(--ease), transform .1s var(--ease);
}
.ctl-btn:active { transform: scale(.97); }
.ctl-btn:disabled { opacity: .5; cursor: not-allowed; transform: none; }
.ctl-row > .ctl-btn { width: auto; flex: 1 1 0; }

.ctl-btn--primary { background: var(--accent); color: var(--accent-fg); }
.ctl-btn--primary:hover { opacity: .88; }

.ctl-btn--ghost { background: transparent; color: var(--fg); border-color: var(--line); }
.ctl-btn--ghost:hover { background: var(--bg-hover); border-color: var(--line-strong); }

.ctl-btn--danger { background: transparent; color: var(--danger); border-color: var(--line); }
.ctl-btn--danger:hover { background: rgba(224, 85, 85, .12); border-color: var(--danger); }

.ctl-btn__icon { display: inline-flex; align-items: center; line-height: 1; }
.ctl-btn__icon svg { width: 16px; height: 16px; }

.ctl-empty__line { margin: 0; }
.ctl-empty .ctl-btn { width: auto; margin-top: var(--s2); }

/* ── dock：按鈕內部結構 ────────────────────────────────────
   dock 是一條藥丸形的圖示列，文字只當滑鼠提示（dock.js 已經把 label 同步寫進 title），
   所以 .dock__label 用「螢幕閱讀器可讀、視覺上不佔位」的方式收起來，
   而不是 display:none —— 後者會讓輔助科技也讀不到，按鈕就變成一顆無名圖示。 */
.dock__item { position: relative; }
.dock__icon { display: inline-flex; align-items: center; justify-content: center; line-height: 1; }
.dock__icon svg { width: 20px; height: 20px; display: block; }
.dock__label {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}
.dock__item.is-disabled { opacity: .35; cursor: not-allowed; }

/* ══════════════════════════════════════════════════════════════
   首頁 home.js 實際使用的 class
   （上面第 8 節寫的是 .card--big / .card--small / .home__recent 那一套規格用語，
     實作端用的是 .home-card / .home-subcard / .home__section 這一組）
   ══════════════════════════════════════════════════════════════ */

.home__title {
  font-size: var(--f-h1);
  font-weight: 800;
  letter-spacing: -.03em;
  line-height: 1.15;
  margin: 0 0 var(--s6);
}

.home-card {
  display: flex;
  flex-direction: column;
  gap: var(--s3);
  padding: var(--s5);
  min-height: 220px;
  background: var(--bg-elev);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  color: inherit;
  text-decoration: none;
  transition: border-color .15s var(--ease);
}
.home-card:hover { border-color: var(--line-strong); }
.home-card__label {
  align-self: flex-start;
  font-size: var(--f-xs);
  font-weight: 600;
  padding: 4px 10px;
  border-radius: var(--r-pill);
  background: var(--bg-inset);
  color: var(--fg-dim);
}
.home-card__title {
  margin: 0;
  font-size: var(--f-xl);
  font-weight: 800;
  letter-spacing: -.02em;
}
.home-card__desc {
  margin: 0;
  flex: 1;
  font-size: var(--f-sm);
  color: var(--fg-dim);
  line-height: 1.7;
}
.home-card__cta {
  align-self: flex-start;
  padding: 10px 20px;
  border-radius: var(--r-pill);
  background: var(--accent);
  color: var(--accent-fg);
  font-size: var(--f-sm);
  font-weight: 600;
}
.home-card:hover .home-card__cta { opacity: .88; }

.home__section { display: flex; flex-direction: column; gap: var(--s3); margin-bottom: var(--s6); }
.home__section-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s3);
}
.home__section-title {
  margin: 0;
  font-size: var(--f-lg);
  font-weight: 800;
  letter-spacing: -.01em;
}
.home__section-link {
  flex-shrink: 0;
  font-size: var(--f-sm);
  color: var(--fg-dim);
  text-decoration: none;
}
.home__section-link:hover { color: var(--fg); }

.home__subgrid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--s3);
}
.home-subcard {
  display: flex;
  flex-direction: column;
  gap: var(--s1);
  padding: var(--s4);
  background: var(--bg-elev);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  color: inherit;
  text-decoration: none;
  transition: border-color .15s var(--ease);
}
.home-subcard:hover { border-color: var(--line-strong); }
.home-subcard__title { margin: 0; font-size: var(--f-md); font-weight: 700; }
.home-subcard__desc { margin: 0; font-size: var(--f-xs); color: var(--fg-dim); line-height: 1.6; }

.home__recent-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  gap: var(--s3);
}
/* 空狀態要橫跨整個格線，不然它會被擠成一欄寬、文字全部折行 */
.home__recent-grid > .ctl-empty { grid-column: 1 / -1; }

.home-recent-card {
  display: flex;
  flex-direction: column;
  gap: var(--s1);
  min-height: 84px;
  padding: var(--s4);
  background: var(--bg-elev);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  color: inherit;
  text-decoration: none;
  transition: border-color .15s var(--ease);
}
.home-recent-card:hover { border-color: var(--line-strong); }
.home-recent-card__name {
  font-size: var(--f-md);
  font-weight: 700;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.home-recent-card__meta { font-size: var(--f-xs); color: var(--fg-dim); }

.home__footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--s2);
  padding-top: var(--s5);
  margin-top: var(--s6);
  border-top: 1px solid var(--line);
  font-size: var(--f-xs);
  color: var(--fg-mute);
}
.home__footer-nav { display: flex; gap: var(--s3); }
.home__footer-nav a { color: var(--fg-dim); text-decoration: none; }
.home__footer-nav a:hover { color: var(--fg); }
.home__footer-brand { color: var(--fg-mute); }

@media (max-width: 819px) {
  .home__subgrid { grid-template-columns: 1fr; }
  .home__title { font-size: var(--f-h2); }
}

@media (max-width: 819px) {
  /* 手機上 rail 是底部橫列，桌面「收合時把品牌改直排」那條規則要收回來，
     否則品牌與展開鈕會在橫列裡疊成兩層、把整條 rail 撐高。 */
  .rail:not(.rail--open) .rail__top {
    flex-direction: row;
    padding: 0;
    gap: var(--s2);
  }
}

/* ══════════════════════════════════════════════════════════════
   對比修正（收口者）— SHELL_SPEC 驗收條款 12：文字對比要 ≥ 4.5:1
   ──────────────────────────────────────────────────────────────
   實測 --fg-mute 在兩個主題下都不到門檻：
     深色 #6e6d6a on #232323 = 3.04:1
     淺色 #8d8c88 on #ffffff = 3.37:1
   （--fg-dim 則是深色 7.10:1 / 淺色 6.55:1，安全。）
   token 值本身是規格白紙黑字寫死的，不去動它（動了會波及所有頁面）；
   改成「**帶資訊的**灰字一律用 --fg-dim」，--fg-mute 只留給純裝飾
   （chevron、空狀態圖示、卡片縮圖佔位這種不承載文字內容的東西）。
   ══════════════════════════════════════════════════════════════ */

/* section 右側那行灰字是「目前值」（120 cm × 60 cm × 74 cm），是資訊不是裝飾 */
.sect__value,
/* slider 旁邊的單位字、色票旁的十六進位值，同理 */
.ctl-slider__unit,
.ctl-color__hex,
/* 頁尾與帳號選單裡的次要文字 */
.foot,
.home__footer,
.home__footer-brand,
.rail__menu-email,
.rail__about-foot {
  color: var(--fg-dim);
}

/* 卡片右上角的「⋯」是可互動元件，非文字元素的門檻是 3:1，
   但 3.04:1 只是剛好擦邊 —— 一起提到 --fg-dim，滑鼠不必猜它在哪。 */
.proj-card__menu-btn { color: var(--fg-dim); }

/* ══════════════════════════════════════════════════════════════
   登入 / 註冊覆蓋層（收口者新增）
   ──────────────────────────────────────────────────────────────
   assets/js/auth-ui.js 的 requireLogin() 會在 document.body 上蓋一層
   #auth-overlay（.auth）。它原本的樣式只寫在 app.css 裡，而 app.css 是
   **設計器專屬**的深色 3D 疊層樣式表 —— index.html / projects.html 沒有
   （也不該）載入它，結果那兩頁的登入表單完全沒有樣式：整塊擠在文件流裡
   把 .shell 往下推，rail 的 sticky 容器也跟著爆掉。
   → 把這一層改用新 token 重寫在 shell.css，三頁共用同一份、而且會跟著
     深／淺色主題走。booth.html 兩支 CSS 都載入時，shell.css 在後面 →
     由這一份勝出，外觀全站一致。
   ══════════════════════════════════════════════════════════════ */

.auth {
  position: fixed;
  inset: 0;
  z-index: 200;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--s4);
  overflow-y: auto;
  background: var(--bg);
}

.auth__card {
  width: 100%;
  max-width: 400px;
  margin: auto;
  display: flex;
  flex-direction: column;
  gap: var(--s4);
  padding: var(--s6);
  background: var(--bg-elev);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  box-shadow: var(--sh-3);
}

.auth__title {
  margin: 0;
  font-size: var(--f-h2);
  font-weight: 800;
  letter-spacing: -.02em;
}
.auth__sub {
  margin: 0;
  font-size: var(--f-sm);
  color: var(--fg-dim);
  line-height: 1.6;
}

.auth__tabs {
  display: flex;
  gap: 2px;
  padding: 2px;
  background: var(--bg-inset);
  border-radius: var(--r-sm);
}
.auth__tab {
  flex: 1;
  border: 0;
  background: transparent;
  color: var(--fg-dim);
  font-size: var(--f-sm);
  font-weight: 600;
  padding: 8px 12px;
  border-radius: var(--r-xs);
  cursor: pointer;
  transition: background .15s var(--ease), color .15s var(--ease);
}
.auth__tab:hover { color: var(--fg); }
.auth__tab.is-on {
  background: var(--bg-elev);
  color: var(--fg);
  box-shadow: var(--sh-1);
}

/* 錯誤訊息：沒有內容時完全不佔位（:empty），才不會在表單裡留一道空隙 */
.auth__err {
  font-size: var(--f-sm);
  line-height: 1.6;
  color: var(--danger);
}
.auth__err:empty { display: none; }

.auth__google { display: flex; flex-direction: column; gap: var(--s4); }
.auth__gbtn { display: flex; justify-content: center; min-height: 40px; }

/* 「或用 Email 登入」：文字兩側各一條線，用 ::before/::after 拉伸 */
.auth__divider {
  display: flex;
  align-items: center;
  gap: var(--s3);
  font-size: var(--f-xs);
  color: var(--fg-mute);
}
.auth__divider::before,
.auth__divider::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--line);
}

.auth__form { display: flex; flex-direction: column; gap: var(--s3); }
.auth__form.is-hidden { display: none; }

.auth__field { display: flex; flex-direction: column; gap: var(--s1); }
.auth__label { font-size: var(--f-xs); color: var(--fg-dim); }

.auth__input {
  width: 100%;
  background: var(--bg-inset);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  color: var(--fg);
  font-size: var(--f-md);
  padding: 10px 12px;
}
.auth__input::placeholder { color: var(--fg-mute); }
.auth__input:focus-visible { border-color: var(--line-strong); outline: none; }

/* 密碼欄：輸入框右側疊一顆小眼睛，右邊 padding 要留給它 */
.auth__passbox { position: relative; display: flex; }
.auth__input--pass { padding-right: 42px; }
.auth__eye {
  position: absolute;
  top: 50%;
  right: 6px;
  transform: translateY(-50%);
  width: 32px;
  height: 32px;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 0;
  border-radius: var(--r-xs);
  background: transparent;
  color: var(--fg-mute);
  cursor: pointer;
}
.auth__eye:hover { color: var(--fg); background: var(--bg-hover); }

.auth__remember {
  display: flex;
  align-items: center;
  gap: var(--s2);
  font-size: var(--f-sm);
  color: var(--fg-dim);
  cursor: pointer;
}
.auth__check {
  width: 16px;
  height: 16px;
  flex-shrink: 0;
  accent-color: var(--teal);
  cursor: pointer;
}

.auth__submit {
  margin-top: var(--s2);
  padding: 12px 20px;
  border: 0;
  border-radius: var(--r-pill);
  background: var(--accent);
  color: var(--accent-fg);
  font-size: var(--f-md);
  font-weight: 700;
  cursor: pointer;
  transition: opacity .15s var(--ease);
}
.auth__submit:hover { opacity: .88; }
/* 送出中：auth-ui.js 會同時 disabled 並寫 data-busy="1" */
.auth__submit:disabled,
.auth__submit[data-busy="1"] { opacity: .55; cursor: progress; }

@media (max-width: 480px) {
  .auth__card { padding: var(--s5) var(--s4); }
  .auth__title { font-size: var(--f-xl); }
}

/* ══════════════════════════════════════════════════════════════
   第二階段收口：inspector 裡的條件顯示與說明文字
   ══════════════════════════════════════════════════════════════ */

/* ── 條件顯示（桌巾選「無」時收起色票與垂長、背景選攝影棚時收起背景色…） ──
   booth-ui.js 的 showIf() 是靠 node.hidden = true 收起控制元件，而 [hidden] 的
   display:none 只來自 UA 樣式表 —— 上面每一個 .ctl-* 都各自宣告了 display:flex/grid，
   類別選擇器一律贏過 UA 樣式，控制元件會「設了 hidden 卻還是看得見」。
   所以這裡要明確把它壓回去。限定在 .inspector 內，不去動全站其他地方的 [hidden] 語意。 */
.inspector [hidden] { display: none !important; }

/* ── 面板裡的說明小字（放到桌上的操作提示、去背匯出的注意事項…） ──
   跟 .sect__value 同一種灰（--fg-dim，對比 ≥ 4.5:1），但它是整行的句子，
   所以要有行高與上下留白，不能直接沿用那個右側單行的樣式。 */
.ctl-note {
  margin: 0;
  padding: 2px 2px 0;
  font-size: var(--f-xs);
  line-height: 1.6;
  color: var(--fg-dim);
}

/* ── 小標題 + 控制元件（ctlTiles 沒有 label 參數，由 booth-ui.js 的 labeled() 包出來） ──
   .sect__body 的 gap 是 var(--s3)，標題直接當兄弟節點會離自己的磚格太遠、
   反而看起來像屬於上面那一組 → 包一層自己的小 gap。 */
.ctl-group { display: flex; flex-direction: column; gap: 6px; }
.ctl-group__label { font-size: var(--f-sm); color: var(--fg-dim); }

/* 展示架／桌腳的線稿磚格：線條要看得出來，所以縮圖底不要用 --bg-elev 的實色塊擋住，
   並讓 SVG 自己填滿整格（.ctl-tiles svg 已經給了 width:100%/aspect-ratio:1）。 */
.ctl-tiles__tile svg { display: block; padding: 6px; box-sizing: border-box; }

/* canvas 縮圖（板材樣式、桌面材質、展示架木色）是「材質的真實長相」，
   用 pixelated 會讓 512² 的貼圖縮到 60px 時出現鋸齒與摩爾紋（本專案地面貼圖踩過），
   維持預設的平滑縮放；圓角跟磚格對齊即可。 */
.ctl-tiles__tile canvas { display: block; }

/* ══════════════════════════════════════════════════════════════
   第三階段收口：商品模擬工作室（studio.html）與設計器的商品庫子模式

   ⚠️ 這一段只**新增** class，沒有動到上面任何既有選擇器 ——
   booth.html 與 index.html 的視覺一個像素都不會變。
   ══════════════════════════════════════════════════════════════ */

/* ── 膠囊的警示狀態（圖稿解析度 < 300 dpi） ────────────────────
   規格要求「dpi < 300 時膠囊變橘色並顯示解析度偏低」。用 --warn 而不是 --danger：
   解析度偏低是「印出來會糊」的提醒，不是「不能用」的錯誤。 */
.stage__badges .badge--warn {
  color: var(--warn);
  border-color: color-mix(in srgb, var(--warn) 55%, var(--line));
  background: color-mix(in srgb, var(--warn) 12%, var(--bg-elev));
}

/* ── 選項會隨商品類型改變的分段選擇插槽 ──────────────────────
   studio-ui.js 的 segmentSlot()。它只是一個容器，唯一需要的樣式是「被 hidden 時要真的消失」
   （.inspector [hidden] 已經處理），以及不要在 .sect__body 的 flex column 裡撐出多餘高度。 */
.ctl-slot { display: block; }

/* ── 圖稿區塊：縮圖 + 名稱 + dpi ──────────────────────────── */
.studio-art {
  display: flex;
  align-items: center;
  gap: var(--s3);
  min-width: 0;
}
.studio-art__thumb {
  flex: 0 0 auto;
  width: 56px;
  height: 56px;
  border-radius: var(--r-sm);
  border: 1px solid var(--line);
  background: var(--bg-inset);
  overflow: hidden;
  display: grid;
  place-items: center;
}
/* 去背 PNG 的縮圖用 contain：cover 會把角色的頭切掉，使用者會以為圖被我們弄壞了。 */
.studio-art__thumb img,
.studio-art__thumb canvas {
  width: 100%;
  height: 100%;
  object-fit: contain;
  display: block;
}
.studio-art__meta { min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.studio-art__name {
  font-size: var(--f-md);
  color: var(--fg);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.studio-art__dpi { font-size: var(--f-xs); color: var(--fg-dim); }
.studio-art__dpi.is-low { color: var(--warn); }

/* ── 我的圖庫（點「從我的圖庫選」才展開） ─────────────────── */
.studio-lib { margin-top: 2px; }
.studio-lib__grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 6px;
}
.studio-lib__item {
  aspect-ratio: 1;
  padding: 2px;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: var(--bg-inset);
  cursor: pointer;
  overflow: hidden;
  transition: border-color .15s var(--ease);
}
.studio-lib__item:hover { border-color: var(--line-strong); }
.studio-lib__item img { width: 100%; height: 100%; object-fit: contain; display: block; }

/* ── 名稱輸入框（inspector.js 沒有 ctlText，工作室自己補一顆） ── */
.ctl-text { display: flex; flex-direction: column; gap: 6px; }
.ctl-text__label { font-size: var(--f-sm); color: var(--fg-dim); }
.ctl-text__input {
  width: 100%;
  padding: 8px 10px;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: var(--bg-inset);
  color: var(--fg);
  font-family: inherit;
  font-size: var(--f-md);
}
.ctl-text__input:hover { border-color: var(--line-strong); }
.ctl-text__input:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

/* ── 匯出分頁的規格摘要 ─────────────────────────────────── */
.studio-summary { display: flex; flex-direction: column; gap: 4px; font-size: var(--f-md); color: var(--fg-dim); }
.studio-summary .is-low { color: var(--warn); }

/* ── 3D 起不來時的說明（WebGL 不支援） ───────────────────── */
.studio-stage-error {
  position: absolute;
  inset: auto var(--s5) var(--s5);
  margin: 0;
  padding: var(--s3) var(--s4);
  border-radius: var(--r-md);
  border: 1px solid var(--line);
  background: var(--bg-elev);
  color: var(--fg-dim);
  font-size: var(--f-md);
  line-height: 1.6;
}

/* ── 工作室自己的 toast（這一頁不載 hud.js，那是攤位那一整套） ── */
.studio-toasts {
  position: fixed;
  left: 50%;
  bottom: 92px;
  transform: translateX(-50%);
  z-index: 60;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  pointer-events: none;
}
.studio-toast {
  padding: 8px 16px;
  border-radius: var(--r-pill);
  border: 1px solid var(--line);
  background: var(--bg-elev);
  color: var(--fg);
  font-size: var(--f-md);
  box-shadow: 0 8px 24px rgba(0, 0, 0, .18);
  max-width: 70vw;
  text-align: center;
}
.studio-toast--success { border-color: color-mix(in srgb, var(--accent) 60%, var(--line)); }
.studio-toast--warn { border-color: color-mix(in srgb, var(--warn) 60%, var(--line)); color: var(--warn); }
.studio-toast--error { border-color: color-mix(in srgb, var(--danger) 60%, var(--line)); color: var(--danger); }

/* ── 設計器「編輯 ▸ 商品」子模式的商品庫清單 ─────────────── */
.merch-lib { display: flex; flex-direction: column; gap: 6px; }
.merch-lib__item {
  display: flex;
  flex-direction: column;
  gap: 2px;
  width: 100%;
  padding: 8px 10px;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: var(--bg-inset);
  color: var(--fg);
  font-family: inherit;
  text-align: left;
  cursor: pointer;
  transition: border-color .15s var(--ease), background .15s var(--ease);
}
.merch-lib__item:hover { background: var(--bg-hover); border-color: var(--line-strong); }
.merch-lib__name { font-size: var(--f-md); font-weight: 600; }
.merch-lib__spec { font-size: var(--f-xs); color: var(--fg-dim); }

/* ── 「編輯」分頁頂端：桌上目前有什麼（展示架／商品／小物） ──────
   常駐一行，不是可摺疊的 .sect，直接是 .insp__body 的第一個 flex 子項（沿用它的 gap）。
   讀不到 PropStore 時用 [hidden] 整列收起——`.inspector [hidden]` 那條規則已經在別處把
   [hidden] 壓回 display:none，這裡不用重複寫。 */
.edit-stats {
  padding: 8px 10px;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: var(--bg-inset);
  color: var(--fg-dim);
  font-size: var(--f-sm);
  line-height: 1.5;
}

/* ── 「匯出 ▸ 出攤清單」預覽區：桌上展示架／商品（工作室） ──────
   跟 .bom__section／.bom__table（app.css 那份匯出面板）是不同的兩套 DOM——這裡是新外殼
   inspector 分頁裡的輕量預覽，不是舊版可列印的那張表，所以另外取名不共用樣式。 */
.bom-preview { display: flex; flex-direction: column; gap: 10px; }
.bom-preview__group { display: flex; flex-direction: column; gap: 4px; }
.bom-preview__title { font-size: var(--f-sm); font-weight: 600; color: var(--fg); }
.bom-preview__row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s2);
  padding: 2px 0;
  font-size: var(--f-xs);
  color: var(--fg-dim);
  border-bottom: 1px dashed var(--line);
}
.bom-preview__row:last-child { border-bottom: 0; }
.bom-preview__name { color: var(--fg); }
.bom-preview__qty { white-space: nowrap; }

/* ══════════════════════════════════════════════════════════════════════
 * 側欄品牌名：兩行（2026-08-25 正名為「創世二次元展場預覽系統」）
 *
 * 全名 12 個字，側欄展開後品牌區只剩約 160px —— 維持單行會被 ellipsis 切成
 * 「創世二次元展場預…」，讀不出這是什麼系統。所以拆成兩行、在語意斷點斷開。
 *
 * ⚠️ 收合狀態靠的是既有的
 *    `.rail:not(.rail--open) .rail__brand > span:not(:first-child) { display:none }`，
 *    它藏的是 .rail__brand 的**直接子元素**。兩行都包在 .rail__brand-text 這一個 span
 *    裡面，所以收合行為完全不受影響（仍然整塊藏起來，只留 logo 圖示）。
 * ══════════════════════════════════════════════════════════════════════ */
.rail__brand-text {
  display: flex;
  flex-direction: column;
  justify-content: center;
  line-height: 1.22;
  white-space: normal;   /* 覆蓋上面單行版的 nowrap */
  overflow: visible;
}
.rail__brand-line {
  display: block;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.rail__brand-line--sub {
  font-size: var(--f-xs);
  font-weight: 600;
  letter-spacing: 0;
  color: var(--fg-dim);
}

/* ══════════════════════════════════════════════════════════════
   第四階段：自訂展板 / 我的物件庫 / 微調工具列

   ⚠️ 同樣只**新增** class，沒有動到上面任何既有選擇器。
   ══════════════════════════════════════════════════════════════ */

/* ── 我的物件庫：一列一個存好的規格 ────────────────────────────
   左邊整塊是「放一個」的按鈕（點擊面積要大，這是主要動作），
   右邊一顆小 ✕ 是「從庫裡刪掉」（次要動作，不要跟主要動作等重）。 */
.lib-list { display: flex; flex-direction: column; gap: 6px; }

.lib-item {
  display: flex;
  align-items: stretch;
  gap: 6px;
}
.lib-item__use {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s2);
  padding: 8px 10px;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: var(--bg-elev);
  color: var(--fg);
  font-size: var(--f-md);
  text-align: left;
  cursor: pointer;
}
.lib-item__use:hover { background: var(--bg-hover); border-color: var(--line-strong); }
.lib-item__name {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.lib-item__kind {
  flex: 0 0 auto;
  font-size: var(--f-xs);
  color: var(--fg-dim);
}
.lib-item__del {
  flex: 0 0 auto;
  width: 32px;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: transparent;
  color: var(--fg-dim);
  cursor: pointer;
  line-height: 1;
}
.lib-item__del:hover {
  color: var(--danger);
  border-color: color-mix(in srgb, var(--danger) 55%, var(--line));
}

/* ── 微調工具列 ────────────────────────────────────────────────
   浮在畫布左下角。z-index 要比 .stage__badges(2) 高，但比 .dock(5) 低 ——
   dock 是常駐工具列，被一個臨時面板蓋住會很怪。
   ⚠️ 位置刻意避開 dock（bottom 大於 dock 的高度 + 間距），
   手機直向時 dock 會換行變高，所以下面有 media query 把它改成貼著上緣。 */
.nudge {
  position: absolute;
  left: var(--s3);
  bottom: 84px;
  z-index: 4;
  width: 190px;
  display: flex;
  flex-direction: column;
  gap: var(--s3);
  padding: var(--s3);
  border-radius: var(--r-md);
  border: 1px solid var(--line);
  background: color-mix(in srgb, var(--bg-elev) 92%, transparent);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  box-shadow: 0 8px 28px rgba(0,0,0,.18);
}
.nudge[hidden] { display: none; }

.nudge__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s2);
}
.nudge__name {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: var(--f-md);
  font-weight: 600;
  color: var(--fg);
}
.nudge__close {
  flex: 0 0 auto;
  width: 22px; height: 22px;
  border: 0;
  border-radius: var(--r-xs);
  background: transparent;
  color: var(--fg-dim);
  cursor: pointer;
  line-height: 1;
}
.nudge__close:hover { background: var(--bg-hover); color: var(--fg); }

.nudge__group { display: flex; flex-direction: column; gap: 6px; }
.nudge__group--row { flex-direction: row; gap: 6px; }
.nudge__label { font-size: var(--f-xs); color: var(--fg-dim); }

/* 方向鍵排成十字：中間那格放一個裝飾符號，四個角留空 —— 這樣「上下左右」的空間關係
   一眼就對得上場景裡的方向，比四顆並排的按鈕好懂太多。 */
.nudge__pad {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 4px;
}
.nudge__padmid {
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: var(--f-xs);
  color: var(--fg-mute);
}

.nudge__spin {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: 4px;
}
.nudge__angle {
  text-align: center;
  font-size: var(--f-sm);
  font-variant-numeric: tabular-nums;
  color: var(--fg-dim);
}

.nudge__btn {
  padding: 6px 4px;
  border: 1px solid var(--line);
  border-radius: var(--r-xs);
  background: var(--bg-elev);
  color: var(--fg);
  font-size: var(--f-md);
  line-height: 1.2;
  cursor: pointer;
}
.nudge__btn:hover { background: var(--bg-hover); border-color: var(--line-strong); }
.nudge__btn:active { transform: translateY(1px); }
.nudge__btn--wide { flex: 1 1 0; font-size: var(--f-sm); }
.nudge__btn--danger { flex: 0 0 auto; padding-inline: 10px; font-size: var(--f-sm); color: var(--danger); }
.nudge__btn--danger:hover { border-color: color-mix(in srgb, var(--danger) 55%, var(--line)); }

.nudge__hint {
  margin: 0;
  font-size: var(--f-xs);
  line-height: 1.5;
  color: var(--fg-dim);
}

/* 微調列裡的分段按鈕要比面板裡的緊湊（190px 寬要塞下四檔角度）。 */
.nudge .ctl-segment { gap: 0; }
.nudge .ctl-segment__btn { padding: 4px 2px; font-size: var(--f-xs); }

/* 窄視窗：dock 會換行變高，微調列改貼上緣（膠囊在左上，所以往右錯開）。 */
@media (max-width: 720px) {
  .nudge { bottom: auto; top: 52px; left: auto; right: var(--s3); width: 168px; }
}

/* ── 單行文字輸入（物件庫命名欄）──────────────────────────────
   跟 .ctl-slider__num 同一套外觀，但它是整行寬（名字會比數字長）。 */
.ctl-text {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.ctl-text__label { font-size: var(--f-sm); color: var(--fg-dim); }
.ctl-text__input {
  width: 100%;
  box-sizing: border-box;
  padding: 7px 10px;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: var(--bg-inset);
  color: var(--fg);
  font: inherit;
  font-size: var(--f-md);
}
.ctl-text__input:focus {
  outline: none;
  border-color: var(--line-strong);
}
