/* ============================================================
   confy web — 滑鼠優先設定樹編輯器
   方向：tech / utility（Datadog / GitHub）。一族字體、密集表格、
   tabular numerics、tinted 狀態 pill。深淺主題切換。
   ============================================================ */
:root[data-theme="light"]{
  --bg:      oklch(98% 0.005 250);
  --surface: oklch(100% 0 0);
  --panel:   oklch(96.5% 0.005 250);
  --fg:      oklch(22% 0.02 240);
  --muted:   oklch(50% 0.018 240);
  --faint:   oklch(64% 0.015 240);
  --border:  oklch(90% 0.008 240);
  --border-strong: oklch(82% 0.01 240);
  --accent:  oklch(55% 0.16 250);
  --accent-soft: oklch(94% 0.04 250);
  --sel:     oklch(93% 0.05 250);
  --sel-edge: oklch(70% 0.12 250);
  --marquee: oklch(55% 0.16 250 / .12);
  --drop:    oklch(58% 0.16 145);
  --shadow:  0 1px 2px oklch(20% 0.02 240 / .08), 0 8px 24px oklch(20% 0.02 240 / .12);
  /* 型別色 */
  --t-string:  oklch(48% 0.13 150);
  --t-number:  oklch(50% 0.15 250);
  --t-bool:    oklch(52% 0.16 25);
  --t-date:    oklch(50% 0.13 300);
  --t-null:    oklch(60% 0.01 240);
  --t-branch:  oklch(45% 0.04 240);
}
:root[data-theme="dark"]{
  --bg:      oklch(20% 0.018 250);
  --surface: oklch(24% 0.02 250);
  --panel:   oklch(27% 0.022 250);
  --fg:      oklch(93% 0.01 240);
  --muted:   oklch(68% 0.015 240);
  --faint:   oklch(55% 0.015 240);
  --border:  oklch(33% 0.02 250);
  --border-strong: oklch(42% 0.025 250);
  --accent:  oklch(72% 0.14 250);
  --accent-soft: oklch(34% 0.06 250);
  --sel:     oklch(34% 0.06 250);
  --sel-edge: oklch(62% 0.12 250);
  --marquee: oklch(72% 0.14 250 / .16);
  --drop:    oklch(72% 0.15 145);
  --shadow:  0 1px 2px oklch(0% 0 0 / .3), 0 12px 32px oklch(0% 0 0 / .42);
  --t-string:  oklch(78% 0.14 150);
  --t-number:  oklch(76% 0.13 250);
  --t-bool:    oklch(76% 0.15 30);
  --t-date:    oklch(76% 0.12 300);
  --t-null:    oklch(62% 0.01 240);
  --t-branch:  oklch(82% 0.03 240);
}
:root{
  --font: -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans TC", system-ui, sans-serif;
  --mono: "JetBrains Mono", "IBM Plex Mono", ui-monospace, "SF Mono", Menlo, monospace;
  --row-h: 30px;
  --indent: 22px;
  /* 觸控 / RWD 基礎令牌（desktop 預設不改） */
  --hit: 44px;           /* 最小觸控目標尺寸 (WCAG 2.5.5) */
  --row-h-touch: 44px;   /* 觸控裝置列高（pointer:coarse 媒體查詢生效） */
}
*{box-sizing:border-box}
html,body{height:100%}
body{
  margin:0; background:var(--bg); color:var(--fg);
  font-family:var(--font); font-size:14px; line-height:1.45;
  -webkit-font-smoothing:antialiased;
  display:flex; flex-direction:column;
  overflow:hidden;
}
button{font-family:inherit;color:inherit;cursor:pointer}
:focus-visible{outline:2px solid var(--accent);outline-offset:1px}

/* ---------- toolbar ---------- */
.toolbar{
  display:flex; align-items:center; gap:10px;
  padding:8px 12px; background:var(--surface);
  border-bottom:1px solid var(--border); flex:0 0 auto;
}
.brand{display:flex;align-items:center;gap:8px;font-weight:700;letter-spacing:-.01em}
.brand .logo{
  width:22px;height:22px;border-radius:6px;display:grid;place-items:center;
  background:var(--accent);color:var(--bg);font-family:var(--mono);font-weight:800;font-size:13px;
}
.fmt-pill{
  font-family:var(--mono);font-size:11px;font-weight:600;letter-spacing:.04em;
  padding:2px 8px;border:1px solid var(--border-strong);border-radius:999px;
  color:var(--muted);background:var(--panel);cursor:pointer;
}
.dirty-dot{width:8px;height:8px;border-radius:50%;background:var(--accent);opacity:0;transition:opacity .2s}
.dirty .dirty-dot{opacity:1}
.spacer{flex:1}
.tbtn{
  display:inline-flex;align-items:center;gap:6px;height:32px;padding:0 12px;
  background:var(--panel);border:1px solid var(--border-strong);border-radius:8px;
  font-size:13px;font-weight:500;color:var(--fg);transition:background .12s,border-color .12s;
  white-space:nowrap;
}
.tbtn:hover{background:var(--accent-soft);border-color:var(--sel-edge)}
.tbtn.primary{background:var(--accent);color:var(--bg);border-color:transparent;font-weight:600}
.tbtn.primary:hover{filter:brightness(1.06)}
.tbtn .ic{width:15px;height:15px;display:block}
.icon-btn{
  width:32px;height:32px;display:grid;place-items:center;padding:0;
  background:var(--panel);border:1px solid var(--border-strong);border-radius:8px;
}
.icon-btn:hover{background:var(--accent-soft);border-color:var(--sel-edge)}
.icon-btn:disabled{opacity:.35;cursor:not-allowed}
.icon-btn .ic{width:16px;height:16px}
.tgroup{display:flex;gap:6px}
.more-btn{display:none;padding:0 10px}

/* ---------- filter row ---------- */
.filterbar{
  display:flex;align-items:center;gap:10px;padding:8px 12px;
  background:var(--surface);border-bottom:1px solid var(--border);flex:0 0 auto;
}
/* min-width:96px (well below the input's ~170px min-content) lets the search box
   yield space to the right-side action buttons as the window narrows, so they
   stay on one line longer instead of collapsing into ⋯ prematurely. */
.search{position:relative;flex:1;min-width:96px}
.search .ic{position:absolute;left:10px;top:50%;transform:translateY(-50%);width:16px;height:16px;color:var(--faint);pointer-events:none}
.search input{
  width:100%;height:34px;padding:0 32px 0 34px;border-radius:9px;
  border:1px solid var(--border-strong);background:var(--panel);color:var(--fg);
  font-size:13.5px;font-family:var(--font);
}
.search input::placeholder{color:var(--faint)}
.search .clear{position:absolute;right:6px;top:50%;transform:translateY(-50%);
  width:22px;height:22px;border:0;background:transparent;border-radius:6px;color:var(--faint);display:none}
.search.has-val .clear{display:grid;place-items:center}
.search .clear:hover{background:var(--border);color:var(--fg)}

/* ---------- main / tree ---------- */
/* overflow:hidden clips the off-canvas detail panel (translateX(100%)) so it
   can never be scrolled into view; the tree-wrap owns its own scrolling. */
.main{flex:1;min-height:0;display:flex;position:relative;overflow:hidden}
.tree-wrap{flex:1;min-width:0;overflow:auto;position:relative;padding:6px 4px 80px}
#tree{position:relative;user-select:none}
.marquee{position:absolute;border:1px solid var(--sel-edge);background:var(--marquee);
  pointer-events:none;z-index:5;border-radius:2px;display:none}

.row{
  display:flex;align-items:center;gap:0;min-height:var(--row-h);
  padding-right:8px;border-radius:7px;position:relative;
  scroll-margin:60px;
}
.row:hover{background:color-mix(in oklch,var(--panel) 60%,transparent)}
.row.selected{background:var(--sel);box-shadow:inset 0 0 0 1px var(--sel-edge)}
.row.cursor::before{content:"";position:absolute;left:2px;top:4px;bottom:4px;width:3px;border-radius:2px;background:var(--accent)}
.row.cut{opacity:.45}
.row.drag-over-into{box-shadow:inset 0 0 0 2px var(--drop)}
.row.drag-src{opacity:.4}
.row .indent{flex:0 0 auto}
.caret{
  flex:0 0 auto;width:20px;height:20px;display:grid;place-items:center;border:0;background:transparent;
  color:var(--faint);border-radius:5px;margin-left:2px;
}
.caret svg{width:11px;height:11px;transition:transform .15s}
.row.open > .caret svg{transform:rotate(90deg)}
.caret.leaf{visibility:hidden}
.caret:hover{background:var(--border);color:var(--fg)}

.key{
  font-family:var(--mono);font-size:13px;font-weight:600;color:var(--fg);
  padding:3px 4px;border-radius:5px;white-space:nowrap;max-width:38vw;flex-shrink:0;overflow:hidden;text-overflow:ellipsis;
}
.row.branch .key{color:var(--t-branch);font-weight:700}
.key:hover{background:color-mix(in oklch,var(--accent-soft) 70%,transparent)}
.eq{color:var(--faint);font-family:var(--mono);margin:0 8px;flex:0 0 auto}
.val{
  font-family:var(--mono);font-size:13px;padding:3px 6px;border-radius:5px;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:46vw;min-width:0;
}
.val:hover{background:color-mix(in oklch,var(--accent-soft) 70%,transparent);cursor:text}
.val.t-string{color:var(--t-string)}
.val.t-number{color:var(--t-number)}
.val.t-bool{color:var(--t-bool)}
.val.t-date{color:var(--t-date)}
.val.t-null{color:var(--t-null);font-style:italic}
.count{font-family:var(--mono);font-size:11.5px;color:var(--faint);margin-left:6px}
.comment{font-family:var(--mono);font-size:12px;color:var(--faint);font-style:italic;margin-left:10px;white-space:nowrap;min-width:0;overflow:hidden;text-overflow:ellipsis}
.comment-row .key,.comment-row .eq{display:none}
.comment-row .comment{margin-left:0;flex:0 1 auto}

.kind{
  flex:0 0 auto;margin-left:8px;font-family:var(--mono);font-size:10.5px;font-weight:600;
  letter-spacing:.02em;padding:2px 7px 2px 8px;border-radius:999px;
  border:1px solid var(--border-strong);background:var(--panel);color:var(--muted);
  display:inline-flex;align-items:center;gap:4px;white-space:nowrap;
}
.kind:hover{border-color:var(--sel-edge);color:var(--fg);background:var(--accent-soft)}
.kind .chev{width:9px;height:9px;opacity:.6}

.row-actions{margin-left:auto;display:flex;gap:2px;opacity:0;flex:0 0 auto;transition:opacity .1s}
.row:hover .row-actions,.row.selected .row-actions{opacity:1}
.row-actions button{
  width:24px;height:24px;display:grid;place-items:center;border:0;background:transparent;
  border-radius:6px;color:var(--faint);
}
.row-actions button:hover{background:var(--border);color:var(--fg)}
.row-actions button .ic{width:14px;height:14px}

/* inline edit input */
.cell-input{
  font-family:var(--mono);font-size:13px;padding:2px 6px;border-radius:5px;
  border:1px solid var(--accent);background:var(--surface);color:var(--fg);
  min-width:80px;max-width:46vw;
}
.key-input{font-weight:600}

/* drag handle */
.drag-handle{
  flex:0 0 auto;width:16px;display:grid;place-items:center;color:var(--faint);
  cursor:grab;opacity:0;transition:opacity .1s;
}
.row:hover .drag-handle{opacity:.55}
.drag-handle:hover{opacity:1}
.drag-handle svg{width:11px;height:13px}
.drop-line{position:absolute;left:0;right:8px;height:2px;background:var(--drop);border-radius:2px;z-index:6;pointer-events:none;display:none}

/* ---------- detail panel ---------- */
.detail{
  flex:0 0 min(320px,92vw);border-left:1px solid var(--border);background:var(--surface);
  display:flex;flex-direction:column;transform:translateX(100%);transition:transform .22s cubic-bezier(.2,.8,.2,1);
  position:absolute;right:0;top:0;bottom:0;width:min(320px, 92vw);z-index:30;box-shadow:var(--shadow);
}
.detail.open{transform:translateX(0)}
.detail-head{display:flex;align-items:center;justify-content:space-between;padding:14px 16px;border-bottom:1px solid var(--border)}
.detail-head h3{margin:0;font-size:14px;font-weight:700}
.detail-body{padding:16px;overflow:auto;font-size:13px}
.detail-body dl{margin:0;display:grid;grid-template-columns:auto 1fr;gap:8px 14px}
.detail-body dt{color:var(--muted);font-size:12px}
.detail-body dd{margin:0;font-family:var(--mono);font-size:12.5px;word-break:break-all}
.detail-body .preview{margin-top:16px;background:var(--bg);border:1px solid var(--border);border-radius:8px;
  padding:10px 12px;font-family:var(--mono);font-size:12px;white-space:pre-wrap;color:var(--fg);max-height:240px;overflow:auto}

/* ---------- footer ---------- */
.footer{
  flex:0 0 auto;display:flex;align-items:center;gap:14px;padding:6px 14px;
  background:var(--surface);border-top:1px solid var(--border);font-size:12.5px;color:var(--muted);
}
.footer .status{flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.footer .status.err{color:var(--t-bool)}
.badge{font-family:var(--mono);font-size:11.5px;padding:2px 8px;border-radius:999px;background:var(--panel);border:1px solid var(--border)}
.badge.on{background:var(--accent-soft);border-color:var(--sel-edge);color:var(--fg)}
.hint{color:var(--faint);font-size:11.5px}
.hint kbd{font-family:var(--mono);font-size:10.5px;background:var(--panel);border:1px solid var(--border);
  border-bottom-width:2px;border-radius:4px;padding:1px 5px;margin:0 1px}

/* ---------- popovers / menu ---------- */
.pop{
  position:fixed;z-index:200;background:var(--surface);border:1px solid var(--border-strong);
  border-radius:10px;box-shadow:var(--shadow);min-width:180px;padding:5px;display:none;
  max-width: min(280px, 92vw); max-height: calc(100vh - 16px); overflow-y: auto;
}
.pop.open{display:block;animation:pop .12s ease}
@keyframes pop{from{opacity:0;transform:translateY(-4px) scale(.98)}to{opacity:1;transform:none}}
.menu-item{
  display:flex;align-items:center;gap:10px;width:100%;border:0;background:transparent;
  padding:7px 10px;border-radius:7px;font-size:13px;text-align:left;color:var(--fg);
}
.menu-item:hover:not(:disabled){background:var(--accent-soft)}
.menu-item:disabled{opacity:.35;cursor:default}
.menu-item .ic{width:15px;height:15px;color:var(--muted);flex:0 0 auto}
.menu-item .sc{margin-left:auto;font-family:var(--mono);font-size:11px;color:var(--faint)}
.menu-item.danger{color:var(--t-bool)}
.menu-item.danger .ic{color:var(--t-bool)}
.menu-sep{height:1px;background:var(--border);margin:5px 6px}
.menu-label{font-size:11px;color:var(--faint);padding:6px 10px 3px;font-weight:600;letter-spacing:.03em}

/* type-filter grid */
.tf{padding:10px;min-width:260px}
.tf h4{margin:0 0 8px;font-size:12.5px;color:var(--muted);font-weight:600}
.tf-grid{display:flex;flex-wrap:wrap;gap:6px}
.tf-cell{
  font-family:var(--mono);font-size:12px;padding:5px 10px;border-radius:7px;cursor:pointer;
  border:1px solid var(--border-strong);background:var(--panel);color:var(--muted);display:flex;align-items:center;gap:6px;
}
.tf-cell .box{width:13px;height:13px;border-radius:3px;border:1.5px solid var(--faint);display:grid;place-items:center}
.tf-cell[data-state="On"]{color:var(--fg);border-color:var(--sel-edge);background:var(--accent-soft)}
.tf-cell[data-state="On"] .box{background:var(--accent);border-color:var(--accent)}
.tf-cell[data-state="On"] .box svg{display:block;color:var(--bg);width:9px;height:9px}
.tf-cell .box svg{display:none}
.tf-foot{display:flex;justify-content:flex-end;gap:8px;margin-top:10px}

/* ---------- dialog ---------- */
dialog{
  border:1px solid var(--border-strong);border-radius:14px;padding:0;background:var(--surface);
  color:var(--fg);box-shadow:var(--shadow);width:min(460px,92vw);
  max-height: calc(100vh - 32px); overflow: auto;
}
dialog::backdrop{background:oklch(15% 0.02 250 / .5);backdrop-filter:blur(2px)}
.dlg-head{padding:18px 20px 4px}
.dlg-head h3{margin:0;font-size:16px;font-weight:700}
.dlg-head p{margin:6px 0 0;color:var(--muted);font-size:12.5px}
.dlg-body{padding:14px 20px}
.field{margin-bottom:14px}
.field label{display:block;font-size:12px;color:var(--muted);margin-bottom:5px;font-weight:600}
.field select,.field input{
  width:100%;height:36px;padding:0 10px;border-radius:9px;border:1px solid var(--border-strong);
  background:var(--panel);color:var(--fg);font-family:var(--mono);font-size:13px;
}
.warns{background:color-mix(in oklch,var(--t-bool) 14%,var(--surface));border:1px solid color-mix(in oklch,var(--t-bool) 40%,transparent);
  border-radius:9px;padding:8px 12px;font-size:12px;color:var(--fg);margin-bottom:14px}
.warns ul{margin:4px 0 0;padding-left:18px}
.warns.hide{display:none}
.dlg-foot{display:flex;justify-content:flex-end;gap:10px;padding:6px 20px 18px}

/* ── 寬度響應式斷點 ─────────────────────────────────────────
   斷點速查（max-width）:
     920px — 平板：收合 detail、隱藏 label-hide
     680px — 窄版：增大列高、動作鈕常駐（含觸控電話橫向）
     600px — 折疊 Tree/Raw 頁籤 → ⋯ More
     500px — 折疊 Expand/Collapse
     440px — 折疊 Undo/Redo/Theme
   以下規則**逐字不動**；觸控能力擴充另在 pointer:coarse 區塊。
   ─────────────────────────────────────────────────────────── */

/* 平板：≤920px */
@media (max-width:920px){
  .detail{flex-basis:0}
  .val,.key{max-width:none}
  .label-hide{display:none}
  .tbtn{padding:0 10px}
}
/* Narrow: instead of one cliff that dumps everything into "⋯ More" (and instead
   of wrapping), fold the right-side action groups into the More popup one step
   at a time, right→left, as width shrinks. #btnMore appears at the first step
   and always lists the full action set (a harmless superset). */
/* 窄：≤600px — step 1: 折疊 Tree/Raw 頁籤 */
@media (max-width:600px){
  #btnMore{display:inline-flex}
  #viewTabs{display:none}            /* step 1: Tree/Raw tabs */
}
/* 窄：≤500px — step 2: 折疊 Expand/Collapse */
@media (max-width:500px){
  #navGroup{display:none}            /* step 2: Expand/Collapse */
}
/* 窄：≤440px — step 3: 折疊 Undo/Redo/Theme */
@media (max-width:440px){
  #editGroup{display:none}           /* step 3: Undo/Redo/Theme */
}
/* 窄版 + 觸控電話橫向：≤680px */
@media (max-width:680px){
  :root{--row-h:38px;--indent:16px}
  body{font-size:14px}
  .kind{margin-left:6px}
  .eq{margin:0 5px}
  .row-actions{opacity:1}            /* 觸控：動作鈕常駐 */
  .drag-handle{opacity:.5}
  /* Rows stay single-line at every width: long key/value/comment cells compress
     with an ellipsis (min-width:0 on each) instead of wrapping onto a new line or
     vanishing. Full text remains in the detail panel (i). */
  .val{max-width:62vw}
  .detail{width:100vw}
  .hint{display:none}
}

/* ── 觸控能力掛鉤（能力閘控，視覺純加法）────────────────────
   pointer:coarse  = 手指/觸控筆輸入（含寬螢幕平板）
   hover:none      = 無滑鼠懸停（手機 / 觸控板主要輸入）
   這些規則**只補充**寬度斷點尚未覆蓋的觸控行為（例如寬螢幕觸控裝置）；
   純滑鼠使用者在任何寬度下均不受影響。
   ─────────────────────────────────────────────────────────── */

/* 觸控裝置：提高命中區域至 --hit；動作鈕常駐 */
@media (pointer:coarse){
  .tbtn{min-height:var(--hit)}
  .icon-btn{width:var(--hit);height:var(--hit)}
  .row-actions{opacity:1}            /* 觸控：動作鈕常駐（寬螢幕亦然） */
  .drag-handle{opacity:.5}
}
/* 無懸停裝置：移除懸停相依的 opacity 漸隱（行為補充） */
@media (hover:none){
  .caret{color:var(--muted)}        /* 無 hover 時保持可見 */
}

/* ── 觸控版面鷹架（本階段全部 .hidden / inert）───────────────
   Bottom-sheet、FAB、Swipe-wrapper 為未來觸控相互動準備的掛載點；
   display:none 確保本階段對桌面版完全無影響。
   ─────────────────────────────────────────────────────────── */
.touch-sheet,.touch-fab,.touch-swipe{display:none}
.sr{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0)}

/* ============================================================
   Compat: fallback #overlay + modals for keyboard-driven modes
   (Detail / Help / Prompt / Filter / KindSwitch / Convert /
   TypeFilter) not yet ported to dedicated chrome. Replaced
   piecewise in later phases.
   ============================================================ */
#overlay {
  position: absolute; left: 50%; top: 40%; transform: translate(-50%, -50%);
  background: var(--surface); border: 1px solid var(--border-strong);
  border-radius: 12px; padding: 16px 20px; max-width: 70vw; max-height: 70vh;
  overflow: auto; box-shadow: var(--shadow); z-index: 40;
}
#overlay h3 { margin: 0 0 10px; font-size: 15px; }
#overlay pre { font-family: var(--mono); font-size: 12.5px; white-space: pre-wrap; margin: 0; }
#overlay .opt { cursor: pointer; padding: 2px 8px; border-radius: 6px; }
#overlay .opt.sel { background: var(--accent-soft); color: var(--fg); }
#overlay .edit-cell { font-family: var(--mono); background: var(--panel); border: 1px solid var(--accent); padding: 2px 6px; border-radius: 5px; }

/* Type-filter popover (#tfPop) extras beyond the design's On/Off cell styles:
   the tri-state Partial group state, the keyboard cursor cell, and the active
   tag. (The facet grid lives in #tfPop now, not the keyboard #overlay.) */
.tf-cell[data-state="Partial"] { color: var(--t-date); border-color: var(--t-date); }
.tf-cell[data-state="Partial"] .box { background: var(--t-date); border-color: var(--t-date); }
.tf-cell.cursor { outline: 2px solid var(--accent); outline-offset: 1px; }
.tf-active { color: var(--t-string); font-size: 11px; font-weight: 600; }
/* Constrain the popover so a tall facet list scrolls instead of overflowing
   off-screen, and give cells room so they don't cram. */
#tfPop { max-height: 72vh; overflow-y: auto; max-width: min(380px, 92vw); }
#tfPop .tf { min-width: 0; }
#tfPop .tf-grid { gap: 7px 8px; margin: 2px 0 4px; }
#tfPop .tf-cell { flex: 0 0 auto; }
/* Convert warnings are a non-fatal lossy notice, not an error: amber, not red. */
#convWarns.warns { background: color-mix(in oklch, var(--t-date) 13%, var(--surface)); border-color: color-mix(in oklch, var(--t-date) 38%, transparent); }
#convWarns.warns strong { color: var(--t-date); }
#convWarns .warns-note { margin: 2px 0 6px; color: var(--muted); }
/* Paste mode: the clipboard freezes the selection, so the cursor row is the
   paste destination — make it unmistakable (a click moves it). */
.paste-mode .row.cursor { background: var(--accent-soft); box-shadow: inset 0 0 0 1px var(--accent); }
.paste-mode .row.cursor::before { background: var(--accent); width: 4px; }
.paste-mode .row.cursor::after { content: "▸ paste here"; margin-left: auto; align-self: center; font-size: 10px; font-weight: 700; letter-spacing: .04em; color: var(--accent); }
.paste-mode .row-actions { display: none; }

.modal { position: fixed; inset: 0; background: oklch(15% 0.02 250 / .5); display: flex; align-items: center; justify-content: center; z-index: 100; padding: 16px; }
.modal-box {
  background: var(--surface); border: 1px solid var(--border-strong); border-radius: 12px;
  padding: 16px 20px; display: flex; flex-direction: column; gap: 10px; box-shadow: var(--shadow);
  width: min(720px, 92vw); max-height: calc(100vh - 32px); overflow: auto;
}
.modal-box h3 { margin: 0; font-size: 15px; }
.modal-box textarea {
  background: var(--panel); color: var(--fg); border: 1px solid var(--border-strong);
  border-radius: 8px; font-family: var(--mono); font-size: 13px; padding: 8px; resize: vertical;
  width: 100%; max-width: 100%; box-sizing: border-box; max-height: 60vh;
}
.modal-box select {
  background: var(--panel); color: var(--fg); border: 1px solid var(--border-strong);
  border-radius: 8px; padding: 6px; font-family: var(--mono);
}
.modal-actions { display: flex; justify-content: flex-end; gap: 8px; }
.modal-actions button {
  height: 32px; padding: 0 14px; border-radius: 8px; border: 1px solid var(--border-strong);
  background: var(--panel); color: var(--fg);
}
.modal-actions button:hover { background: var(--accent-soft); border-color: var(--sel-edge); }

/* App-only utilities (mockup uses .hide / inline var(--mono); these mirror them
   for index.html + render.ts which use .hidden / .mono / .kind-note). */
.hidden { display: none !important; }
.mono { font-family: var(--mono); }
.kind-note { opacity: .6; }
.key.elem { color: var(--faint); }  /* dim [0]/[1] array-element index label */

/* Tree/Raw view toggle + read-only raw text (#12). The active tab reads like a
   pressed segment; the raw pane fills the tree-wrap and scrolls on its own. */
.viewtab.active { background: var(--accent); color: var(--bg); border-color: transparent; font-weight: 600; }
.raw-view {
  margin: 0; padding: 8px 10px; white-space: pre; tab-size: 2;
  font-size: 13px; line-height: 1.55; color: var(--fg);
  user-select: text; cursor: text; outline: none;
}

/* Clipboard source rows — distinct from the selection box (filled --sel + solid
   --sel-edge ring). A *dashed* ring in a different hue marks "captured" so it
   reads apart even when the row is also selected: copy = green-string dashed,
   cut = purple-date dashed + dimmed + strike. */
.row.clip-copy { outline: 1.5px dashed var(--t-string); outline-offset: -3px; border-radius: 6px; }
.row.clip-cut { outline: 1.5px dashed var(--t-date); outline-offset: -3px; border-radius: 6px; opacity: .6; }
.row.clip-cut .key, .row.clip-cut .val { text-decoration: line-through; text-decoration-thickness: 1px; }

/* Type-filter popup header: title row + a `×` clear button (replaces the old
   Apply/Cancel foot — the filter applies live and persists when the popup closes). */
.tf-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.tf-clear {
  width: 22px; height: 22px; border: 0; background: transparent; border-radius: 6px;
  color: var(--faint); font-size: 13px; line-height: 1; cursor: pointer;
}
.tf-clear:hover { background: var(--border); color: var(--fg); }

/* Filename chip in the brand — always visible (the design's brand text used the
   generic .label-hide, which display:none'd it under 920px; we repurposed it for
   the real filename, so it must survive narrow widths). Truncates gracefully and
   exposes the full name via the title tooltip. */
.brand .doc-name {
  max-width: min(38vw, 220px); overflow: hidden; text-overflow: ellipsis;
  white-space: nowrap; color: var(--muted); font-weight: 600;
}

/* Footer warning/error gets its own full-width line and wraps, so a long message
   reads in full at any width (the design clipped it with nowrap+ellipsis). It is
   emphasized as a tinted pill in the warning hue so it stands apart from status. */
.footer { flex-wrap: wrap; }
.footer .status.err {
  flex-basis: 100%; white-space: normal; overflow: visible;
  color: var(--t-bool); font-weight: 600;
  background: color-mix(in oklch, var(--t-bool) 14%, transparent);
  border: 1px solid color-mix(in oklch, var(--t-bool) 35%, transparent);
  border-radius: 7px; padding: 3px 9px;
}

/* Comment rows: only the first line shows in the tree (full text in detail);
   `…` marks continuation. The inline comment editor gets extra room. */
.comment-more { font-style: normal; opacity: .7; }
.comment-input { min-width: 240px; font-style: normal; }

/* The format pill is interactive only for the built-in sample (cycles backends);
   for a loaded file it's a static label. `.toggleable` carries the affordance. */
.fmt-pill:not(.toggleable) { cursor: default; }
.fmt-pill.toggleable { border-color: var(--sel-edge); color: var(--fg); }

/* Shared editable node panel (web/panel.ts) inside the desktop detail aside.
   panel.ts wraps its body in a `<div class="detail">`, which would otherwise
   inherit the slide-in aside's `.detail` rule (absolute + translateX off-screen)
   — so first neutralize that, then style the panel's own classes (desktop-sized,
   tighter than the touch sheet). */
#detailBody .detail {
  position: static; transform: none; inset: auto; width: auto; flex: initial;
  border: 0; background: transparent; box-shadow: none;
  display: flex; flex-direction: column;
}
#detailBody .field-label { font-size: 12px; color: var(--muted); margin-bottom: 5px; font-weight: 600; }
#detailBody .v-edit, #detailBody .k-edit, #detailBody .c-edit {
  width: 100%; box-sizing: border-box; height: 34px; padding: 0 10px; border-radius: 8px;
  font-family: var(--mono); font-size: 13px; color: var(--fg);
  background: var(--panel); border: 1px solid var(--accent); margin-bottom: 12px;
}
#detailBody .c-edit { border-color: var(--border-strong); font-style: italic; }
#detailBody .v-edit[disabled] { border-color: var(--border); color: var(--muted); opacity: .7; }
#detailBody .hint-line { font-size: 11.5px; color: var(--faint); margin: -8px 0 12px; line-height: 1.5; }
#detailBody .kindbtn { width: 100%; justify-content: flex-start; gap: 9px; margin-bottom: 12px; }
#detailBody .kindbtn .dotc { width: 11px; height: 11px; border-radius: 50%; flex: 0 0 auto; }
#detailBody .btn {
  height: 34px; padding: 0 12px; border-radius: 8px; font-size: 13px; font-weight: 600;
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  background: var(--panel); border: 1px solid var(--border-strong); color: var(--fg); cursor: pointer;
}
#detailBody .btn:hover { background: var(--accent-soft); border-color: var(--sel-edge); }
#detailBody .btn.danger { color: var(--t-bool); border-color: color-mix(in oklch, var(--t-bool) 40%, transparent); }
#detailBody .row-btns { display: flex; gap: 10px; margin-top: 6px; }
#detailBody .row-btns .btn { flex: 1; }
.fmt-pill.toggleable:hover { background: var(--accent-soft); }
