/* =====================================================================
   Story Designer — minimal Miro-like infinite canvas
   Theme tokens (light / dark) live on :root and [data-theme].
   ===================================================================== */

:root,
html[data-theme="dark"] {
  --bg: #14161a;
  --bg-elev: #1d2027;
  --bg-elev-2: #252932;
  --panel: #1b1e24;
  --border: #2c313b;
  --border-strong: #3a4150;
  --text: #e6e9ef;
  --text-dim: #9aa3b2;
  --text-faint: #6b7280;
  --accent: #4f8cff;
  --accent-soft: rgba(79, 140, 255, 0.18);
  --danger: #ff5c5c;
  --grid-dot: rgba(255, 255, 255, 0.07);
  --shadow: 0 8px 30px rgba(0, 0, 0, 0.45);
  --shadow-sm: 0 2px 10px rgba(0, 0, 0, 0.35);
  --obj-shadow: 0 2px 8px rgba(0, 0, 0, 0.35);
  color-scheme: dark;
}

/* ---------------------------------------------------------------------
   Safe areas, once. Every rule that has to clear a notch, a Dynamic
   Island or a home indicator reads these instead of calling env()
   itself — so there is one definition to audit, and a test can set them
   to a real device's values to check the layout on any browser.

   iPhone 14 Pro portrait, for reference: top 59px, bottom 34px.

   NB these live on :root only, NOT inside the theme blocks: a theme
   switch must never change the layout.
   --------------------------------------------------------------------- */
:root {
  --safe-t: env(safe-area-inset-top, 0px);
  --safe-r: env(safe-area-inset-right, 0px);
  --safe-b: env(safe-area-inset-bottom, 0px);
  --safe-l: env(safe-area-inset-left, 0px);
  /* Apple's minimum comfortable touch target. */
  --tap: 44px;
}

html[data-theme="light"] {
  --bg: #f4f6fa;
  --bg-elev: #ffffff;
  --bg-elev-2: #f0f2f6;
  --panel: #ffffff;
  --border: #e2e6ee;
  --border-strong: #cfd6e2;
  --text: #1a1d24;
  --text-dim: #5a6472;
  --text-faint: #97a0af;
  --accent: #2f6bff;
  --accent-soft: rgba(47, 107, 255, 0.14);
  --danger: #e23b3b;
  --grid-dot: rgba(20, 30, 60, 0.10);
  --shadow: 0 10px 34px rgba(30, 42, 70, 0.16);
  --shadow-sm: 0 2px 10px rgba(30, 42, 70, 0.10);
  --obj-shadow: 0 2px 10px rgba(30, 42, 70, 0.12);
  color-scheme: light;
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  height: 100%;
  overflow: hidden;
  font-family: "Inter", system-ui, -apple-system, "Segoe UI", Roboto, Arial, sans-serif;
  background: var(--bg);
  color: var(--text);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

html { touch-action: manipulation; }   /* no double-tap-to-zoom, no 300ms click delay */
body {
  display: grid;
  grid-template-rows: auto auto 1fr;    /* toolbar · breadcrumb bar · viewport */
  grid-template-columns: 1fr;
  height: 100vh;
  overscroll-behavior: none;            /* no rubber-band / pull-to-refresh page shift */
}

/* ===================== Toolbar ===================== */
#toolbar {
  grid-row: 1;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 8px 14px;
  background: var(--bg-elev);
  border-bottom: 1px solid var(--border);
  z-index: 30;
  user-select: none;
}

.toolbar-left { display: flex; align-items: center; gap: 14px; min-width: 0; }
.toolbar-right { display: flex; align-items: center; gap: 8px; flex-shrink: 0; }

.brand { display: flex; align-items: center; gap: 8px; font-weight: 650; letter-spacing: 0.2px; }
.brand-mark { color: var(--accent); font-size: 18px; }
.brand-name { font-size: 15px; }

.divider { width: 1px; height: 24px; background: var(--border); }

/* Autosave status indicator */
.save-status {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  height: 34px;
  padding: 0 10px;
  font-size: 12.5px;
  color: var(--text-dim);
  user-select: none;
  white-space: nowrap;
}
.save-status::before {
  content: "";
  width: 7px; height: 7px;
  border-radius: 50%;
  background: #43c59e;
  transition: background 0.2s ease;
}
.save-status[data-state="saving"] { color: var(--text-faint); }
.save-status[data-state="saving"]::before { background: #f2b134; animation: save-pulse 0.9s ease-in-out infinite; }
.save-status[data-state="error"] { color: var(--danger); }
.save-status[data-state="error"]::before { background: var(--danger); }
.save-status[data-state="local"] { color: var(--text-dim); }
.save-status[data-state="local"]::before { background: var(--accent); }
@keyframes save-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.35; } }

@media (max-width: 1080px) { .save-status { padding: 0 6px; } .save-status span { display: none; } }

.btn-group { display: flex; align-items: center; gap: 4px; }

.tbtn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 34px;
  padding: 0 10px;
  border: 1px solid transparent;
  border-radius: 9px;
  background: transparent;
  color: var(--text);
  font-size: 13px;
  font-family: inherit;
  cursor: pointer;
  transition: background 0.12s ease, border-color 0.12s ease, transform 0.06s ease;
  white-space: nowrap;
}
.tbtn span { font-size: 12.5px; }
.tbtn:hover { background: var(--bg-elev-2); }
.tbtn:active { transform: translateY(1px); }
.tbtn.icon { width: 34px; justify-content: center; padding: 0; font-size: 17px; }
.tbtn.active { background: var(--accent-soft); border-color: var(--accent); color: var(--accent); }
.zoom-label { min-width: 56px; justify-content: center; font-variant-numeric: tabular-nums; }

/* ===================== Breadcrumb ===================== */
.breadcrumb {
  display: flex;
  align-items: center;
  gap: 2px;
  font-size: 13px;
  color: var(--text-dim);
  overflow: hidden;
  flex-wrap: nowrap;
}
.crumb {
  padding: 4px 8px;
  border-radius: 7px;
  cursor: pointer;
  white-space: nowrap;
  max-width: 220px;
  overflow: hidden;
  text-overflow: ellipsis;
  border: 1px solid transparent;
}
.crumb:hover { background: var(--bg-elev-2); color: var(--text); }
.crumb.current { color: var(--text); font-weight: 600; cursor: default; }
.crumb.current:hover { background: transparent; }
.crumb-sep { color: var(--text-faint); padding: 0 1px; }

/* ===================== Dedicated breadcrumb bar (desktop) ===================== */
/* The toolbar breadcrumb now only serves touch (as the collapsed nav-hub); the
   full path lives in its own bar below the toolbar on desktop. */
body:not(.touch-mode) #toolbar #breadcrumb { display: none; }
.touch-mode .breadcrumb-bar { display: none; }

.breadcrumb-bar {
  grid-row: 2;
  position: relative;
  display: flex;
  align-items: stretch;
  height: 30px;
  background: var(--bg-elev);
  border-bottom: 1px solid var(--border);
  z-index: 29;
  user-select: none;
  overflow: hidden;
}
/* Edge fades appear only when the path overflows (toggled from JS). */
.breadcrumb-bar::before,
.breadcrumb-bar::after {
  content: ""; position: absolute; top: 0; bottom: 0; width: 26px; z-index: 2;
  pointer-events: none; opacity: 0; transition: opacity 0.15s ease;
}
.breadcrumb-bar::before { left: 0; background: linear-gradient(to right, var(--bg-elev), transparent); }
.breadcrumb-bar::after { right: 0; background: linear-gradient(to left, var(--bg-elev), transparent); }
.breadcrumb-bar.fade-left::before { opacity: 1; }
.breadcrumb-bar.fade-right::after { opacity: 1; }

.breadcrumb-scroll {
  display: flex; align-items: center; gap: 1px;
  padding: 0 14px; width: 100%;
  overflow-x: auto; overflow-y: hidden;
  white-space: nowrap;
  scrollbar-width: none;
  font-size: 13px; color: var(--text-dim);
}
.breadcrumb-scroll::-webkit-scrollbar { display: none; }

.bc-crumb {
  display: inline-flex; align-items: center; gap: 5px;
  flex: 0 0 auto;
  padding: 3px 9px; border-radius: 7px;
  max-width: 240px;
  cursor: pointer; color: var(--text-dim);
  border: 1px solid transparent;
  transition: background 0.12s ease, color 0.12s ease, transform 0.06s ease;
}
.bc-crumb:hover { background: var(--bg-elev-2); color: var(--text); }
.bc-crumb:active { transform: translateY(0.5px); }
.bc-crumb .bc-ico { font-size: 13px; flex: 0 0 auto; line-height: 1; }
.bc-crumb .bc-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bc-crumb.current { color: var(--accent); font-weight: 650; cursor: default; }
.bc-crumb.current:hover { background: color-mix(in srgb, var(--accent) 14%, transparent); }
.bc-crumb.bc-ellipsis { font-weight: 700; letter-spacing: 1px; padding: 3px 8px; }
.bc-sep { flex: 0 0 auto; color: var(--text-faint); padding: 0 2px; }

/* Drag ghost while dropping a crumb onto the canvas. */
.bc-drag-ghost {
  position: fixed; z-index: 200; pointer-events: none;
  display: inline-flex; align-items: center; gap: 6px;
  padding: 5px 10px; border-radius: 8px;
  background: var(--bg-elev-2); border: 1px solid var(--accent);
  color: var(--text); font-size: 13px; box-shadow: 0 6px 18px rgba(0,0,0,0.35);
}
body.bc-dragging, body.bc-dragging * { cursor: copy !important; }

/* ---- Breadcrumb hover dropdowns (sibling / nested navigation) ---- */
.bc-dropdown {
  position: fixed; z-index: 210;
  min-width: 190px; max-width: 300px;
  background: var(--bg-elev-2); border: 1px solid var(--border);
  border-radius: 10px; padding: 5px;
  box-shadow: 0 10px 30px rgba(0,0,0,0.4);
  animation: bc-pop 0.1s ease;
}
@keyframes bc-pop { from { opacity: 0; transform: translateY(-3px); } to { opacity: 1; transform: none; } }
.bc-dd-search { width: 100%; margin: 2px 0 6px; font-size: 12.5px; padding: 5px 8px; }
.bc-dd-list { max-height: 320px; overflow-y: auto; scrollbar-width: thin; }
.bc-dd-row {
  display: flex; align-items: center; gap: 8px;
  padding: 6px 8px; border-radius: 7px; cursor: pointer;
  color: var(--text-dim); font-size: 13px;
  transition: background 0.1s ease, color 0.1s ease;
}
.bc-dd-row:hover { background: var(--bg-elev); color: var(--text); }
.bc-dd-row.current { color: var(--accent); font-weight: 600; }
.bc-dd-ico { flex: 0 0 auto; font-size: 14px; line-height: 1; }
.bc-dd-label { flex: 1 1 auto; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bc-dd-count {
  flex: 0 0 auto; font-size: 11px; color: var(--text-faint);
  background: var(--bg); border-radius: 10px; padding: 1px 7px; min-width: 18px; text-align: center;
}
.bc-dd-arrow { flex: 0 0 auto; color: var(--text-faint); font-size: 11px; }
.bc-dd-more { color: var(--accent); font-weight: 600; justify-content: center; }
.bc-dd-empty { padding: 10px; text-align: center; color: var(--text-faint); font-size: 12.5px; }

/* ===================== Viewport / world ===================== */
.viewport {
  grid-row: 3;
  position: relative;
  overflow: hidden;
  background-color: var(--bg);
  background-image: radial-gradient(circle at center, var(--grid-dot) 1.4px, transparent 1.4px);
  background-repeat: repeat;
  cursor: default;
  touch-action: none;
  /* The canvas is not a document — dragging must never highlight text.
     Text stays selectable only inside a text object you're editing (below). */
  user-select: none;
  -webkit-user-select: none;
}
.viewport.panning { cursor: grabbing; }
.viewport.space-pan { cursor: grab; }
.viewport.alt-pan, .viewport.alt-pan * { cursor: grab !important; }
.viewport.connect-mode { cursor: crosshair; }

/* Moving background effect: soft aurora glows that slowly drift behind the
   objects (above the dot grid). Purely decorative and subtle so it never
   competes with content; honoured only when motion is allowed. */
.viewport::before {
  content: ""; position: absolute; inset: -25%;
  pointer-events: none; z-index: 0;
  background:
    radial-gradient(36% 40% at 24% 30%, color-mix(in srgb, var(--accent) 26%, transparent), transparent 70%),
    radial-gradient(34% 38% at 78% 66%, color-mix(in srgb, #a06bff 22%, transparent), transparent 70%),
    radial-gradient(30% 34% at 58% 14%, color-mix(in srgb, #43c59e 18%, transparent), transparent 70%);
  filter: blur(34px);
  opacity: 0.5;
  will-change: transform;
  animation: canvas-aurora 30s ease-in-out infinite alternate;
}
@keyframes canvas-aurora {
  0%   { transform: translate3d(-3%, -2%, 0) scale(1.06) rotate(0deg); }
  50%  { transform: translate3d(3%, 3%, 0)  scale(1.14) rotate(4deg); }
  100% { transform: translate3d(4%, -3%, 0) scale(1.08) rotate(-3deg); }
}
@media (prefers-reduced-motion: reduce) {
  .viewport::before { animation: none; }
}
/* Objects sit in their own layer above the decorative glow. */
.world, .object-layer { position: relative; z-index: 1; }

.world {
  position: absolute;
  top: 0;
  left: 0;
  transform-origin: 0 0;
  /* NOTE: no `will-change: transform` here. It pins the world to a cached
     compositor layer rasterized at ~1×, so zooming in bitmap-scales that
     raster and text turns pixelated. Without it the browser re-rasterizes
     at the real zoom level — crisp text at any scale. */
}

.object-layer { position: absolute; top: 0; left: 0; }
.connector-layer {
  position: absolute;
  top: 0; left: 0;
  overflow: visible;
  width: 1px; height: 1px;
  pointer-events: none;
}
.connector-layer .hit { pointer-events: stroke; cursor: pointer; }
.connector-layer .wire { pointer-events: none; }
.connector-layer .arrow-start,
.connector-layer .arrow-end { pointer-events: none; }
.connector-layer .clabels { pointer-events: none; user-select: none; }
/* colored text with a dark halo so it stays readable over the tinted pill + lines */
.connector-layer .clabel-txt { font-weight: 700; paint-order: stroke; stroke: rgba(0, 0, 0, 0.62); stroke-width: 3px; stroke-linejoin: round; }
.connector.is-selected .wire { filter: drop-shadow(0 0 2px var(--accent)); }

/* ===================== Objects ===================== */
.obj {
  position: absolute;
  top: 0; left: 0;
  border-radius: 10px;
  box-shadow: var(--obj-shadow);
  --sel: 2px;                       /* selection ring width in screen px */
  outline: none;
}
.obj.selected {
  outline: calc(var(--sel) * var(--inv-scale, 1)) solid var(--accent);
  outline-offset: calc(2px * var(--inv-scale, 1));
}
.obj.link-target-candidate { outline: calc(2px * var(--inv-scale,1)) dashed var(--accent); outline-offset: 2px; }
.obj.connect-hover { outline: calc(2px * var(--inv-scale,1)) solid var(--accent); outline-offset: 2px; }

/* ===================== Group (visual container) ===================== */
/* A titled backdrop that holds member cards by reference. The fill is
   click-through so the canvas + member cards stay usable; only the title bar
   grabs (select / drag / rename). Member cards paint above via z-index. */
.obj-group {
  --group-color: #4f8cff;
  pointer-events: none;
  border: calc(2px * var(--inv-scale, 1)) solid var(--group-color);
  border-radius: 16px;
  background: color-mix(in srgb, var(--group-color) 12%, transparent);
  box-shadow: none;
}
.obj-group.selected { outline-color: var(--group-color); }
.obj-group.group-drop-target {
  background: color-mix(in srgb, var(--group-color) 26%, transparent);
  outline: calc(2px * var(--inv-scale, 1)) dashed var(--group-color);
  outline-offset: calc(2px * var(--inv-scale, 1));
}
.group-title {
  pointer-events: auto;
  position: absolute; top: 8px; left: 10px;
  max-width: calc(100% - 20px);
  display: inline-flex; align-items: center;
  padding: 3px 10px; border-radius: 8px;
  background: var(--group-color); color: var(--group-ink, #fff);
  font-weight: 700; font-size: 13.5px; line-height: 1.3;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  cursor: grab; user-select: none; box-shadow: var(--shadow-sm);
}
.group-title.empty { opacity: 0.8; font-style: italic; }
.group-title[contenteditable="true"] {
  cursor: text; user-select: text;
  background: var(--bg-elev); color: var(--text); outline: 2px solid var(--group-color);
}
.group-empty-hint {
  pointer-events: none;
  position: absolute; inset: 30px 12px 12px;
  display: flex; align-items: center; justify-content: center;
  color: var(--group-color); opacity: 0.7;
  font-size: 13px; font-weight: 600; text-align: center;
}
/* "Will be added to this group" hint that follows below the cursor. */
.cursor-hint {
  position: fixed; z-index: 240; pointer-events: none;
  background: var(--accent); color: #fff;
  padding: 6px 12px; border-radius: 999px;
  font-size: 12.5px; font-weight: 600; white-space: nowrap;
  box-shadow: var(--shadow);
}
.cursor-hint[hidden] { display: none; }

/* Text */
.obj-text {
  background: var(--text-bg, #fffbe6);
  color: var(--text-color, #1a1d24);
  padding: 10px 12px;
  font-size: 15px;
  line-height: 1.4;
  overflow: hidden;
  cursor: grab;
  border: 1px solid rgba(0,0,0,0.06);
  display: flex;
  flex-direction: column;   /* justify-content = vertical align (set inline) */
}
.obj-text .txt { width: 100%; white-space: pre-wrap; word-break: break-word; }
/* When selected, don't clip the corner resize handles (they straddle the edge). */
.obj-text.selected { overflow: visible; }
.obj-text.editing { cursor: text; overflow: auto; user-select: text; -webkit-user-select: text; box-shadow: none; }
.obj-text .txt[contenteditable="true"] { outline: none; }

/* ==================== Task List card ==================== */
.obj-tasklist {
  background: var(--tl-bg, #f4f7ff);
  color: var(--tl-color, #1a1d24);
  padding: 10px 12px;
  font-size: 15px;
  line-height: 1.35;
  overflow: hidden;
  cursor: grab;
  border: 1px solid rgba(128,128,128,0.26);
}
.obj-tasklist.selected { overflow: visible; }
.obj-tasklist.editing { cursor: text; user-select: text; -webkit-user-select: text; box-shadow: none; }
.tl-card { display: block; width: 100%; }
.tl-title { font-weight: 700; font-size: 1.06em; margin-bottom: 5px; outline: none; word-break: break-word; }
.tl-title:empty::before { content: attr(data-ph); opacity: 0.4; font-weight: 600; }
.tl-list { display: block; }
.tl-row { display: flex; align-items: flex-start; gap: 7px; }
.tl-row + .tl-row { margin-top: var(--tl-gap, 3px); }
.tl-check {
  flex: 0 0 auto; width: 16px; height: 16px; margin-top: 1px;
  border: 2px solid currentColor; border-radius: 5px; opacity: 0.7;
  cursor: pointer; display: inline-flex; align-items: center; justify-content: center;
  box-sizing: border-box; transition: background 0.12s, border-color 0.12s;
}
.tl-check:hover { opacity: 1; }
.tl-check.is-done { background: var(--accent, #4f8cff); border-color: var(--accent, #4f8cff); opacity: 1; }
.tl-check.is-done::after { content: "✓"; color: #fff; font-size: 12px; line-height: 1; font-weight: 800; }
.tl-bullet { flex: 0 0 auto; width: 16px; }
.tl-text { flex: 1 1 auto; min-height: 1.35em; white-space: pre-wrap; word-break: break-word; outline: none; }
.tl-text:empty::before { content: attr(data-ph); opacity: 0.4; pointer-events: none; }
.tl-row.done .tl-text { text-decoration: line-through; opacity: 0.5; }

/* Temporary "Fit size" button shown under a text box while editing it. */
.txt-editbar { position: absolute; z-index: 22; display: flex; gap: 6px; }
.txt-fit {
  height: 28px; padding: 0 12px;
  border: 1px solid var(--border-strong); border-radius: 8px;
  background: var(--bg-elev); color: var(--text);
  font: inherit; font-size: 12.5px; cursor: pointer;
  box-shadow: var(--shadow-sm); white-space: nowrap;
}
.txt-fit:hover { border-color: var(--accent); color: var(--accent); }

/* Markdown-lite rendering (text cards + prose field cards) */
.md-h { font-weight: 700; line-height: 1.25; margin: 0.2em 0 0.1em; }
.md-h1 { font-size: 1.5em; }
.md-h2 { font-size: 1.28em; }
.md-h3 { font-size: 1.1em; }
.md-p { margin: 0; line-height: 1.5; }
.md-p:not(:last-child) { margin-bottom: 0.4em; }
.md-list { margin: 0.2em 0; padding-left: 1.4em; line-height: 1.5; }
.md-list li { margin: 0.1em 0; }
.md-quote {
  margin: 0.3em 0; padding: 0.1em 0 0.1em 0.7em;
  border-left: 3px solid var(--border-strong); color: var(--text-dim);
}
.md-code {
  font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace; font-size: 0.9em;
  background: var(--bg-elev-2); border-radius: 4px; padding: 0.05em 0.35em;
}

/* Big / distraction-free editor modal */
.big-editor-wrap { display: flex; flex-direction: column; gap: 10px; width: min(760px, 86vw); }
.big-editor {
  min-height: 44vh; max-height: 64vh; overflow: auto;
  background: var(--bg-elev-2); border: 1px solid var(--border); border-radius: 10px;
  padding: 20px 22px; outline: none;
  font-size: 16px; line-height: 1.65; color: var(--text);
  white-space: pre-wrap; word-break: break-word;
}
.big-editor .txt { max-width: none; }
.big-editor-foot { display: flex; justify-content: space-between; align-items: center; gap: 12px; }
.big-editor-hint { font-size: 11px; color: var(--text-faint); }
.big-editor-count { font-size: 11.5px; color: var(--text-dim); white-space: nowrap; }

/* Image */
.obj-image { overflow: hidden; background: var(--bg-elev-2); cursor: grab; }
/* Loading spinner while an image (esp. a peer's upload) is still fetching. */
.img-spinner { position: absolute; inset: 0; display: none; align-items: center; justify-content: center; pointer-events: none; }
.obj-image.img-loading .img-spinner { display: flex; }
.img-spinner::after {
  content: ""; width: 26px; height: 26px;
  border: 3px solid var(--border-strong); border-top-color: var(--accent);
  border-radius: 50%; animation: sd-spin 0.7s linear infinite;
}
@keyframes sd-spin { to { transform: rotate(360deg); } }
/* Inner img is absolutely positioned/sized so the crop source-rect fills the box. */
.obj-image img { position: absolute; top: 0; left: 0; max-width: none; display: block; pointer-events: none; -webkit-user-drag: none; }
/* On-image icons stay a constant on-screen size (scale = --inv-scale cancels the
   world zoom), BUT capped so they never exceed ~22% of the image's smaller side
   (--img-min-num, world px). min() picks the smaller factor, so on a small or
   zoomed-out image the icon shrinks with the image instead of overwhelming it. */
.image-size-alert {
  position: absolute;
  top: 5px; right: 5px;
  width: 18px; height: 18px;
  transform-origin: top right;
  transform: scale(min(var(--inv-scale, 1), calc(var(--img-min-num, 99999) * 0.22 / 18)));
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--danger);
  color: #fff;
  border: 1.5px solid rgba(255,255,255,0.9);
  box-shadow: 0 2px 8px rgba(0,0,0,0.28);
  font-size: 12px;
  font-weight: 800;
  line-height: 1;
  z-index: 7;
  pointer-events: auto;
  cursor: help;
}
.obj.editing-image { outline: calc(2px * var(--inv-scale, 1)) solid var(--accent); outline-offset: calc(2px * var(--inv-scale, 1)); }

/* Board's primary/cover image gets a yellow ring so it's easy to spot. */
.obj-image.is-primary { box-shadow: 0 0 0 calc(3px * var(--inv-scale, 1)) #f2c94c, var(--obj-shadow); }

/* Crop handles (bar-shaped, on edge midpoints) — distinct from square scale handles */
.crop-handle {
  position: absolute;
  background: var(--accent);
  border: calc(1.5px * var(--inv-scale, 1)) solid var(--bg-elev);
  border-radius: calc(2px * var(--inv-scale, 1));
  z-index: 7;
}
.crop-handle.n, .crop-handle.s { left: 50%; width: calc(30px * var(--inv-scale, 1) * var(--handle-scale, 1.7)); height: calc(11px * var(--inv-scale, 1) * var(--handle-scale, 1.7)); cursor: ns-resize; }
.crop-handle.e, .crop-handle.w { top: 50%; width: calc(11px * var(--inv-scale, 1) * var(--handle-scale, 1.7)); height: calc(30px * var(--inv-scale, 1) * var(--handle-scale, 1.7)); cursor: ew-resize; }
.crop-handle.n { top: 0;    transform: translate(-50%, -50%); }
.crop-handle.s { bottom: 0; transform: translate(-50%, 50%); }
.crop-handle.w { left: 0;   transform: translate(-50%, -50%); }
.crop-handle.e { right: 0;  transform: translate(50%, -50%); }

/* Rotate buttons, above the image, counter-rotated to stay upright */
.rotate-bar {
  position: absolute;
  left: 50%; top: 0;
  transform: translate(-50%, calc(-100% - 14px * var(--inv-scale, 1))) rotate(calc(var(--obj-rot, 0) * -1deg));
  transform-origin: center bottom;
  display: flex;
  gap: calc(7px * var(--inv-scale, 1));
  z-index: 8;
}
.rotate-btn {
  width: calc(28px * var(--inv-scale, 1) * var(--handle-scale, 1.7));
  height: calc(28px * var(--inv-scale, 1) * var(--handle-scale, 1.7));
  border-radius: 50%;
  background: var(--bg-elev);
  border: calc(1px * var(--inv-scale, 1)) solid var(--border-strong);
  color: var(--text);
  display: flex; align-items: center; justify-content: center;
  cursor: pointer;
  box-shadow: var(--shadow-sm);
  font-size: calc(16px * var(--inv-scale, 1));
  line-height: 1;
}
.rotate-btn:hover { border-color: var(--accent); color: var(--accent); }

/* Shape (rendered via inner SVG so triangle/diamond fill correctly) */
.obj-shape { background: transparent; box-shadow: none; cursor: grab; }
.obj-shape svg { width: 100%; height: 100%; display: block; overflow: visible; }
.obj-shape .shape-label {
  position: absolute; inset: 0;
  display: flex; align-items: center; justify-content: center;
  text-align: center; padding: 8px;
  font-size: 14px; color: var(--shape-label, #fff);
  pointer-events: none; white-space: pre-wrap; word-break: break-word;
}

/* Board (frame / folder look) */
.obj-board {
  background: var(--board-bg, rgba(79,140,255,0.06));
  border: 1.5px solid var(--board-color, var(--accent));
  cursor: grab;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
.obj-board .board-header {
  display: flex; align-items: center; gap: 6px;
  padding: 7px 10px;
  background: var(--board-color, var(--accent));
  color: #fff;
  font-weight: 600; font-size: 13px;
  flex-shrink: 0;
}
.obj-board .board-header .ico { font-size: 14px; }
.obj-board .board-header .name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.obj-board .board-body {
  flex: 1;
  position: relative;
  display: flex; align-items: center; justify-content: center;
  color: var(--text-dim);
  font-size: 12px;
}
.obj-board .board-open-hint {
  position: absolute; bottom: 8px; left: 50%; transform: translateX(-50%);
  opacity: 0.7; white-space: nowrap;
  max-width: calc(100% - 16px); overflow: hidden; text-overflow: ellipsis;
}

/* Board highlighted as a drop target while dragging objects onto it */
.obj-board.drop-target {
  box-shadow: 0 0 0 calc(3px * var(--inv-scale, 1)) var(--accent), var(--obj-shadow);
}
.obj-board.drop-target .board-body { background: var(--accent-soft); }
.obj-field.drop-target {
  box-shadow: 0 0 0 calc(3px * var(--inv-scale, 1)) var(--accent), var(--obj-shadow);
}
.obj-field.drop-target .fld-media { background-color: var(--accent-soft); }

/* Link badge */
.link-badge {
  position: absolute;
  top: 0; right: 0;
  transform: translate(35%, -35%) scale(var(--inv-scale, 1));
  transform-origin: top right;
  width: 22px; height: 22px;
  border-radius: 50%;
  background: var(--accent);
  color: #fff;
  display: flex; align-items: center; justify-content: center;
  font-size: 12px;
  box-shadow: var(--shadow-sm);
  cursor: pointer;
  z-index: 5;
}
.link-badge:hover { filter: brightness(1.12); }

/* Position-locked cards: a small lock badge (bottom-right, out of the link
   badge's way) and a default cursor so the body doesn't invite a drag. */
.obj.locked { cursor: default; }
.lock-badge {
  position: absolute;
  bottom: 0; right: 0;
  transform: translate(30%, 30%) scale(var(--inv-scale, 1));
  transform-origin: bottom right;
  width: 20px; height: 20px;
  border-radius: 50%;
  background: var(--bg-elev-2);
  border: 1px solid var(--border-strong);
  display: flex; align-items: center; justify-content: center;
  font-size: 10px;
  box-shadow: var(--shadow-sm);
  pointer-events: none;
  z-index: 5;
}

/* Resize handles. --handle-scale (set from local Settings) enlarges the
   image-edit handles so they're easy to grab; regular handles get a modest
   base size. */
.handle {
  position: absolute;
  width: calc(12px * var(--inv-scale, 1));
  height: calc(12px * var(--inv-scale, 1));
  background: var(--bg-elev);
  border: calc(2px * var(--inv-scale, 1)) solid var(--accent);
  border-radius: calc(3px * var(--inv-scale, 1));
  box-shadow: 0 0 0 calc(1px * var(--inv-scale, 1)) rgba(0,0,0,0.25);
  z-index: 6;
}
.handle.nw { left: 0; top: 0; transform: translate(-50%, -50%); cursor: nwse-resize; }
.handle.ne { right: 0; top: 0; transform: translate(50%, -50%); cursor: nesw-resize; }
.handle.sw { left: 0; bottom: 0; transform: translate(-50%, 50%); cursor: nesw-resize; }
.handle.se { right: 0; bottom: 0; transform: translate(50%, 50%); cursor: nwse-resize; }

/* In Edit-Image mode the handles are significantly larger (easier to drag). */
.obj.editing-image .handle {
  width: calc(16px * var(--inv-scale, 1) * var(--handle-scale, 1.7));
  height: calc(16px * var(--inv-scale, 1) * var(--handle-scale, 1.7));
  border-width: calc(2px * var(--inv-scale, 1));
}

/* Connector anchor dots (shown on hover/selection to start a connector) */
.anchor {
  position: absolute;
  width: calc(11px * var(--inv-scale, 1));
  height: calc(11px * var(--inv-scale, 1));
  border-radius: 50%;
  background: var(--accent);
  border: calc(2px * var(--inv-scale,1)) solid var(--bg-elev);
  z-index: 6;
  cursor: crosshair;
  opacity: 0;
  transition: opacity 0.1s ease;
}
.anchor { pointer-events: none; }
.obj:hover .anchor, .obj.selected .anchor { opacity: 0.9; pointer-events: auto; }
.anchor.top { left: 50%; top: 0; transform: translate(-50%, -50%); }
.anchor.bottom { left: 50%; bottom: 0; transform: translate(-50%, 50%); }
.anchor.left { left: 0; top: 50%; transform: translate(-50%, -50%); }
.anchor.right { right: 0; top: 50%; transform: translate(50%, -50%); }

/* ===================== Marquee & pending line ===================== */
.marquee {
  position: absolute;
  border: 1px solid var(--accent);
  background: var(--accent-soft);
  border-radius: 4px;
  pointer-events: none;
  z-index: 20;
}
.pending-layer {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  pointer-events: none;
  z-index: 20;
  display: none;               /* toggled via style.display (svg ignores [hidden]) */
}
.pending-layer line {
  stroke: var(--accent);
  stroke-width: 2;
  stroke-dasharray: 6 5;
}

/* ===================== Version badge ===================== */
.version-badge {
  position: fixed;
  right: 8px; bottom: 6px;
  z-index: 600;                 /* above the login overlay too */
  font-size: 11px;
  color: var(--text-faint);
  background: var(--bg-elev);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 2px 7px;
  opacity: 0.7;
  pointer-events: none;
  user-select: none;
  font-variant-numeric: tabular-nums;
}

/* ===================== Login gate ===================== */
.login-overlay {
  position: fixed; inset: 0; z-index: 500;
  display: flex; align-items: center; justify-content: center;
  background: var(--bg);
  padding: 24px;
}
.login-overlay[hidden] { display: none; }
.login-card {
  width: 340px; max-width: 100%;
  display: flex; flex-direction: column; gap: 12px;
  background: var(--bg-elev); border: 1px solid var(--border-strong);
  border-radius: 16px; box-shadow: var(--shadow); padding: 26px 24px;
}
.login-brand { font-weight: 700; font-size: 18px; display: flex; align-items: center; gap: 8px; }
.login-brand .brand-mark { color: var(--accent); }
.login-sub { color: var(--text-dim); font-size: 13px; line-height: 1.5; margin-bottom: 4px; }
.login-error { color: var(--danger); font-size: 12.5px; }
.login-error[hidden] { display: none; }
.login-card .mini-btn.full { flex: none; width: 100%; margin-top: 4px; }

/* Password reveal. The input keeps its own padding-right so long passwords
   scroll under the button rather than behind it. */
.pw-field { position: relative; display: block; }
.pw-field .field { padding-right: 38px; }
.pw-toggle {
  position: absolute; right: 4px; top: 50%; transform: translateY(-50%);
  display: flex; align-items: center; justify-content: center;
  width: 30px; height: 26px; padding: 0; cursor: pointer;
  background: none; border: 0; border-radius: 6px; color: var(--text-dim);
}
.pw-toggle:hover { color: var(--text); background: var(--bg-elev); }
.pw-toggle:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.pw-ico { width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 1.4; stroke-linecap: round; }
/* Hidden password → offer the plain eye ("reveal"); revealed → the struck-out
   eye ("hide"), which is the state the click will move you to. */
.pw-toggle .pw-ico-hide,
.pw-toggle.on .pw-ico-show { display: none; }
.pw-toggle.on .pw-ico-hide { display: block; }
.pw-toggle.on { color: var(--accent); }
.touch-mode .pw-toggle { width: 34px; height: 32px; }

/* While locked (pre-auth), show ONLY the login screen. */
body.locked #toolbar,
body.locked .breadcrumb-bar,
body.locked .viewport,
body.locked #sidebar-rail,
body.locked #sidebar-panel,
body.locked #inspector,
body.locked #presence-layer,
body.locked #context-menu,
body.locked #drop-hint { display: none !important; }

/* ===================== Touch mode ===================== */
/* Touch targets: --tap (44px) is Apple's minimum comfortable size, and these
   were all measured under it on an iPhone 14 Pro — the toolbar buttons at 42,
   the project button at 40, the breadcrumb at 25 and the view switch at 38. */
.touch-mode .tbtn { min-height: var(--tap); padding: 0 12px; font-size: 14px; }
.touch-mode .tbtn.icon { min-width: var(--tap); font-size: 19px; }
.touch-mode .project-btn { min-height: var(--tap); }
.touch-mode #breadcrumb .crumb.current { min-height: var(--tap); display: inline-flex; align-items: center; }
.touch-mode .view-switch button { min-height: 40px; min-width: 88px; }
.touch-mode .cm-item { min-height: var(--tap); }
.touch-mode .tlv-ctl,
.touch-mode .gv-ctl,
.touch-mode .ft-ctl { min-width: var(--tap); min-height: var(--tap); }
/* Measured under 44px on an iPhone 14 Pro and worth the space: the dialog
   close button was 26px, dialog buttons 38, text fields and the view switch 40. */
.touch-mode .modal-x { min-width: var(--tap); min-height: var(--tap); font-size: 17px; }
.touch-mode .mini-btn { min-height: var(--tap); }
.touch-mode .btn { min-height: var(--tap); }
.touch-mode .field { min-height: var(--tap); }
.touch-mode textarea.field { min-height: 72px; }
.touch-mode .view-switch button { min-height: var(--tap); }
.touch-mode .reader-ctl { min-width: var(--tap); min-height: var(--tap); }
.touch-mode .psheet-row { min-height: 52px; }
/* Overlay-view controls. These live on their own bars, away from the toolbar,
   and were sized for a mouse: the graph's mode segments were 27px tall and the
   flow-tree's title button 34px. */
.touch-mode .gv-seg { min-height: var(--tap); }
.touch-mode .ft-title { height: var(--tap); }
.touch-mode .tlv-range-in { min-height: var(--tap); }

/* The brush panel is a dense tool, so it gets a wider body on touch and its
   controls grow with it. The option grids settle at 36px rather than 44: five
   across at 44 would not fit any phone, and they are secondary to the sliders
   and swatches you actually reach for mid-drawing. */
.touch-mode .svg-brush { width: min(320px, calc(100vw - 20px)); }
.touch-mode .svg-tool-btn { width: var(--tap); height: var(--tap); font-size: 18px; }
.touch-mode .svg-opt, .touch-mode .svg-shape-btn { height: 36px; }
.touch-mode .svg-swatch { border-radius: 7px; }
.touch-mode .svg-brush-swatches { gap: 5px; }
.touch-mode .svg-swatch-sm, .touch-mode .svg-swatch-none { width: 26px; height: 26px; }
.touch-mode .svg-brush-color { width: 32px; height: 30px; }
.touch-mode .svg-brush-range { height: 28px; }
.touch-mode .svg-brush-range::-webkit-slider-thumb { width: 26px; height: 26px; }
.touch-mode .svg-brush-range::-moz-range-thumb { width: 26px; height: 26px; }
.touch-mode .svg-brush-done, .touch-mode .svg-brush .mini-btn { min-height: var(--tap); }
.touch-mode .rail-btn { width: 50px; height: 50px; font-size: 22px; border-radius: 14px; }
.touch-mode .cm-item { padding: 12px 12px; font-size: 15px; }
.touch-mode .cm-item .cm-ico { width: 22px; }
.touch-mode .link-badge { width: 30px; height: 30px; font-size: 15px; }
.touch-mode .mini-btn { padding: 10px 10px; font-size: 13.5px; }
.touch-mode .field { padding: 10px 10px; font-size: 15px; }   /* avoid iOS zoom-on-focus (<16px) */
/* Mobile keeps editing simple: only MOVE cards. Hide resize/crop/rotate and
   connector anchors, and the create tools (keep the Search/Tags/Settings tabs). */
.touch-mode .handle,
.touch-mode .crop-handle,
.touch-mode .rotate-bar,
.touch-mode .anchor { display: none !important; }
.touch-mode .rail-btn[data-create],
.touch-mode .rail-btn[data-action="add-image"] { display: none; }
/* No property editing on mobile (move-only) → hide the inspector. */
.touch-mode .inspector { display: none !important; }

/* Mobile: the full ancestor path is too long for a phone. Collapse the
   breadcrumb to just the current board name — a tappable "nav hub" (▾) that
   opens the full path + sub-boards. The Back button handles going up. */
.touch-mode #breadcrumb .crumb:not(.current),
.touch-mode #breadcrumb .crumb-sep { display: none; }
.touch-mode #breadcrumb .crumb.current {
  display: inline-flex; align-items: center; gap: 3px;
  max-width: 52vw; padding-left: 4px; cursor: pointer;
}
.touch-mode #breadcrumb .crumb.current .crumb-label {
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0;
}
.crumb-label { display: inline; }
.crumb-caret { display: none; }
.touch-mode #breadcrumb .crumb.current .crumb-caret {
  display: inline; flex: 0 0 auto; font-size: 10px; color: var(--text-faint);
}
.touch-mode #breadcrumb .crumb.current:active { background: var(--bg-elev-2); }

/* Mobile: collapse the toolbar action clusters into one ⋯ overflow menu, and
   drop the zoom buttons entirely (pinch + double-tap handle zoom on touch). */
.overflow-btn { display: none; }
.touch-mode #toolbar .btn-group,
.touch-mode #toolbar .divider { display: none !important; }
.touch-mode #overflow-btn { display: inline-flex; }

/* Mobile View/Edit toggle */
.mode-toggle {
  position: fixed;
  left: 50%;
  bottom: calc(12px + var(--safe-b));
  transform: translateX(-50%);
  z-index: 45; display: none;
  align-items: center; gap: 6px;
  height: 46px; padding: 0 22px;
  border: none; border-radius: 999px;
  background: var(--accent); color: #fff;
  font: inherit; font-size: 15px; font-weight: 650;
  box-shadow: var(--shadow); cursor: pointer;
}
.mode-toggle:not([hidden]) { display: inline-flex; }
.mode-toggle.editing { background: var(--danger); }

/* Quieter Edit affordance while viewing: the canvas is the star, not the button.
   It becomes a small translucent chip in the corner; Edit mode makes it a
   prominent "Done" pill again. */
.touch-mode.view-mode .mode-toggle {
  left: auto; transform: none;
  right: calc(12px + var(--safe-r));
  width: 44px; height: 44px; padding: 0; justify-content: center;
  background: color-mix(in srgb, var(--panel) 82%, transparent);
  color: var(--text-dim);
  border: 1px solid var(--border);
  backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
  font-size: 18px; font-weight: 600;
  box-shadow: var(--shadow-sm);
}

/* View mode is read-first: hide every creation tool, keep only the
   Search / Tags / Settings tabs (they have no data-create/-action). */
.touch-mode.view-mode .sidebar-rail .rail-btn[data-create],
.touch-mode.view-mode .sidebar-rail .rail-btn[data-entity-menu],
.touch-mode.view-mode .sidebar-rail .rail-btn[data-action="add-image"],
.touch-mode.view-mode .sidebar-rail .rail-add,
.touch-mode.view-mode .sidebar-rail .rail-sep { display: none !important; }

/* View mode also drops editing/authoring controls from the toolbar (undo/redo,
   Update DB) so the top bar reads as a calm viewer, not an editor. */
.touch-mode.view-mode [data-action="undo"],
.touch-mode.view-mode [data-action="redo"],
.touch-mode.view-mode [data-action="update-db"] { display: none !important; }

/* ===================== Mobile detail sheet ===================== */
.sheet-scrim {
  position: fixed; inset: 0; z-index: 60;
  display: flex; align-items: flex-end; justify-content: center;
  background: rgba(0, 0, 0, 0); transition: background .24s ease;
  -webkit-tap-highlight-color: transparent;
}
.sheet-scrim.show { background: rgba(0, 0, 0, 0.42); }
.detail-sheet {
  width: 100%; max-width: 560px;
  max-height: 82vh; overflow-y: auto; -webkit-overflow-scrolling: touch;
  background: var(--panel);
  border-radius: 20px 20px 0 0;
  border: 1px solid var(--border); border-bottom: none;
  box-shadow: 0 -10px 40px rgba(0, 0, 0, 0.35);
  padding: 8px 18px calc(20px + var(--safe-b));
  transform: translateY(102%); transition: transform .26s cubic-bezier(.22,.61,.36,1);
  touch-action: pan-y;
}
.sheet-scrim.show .detail-sheet { transform: translateY(0); }

/* Desktop: the mobile bottom-sheet becomes a centred, fully-visible modal that
   scrolls internally when its content is taller than the viewport. */
body:not(.touch-mode) .sheet-scrim { align-items: center; }
body:not(.touch-mode) .detail-sheet {
  margin: 16px; max-height: calc(100vh - 32px);
  border-radius: 16px; border-bottom: 1px solid var(--border);
  box-shadow: var(--shadow);
  padding: 16px 18px 18px;
  transform: translateY(10px); opacity: 0;
  transition: transform .18s ease, opacity .18s ease;
}
body:not(.touch-mode) .sheet-scrim.show .detail-sheet { transform: translateY(0); opacity: 1; }
body:not(.touch-mode) .sheet-grab { display: none; }   /* drag handle is a touch affordance */
.sheet-grab {
  width: 40px; height: 4px; border-radius: 999px;
  background: var(--border-strong); margin: 6px auto 12px;
}
.sheet-head { display: flex; align-items: flex-start; gap: 12px; margin-bottom: 6px; }
.sheet-ico { font-size: 26px; line-height: 1.1; flex: 0 0 auto; }
.sheet-titles { flex: 1; min-width: 0; }
.sheet-title { font-size: 18px; font-weight: 700; color: var(--text); line-height: 1.25; word-break: break-word; }
.sheet-sub { font-size: 12.5px; color: var(--text-dim); margin-top: 2px; }
.sheet-x {
  flex: 0 0 auto; width: 34px; height: 34px; border-radius: 10px;
  border: none; background: var(--bg-elev-2); color: var(--text-dim);
  font-size: 16px; cursor: pointer;
}
.sheet-media {
  width: 100%; height: 200px; margin: 12px 0 4px;
  border-radius: 14px; background-size: cover; background-position: center;
  background-color: var(--bg-elev-2); border: 1px solid var(--border);
}
.sheet-media.tappable { cursor: zoom-in; }
.sheet-body { display: flex; flex-direction: column; gap: 12px; margin: 14px 0 4px; }
.sheet-row { display: flex; flex-direction: column; gap: 3px; }
.sheet-row-label { font-size: 11.5px; font-weight: 650; text-transform: uppercase; letter-spacing: .04em; color: var(--text-faint); }
.sheet-row-val { font-size: 14.5px; color: var(--text); line-height: 1.45; white-space: pre-wrap; word-break: break-word; }
.sheet-text { font-size: 15px; color: var(--text); line-height: 1.5; white-space: pre-wrap; word-break: break-word; }
.sheet-tags { display: flex; flex-wrap: wrap; gap: 6px; }
.sheet-tag {
  font-size: 12px; font-weight: 600; padding: 4px 10px; border-radius: 999px;
  color: var(--tag-color, var(--accent));
  background: color-mix(in srgb, var(--tag-color, var(--accent)) 16%, transparent);
}
.sheet-actions { display: flex; flex-direction: column; gap: 8px; margin-top: 16px; }
.sheet-btn {
  display: flex; align-items: center; justify-content: center; gap: 8px;
  height: 48px; border-radius: 13px; border: 1px solid var(--border);
  background: var(--bg-elev-2); color: var(--text);
  font: inherit; font-size: 15px; font-weight: 600; cursor: pointer;
}
.sheet-btn.primary { background: var(--accent); border-color: var(--accent); color: #fff; }
.sheet-btn-ico { font-size: 17px; }

/* Board navigation hub (mobile title tap) */
.nav-sheet-body { gap: 2px; }
.nav-sheet .sheet-row-label { margin-top: 4px; }
.nav-sheet .sheet-row-label.nav-section { margin-top: 16px; }
.nav-row {
  display: flex; align-items: center; gap: 11px;
  padding: 12px 14px; border-radius: 12px; cursor: pointer;
  background: var(--bg-elev-2); border: 1px solid transparent;
}
.nav-row:active { border-color: var(--border-strong); }
.nav-row.current { background: var(--accent-soft); cursor: default; }
.nav-row-ico { font-size: 20px; flex: 0 0 auto; }
.nav-row-label { flex: 1; min-width: 0; font-size: 15px; font-weight: 600; color: var(--text);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.nav-row-here { flex: 0 0 auto; font-size: 11px; font-weight: 650; color: var(--accent); text-transform: uppercase; letter-spacing: 0.04em; }
.nav-row-sub { flex: 0 0 auto; font-size: 12.5px; color: var(--text-faint); font-variant-numeric: tabular-nums; }

/* ===================== Full-screen image lightbox ===================== */
.lightbox {
  position: fixed; inset: 0; z-index: 70;
  display: flex; align-items: center; justify-content: center;
  background: rgba(0, 0, 0, 0); transition: background .2s ease;
  overflow: hidden; touch-action: none;
  -webkit-tap-highlight-color: transparent;
}
.lightbox.show { background: rgba(0, 0, 0, 0.92); }
.lightbox-img {
  max-width: 100%; max-height: 100%; object-fit: contain;
  transform-origin: center center; will-change: transform;
  user-select: none; -webkit-user-drag: none; touch-action: none;
}
.lightbox-cap {
  position: absolute; left: 0; right: 0;
  bottom: calc(16px + var(--safe-b));
  text-align: center; color: #fff; font-size: 13px;
  padding: 0 20px; text-shadow: 0 1px 4px rgba(0, 0, 0, 0.8);
  pointer-events: none;
}
.lightbox-x {
  position: absolute; top: calc(12px + var(--safe-t));
  right: calc(12px + var(--safe-r));
  width: 42px; height: 42px; border-radius: 999px; border: none;
  background: rgba(255, 255, 255, 0.14); color: #fff; font-size: 18px; cursor: pointer;
  backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);
}

/* ===================== Mobile view-type switch (Canvas / Gallery) ===================== */
.view-switch {
  position: fixed; z-index: 46;
  left: 50%; transform: translateX(-50%);
  bottom: calc(12px + var(--safe-b));
  display: none; align-items: center; gap: 2px;
  padding: 4px; border-radius: 999px;
  background: color-mix(in srgb, var(--panel) 88%, transparent);
  border: 1px solid var(--border);
  box-shadow: var(--shadow-sm);
  backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
}
.view-switch:not([hidden]) { display: inline-flex; }
.view-switch button {
  display: inline-flex; align-items: center; gap: 6px;
  height: 38px; padding: 0 14px; border: none; border-radius: 999px;
  background: transparent; color: var(--text-dim);
  font: inherit; font-size: 13px; font-weight: 600; cursor: pointer;
}
.view-switch button.active { background: var(--accent); color: #fff; }
.view-switch .vs-ico { font-size: 14px; }

/* Mobile Back button — bottom-left circle, thumb-reachable, symmetric with the
   Edit chip on the right; returns to the previous board. */
.nav-back {
  position: fixed; z-index: 46;
  left: calc(12px + var(--safe-l));
  bottom: calc(12px + var(--safe-b));
  display: none; align-items: center; justify-content: center;
  width: 44px; height: 44px; padding: 0;
  border: 1px solid var(--border); border-radius: 999px;
  background: color-mix(in srgb, var(--panel) 88%, transparent);
  color: var(--text); font: inherit; cursor: pointer;
  box-shadow: var(--shadow-sm);
  backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
}
.nav-back:not([hidden]) { display: inline-flex; }
.nav-back:active { transform: translateY(1px); }
.nav-back-ico { font-size: 26px; line-height: 1; margin-top: -3px; }
.nav-back-label { display: none; }   /* icon-only; the title tooltip names it */

/* ===================== Mobile gallery view ===================== */
.gallery {
  position: absolute; inset: 0; z-index: 15;
  overflow-y: auto; -webkit-overflow-scrolling: touch;
  background: var(--bg); touch-action: pan-y;
  padding: 12px 10px calc(88px + var(--safe-b));
}
.gallery[hidden] { display: none; }
/* Desktop keeps the tool rail visible, so inset the gallery to clear it and
   drop the mobile bottom-toolbar padding. */
body:not(.touch-mode) .gallery { padding-left: 72px; padding-bottom: 16px; }
.gallery-empty { text-align: center; color: var(--text-dim); font-size: 14px; padding: 60px 20px; }
.gallery-grid { column-count: 2; column-gap: 10px; }
@media (min-width: 520px) { .gallery-grid { column-count: 3; } }
@media (min-width: 820px) { .gallery-grid { column-count: 4; } }

.gal-tile {
  position: relative; break-inside: avoid; margin: 0 0 10px;
  border-radius: 14px; overflow: hidden;
  background: var(--bg-elev); border: 1px solid var(--border);
  box-shadow: var(--obj-shadow); cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.gal-tile:active { transform: scale(0.985); }
.gal-img { display: block; width: 100%; height: auto; }
.gal-media {
  position: relative; width: 100%; aspect-ratio: 16 / 10;
  background-size: cover; background-position: center; background-color: var(--bg-elev-2);
}
.gal-cover, .gal-portrait { aspect-ratio: 4 / 3; }
.gal-media.gal-icon {
  display: flex; align-items: center; justify-content: center;
  background: color-mix(in srgb, var(--tile-color, var(--accent)) 16%, var(--bg-elev-2));
}
.gal-folder { font-size: 34px; }
.gal-video-media { display: flex; align-items: center; justify-content: center; }
.gal-play {
  width: 44px; height: 44px; border-radius: 999px;
  display: flex; align-items: center; justify-content: center;
  background: rgba(0,0,0,0.55); color: #fff; font-size: 16px; padding-left: 3px;
}
.gal-cap { padding: 8px 10px; font-size: 12.5px; color: var(--text-dim); line-height: 1.35;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.gal-meta { padding: 9px 11px; }
.gal-title { font-size: 13.5px; font-weight: 650; color: var(--text); line-height: 1.3;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.gal-sub { font-size: 11.5px; color: var(--text-faint); margin-top: 2px; }
.gal-badge {
  position: absolute; top: 8px; left: 8px;
  width: 26px; height: 26px; border-radius: 8px;
  display: flex; align-items: center; justify-content: center; font-size: 14px;
  background: color-mix(in srgb, var(--panel) 80%, transparent);
  backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);
}
/* Text notes render their own content, tinted by the card's colours. */
.gal-note-tile { border: none; box-shadow: var(--obj-shadow); }
.gal-note-body {
  padding: 14px 13px; font-size: 13.5px; line-height: 1.45;
  background: var(--bg-elev); color: var(--text);
  white-space: pre-wrap; word-break: break-word;
  display: -webkit-box; -webkit-line-clamp: 10; -webkit-box-orient: vertical; overflow: hidden;
  min-height: 60px;
}
.gal-shape { width: 100%; aspect-ratio: 3 / 2; }
.gal-shortcut { display: flex; align-items: center; gap: 10px; padding: 12px 12px; }
.gal-sc-ico { font-size: 22px; color: var(--accent); flex: 0 0 auto; }

/* Entity attribute cards (Name, Species, Background…) inside an entity's board. */
.gal-field { padding: 11px 12px; }
.gal-field-label { font-size: 11px; font-weight: 650; text-transform: uppercase; letter-spacing: 0.05em; color: var(--text-faint); margin-bottom: 3px; }
.gal-field-val { font-size: 13.5px; color: var(--text); line-height: 1.4; word-break: break-word; }
.gal-field-val.empty { color: var(--text-faint); }
.gal-field-name .gal-field-val { font-size: 17px; font-weight: 700; line-height: 1.25; }
.gal-field-long .gal-field-val {
  display: -webkit-box; -webkit-line-clamp: 6; -webkit-box-orient: vertical; overflow: hidden;
}
.gal-field-read { display: block; padding: 0 12px 10px; font-size: 12px; font-weight: 650; color: var(--accent); }
/* Normalise markdown inside a small field tile so headings don't blow it up. */
.gal-field-val .md-h { font-size: 1em; font-weight: 700; margin: 0 0 0.3em; }
.gal-field-val .md-p { margin: 0 0 0.4em; }
.gal-field-val .md-p:last-child { margin-bottom: 0; }
.gal-field-val .md-list { margin: 0; padding-left: 1.2em; }
.gal-field-val .md-quote { margin: 0.3em 0; padding-left: 0.6em; border-left: 2px solid var(--border-strong); color: var(--text-dim); }

/* Gallery takes over the canvas area: hide canvas-only chrome while it's up. */
.touch-mode.gallery-view .sidebar-rail { display: none !important; }
.gallery-view #hint { display: none !important; }   /* hint never shows over the gallery (desktop + mobile) */

/* ===================== Focus reading mode ===================== */
.reader {
  position: fixed; inset: 0; z-index: 68;
  background: var(--bg); color: var(--text);
  display: flex; flex-direction: column;
  opacity: 0; transition: opacity 0.2s ease;
}
.reader.show { opacity: 1; }
.reader-bar {
  flex: 0 0 auto;
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding: max(10px, var(--safe-t)) max(14px, var(--safe-r)) 10px max(14px, var(--safe-l));
  border-bottom: 1px solid var(--border); background: var(--bg-elev);
}
.reader-ctl-group { display: flex; align-items: center; gap: 6px; }
.reader-ctl {
  height: 38px; min-width: 40px; padding: 0 12px;
  display: inline-flex; align-items: center; justify-content: center;
  border: 1px solid var(--border); border-radius: 10px;
  background: var(--bg-elev-2); color: var(--text);
  font: inherit; font-size: 14px; font-weight: 600; cursor: pointer;
  font-variant-numeric: tabular-nums;
}
.reader-ctl:active { transform: translateY(1px); }
.reader-ctl-wide { min-width: 64px; }
.reader-close { min-width: 40px; padding: 0; border-radius: 999px; font-size: 16px; color: var(--text-dim); }
.reader-find-btn { min-width: 40px; padding: 0; font-size: 15px; }

/* Find in text — its own full-width row under the controls, so nothing in the
   bar gets squeezed on a phone and the type controls stay usable mid-search. */
.reader-find {
  flex: 0 0 auto;
  display: flex; align-items: center; gap: 8px;
  padding: 8px max(14px, var(--safe-r)) 8px max(14px, var(--safe-l));
  border-bottom: 1px solid var(--border); background: var(--bg-elev);
}
.reader-find[hidden] { display: none; }
.reader-find-ico { font-size: 13px; opacity: 0.75; flex: 0 0 auto; }
.reader-find-input {
  flex: 1 1 auto; min-width: 0; height: 38px; padding: 0 12px;
  border: 1px solid var(--border); border-radius: 10px;
  background: var(--bg); color: var(--text);
  font: inherit; font-size: 15px;
}
.reader-find-input:focus { outline: none; border-color: var(--accent); }
/* Kill WebKit's own clear button — the row already has an explicit close. */
.reader-find-input::-webkit-search-cancel-button { -webkit-appearance: none; appearance: none; }
.reader-find-count {
  flex: 0 0 auto; color: var(--text-dim); font-size: 12.5px; font-weight: 600;
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
.reader-find-nav { min-width: 36px; padding: 0; font-size: 17px; line-height: 1; }
.reader-find-done { min-width: 36px; padding: 0; border-radius: 999px; font-size: 14px; color: var(--text-dim); }

/* Every hit is tinted; the one you're on is filled so it reads at a glance. */
.reader-mark {
  background: color-mix(in srgb, var(--accent) 30%, transparent);
  color: inherit; border-radius: 3px; padding: 0 1px;
}
.reader-mark--on {
  background: var(--accent); color: #fff;
  box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent) 40%, transparent);
}
/* Fallback for engines without color-mix(). */
@supports not (background: color-mix(in srgb, red 30%, transparent)) {
  .reader-mark { background: rgba(79, 140, 255, 0.3); }
  .reader-mark--on { background: var(--accent); box-shadow: 0 0 0 2px rgba(79, 140, 255, 0.4); }
}

/* Narrow phones: shrink the wide type buttons so bar + find both fit. */
@media (max-width: 430px) {
  .reader-ctl-wide { min-width: 52px; padding: 0 8px; font-size: 13px; }
  .reader-bar { gap: 6px; }
}

.reader-scroll { flex: 1; overflow-y: auto; overflow-x: hidden; -webkit-overflow-scrolling: touch; }
.reader-article {
  max-width: min(var(--reader-measure, 64ch), 100%);
  margin: 0 auto;
  box-sizing: border-box;
  padding: 34px 22px calc(96px + var(--safe-b));
  font-size: var(--reader-fs, 21px); line-height: 1.75;
  /* Long words, URLs and inline code wrap to the column so the reader only
     ever scrolls vertically (never sideways) on narrow / mobile screens. */
  overflow-wrap: break-word; word-break: break-word;
}
.reader--serif .reader-article { font-family: Georgia, "Iowan Old Style", "Palatino Linotype", "Times New Roman", serif; }
.reader--sans .reader-article { font-family: inherit; }
.reader-title { font-size: 1.7em; line-height: 1.2; font-weight: 750; margin: 0 0 6px; letter-spacing: -0.01em; }
.reader-subtitle { color: var(--text-dim); font-size: 0.72em; text-transform: uppercase; letter-spacing: 0.07em; font-weight: 650; }
.reader-meta { color: var(--text-faint); font-size: 0.7em; margin: 12px 0 28px; letter-spacing: 0.02em; }
.reader-h { font-size: 1.28em; font-weight: 700; margin: 1.6em 0 0.5em; line-height: 1.3; }
.reader-prose > :first-child { margin-top: 0; }
/* Justify toggle (the ↔ control): applies to running prose only — headings,
   the title and meta line stay left-aligned. hyphens/word-break keep the
   justified column tidy on narrow / mobile widths (no big rivers). */
.reader--justify .reader-prose .md-p,
.reader--justify .reader-prose .md-list li,
.reader--justify .reader-prose .md-quote,
.reader--justify .reader-editor {
  text-align: justify;
  -webkit-hyphens: auto; hyphens: auto;
}
/* Editable focus-write mode: a borderless textarea matching the reader type. */
.reader-editor {
  width: 100%; box-sizing: border-box; min-height: 60vh;
  background: transparent; border: none; outline: none; resize: none;
  color: var(--text); font: inherit; font-size: 1em; line-height: 1.75;
  padding: 0;
}
.reader--serif .reader-editor { font-family: Georgia, "Iowan Old Style", "Palatino Linotype", "Times New Roman", serif; }

/* Reader-scoped markdown (larger, more generous than the canvas text card). */
.reader-prose .md-p { margin: 0 0 1.05em; }
.reader-prose .md-h1 { font-size: 1.5em; font-weight: 750; margin: 1.4em 0 0.5em; line-height: 1.25; }
.reader-prose .md-h2 { font-size: 1.28em; font-weight: 700; margin: 1.3em 0 0.45em; line-height: 1.3; }
.reader-prose .md-h3 { font-size: 1.1em; font-weight: 700; margin: 1.2em 0 0.4em; }
.reader-prose .md-list { margin: 0 0 1.05em; padding-left: 1.4em; }
.reader-prose .md-list li { margin: 0.3em 0; }
.reader-prose .md-quote {
  margin: 1.2em 0; padding: 0.3em 0 0.3em 1em;
  border-left: 3px solid var(--accent); color: var(--text-dim); font-style: italic;
}
.reader-prose .md-code {
  font-family: ui-monospace, "SF Mono", Menlo, monospace; font-size: 0.88em;
  background: var(--bg-elev-2); padding: 0.1em 0.35em; border-radius: 5px;
  overflow-wrap: anywhere; word-break: break-word;
}
/* Reader Mode: mentions read as inline links — just the coloured text (no chip
   background, border, padding or icon), still clickable. */
.reader-prose .mention {
  cursor: pointer;
  background: none; border: none; padding: 0; margin: 0; border-radius: 0;
  white-space: normal;
}
.reader-prose .mention .mention-ico { display: none; }
.reader-prose .mention:hover { text-decoration: underline; }
.reader-prose .mention.missing { cursor: default; text-decoration: none; }

/* ==================== Zoom level-of-detail (performance) ====================
   Applied by updateCamera(): .lod-far below 45% zoom, .lod-tiny below 22%.
   Fine print that can't be read at that scale is removed from layout entirely,
   which keeps big boards (hundreds of cards + connectors) smooth. */
.viewport.lod-far .ent-open,
.viewport.lod-far .ent-sub,
.viewport.lod-far .board-open-hint,
.viewport.lod-far .fld-lock,
.viewport.lod-far .vid-bar .vid-time,
.viewport.lod-far .au-times,
.viewport.lod-far .au-size,
.viewport.lod-far .file-sub,
.viewport.lod-far .file-open,
.viewport.lod-far .link-badge,
.viewport.lod-far .tag-dots,
.viewport.lod-far .clabels { display: none; }

/* Tiny: only silhouettes read — drop names/bars/shadows too. */
.viewport.lod-tiny .ent-body,
.viewport.lod-tiny .vid-bar,
.viewport.lod-tiny .au-foot,
.viewport.lod-tiny .file-meta,
.viewport.lod-tiny .fld-head,
.viewport.lod-tiny .board-header .name { display: none; }
.viewport.lod-tiny .obj { box-shadow: none; }

/* ============================== Image viewer ============================== */
.iv-overlay {
  position: fixed; inset: 0; z-index: 4000;             /* above the All-images modal (400) */
  background: rgba(8, 10, 14, 0.92);
  display: grid; grid-template-columns: 1fr 300px;
  /* The bar row grows by the notch so its controls stay reachable. */
  grid-template-rows: calc(52px + var(--safe-t)) 1fr;
  grid-template-areas: "bar bar" "stage info";
  opacity: 0; transition: opacity 0.16s ease;
}
.iv-overlay.show { opacity: 1; }
.iv-bar {
  grid-area: bar; display: flex; align-items: center; justify-content: space-between;
  gap: 10px; background: rgba(0,0,0,0.35);
  padding: var(--safe-t) calc(12px + var(--safe-r)) 0 calc(12px + var(--safe-l));
}
.iv-bar-l, .iv-bar-c, .iv-bar-r { display: flex; align-items: center; gap: 6px; }
.iv-count { color: var(--text-dim); font-size: 13px; }
.iv-ctl {
  min-width: 34px; height: 34px; padding: 0 9px; border-radius: 8px;
  border: 1px solid var(--border); background: var(--bg-elev); color: var(--text);
  cursor: pointer; font-size: 14px; display: flex; align-items: center; justify-content: center;
}
.iv-ctl:hover { border-color: var(--accent); color: var(--accent); }
.iv-close:hover { border-color: var(--danger); color: var(--danger); }
.iv-zoom { min-width: 52px; text-align: center; color: var(--text); font-size: 13px; font-variant-numeric: tabular-nums; }
.iv-stage {
  grid-area: stage; position: relative; overflow: hidden; cursor: grab;
  display: flex; align-items: center; justify-content: center;
}
.iv-stage.iv-grabbing { cursor: grabbing; }
.iv-img { max-width: none; max-height: none; transform-origin: center center; will-change: transform; user-select: none; -webkit-user-drag: none; }
.iv-nav {
  position: absolute; top: 50%; transform: translateY(-50%); z-index: 2;
  width: 44px; height: 64px; border-radius: 10px; border: none;
  background: rgba(0,0,0,0.45); color: #fff; font-size: 30px; cursor: pointer;
  display: flex; align-items: center; justify-content: center;
}
.iv-nav:hover { background: rgba(0,0,0,0.7); }
.iv-nav:disabled { opacity: 0.25; cursor: default; }
.iv-prev { left: 14px; }
.iv-next { right: 314px; }        /* clear of the info panel */
.iv-info {
  grid-area: info; background: var(--panel); border-left: 1px solid var(--border);
  padding: 18px 16px; overflow-y: auto; display: flex; flex-direction: column; gap: 14px;
}
.iv-name { font-size: 16px; font-weight: 700; color: var(--text); word-break: break-word; }
.iv-rows { display: flex; flex-direction: column; gap: 8px; }
.iv-row { display: flex; justify-content: space-between; gap: 12px; font-size: 13px; }
.iv-k { color: var(--text-faint); font-size: 11px; text-transform: uppercase; letter-spacing: 0.04em; font-weight: 650; }
.iv-v { color: var(--text); text-align: right; word-break: break-word; font-variant-numeric: tabular-nums; }
.iv-tags { display: flex; flex-direction: column; gap: 6px; }
.iv-tagrow { display: flex; flex-wrap: wrap; gap: 6px; }
.iv-tag { font-size: 12px; padding: 2px 9px; border-radius: 999px; background: var(--bg-elev); border: 1px solid var(--border); color: var(--text); }
@media (max-width: 720px) {
  .iv-overlay { grid-template-columns: 1fr; grid-template-rows: 52px 1fr auto; grid-template-areas: "bar" "stage" "info"; }
  .iv-info { flex-direction: row; flex-wrap: wrap; border-left: none; border-top: 1px solid var(--border); max-height: 34vh; }
  .iv-next { right: 14px; }
}

/* ========================= Relationship Graph View ========================= */
.gv-overlay {
  position: fixed; inset: 0; z-index: 70; background: var(--bg);
  opacity: 0; transition: opacity 0.2s ease; overflow: hidden;
}
.gv-overlay.show { opacity: 1; }
.gv-canvas { position: absolute; inset: 0; width: 100%; height: 100%; cursor: grab; touch-action: none; }
.gv-canvas:active { cursor: grabbing; }

.gv-bar {
  position: absolute; top: 0; left: 0; right: 0; z-index: 2;
  /* Wraps, like .ft-bar: on a phone the middle group alone is wider than the
     screen, and without this the zoom and close buttons are laid out past the
     right edge — drawn, but off-screen and unreachable. */
  display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
  /* Full-screen overlay: its own bar has to clear the notch, since there is
     no toolbar above it doing that. */
  padding: calc(12px + var(--safe-t)) calc(16px + var(--safe-r)) 12px calc(16px + var(--safe-l));
  background: linear-gradient(var(--bg), color-mix(in srgb, var(--bg) 40%, transparent));
  pointer-events: none;
}
.gv-bar > * { pointer-events: auto; }
.gv-title { display: flex; align-items: center; gap: 8px; font-weight: 650; font-size: 15px; color: var(--text); white-space: nowrap; }
.gv-title-ico { font-size: 17px; }
.gv-count { font-size: 12px; color: var(--text-dim); font-weight: 500; margin-left: 4px; }
.gv-bar-mid { display: flex; align-items: center; gap: 10px; flex: 1; justify-content: center; flex-wrap: wrap; }
.gv-bar-right { display: flex; align-items: center; gap: 6px; }

/* ==================== Timeline view ==================== */
.tlv-overlay {
  position: fixed; inset: 0; z-index: 70; background: var(--tlv-bg, var(--bg));
  opacity: 0; transition: opacity 0.2s ease; overflow: hidden;
}
.tlv-overlay.show { opacity: 1; }
.tlv-bar {
  position: absolute; top: 0; left: 0; right: 0; z-index: 3;
  /* See .gv-bar — wraps so the right-hand controls stay on screen. */
  display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
  padding: calc(12px + var(--safe-t)) calc(16px + var(--safe-r)) 12px calc(16px + var(--safe-l));
  background: linear-gradient(var(--bg), color-mix(in srgb, var(--bg) 30%, transparent));
  pointer-events: none;
}
.tlv-bar > * { pointer-events: auto; }
.tlv-bar-mid { display: flex; align-items: center; gap: 10px; flex: 1; justify-content: center; flex-wrap: wrap; }
.tlv-bar-right { display: flex; align-items: center; gap: 6px; }
.tlv-title { display: flex; align-items: center; gap: 8px; font-weight: 700; }
.tlv-title-ico { font-size: 18px; }
.tlv-count { opacity: 0.6; font-weight: 500; font-size: 13px; }
.tlv-range { display: flex; align-items: center; gap: 6px; font-size: 13px; opacity: 0.9; }
.tlv-range label { opacity: 0.7; }
.tlv-range-in { width: 84px; }
.tlv-range-dash { opacity: 0.5; }
.tlv-filter { font-size: 12px; opacity: 0.7; padding: 3px 10px; border-radius: 999px; background: rgba(128,128,128,0.14); white-space: nowrap; }
.tlv-filter.has-hidden { color: #f2c94c; opacity: 0.95; }

/* ---- narrow screens ----
   The range inputs and the filter pill get a row of their own, so the title
   and the controls share the first row and nothing is pushed off the edge.
   Both floating panels re-anchor to the bottom, since a wrapped bar is taller
   than the fixed offsets they used to clear it by. */
@media (max-width: 900px) {
  .tlv-bar { gap: 10px; row-gap: 8px; }
  .tlv-bar-mid { order: 3; width: 100%; justify-content: flex-start; }
  .tlv-bar-right { margin-left: auto; }
  .tlv-range-in { width: 68px; }
  .tlv-inspector { top: auto; left: 12px; right: 12px; bottom: 12px; width: auto; max-height: 52vh; }
  .tlv-dev { top: auto; left: 12px; right: 12px; bottom: 12px; width: auto; max-height: 52vh; }
}

/* Event details panel (click a marker) */
.tlv-inspector {
  position: absolute; top: 68px; right: 16px; width: 264px; z-index: 10;
  background: color-mix(in srgb, var(--bg) 86%, #808080 8%); border: 1px solid rgba(128,128,128,0.3);
  border-radius: 12px; padding: 12px 14px; box-shadow: 0 10px 34px rgba(0,0,0,0.5);
}
.tlv-insp-head { display: flex; align-items: center; gap: 6px; margin-bottom: 8px; }
.tlv-insp-ico { flex: 0 0 auto; }
.tlv-insp-title { font-weight: 700; flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tlv-insp-close { flex: 0 0 auto; width: 26px; height: 26px; font-size: 13px; }
.tlv-insp-row { display: flex; justify-content: space-between; gap: 10px; padding: 3px 0; font-size: 13px; }
.tlv-insp-k { opacity: 0.6; }
.tlv-insp-v { text-align: right; }
.tlv-insp-open { margin-top: 10px; width: 100%; padding: 7px; border-radius: 8px; border: 1px solid var(--accent, #4f8cff); background: var(--accent, #4f8cff); color: #fff; cursor: pointer; font-weight: 600; }
.tlv-insp-open:hover { filter: brightness(1.08); }
.tlv-ctl {
  width: 34px; height: 34px; display: inline-flex; align-items: center; justify-content: center;
  border-radius: 8px; border: 1px solid var(--border, rgba(128,128,128,0.3)); background: var(--bg-elev-1, rgba(128,128,128,0.08));
  color: inherit; cursor: pointer; font-size: 16px;
}
.tlv-ctl:hover { background: rgba(128,128,128,0.18); }
.tlv-close:hover { background: #e5484d; color: #fff; border-color: #e5484d; }

.tlv-stage { position: absolute; inset: 0; overflow: hidden; cursor: grab; touch-action: none; }
.tlv-stage:active { cursor: grabbing; }
.tlv-axis { position: absolute; left: 0; right: 0; height: var(--tlv-axis-h, 2px); background: var(--tlv-axis-color, color-mix(in srgb, var(--text, #888) 55%, transparent)); transform: translateY(-50%); }
.tlv-ticks { position: absolute; inset: 0; pointer-events: none; }
.tlv-tick { position: absolute; width: 1px; height: var(--tlv-tick-h, 9px); background: var(--tlv-axis-color, color-mix(in srgb, var(--text, #888) 40%, transparent)); transform: translate(-50%, -50%); }
.tlv-tick-label { position: absolute; transform: translate(-50%, 0); font-size: var(--tlv-tick-font, 11px); opacity: 0.55; white-space: nowrap; }

.tlv-ev { position: absolute; inset: 0; pointer-events: none; transition: opacity 0.12s ease; }
.tlv-ev > * { pointer-events: auto; }
/* Hovering a marker fades the others so the focused one stands out. */
.tlv-stage.has-hover .tlv-ev:not(.hover) { opacity: var(--tlv-dim, 0.28); }

/* Start/end guide lines for a hovered/selected duration event. */
.tlv-guides { position: absolute; inset: 0; pointer-events: none; z-index: 1; }
.tlv-guide { position: absolute; top: 0; bottom: 0; width: 0; border-left: 1px dashed color-mix(in srgb, var(--tlv-c, #4f8cff) 80%, transparent); transform: translateX(-50%); }
.tlv-guide-tag {
  position: absolute; transform: translate(-50%, -50%);
  background: var(--tlv-c, #4f8cff); color: #fff; padding: 1px 7px; border-radius: 6px;
  font-size: 11px; font-weight: 700; font-variant-numeric: tabular-nums;
  box-shadow: 0 1px 4px rgba(0,0,0,0.45); white-space: nowrap;
}
.tlv-stem {
  position: absolute; z-index: 1; width: var(--tlv-stem-w, 12px); padding: 0 5px; box-sizing: border-box; background-clip: content-box;
  background: color-mix(in srgb, var(--tlv-c, #888) 45%, transparent); transform: translateX(-50%); cursor: grab;
}
.tlv-stem:hover { background: color-mix(in srgb, var(--tlv-c, #888) 80%, transparent); }
.tlv-dot {
  position: absolute; z-index: 3; width: var(--tlv-dot, 14px); height: var(--tlv-dot, 14px); border-radius: 50%;
  background: var(--tlv-c, #888); border: var(--tlv-dot-border, 2px) solid var(--bg); transform: translate(-50%, -50%);
  cursor: grab; box-shadow: 0 1px 4px rgba(0,0,0,0.4);
}
.tlv-point.sel .tlv-dot, .tlv-dur.sel .tlv-bar-ev { outline: 2px solid var(--tlv-accent, var(--accent, #4f8cff)); outline-offset: 2px; }
.tlv-label {
  position: absolute; z-index: 5; transform: translateX(-50%); max-width: var(--tlv-label-maxw, 200px);
  padding: var(--tlv-label-py, 3px) var(--tlv-label-px, 9px); border-radius: var(--tlv-label-radius, 7px); font-size: var(--tlv-label-font, 12.5px); cursor: grab; text-align: center;
  color: var(--tlv-label-text, #fff); background: var(--tlv-label-bg, rgba(17,19,25,0.92)); border: 1px solid var(--tlv-label-border, rgba(255,255,255,0.16));
  box-shadow: 0 1px 4px rgba(0,0,0,0.4); line-height: 1.25;
}
.tlv-label.above { transform: translate(-50%, -100%); }
.tlv-label.below { transform: translate(-50%, 0); }
.tlv-label-title { font-weight: var(--tlv-title-weight, 700); word-break: break-word; overflow-wrap: anywhere; }
.tlv-label-year { opacity: 0.7; font-size: var(--tlv-sub-font, 11.5px); font-variant-numeric: tabular-nums; margin-top: 1px; }
.tlv-ev-ico { opacity: 0.9; }
.tlv-bar-ev {
  position: absolute; z-index: 3; height: var(--tlv-bar-h, 18px); border-radius: var(--tlv-bar-radius, 5px); cursor: grab;
  background: color-mix(in srgb, var(--tlv-c, #888) 80%, transparent);
  border: 1px solid color-mix(in srgb, var(--tlv-c, #888) 100%, #000 15%);
}

/* Dev inspector — floating panel to restyle the timeline (persisted). */
.tlv-dev {
  position: absolute; top: 60px; right: 12px; width: 266px;
  max-height: calc(100vh - 84px); overflow-y: auto; z-index: 6;
  background: var(--bg-elev, #1b1e24); border: 1px solid var(--border-strong, #333);
  border-radius: 12px; padding: 12px; box-shadow: var(--shadow, 0 10px 34px rgba(0,0,0,0.45));
  font-size: 13px; pointer-events: auto;
}
.tlv-dev[hidden] { display: none; }
.tlv-dev-head { display: flex; align-items: center; gap: 6px; margin-bottom: 4px; }
.tlv-dev-title { flex: 1 1 auto; font-weight: 700; }
.tlv-dev-note { font-size: 11.5px; opacity: 0.55; margin-bottom: 8px; }
.tlv-dev h4 { margin: 12px 0 6px; font-size: 11px; text-transform: uppercase; letter-spacing: 0.05em; opacity: 0.55; }
.tlv-dev-row { display: flex; align-items: center; gap: 8px; margin-bottom: 6px; }
.tlv-dev-row label { flex: 0 0 88px; opacity: 0.78; font-size: 12px; text-transform: capitalize; }
.tlv-dev-row input[type="range"] { flex: 1 1 auto; min-width: 0; }
.tlv-dev-num { flex: 0 0 46px; width: 46px; text-align: center; padding: 4px 4px; }
.tlv-dev-num::-webkit-outer-spin-button, .tlv-dev-num::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.tlv-dev-num { -moz-appearance: textfield; }
.tlv-dev-row input[type="color"] { flex: 0 0 auto; width: 40px; height: 26px; padding: 0; border: 1px solid var(--border); border-radius: 6px; background: transparent; cursor: pointer; margin-left: auto; }
.tlv-dev-mini { flex: 0 0 auto; width: 24px; height: 24px; border: 1px solid var(--border); border-radius: 6px; background: var(--bg-elev-2); color: var(--text-dim); cursor: pointer; font-size: 12px; }
.tlv-dev-mini:hover { color: var(--text); border-color: var(--border-strong); }
.tlv-dev-actions { margin-top: 12px; }
.tlv-handle { position: absolute; top: 0; bottom: 0; width: 9px; cursor: ew-resize; }
.tlv-handle.l { left: -2px; } .tlv-handle.r { right: -2px; }
.tlv-empty {
  position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 6px; text-align: center; pointer-events: none; opacity: 0.85;
}
.tlv-empty[hidden] { display: none; }
.tlv-empty-ico { font-size: 40px; }
.tlv-empty-title { font-size: 18px; font-weight: 700; }
.tlv-empty-sub { opacity: 0.6; max-width: 380px; }
.tlv-hint { position: absolute; bottom: 12px; left: 0; right: 0; text-align: center; font-size: 12px; opacity: 0.4; pointer-events: none; }

/* Inspector slider + numeric pair for float timeline fields */
.tlv-dual { display: flex; align-items: center; gap: 8px; flex: 1 1 auto; width: 100%; min-width: 0; }
.tlv-dual-range { flex: 1 1 auto; min-width: 0; }
.tlv-dual-num { flex: 0 0 74px; width: 74px; }

/* Dual-thumb (min–max) slider for a duration's start & end */
.tlv-mm-wrap { display: flex; flex-direction: column; gap: 8px; width: 100%; }
.tlv-mm { position: relative; height: 24px; }
.tlv-mm-track { position: absolute; top: 50%; left: 0; right: 0; height: 4px; border-radius: 2px; background: rgba(128,128,128,0.32); transform: translateY(-50%); }
.tlv-mm-fill { position: absolute; top: 50%; height: 4px; border-radius: 2px; background: var(--accent, #4f8cff); transform: translateY(-50%); }
.tlv-mm-range { position: absolute; top: 0; left: 0; width: 100%; height: 24px; margin: 0; padding: 0; background: none; -webkit-appearance: none; appearance: none; pointer-events: none; }
.tlv-mm-range:focus { outline: none; }
.tlv-mm-range::-webkit-slider-runnable-track { background: transparent; height: 24px; border: none; }
.tlv-mm-range::-moz-range-track { background: transparent; height: 24px; border: none; }
.tlv-mm-range::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none; pointer-events: auto; margin-top: 4px;
  width: 16px; height: 16px; border-radius: 50%; background: var(--accent, #4f8cff);
  border: 2px solid var(--bg); cursor: grab; box-shadow: 0 1px 3px rgba(0,0,0,0.4);
}
.tlv-mm-range::-moz-range-thumb {
  pointer-events: auto; width: 16px; height: 16px; border-radius: 50%; background: var(--accent, #4f8cff);
  border: 2px solid var(--bg); cursor: grab;
}
.tlv-mm-nums { display: flex; align-items: center; gap: 8px; }
.tlv-mm-lab { opacity: 0.6; font-size: 12px; }
.tlv-mm-nums .tlv-dual-num { flex: 1 1 0; width: auto; min-width: 0; }
/* Duration "Range": stacked label / slider / min–max inputs */
.tlv-range-block { display: flex; flex-direction: column; gap: 8px; width: 100%; }
.tlv-range-label { color: var(--text-dim); font-size: inherit; }
.gv-segs { display: inline-flex; background: var(--bg-elev); border: 1px solid var(--border); border-radius: 999px; padding: 3px; }
.gv-seg {
  border: none; background: transparent; color: var(--text-dim); cursor: pointer;
  font-size: 12.5px; font-weight: 600; padding: 6px 14px; border-radius: 999px; transition: background 0.15s, color 0.15s;
}
.gv-seg.active { background: var(--accent); color: #fff; }
.gv-search { width: 180px; max-width: 40vw; }
.gv-ctl {
  width: 36px; height: 36px; border-radius: 9px; border: 1px solid var(--border);
  background: var(--bg-elev); color: var(--text); cursor: pointer; font-size: 15px;
  display: flex; align-items: center; justify-content: center;
}
.gv-ctl:hover { border-color: var(--accent); color: var(--accent); }
.gv-ctl.active { border-color: var(--accent); color: var(--accent); background: var(--accent-soft); }
.gv-close:hover { border-color: var(--danger); color: var(--danger); }

/* Physics debug panel — live layout-force tuning */
.gv-debug {
  position: absolute; left: 16px; top: 66px; z-index: 4; width: 262px; max-width: calc(100vw - 32px);
  max-height: calc(100vh - 96px); overflow-y: auto;
  background: var(--panel); border: 1px solid var(--border); border-radius: 14px; box-shadow: var(--shadow);
  display: flex; flex-direction: column; padding: 12px 14px;
}
.gv-debug[hidden] { display: none; }
.gv-dbg-head { display: flex; align-items: center; gap: 8px; margin-bottom: 10px; }
.gv-dbg-ico { font-size: 15px; }
.gv-dbg-title { font-size: 14px; font-weight: 700; color: var(--text); }
.gv-dbg-mode {
  margin-left: 4px; font-size: 10.5px; font-weight: 600; color: var(--accent);
  background: var(--accent-soft); border-radius: 999px; padding: 1px 8px; letter-spacing: 0.02em;
}
.gv-dbg-head .gv-ins-x { margin-left: auto; }
.gv-dbg-rows { display: flex; flex-direction: column; gap: 11px; }
.gv-dbg-row { display: flex; flex-direction: column; gap: 4px; }
.gv-dbg-labs { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; }
.gv-dbg-lab { font-size: 12px; color: var(--text-dim); }
.gv-dbg-val { font-size: 11.5px; font-weight: 650; color: var(--text); font-variant-numeric: tabular-nums; }
.gv-dbg-range { width: 100%; accent-color: var(--accent); cursor: pointer; margin: 0; }
.gv-dbg-actions { margin-top: 12px; }
.gv-dbg-hint { margin-top: 8px; font-size: 10.5px; color: var(--text-faint); line-height: 1.4; }

@media (max-width: 640px) {
  .gv-debug { top: auto; bottom: 8px; left: 8px; width: 240px; max-height: 52vh; }
}

.gv-legend {
  position: absolute; left: 16px; bottom: 16px; z-index: 2;
  display: flex; flex-direction: column; gap: 5px; max-height: 40vh; overflow: auto;
  background: color-mix(in srgb, var(--bg-elev) 82%, transparent); border: 1px solid var(--border);
  border-radius: 10px; padding: 10px 12px; backdrop-filter: blur(6px);
}
.gv-leg { display: flex; align-items: center; gap: 7px; font-size: 12px; color: var(--text-dim); }
.gv-leg-dot { width: 10px; height: 10px; border-radius: 50%; flex: 0 0 auto; }

.gv-empty {
  position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center;
  justify-content: center; gap: 8px; text-align: center; color: var(--text-dim); pointer-events: none;
}
.gv-empty[hidden] { display: none; }
.gv-empty-ico { font-size: 52px; opacity: 0.5; }
.gv-empty-title { font-size: 18px; font-weight: 650; color: var(--text); }
.gv-empty-sub { font-size: 13px; max-width: 320px; }

/* Inspector panel */
.gv-inspector {
  position: absolute; top: 66px; right: 16px; bottom: 16px; width: 340px; max-width: calc(100vw - 32px); z-index: 3;
  background: var(--panel); border: 1px solid var(--border); border-radius: 14px;
  box-shadow: var(--shadow); display: flex; flex-direction: column; overflow: hidden;
  transform: translateX(16px); opacity: 0; transition: transform 0.18s ease, opacity 0.18s ease;
}
.gv-inspector[hidden] { display: none; }
.gv-inspector.show { transform: none; opacity: 1; }
.gv-ins-head { display: flex; align-items: center; gap: 12px; padding: 16px; border-bottom: 1px solid var(--border); }
.gv-ins-avatar {
  width: 52px; height: 52px; border-radius: 50%; flex: 0 0 auto; border: 2px solid var(--accent);
  background: var(--bg-elev-2) center/cover no-repeat; display: flex; align-items: center; justify-content: center; font-size: 26px;
}
.gv-ins-htext { flex: 1; min-width: 0; }
.gv-ins-name { font-size: 16px; font-weight: 700; color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.gv-ins-type { display: flex; align-items: center; gap: 6px; margin-top: 3px; font-size: 12px; color: var(--text-dim); }
.gv-ins-tdot { width: 9px; height: 9px; border-radius: 50%; }
.gv-ins-x { border: none; background: transparent; color: var(--text-dim); cursor: pointer; font-size: 16px; width: 28px; height: 28px; border-radius: 7px; flex: 0 0 auto; }
.gv-ins-x:hover { background: var(--bg-elev); color: var(--text); }
.gv-inspector > .gv-ins-facts,
.gv-inspector > .gv-ins-rels { padding: 12px 16px; overflow-y: auto; }
.gv-ins-rels { flex: 1; border-top: 1px solid var(--border); }
.gv-fact { margin-bottom: 10px; }
.gv-fact-k { font-size: 11px; text-transform: uppercase; letter-spacing: 0.04em; color: var(--text-faint); font-weight: 650; margin-bottom: 2px; }
.gv-fact-v { font-size: 13px; color: var(--text); line-height: 1.45; }
.gv-rel-h { display: flex; align-items: center; gap: 7px; font-size: 12.5px; font-weight: 650; color: var(--text); margin: 12px 0 7px; }
.gv-rel-h:first-child { margin-top: 0; }
.gv-rel-n { margin-left: auto; font-size: 11px; color: var(--text-dim); background: var(--bg-elev); border-radius: 999px; padding: 1px 8px; }
.gv-rel-chips { display: flex; flex-wrap: wrap; gap: 6px; }
.gv-chip {
  display: inline-flex; align-items: center; gap: 5px; max-width: 100%;
  border: 1px solid var(--border); background: var(--bg-elev); color: var(--text);
  border-radius: 999px; padding: 4px 11px; font-size: 12px; cursor: pointer;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.gv-chip:hover { border-color: var(--accent); background: var(--accent-soft); }
.gv-chip-ico { flex: 0 0 auto; font-size: 12px; }
.gv-ins-none { font-size: 12.5px; color: var(--text-dim); }
.gv-ins-actions { padding: 12px 16px; border-top: 1px solid var(--border); }

@media (max-width: 640px) {
  .gv-inspector { top: auto; right: 8px; left: 8px; bottom: 8px; width: auto; max-height: 55vh; }
  .gv-title span:not(.gv-title-ico):not(.gv-count) { display: none; }
  /* Search and the mode segments take their own row; the controls stay top-right. */
  .gv-bar { gap: 10px; row-gap: 8px; }
  .gv-bar-mid { order: 3; width: 100%; justify-content: flex-start; }
  .gv-bar-right { margin-left: auto; }
  .gv-search { width: 100%; max-width: none; }
}

/* Detail-sheet long-text preview: clamp; the Read action opens the full page. */
.sheet-text.clamp { display: -webkit-box; -webkit-line-clamp: 6; -webkit-box-orient: vertical; overflow: hidden; }

/* ===================== "How to share" onboarding page ===================== */
.howto {
  position: fixed; inset: 0; z-index: 69;
  background: var(--bg); color: var(--text);
  display: flex; flex-direction: column;
  opacity: 0; transition: opacity 0.2s ease;
}
.howto.show { opacity: 1; }
.howto-bar {
  flex: 0 0 auto; display: flex; align-items: center; justify-content: space-between;
  padding: max(12px, var(--safe-t)) max(16px, var(--safe-r)) 12px max(16px, var(--safe-l));
  border-bottom: 1px solid var(--border); background: var(--bg-elev);
}
.howto-bar-title { font-size: 16px; font-weight: 700; }
.howto-x {
  width: 38px; height: 38px; border-radius: 999px; border: 1px solid var(--border);
  background: var(--bg-elev-2); color: var(--text-dim); font-size: 16px; cursor: pointer;
}
.howto-scroll { flex: 1; overflow-y: auto; -webkit-overflow-scrolling: touch; }
.howto-body { max-width: 560px; margin: 0 auto; padding: 24px 20px calc(40px + var(--safe-b)); }
.howto-hero { font-size: 46px; text-align: center; margin-bottom: 6px; }
.howto-lead { text-align: center; font-size: 15.5px; line-height: 1.5; color: var(--text-dim); margin: 0 auto 24px; max-width: 40ch; }
.howto-steps { display: flex; flex-direction: column; gap: 12px; }
.howto-step {
  display: flex; gap: 14px; align-items: flex-start;
  padding: 15px; border: 1px solid var(--border); border-radius: 14px; background: var(--bg-elev);
}
.howto-num {
  flex: 0 0 auto; width: 30px; height: 30px; border-radius: 999px;
  display: flex; align-items: center; justify-content: center;
  background: var(--accent); color: #fff; font-weight: 700; font-size: 15px;
}
.howto-step-body { min-width: 0; }
.howto-step-title { font-size: 15.5px; font-weight: 650; margin-bottom: 3px; }
.howto-step-desc { font-size: 14px; line-height: 1.5; color: var(--text-dim); }
.howto-note { margin-top: 20px; font-size: 13px; line-height: 1.5; color: var(--text-faint); text-align: center; }
.howto-cta {
  display: block; width: 100%; margin-top: 22px; height: 50px;
  border: none; border-radius: 13px; background: var(--accent); color: #fff;
  font: inherit; font-size: 16px; font-weight: 650; cursor: pointer;
}
.howto-cta:active { transform: translateY(1px); }
/* One-tap install (beta) */
.howto-install {
  margin: 0 0 6px; padding: 16px; border-radius: 14px;
  border: 1px solid var(--accent); background: var(--accent-soft);
}
.howto-install-title { font-size: 15px; font-weight: 700; margin-bottom: 4px; }
.howto-install-sub { font-size: 12.5px; line-height: 1.45; color: var(--text-dim); margin-bottom: 12px; }
.howto-install-btn {
  display: block; text-align: center; text-decoration: none;
  height: 46px; line-height: 46px; margin-top: 8px;
  border-radius: 12px; background: var(--accent); color: #fff;
  font-size: 14.5px; font-weight: 650;
}
.howto-install-btn:active { transform: translateY(1px); }
.howto-or {
  text-align: center; font-size: 12px; font-weight: 650; letter-spacing: 0.04em;
  text-transform: uppercase; color: var(--text-faint); margin: 22px 0 12px;
}

/* ===================== Safe areas (notches / camera cutouts) ===================== */
#toolbar {
  padding-top: max(8px, var(--safe-t));
  padding-left: max(14px, var(--safe-l));
  padding-right: max(14px, var(--safe-r));
}
.login-overlay { padding: max(24px, var(--safe-t)) max(24px, var(--safe-r)) max(24px, var(--safe-b)) max(24px, var(--safe-l)); }
.version-badge { right: max(8px, var(--safe-r)); bottom: max(6px, var(--safe-b)); }

/* ============ Project sheet (touch replacement for the dropdown) ============
   Rows are full-width and --tap tall, the list scrolls inside the dialog, and
   the actions that were nested inside a "Switch project" submenu are laid out
   alongside the projects instead of hidden behind it. */
.psheet { display: flex; flex-direction: column; min-height: 0; }
.psheet-list { display: flex; flex-direction: column; gap: 4px; overflow-y: auto; max-height: min(70vh, 620px); }
.psheet-head {
  font-size: 10.5px; text-transform: uppercase; letter-spacing: 0.5px;
  color: var(--text-faint); margin: 10px 2px 2px;
}
.psheet-head:first-child { margin-top: 0; }
.psheet-row {
  display: flex; align-items: center; gap: 12px; width: 100%; text-align: left;
  min-height: var(--tap); padding: 8px 12px; cursor: pointer;
  background: var(--bg-elev); color: var(--text);
  border: 1px solid var(--border); border-radius: 11px;
}
.psheet-row:hover { border-color: var(--accent); }
.psheet-row:active { transform: translateY(1px); }
.psheet-row.active { border-color: var(--accent); background: var(--accent-soft); }
.psheet-row.danger { color: var(--danger); }
.psheet-ico { flex: 0 0 auto; font-size: 17px; width: 22px; text-align: center; }
.psheet-text { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.psheet-label { font-size: 14.5px; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.psheet-sub { font-size: 11.5px; color: var(--text-dim); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.psheet-check { flex: 0 0 auto; color: var(--accent); font-weight: 700; }
.psheet-loading { font-size: 12.5px; color: var(--text-faint); padding: 8px 12px; }

/* ===================== Live collaboration ===================== */
.presence-layer {
  position: absolute; inset: 0;
  pointer-events: none;
  z-index: 40;
  overflow: hidden;
}
.remote-cursor {
  position: absolute;
  transform: translate(-2px, -2px);
  will-change: left, top;
  display: flex; align-items: flex-start; gap: 4px;
}
.remote-cursor .rc-arrow {
  color: var(--rc-color, #4f8cff);
  font-size: 15px; line-height: 1;
  text-shadow: 0 1px 2px rgba(0,0,0,0.4);
  transform: rotate(-30deg);
}
.remote-cursor .rc-label {
  margin-top: 8px;
  background: var(--rc-color, #4f8cff);
  color: #fff; font-size: 11px; font-weight: 600;
  padding: 2px 7px; border-radius: 10px; white-space: nowrap;
  box-shadow: var(--shadow-sm);
}
/* Another user's current selection */
.obj.remote-selected {
  outline: calc(2px * var(--inv-scale, 1)) dashed var(--accent);
  outline-offset: calc(3px * var(--inv-scale, 1));
}

/* ===================== Context menu ===================== */
.context-menu {
  position: fixed;
  min-width: 190px;
  background: var(--bg-elev);
  border: 1px solid var(--border-strong);
  border-radius: 12px;
  box-shadow: var(--shadow);
  padding: 6px;
  z-index: 500;   /* above modals (400) so right-click menus work inside pickers */
  user-select: none;
  font-size: 13.5px;
}
.cm-item {
  display: flex; align-items: center; gap: 10px;
  padding: 8px 10px;
  border-radius: 8px;
  cursor: pointer;
  color: var(--text);
  position: relative;
  white-space: nowrap;   /* keep labels (e.g. project names) on one line */
}
.cm-item:hover { background: var(--bg-elev-2); }
.cm-item .cm-ico { width: 18px; text-align: center; opacity: 0.9; }
/* Loading row (e.g. "Loading projects…" while the server list is fetched). */
.cm-item.cm-loading .cm-ico::before {
  content: ""; display: inline-block; width: 12px; height: 12px; vertical-align: -1px;
  border: 2px solid var(--border); border-top-color: var(--accent);
  border-radius: 50%; animation: cm-spin 0.7s linear infinite;
}
@keyframes cm-spin { to { transform: rotate(360deg); } }
.cm-item .cm-key { margin-left: auto; color: var(--text-faint); font-size: 11.5px; }
.cm-item.danger { color: var(--danger); }
.cm-item.has-sub::after { content: "›"; margin-left: auto; color: var(--text-faint); }
.cm-item.has-sub .cm-key { display: none; }
.cm-sep { height: 1px; background: var(--border); margin: 5px 4px; }
.cm-sub {
  position: absolute;
  left: 100%; top: -6px;
  margin-left: 2px;
  min-width: 240px;
  background: var(--bg-elev);
  border: 1px solid var(--border-strong);
  border-radius: 12px;
  box-shadow: var(--shadow);
  padding: 6px;
  display: none;
}
.cm-item.has-sub:hover > .cm-sub { display: block; }
.cm-item.has-sub.cm-open > .cm-sub { display: block; }
/* Touch: no hover, so tapping expands the submenu inline (flows below the row)
   rather than flying out to the side where it would run off a narrow screen. */
.touch-mode .cm-sub {
  position: static; left: auto; top: auto; margin: 4px 0 4px 10px; min-width: 0;
  box-shadow: none; border: none; border-left: 2px solid var(--border-strong);
  border-radius: 0; background: transparent; padding: 2px 0 2px 6px; display: none;
}
.touch-mode .cm-item.has-sub.cm-open { background: var(--bg-elev-2); }
.touch-mode .cm-item.has-sub.cm-open::after { transform: rotate(90deg); }
.touch-mode .cm-item.has-sub::after { transition: transform 0.12s ease; display: inline-block; }
.swatch-row { display: flex; gap: 6px; padding: 6px 8px; flex-wrap: wrap; max-width: 190px; }
.swatch { width: 20px; height: 20px; border-radius: 5px; cursor: pointer; border: 1px solid var(--border-strong); }
.swatch:hover { transform: scale(1.12); }

/* ===================== Inspector ===================== */
.inspector {
  position: fixed;
  top: 95px;
  right: 12px;
  width: 400px;
  max-height: calc(100vh - 111px);
  overflow-y: auto;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 14px;
  box-shadow: var(--shadow);
  z-index: 25;
  padding: 14px;
  font-size: 13px;
}
.insp-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 12px; }
.insp-title { font-weight: 650; font-size: 14px; text-transform: capitalize; }
.insp-type-badge { font-size: 11px; color: var(--text-dim); background: var(--bg-elev-2); padding: 2px 8px; border-radius: 20px; }
.insp-section { margin-bottom: 14px; }
.insp-section > label { display: block; font-size: 11px; text-transform: uppercase; letter-spacing: 0.5px; color: var(--text-faint); margin-bottom: 6px; }
.insp-row { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; }
.insp-row label { flex: 0 0 auto; color: var(--text-dim); min-width: 62px; }
/* Controls must shrink to the space next to the label — a fixed-width child
   (e.g. .mini-btn.full's width:100%) otherwise overflows and gets cropped. */
.insp-row > * { min-width: 0; }
.insp-row .mini-btn.full { width: auto; flex: 1; margin-top: 0; }

/* Slider + numeric value input, side by side. */
.range-num { display: flex; align-items: center; gap: 8px; flex: 1 1 auto; min-width: 0; }
.range-num-slider { flex: 1 1 auto; min-width: 0; padding: 0; background: transparent; }
.range-num-input { flex: 0 0 52px; width: 52px; text-align: center; padding: 4px 4px; }
/* Trim the number spinners so the compact field isn't crowded. */
.range-num-input::-webkit-outer-spin-button,
.range-num-input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.range-num-input { -moz-appearance: textfield; }

/* Stackable image shaders */
.shader-stack { display: flex; flex-direction: column; gap: 8px; margin-bottom: 8px; }
.shader-empty { font-size: 12px; color: var(--text-faint); padding: 6px 2px; }
.shader-item { border: 1px solid var(--border); border-radius: 9px; overflow: hidden; background: var(--bg-elev-2); }
.shader-item.off { opacity: 0.55; }
.shader-head { display: flex; align-items: center; gap: 6px; padding: 6px 8px; background: color-mix(in srgb, var(--bg-elev-2) 60%, var(--bg)); }
.shader-ico { flex: 0 0 auto; }
.shader-name { flex: 1 1 auto; min-width: 0; font-size: 12.5px; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.shader-tog { flex: 0 0 auto; display: inline-flex; align-items: center; }
.shader-btn { flex: 0 0 auto; width: 22px; height: 22px; padding: 0; border: 1px solid var(--border); border-radius: 5px; background: var(--bg-elev); color: var(--text-dim); cursor: pointer; font-size: 11px; line-height: 1; }
.shader-btn:hover { color: var(--text); border-color: var(--border-strong); }
.shader-btn:disabled { opacity: 0.35; cursor: default; }
.shader-del:hover { color: var(--danger); border-color: var(--danger); }
.shader-body { padding: 8px; display: flex; flex-direction: column; gap: 0; }
.shader-body .insp-row { margin-bottom: 6px; }
.shader-body .insp-row:last-child { margin-bottom: 0; }
.shader-body .insp-row label { min-width: 74px; }

/* The inspector often needs to scroll (entities have many fields) — keep the
   scrollbar visible so fields below the fold aren't "hidden". */
.inspector { scrollbar-width: thin; scrollbar-color: var(--border-strong) transparent; }
.inspector::-webkit-scrollbar { width: 8px; }
.inspector::-webkit-scrollbar-thumb { background: var(--border-strong); border-radius: 4px; }
.inspector::-webkit-scrollbar-thumb:hover { background: var(--text-faint); }
.inspector::-webkit-scrollbar-track { background: transparent; }

.field {
  width: 100%;
  background: var(--bg-elev-2);
  border: 1px solid var(--border);
  border-radius: 8px;
  color: var(--text);
  padding: 7px 9px;
  font-size: 13px;
  font-family: inherit;
}
.field:focus { outline: none; border-color: var(--accent); }
textarea.field { resize: vertical; min-height: 60px; line-height: 1.4; }
/* Auto-growing fields (descriptions, notes) — JS sizes them to their content;
   no manual resize handle, no min-height forcing empty space. */
textarea.field.field-autosize { resize: none; min-height: 34px; overflow-y: hidden; transition: height .08s ease; }

/* Connector inspector — source/target card previews */
.conn-card {
  display: flex; align-items: center; gap: 9px; width: 100%;
  background: var(--bg-elev-2); border: 1px solid var(--border);
  border-radius: 9px; padding: 7px 9px; cursor: pointer; text-align: left;
  color: var(--text); font: inherit; transition: border-color .12s, background .12s;
}
.conn-card:hover { border-color: var(--accent); background: var(--bg-elev-3, var(--bg-elev-2)); }
.conn-card.missing { cursor: default; opacity: .6; }
.conn-role {
  flex: none; font-size: 10px; font-weight: 600; letter-spacing: .04em; text-transform: uppercase;
  color: var(--text-faint); width: 46px;
}
.conn-card-thumb {
  flex: none; width: 40px; height: 40px; border-radius: 7px;
  background: var(--bg-elev-1) center/cover no-repeat; border: 1px solid var(--border);
  display: flex; align-items: center; justify-content: center; font-size: 19px;
}
.conn-card-thumb.ph { color: var(--text-faint); }
.conn-card-meta { min-width: 0; flex: 1; }
.conn-card-title {
  font-size: 13px; font-weight: 600; color: var(--text);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.conn-card-type {
  font-size: 11px; color: var(--text-faint);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; margin-top: 1px;
}
.conn-arrow-sep { text-align: center; color: var(--text-faint); font-size: 15px; line-height: 1; margin: 1px 0; }

/* Add-connection modal (from the Connections view) */
.addconn { display: flex; flex-direction: column; gap: 7px; min-width: 340px; }
.addconn-lbl { font-size: 11px; font-weight: 600; letter-spacing: .03em; text-transform: uppercase; color: var(--text-faint); margin-top: 4px; }
.addconn-list {
  max-height: 240px; overflow-y: auto; display: flex; flex-direction: column; gap: 2px;
  border: 1px solid var(--border); border-radius: 8px; padding: 4px; background: var(--bg-elev-1);
}
.addconn-row {
  display: flex; align-items: center; gap: 8px; width: 100%; text-align: left;
  background: transparent; border: 1px solid transparent; border-radius: 6px;
  padding: 6px 8px; cursor: pointer; color: var(--text); font: inherit;
}
.addconn-row:hover { background: var(--bg-elev-2); }
.addconn-row.sel { border-color: var(--accent); background: var(--bg-elev-2); }
.addconn-ico { flex: none; width: 18px; text-align: center; }
.addconn-name { flex: 1; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.addconn-type { flex: none; font-size: 11px; color: var(--text-faint); }
select.field { cursor: pointer; }
input[type="color"].field { padding: 2px; height: 32px; cursor: pointer; }
input[type="range"].field { padding: 0; background: transparent; }

.color-cell { display: flex; align-items: center; gap: 6px; }
.color-cell input[type="color"] { width: 34px; flex: 0 0 34px; }

.insp-actions { display: flex; gap: 6px; flex-wrap: wrap; margin-top: 4px; }
.insp-btn-row { display: flex; gap: 6px; }
.insp-btn-row .mini-btn { flex: 1 1 0; min-width: 0; }
.mini-btn {
  flex: 1;
  min-width: 70px;
  padding: 7px 8px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--bg-elev-2);
  color: var(--text);
  cursor: pointer;
  font-size: 12.5px;
  font-family: inherit;
}
.mini-btn:hover { border-color: var(--border-strong); }
.mini-btn.danger { color: var(--danger); }
.mini-btn.accent { background: var(--accent); border-color: var(--accent); color: #fff; }
.checkbox-row { display: flex; align-items: center; gap: 8px; }
.checkbox-row input { width: 16px; height: 16px; accent-color: var(--accent); }

/* ===================== Hint & toast ===================== */
.hint {
  position: absolute;
  bottom: 16px;
  left: 50%;
  transform: translateX(-50%);
  text-align: center;
  color: var(--text-faint);
  font-size: 12.5px;
  pointer-events: none;
  line-height: 1.7;
  transition: opacity 0.4s ease;
}
.hint p { margin: 2px 0; }
.hint strong { color: var(--text-dim); font-weight: 600; }
.hint.faded { opacity: 0; }

.toast {
  position: fixed;
  bottom: 22px;
  left: 50%;
  transform: translateX(-50%) translateY(10px);
  background: var(--bg-elev);
  border: 1px solid var(--border-strong);
  color: var(--text);
  padding: 10px 16px;
  border-radius: 10px;
  box-shadow: var(--shadow);
  font-size: 13px;
  z-index: 200;
  opacity: 0;
  transition: opacity 0.2s ease, transform 0.2s ease;
  max-width: min(460px, 90vw);
  line-height: 1.45;
  text-align: center;
}
.toast.show { opacity: 1; transform: translateX(-50%) translateY(0); }

/* Drop-target banner (shown while dragging an object/file over a board) */
.drop-hint {
  position: fixed;
  bottom: 22px;
  left: 50%;
  transform: translateX(-50%);
  display: flex; align-items: center; gap: 8px;
  background: var(--accent);
  color: #fff;
  padding: 10px 18px;
  border-radius: 999px;
  box-shadow: var(--shadow);
  font-size: 13.5px;
  font-weight: 600;
  z-index: 210;
  pointer-events: none;
  white-space: nowrap;
  max-width: 90vw;
  overflow: hidden; text-overflow: ellipsis;
}
.drop-hint::before { content: "⬇"; font-size: 15px; }
.drop-hint[hidden] { display: none; }   /* class sets display:flex, so [hidden] must override */

/* Hover tooltip (image descriptions, etc.) */
.tooltip {
  position: fixed;
  left: 0; top: 0;
  z-index: 300;
  max-width: 280px;
  padding: 7px 10px;
  background: var(--bg-elev);
  color: var(--text);
  border: 1px solid var(--border-strong);
  border-radius: 8px;
  box-shadow: var(--shadow-sm);
  font-size: 12.5px;
  line-height: 1.4;
  white-space: pre-wrap;
  word-break: break-word;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.12s ease;
}
.tooltip.show { opacity: 1; }

/* Centered-below-image tooltip: solid so it stays readable over any image. */
.tooltip.centered { text-align: center; background: var(--bg-elev); }

/* Rich hover preview for an entity/board link (icon + title + type). */
.tooltip.mention-preview {
  display: inline-flex; align-items: center; gap: 9px; text-align: left;
  white-space: nowrap; z-index: 320;
}
.tooltip.mention-preview .mp-ico { font-size: 20px; line-height: 1; flex: 0 0 auto; }
.tooltip.mention-preview .mp-text { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.tooltip.mention-preview .mp-title { font-weight: 650; color: var(--text); overflow: hidden; text-overflow: ellipsis; }
.tooltip.mention-preview .mp-kind { font-size: 11px; color: var(--text-dim); }

/* Grid can be hidden via local Settings. */
.viewport.no-grid { background-image: none; }

/* ===================== Project button + name ===================== */
.project-btn {
  display: inline-flex; align-items: center; gap: 6px;
  height: 30px; padding: 0 10px;
  border: 1px solid var(--border); border-radius: 9px;
  background: var(--bg-elev-2); color: var(--text);
  font: inherit; font-size: 13px; cursor: pointer; white-space: nowrap;
  max-width: 220px;
}
.project-btn:hover { border-color: var(--border-strong); }
.project-btn #project-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 150px; }
.project-btn .project-caret { color: var(--text-faint); font-size: 10px; }
@media (max-width: 1080px) { .project-btn #project-name { max-width: 84px; } }

/* ===================== Sidebar rail + panel ===================== */
.sidebar-rail {
  position: fixed; left: 10px; top: calc(100px + var(--safe-t));
  display: flex; flex-direction: column; gap: 8px;
  max-height: calc(100vh - 120px - var(--safe-t) - var(--safe-b));
  overflow-y: auto; overflow-x: hidden;
  z-index: 26;
  scrollbar-width: none;
}
.sidebar-rail::-webkit-scrollbar { width: 0; height: 0; }
.rail-sep { height: 1px; margin: 2px 6px; background: var(--border); flex-shrink: 0; }
.rail-btn {
  width: 42px; height: 42px; flex-shrink: 0;
  display: flex; align-items: center; justify-content: center;
  border: 1px solid var(--border); border-radius: 12px;
  /* Translucent (30% opacity) so the moving canvas shows through the rail. */
  background: color-mix(in srgb, var(--bg-elev) 30%, transparent); color: var(--text);
  backdrop-filter: blur(9px); -webkit-backdrop-filter: blur(9px);
  font-size: 18px; cursor: pointer; box-shadow: var(--shadow-sm);
  transition: background 0.12s ease, border-color 0.12s ease, transform 0.06s ease;
}
.rail-btn:hover { background: color-mix(in srgb, var(--bg-elev-2) 55%, transparent); }
.rail-btn:active { transform: translateY(1px); }
.rail-btn.active { border-color: var(--accent); color: var(--accent); background: var(--accent-soft); }
/* Grouped "Add" button — the primary create action. */
.rail-add { background: var(--accent); border-color: var(--accent); color: #fff; font-size: 24px; font-weight: 300; line-height: 1; }
.rail-add:hover { background: var(--accent); filter: brightness(1.08); }

.sidebar-panel {
  position: fixed; left: 62px; top: calc(64px + var(--safe-t));
  width: 312px; max-width: calc(100vw - 84px);
  max-height: calc(100vh - 84px - var(--safe-t) - var(--safe-b));
  display: flex; flex-direction: column;
  background: var(--panel); border: 1px solid var(--border);
  border-radius: 14px; box-shadow: var(--shadow); z-index: 25;
  opacity: 0; transform: translateX(-10px);
  transition: opacity 0.16s ease, transform 0.16s ease;
}
.sidebar-panel.show { opacity: 1; transform: none; }
/* The base rule's `display:flex` overrides the UA `[hidden]` rule, so a closed
   panel stayed in the layout at opacity:0 — invisibly swallowing taps across
   the canvas (catastrophic on mobile, where it spans most of the screen).
   Re-assert display:none while hidden. */
.sidebar-panel[hidden] { display: none !important; }
.panel-head {
  display: flex; align-items: center; justify-content: space-between;
  padding: 12px 14px; border-bottom: 1px solid var(--border); flex-shrink: 0;
}
.panel-title { font-weight: 650; font-size: 14px; }
.panel-x, .modal-x {
  width: 26px; height: 26px; border: none; border-radius: 7px;
  background: transparent; color: var(--text-dim); cursor: pointer; font-size: 14px;
}
.panel-x:hover, .modal-x:hover { background: var(--bg-elev-2); color: var(--text); }
.panel-body { padding: 12px 14px; overflow-y: auto; }
.panel-body > .field { margin-bottom: 10px; }
.panel-empty { color: var(--text-faint); font-size: 12.5px; line-height: 1.5; padding: 6px 2px; }
.panel-note { color: var(--text-faint); font-size: 12px; line-height: 1.5; margin-bottom: 12px; }
.panel-count { color: var(--text-faint); font-size: 11.5px; text-transform: uppercase; letter-spacing: 0.4px; margin: 2px 0 8px; }
.mini-btn.full { width: 100%; flex: none; margin-top: 8px; }

/* ---- Search results ---- */
.search-results { margin-top: 6px; }
.result-row {
  display: flex; align-items: center; gap: 10px;
  padding: 8px; border-radius: 9px; cursor: pointer;
}
.result-row:hover { background: var(--bg-elev-2); }
.result-ico { width: 20px; text-align: center; flex-shrink: 0; }
.result-main { min-width: 0; }
.result-title { font-size: 13px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.result-sub { font-size: 11.5px; color: var(--text-faint); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* ---- Activity history ---- */
.act-hist { display: flex; flex-direction: column; gap: 10px; min-width: 0; }
.act-chips { display: flex; gap: 6px; flex-wrap: wrap; }
.act-chip { font-size: 12px; padding: 4px 11px; border-radius: 999px; border: 1px solid var(--border); background: transparent; color: var(--text); cursor: pointer; }
.act-chip:hover { border-color: var(--border-strong); }
.act-chip.active { background: var(--accent-soft); border-color: var(--accent); color: var(--accent); }
.act-list { display: flex; flex-direction: column; gap: 2px; max-height: 60vh; overflow-y: auto; }
.act-row { display: flex; align-items: center; gap: 10px; padding: 8px; border-radius: 9px; }
.act-row.act-go, .act-row a.act-url { cursor: pointer; }
.act-row.act-go:hover, .act-row:has(.act-url):hover { background: var(--bg-elev-2); }
.act-thumb { flex: 0 0 auto; width: 40px; height: 40px; border-radius: 7px; background-size: cover; background-position: center; background-color: var(--bg-elev-2); border: 1px solid var(--border); }
.act-ico { flex: 0 0 auto; width: 40px; height: 40px; border-radius: 7px; display: flex; align-items: center; justify-content: center; font-size: 18px; background: var(--bg-elev-2); border: 1px solid var(--border); }
.act-main { min-width: 0; flex: 1 1 auto; }
.act-label { font-size: 13px; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.act-label.gone { color: var(--text-dim); text-decoration: line-through; font-weight: 500; }
.act-sub { font-size: 11.5px; color: var(--text-faint); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; text-transform: capitalize; }
.act-url { display: block; font-size: 11.5px; color: var(--accent); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; text-decoration: none; }
.act-url:hover { text-decoration: underline; }

/* ---- Tags panel ---- */
.tag-block { border: 1px solid var(--border); border-radius: 10px; padding: 8px; margin-bottom: 10px; }
.tag-manager .tm-row { margin-bottom: 10px; }
.tag-manager .tm-row .tag-name { flex: 1; }
.tag-row-head { display: flex; align-items: center; gap: 8px; }
.tag-swatch { width: 26px; height: 26px; flex: 0 0 26px; padding: 2px; border-radius: 7px; border: 1px solid var(--border); background: var(--bg-elev-2); cursor: pointer; }
.tag-name { flex: 1; min-width: 0; }
.tag-count { color: var(--text-faint); font-size: 12px; font-variant-numeric: tabular-nums; }
.icon-btn { border: none; background: transparent; cursor: pointer; font-size: 14px; opacity: 0.7; }
.icon-btn:hover { opacity: 1; }
.tag-objs { margin-top: 8px; border-top: 1px solid var(--border); padding-top: 6px; display: flex; flex-direction: column; gap: 2px; }
.tag-obj { display: flex; align-items: center; gap: 8px; padding: 5px 6px; border-radius: 7px; cursor: pointer; font-size: 12.5px; }
.tag-obj:hover { background: var(--bg-elev-2); }
.tag-obj-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* ---- Settings rows ---- */
.set-row { margin-bottom: 14px; }
.set-label { display: block; font-size: 12.5px; color: var(--text-dim); margin-bottom: 5px; }
.set-range { display: flex; align-items: center; gap: 10px; }
.set-range input[type="range"] { flex: 1; }
.set-val { min-width: 30px; text-align: right; font-variant-numeric: tabular-nums; color: var(--text-dim); font-size: 12.5px; }
.set-hint { font-size: 11.5px; color: var(--text-faint); margin-top: 4px; line-height: 1.4; }

/* ===================== Tag chips (inspector) + dots (canvas) ===================== */
.tag-chips { display: flex; flex-wrap: wrap; gap: 6px; }
.tag-chip {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 4px 9px; border-radius: 20px;
  border: 1px solid var(--border); background: var(--bg-elev-2);
  color: var(--text-dim); font: inherit; font-size: 12px; cursor: pointer;
}
.tag-chip:hover { border-color: var(--border-strong); }
.tag-chip.on { border-color: var(--accent); color: var(--text); background: var(--accent-soft); }
.rel-chip { font-size: 11.5px; padding: 3px 8px; }

/* Fallback card for an unrecognized object type */
.obj-unknown { background: var(--bg-elev-2); border: 1px dashed var(--border-strong); border-radius: 12px; }
.unknown-card { padding: 12px 14px; }
.unknown-title { font-weight: 650; font-size: 14px; color: var(--text); overflow: hidden; text-overflow: ellipsis; }
.unknown-sub { font-size: 11.5px; color: var(--text-faint); margin-top: 4px; }

/* ---- Inspector connections list ---- */
.conn-list { display: flex; flex-direction: column; gap: 3px; }
.conn-row { display: flex; align-items: center; gap: 8px; width: 100%; text-align: left; padding: 6px 8px; border: 1px solid transparent; border-radius: 8px; background: var(--bg-elev-2); color: var(--text); cursor: pointer; font: inherit; transition: border-color 0.1s ease, background 0.1s ease; }
.conn-row:hover { border-color: var(--border-strong); background: var(--panel); }
.conn-dir { flex: 0 0 auto; width: 14px; text-align: center; color: var(--text-faint); font-weight: 700; }
.conn-ico { flex: 0 0 auto; font-size: 15px; width: 20px; text-align: center; }
.conn-main { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.conn-name { font-size: 13px; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.conn-rel { font-size: 11px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* ---- Batch lore import modal ---- */
.lore-import { display: flex; flex-direction: column; gap: 12px; min-width: 460px; }
.lore-import-row { display: flex; align-items: center; gap: 10px; }
.lore-import-row label { flex: 0 0 auto; color: var(--text-dim); font-size: 13px; }
.lore-import-ta { width: 100%; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12px; line-height: 1.45; resize: vertical; }
.tag-chip.partial { border-color: var(--accent); border-style: dashed; color: var(--text); background: transparent; }
.tag-chip.add { border-style: dashed; color: var(--text-faint); }
.tag-chip-dot { width: 10px; height: 10px; border-radius: 50%; flex-shrink: 0; }
.tag-chip-count { font-size: 10px; color: var(--text-dim); font-variant-numeric: tabular-nums; }

/* ---- Tag picker panel (multi-select "Add tags") ---- */
.tagpick { display: flex; flex-direction: column; gap: 12px; min-width: 320px; }
.tagpick-hint { font-size: 12px; color: var(--text-dim); }
.tagpick-list { display: flex; flex-direction: column; gap: 3px; max-height: 46vh; overflow-y: auto; margin: -2px; padding: 2px; }
.tagpick-empty { padding: 14px 4px; text-align: center; color: var(--text-dim); font-size: 13px; }
.tagpick-row { display: flex; align-items: center; gap: 10px; width: 100%; text-align: left; padding: 8px 10px; border: 1px solid var(--border); border-radius: 9px; background: var(--bg-elev-2); color: var(--text); cursor: pointer; font: inherit; font-size: 13px; transition: border-color 0.1s ease, background 0.1s ease; }
.tagpick-row:hover { border-color: var(--border-strong); }
.tagpick-row.is-all { border-color: var(--accent); background: var(--accent-soft); }
.tagpick-row.is-some { border-color: var(--accent); border-style: dashed; }
.tagpick-check { flex: 0 0 auto; width: 14px; text-align: center; color: var(--accent); font-weight: 700; }
.tagpick-name { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tagpick-count { flex: 0 0 auto; font-size: 10.5px; color: var(--text-dim); font-variant-numeric: tabular-nums; }
.tagpick-new { display: flex; gap: 8px; border-top: 1px solid var(--border); padding-top: 12px; }
.tagpick-new .field { flex: 1 1 auto; }
.tagpick-new .mini-btn { flex: 0 0 auto; }

/* Constant on-screen size, capped to ~22% of the image's smaller side on image
   cards (--img-min-num); other object types have no --img-min-num so they fall
   back to plain constant-size. See .image-size-alert for the rationale. */
.tag-dots {
  position: absolute;
  left: 7px; bottom: 7px;
  display: flex; gap: 3px; z-index: 5; pointer-events: none;
  transform-origin: bottom left;
  transform: scale(min(var(--inv-scale, 1), calc(var(--img-min-num, 99999) * 0.22 / 11)));
}
.tag-dot {
  width: 11px; height: 11px;
  border-radius: 50%; border: 1.5px solid var(--bg-elev);
  box-shadow: 0 0 0 0.5px rgba(0,0,0,0.3);
  pointer-events: auto; cursor: help;   /* so the title tooltip / tap works */
}

/* ===================== Board cover + shortcut object ===================== */
.obj-board .board-body.has-cover { background-size: cover; background-position: center; }
.obj-board .board-body.has-cover .board-open-hint {
  color: #fff; background: rgba(0,0,0,0.5); padding: 2px 8px; border-radius: 6px;
}

.obj-shortcut {
  background: var(--panel);
  border: 1.5px solid var(--board-color, var(--accent));
  border-left-width: 5px;
  cursor: grab; overflow: hidden;
  display: flex; align-items: center;
}
.obj-shortcut .sc-row { display: flex; align-items: center; gap: 10px; width: 100%; padding: 10px 12px; min-width: 0; }
.sc-thumb {
  width: 44px; height: 44px; flex: 0 0 44px; border-radius: 8px;
  display: flex; align-items: center; justify-content: center;
  background: var(--bg-elev-2); font-size: 20px; background-size: cover; background-position: center;
}
.sc-thumb.has-cover span { display: none; }
.sc-text { min-width: 0; flex: 1; }
.sc-name { font-weight: 600; font-size: 13.5px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sc-desc { font-size: 12px; color: var(--text-dim); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sc-open {
  flex: 0 0 auto; width: 26px; height: 26px; border-radius: 7px;
  display: flex; align-items: center; justify-content: center;
  background: var(--accent); color: #fff; font-size: 15px; cursor: pointer;
}
.sc-open:hover { filter: brightness(1.12); }

/* Compact shortcut: below a certain size, a shortcut WITH a thumbnail becomes
   a full-bleed clickable icon — text + open button hidden, thumbnail fills the
   whole card with no padding. (Shortcuts without a thumbnail never get this.) */
.obj-shortcut.sc-compact { border-left-width: 1.5px; cursor: pointer; }
.obj-shortcut.sc-compact .sc-row { padding: 0; gap: 0; height: 100%; }
.obj-shortcut.sc-compact .sc-text,
.obj-shortcut.sc-compact .sc-open { display: none; }
.obj-shortcut.sc-compact .sc-thumb {
  width: 100%; height: 100%; flex: 1 1 auto; border-radius: 0;
}

/* ===================== Video card (YouTube) ===================== */
/* ---- 3D model card (features/model3d/) ---- */
.obj-model3d {
  background: #0e1016;
  overflow: hidden;
  display: flex; flex-direction: column;
  cursor: default;
}
.obj-model3d .m3d-media {
  position: relative; flex: 1; min-height: 0;
  background: #12141a center/cover no-repeat;
  display: flex; align-items: center; justify-content: center;
}
.obj-model3d .m3d-media.m3d-empty {
  background: radial-gradient(circle at 50% 40%, #232838, #12141a 70%);
}
.obj-model3d .m3d-open {
  position: absolute; padding: calc(6px * var(--inv-scale, 1)) calc(12px * var(--inv-scale, 1));
  border-radius: calc(8px * var(--inv-scale, 1)); background: rgba(0,0,0,0.6); color: #fff;
  font-size: calc(13px * var(--inv-scale, 1)); font-weight: 600; letter-spacing: .3px;
  cursor: pointer; z-index: 3; backdrop-filter: blur(2px); user-select: none;
}
.obj-model3d .m3d-open:hover { background: var(--accent); }
.obj-model3d .m3d-bar {
  display: flex; align-items: center; gap: calc(8px * var(--inv-scale, 1));
  padding: calc(7px * var(--inv-scale, 1)) calc(10px * var(--inv-scale, 1));
  background: var(--bg-elev-2); border-top: 1px solid var(--border); flex: 0 0 auto;
}
.obj-model3d .m3d-ico { font-size: calc(15px * var(--inv-scale, 1)); }
.obj-model3d .m3d-texts { min-width: 0; }
.obj-model3d .m3d-title { font-size: calc(13px * var(--inv-scale, 1)); font-weight: 600; color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.obj-model3d .m3d-fmt { font-size: calc(11px * var(--inv-scale, 1)); color: var(--text-faint); }
/* Modal viewer */
/* The 3D modal gets a wider, viewport-bounded card; the viewer fills it exactly
   (a fixed vw width could exceed the card and add a horizontal scrollbar). The
   `.wide` in the selector out-specifies `.modal-card.wide { width: 860px }`. */
.modal-card.wide:has(.m3d-viewer) { width: min(1040px, 92vw); }
.modal-card.wide:has(.m3d-viewer) .modal-body { overflow-x: hidden; }
.m3d-viewer { position: relative; width: 100%; height: 66vh; background: #12141a; border-radius: 10px; overflow: hidden; }
.m3d-canvas { display: block; width: 100%; height: 100%; }
.m3d-hint { position: absolute; left: 12px; bottom: 10px; font-size: 12px; color: rgba(255,255,255,0.55); pointer-events: none; }
.m3d-tools { position: absolute; top: 10px; left: 50%; transform: translateX(-50%); display: flex; gap: 4px; padding: 4px; border-radius: 10px; background: rgba(0,0,0,0.5); backdrop-filter: blur(4px); z-index: 3; }
.m3d-tool { padding: 5px 12px; border: 0; border-radius: 7px; background: transparent; color: rgba(255,255,255,0.8); font-size: 12.5px; font-weight: 600; cursor: pointer; }
.m3d-tool:hover { background: rgba(255,255,255,0.12); }
.m3d-tool.active { background: var(--accent); color: #fff; }
.m3d-stats { position: absolute; right: 12px; bottom: 10px; font-size: 12px; font-variant-numeric: tabular-nums; color: rgba(255,255,255,0.72); background: rgba(0,0,0,0.42); padding: 3px 9px; border-radius: 7px; pointer-events: none; }
.m3d-error { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; color: var(--danger); padding: 20px; text-align: center; }

.obj-video {
  background: #000;
  overflow: hidden;
  display: flex; flex-direction: column;
  cursor: default;
}
.obj-video .vid-media {
  position: relative; flex: 1; min-height: 0;
  background: #000 center/cover no-repeat;
  display: flex; align-items: center; justify-content: center;
}
.obj-video .vid-media.empty { background: var(--bg-elev-2); }
/* Providers without a public thumbnail: a neutral placeholder… */
.obj-video .vid-media.vid-nothumb { background: linear-gradient(135deg, #2a2f3a, #171a21); }
/* …except Instagram, which gets its brand gradient. */
.obj-video .vid-media.vid-instagram { background: linear-gradient(45deg, #feda75, #fa7e1e, #d62976, #962fbf, #4f5bd5); }
.obj-video .vid-frame,
.obj-video .vid-target,
.obj-video .vid-media iframe { position: absolute; inset: 0; width: 100% !important; height: 100% !important; border: 0; }
/* Embeds carry their own controls, so they receive taps by default… */
.obj-video .vid-media iframe { pointer-events: auto; }
/* …but the YouTube player is driven by our own buttons, so block its layer. */
.obj-video .vid-youtube iframe { pointer-events: none; }
.obj-video .vid-toggle {
  position: absolute; left: calc(8px * var(--inv-scale, 1)); bottom: calc(8px * var(--inv-scale, 1));
  min-width: calc(30px * var(--inv-scale, 1)); height: calc(30px * var(--inv-scale, 1)); padding: 0 calc(8px * var(--inv-scale, 1));
  border-radius: calc(6px * var(--inv-scale, 1)); background: rgba(0,0,0,0.7); color: #fff;
  display: flex; align-items: center; justify-content: center;
  font-size: calc(13px * var(--inv-scale, 1)); cursor: pointer; z-index: 4; letter-spacing: 1px;
}
.obj-video .vid-toggle:hover { background: #f00; }
.obj-video .vid-play {
  width: calc(56px * var(--inv-scale, 1)); height: calc(56px * var(--inv-scale, 1));
  border-radius: 50%;
  background: rgba(0,0,0,0.6); border: 2px solid #fff; color: #fff;
  display: flex; align-items: center; justify-content: center;
  font-size: calc(22px * var(--inv-scale, 1)); cursor: pointer; padding-left: 3px;
  transition: background 0.12s ease, transform 0.06s ease;
}
.obj-video .vid-play:hover { background: #f00; transform: scale(1.06); }
.obj-video .vid-stop {
  position: absolute; top: calc(6px * var(--inv-scale, 1)); right: calc(6px * var(--inv-scale, 1));
  width: calc(24px * var(--inv-scale, 1)); height: calc(24px * var(--inv-scale, 1));
  border-radius: 50%; background: rgba(0,0,0,0.7); color: #fff;
  display: flex; align-items: center; justify-content: center;
  font-size: calc(13px * var(--inv-scale, 1)); cursor: pointer; z-index: 4;
}
.obj-video .vid-stop:hover { background: #f00; }
.obj-video .vid-bar {
  display: flex; align-items: center; gap: 7px;
  padding: 6px 9px; background: var(--bg-elev); color: var(--text);
  font-size: 12.5px; cursor: grab; flex-shrink: 0;
  border-top: 1px solid var(--border);
}
.obj-video .vid-bar .vid-ico { color: #f00; font-size: 11px; flex: 0 0 auto; }
.obj-video .vid-bar .vid-texts { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.obj-video .vid-bar .vid-title { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.obj-video .vid-bar .vid-author { font-size: 11px; color: var(--text-dim); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.obj-video .vid-bar .vid-author-loading { font-style: italic; color: var(--text-faint); }
.obj-video .vid-bar .vid-time { color: var(--text-dim); font-variant-numeric: tabular-nums; white-space: nowrap; flex: 0 0 auto; }
.obj-video .vid-bar .vid-loading {
  flex: 0 0 auto; width: 12px; height: 12px; border-radius: 50%;
  border: 2px solid var(--border); border-top-color: var(--accent);
  animation: vid-spin 0.7s linear infinite;
}
@keyframes vid-spin { to { transform: rotate(360deg); } }

/* ============ Audio card ============ */
.obj-audio {
  background: var(--panel); border: 1.5px solid var(--border); border-radius: 12px;
  display: flex; flex-direction: column; justify-content: center; gap: 8px;
  padding: 12px 12px 10px; cursor: grab; overflow: hidden;
}
.obj-audio .au-row { display: flex; align-items: center; gap: 10px; }
.obj-audio .au-play {
  flex: 0 0 auto; width: 34px; height: 34px; border-radius: 50%;
  background: var(--accent); color: #fff; border: none; cursor: pointer;
  display: flex; align-items: center; justify-content: center; font-size: 13px;
  transition: transform 0.06s ease, filter 0.12s ease;
}
.obj-audio .au-play:hover { filter: brightness(1.1); transform: scale(1.05); }
.obj-audio .au-mid { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 5px; }
.obj-audio .au-bar {
  position: relative; height: 7px; border-radius: 4px; background: var(--bg-elev-2);
  cursor: pointer; overflow: hidden;
}
.obj-audio .au-fill { position: absolute; inset: 0 auto 0 0; width: 0; background: var(--accent); border-radius: 4px; }
.obj-audio .au-times { display: flex; justify-content: space-between; font-size: 11px; color: var(--text-dim); font-variant-numeric: tabular-nums; }
.obj-audio .au-foot { display: flex; align-items: center; gap: 6px; min-width: 0; }
.obj-audio .au-ico { flex: 0 0 auto; font-size: 13px; }
.obj-audio .au-name { flex: 1; min-width: 0; font-size: 12.5px; color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.obj-audio .au-size { flex: 0 0 auto; font-size: 11px; color: var(--text-dim); }
.obj-audio audio { display: none; }

/* ============ File card ============ */
.obj-file {
  background: var(--panel); border: 1.5px solid var(--border); border-radius: 12px;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 8px;
  padding: 12px; cursor: grab; overflow: hidden; text-align: center;
}
.obj-file .file-ico { font-size: 34px; line-height: 1; }
.obj-file .file-meta { min-width: 0; width: 100%; }
.obj-file .file-name { font-size: 13px; font-weight: 600; color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.obj-file .file-sub { display: flex; gap: 6px; align-items: center; justify-content: center; margin-top: 3px; font-size: 11px; color: var(--text-dim); flex-wrap: wrap; }
.obj-file .file-ext { font-family: var(--mono, monospace); color: var(--accent); }
.obj-file .file-lang { }
.obj-file .file-open {
  font-size: 11px; color: var(--text-dim); border: 1px solid var(--border);
  border-radius: 6px; padding: 2px 8px; background: var(--bg-elev);
}
.obj-file:hover .file-open { color: var(--accent); border-color: var(--accent); }

/* ============ File viewer modal ============ */
.fv-modal { display: flex; flex-direction: column; gap: 10px; min-height: 0; }
.fv-bar { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.fv-lang { display: flex; align-items: center; gap: 4px; font-size: 13px; color: var(--text); white-space: nowrap; }
.fv-search-wrap { flex: 1; display: flex; align-items: center; gap: 8px; min-width: 140px; }
.fv-search { flex: 1; min-width: 0; }
.fv-count { font-size: 11.5px; color: var(--text-dim); white-space: nowrap; }
.fv-actions { display: flex; gap: 6px; }
.fv-scroll {
  display: flex; overflow: auto; max-height: 62vh; min-height: 120px;
  background: var(--bg-elev-2); border: 1px solid var(--border); border-radius: 8px;
  font-family: var(--mono, ui-monospace, "SF Mono", Menlo, Consolas, monospace); font-size: 12.5px; line-height: 1.55;
}
.fv-gutter {
  flex: 0 0 auto; padding: 10px 8px; text-align: right; color: var(--text-dim);
  background: var(--bg-elev); border-right: 1px solid var(--border); user-select: none;
}
.fv-ln { white-space: pre; }
.fv-pre { margin: 0; padding: 10px 12px; overflow: visible; }
.fv-code { white-space: pre; color: var(--text); }
.fv-code mark { background: var(--accent); color: #fff; border-radius: 2px; }
.hl-cmt { color: #6a9955; font-style: italic; }
.hl-str { color: #ce9178; }
.hl-kw  { color: #569cd6; }
.hl-num { color: #b5cea8; }

/* ============ Markdown preview (.md / .markdown file viewer) ============
   The viewer opens rendered by default and toggles to the code pane above.
   Everything is scoped under .mdv-* so a document can never restyle the app. */
.fv-panes { display: flex; min-height: 0; }
.fv-panes > * { flex: 1; min-width: 0; }
/* The mode toggle hides panes with the `hidden` property, and a class rule
   carrying `display` outranks the UA's [hidden] { display: none }. Without
   these the "hidden" pane stays on screen at half width. */
.fv-scroll[hidden], .fv-preview[hidden], .fv-search-wrap[hidden], .fv-stats[hidden] { display: none; }
.fv-modes { display: flex; gap: 0; border: 1px solid var(--border); border-radius: 7px; overflow: hidden; }
.fv-mode {
  padding: 4px 10px; font-size: 12px; line-height: 1.6; cursor: pointer; white-space: nowrap;
  background: var(--bg-elev); color: var(--text-dim); border: 0; border-right: 1px solid var(--border);
}
.fv-mode:last-child { border-right: 0; }
.fv-mode:hover { color: var(--text); }
.fv-mode.on { background: var(--accent); color: #fff; }
.fv-stats { flex: 1; text-align: right; font-size: 11.5px; color: var(--text-dim); white-space: nowrap; }

.fv-preview {
  overflow: auto; max-height: 62vh; min-height: 120px; padding: 22px 26px;
  background: var(--bg-elev-2); border: 1px solid var(--border); border-radius: 8px;
}

/* ---- Full screen (the ⤢ button, or F) ----
   The card normally sizes itself and the panes cap at 62vh. Filling the window
   means handing the height down the whole chain — card → body → .fv-modal →
   .fv-panes → pane — since every one of them would otherwise collapse to its
   content and leave the pane its old 62vh. `min-height: 0` at each level lets
   a flex child actually shrink; without it the pane overflows instead of
   scrolling. The reading column keeps its measure, so this buys height. */
/* The backdrop insets every modal by 24px; a full-screen card must sit flush.
   (`:has()` is already used for the 3D viewer's width override above.) */
.modal-backdrop:has(> .modal-card.fv-full) { padding: 0; }
/* Edge-to-edge means the card itself owns the insets — otherwise the title and
   close button sit under a notch, and the footer under a home indicator. */
.modal-card.fv-full .modal-head { padding-top: calc(14px + var(--safe-t)); }
.modal-card.fv-full .modal-body { padding-bottom: calc(16px + var(--safe-b)); }
.modal-card.fv-full,
.modal-card.wide.fv-full {
  width: 100vw; max-width: 100vw;
  height: 100vh; height: 100dvh; max-height: 100dvh;
  border-radius: 0; border-width: 0;
}
.modal-card.fv-full { overflow: hidden; }
.modal-card.fv-full .modal-body { flex: 1; min-height: 0; min-width: 0; display: flex; }
/* min-width:0 as well as min-height:0 — a flex child defaults to min-width:auto,
   so the code pane (white-space:pre, never wraps) pushed these wider than the
   window instead of scrolling inside them. */
.modal-card.fv-full .fv-modal { flex: 1; min-height: 0; min-width: 0; }
.modal-card.fv-full .fv-panes { flex: 1; min-height: 0; min-width: 0; }
.modal-card.fv-full .fv-scroll,
.modal-card.fv-full .fv-preview { max-height: none; height: 100%; }
.modal-card.fv-full .fv-preview { padding: 32px 26px; }
.fv-full-btn { font-size: 13px; line-height: 1; padding: 5px 9px; }
.mdv-doc { max-width: 74ch; margin: 0 auto; color: var(--text); font-size: 14.5px; line-height: 1.7; }
.mdv-doc > :first-child { margin-top: 0; }

.mdv-h { margin: 1.5em 0 .55em; line-height: 1.3; font-weight: 700; scroll-margin-top: 12px; }
.mdv-h1 { font-size: 1.85em; padding-bottom: .25em; border-bottom: 1px solid var(--border); }
.mdv-h2 { font-size: 1.45em; padding-bottom: .22em; border-bottom: 1px solid var(--border); }
.mdv-h3 { font-size: 1.2em; }
.mdv-h4 { font-size: 1.05em; }
.mdv-h5 { font-size: .95em; }
.mdv-h6 { font-size: .88em; color: var(--text-dim); }
.mdv-flash { animation: mdv-flash 900ms ease-out; border-radius: 4px; }
@keyframes mdv-flash { from { background: var(--accent); } to { background: transparent; } }

.mdv-p { margin: 0 0 1em; }
.mdv-hr { margin: 1.6em 0; border: 0; border-top: 1px solid var(--border); }
.mdv-link { color: var(--accent); text-decoration: none; }
.mdv-link:hover { text-decoration: underline; }
.mdv-deadlink { color: var(--text-dim); text-decoration: line-through dotted; }

.mdv-list { margin: 0 0 1em; padding-left: 1.6em; }
.mdv-list .mdv-list { margin-bottom: 0; }
.mdv-li { margin: .25em 0; }
.mdv-li.mdv-task { list-style: none; margin-left: -1.35em; }
.mdv-checkbox { margin-right: .5em; vertical-align: middle; }

.mdv-quote {
  margin: 0 0 1em; padding: .1em 0 .1em 1em;
  border-left: 3px solid var(--border); color: var(--text-dim);
}
.mdv-quote > :last-child { margin-bottom: 0; }
.mdv-alert { border-left-width: 4px; color: var(--text); }
.mdv-alert-head { font-weight: 600; margin-bottom: .35em; }
.mdv-alert-note      { border-left-color: #4f8cff; }
.mdv-alert-tip       { border-left-color: #3fb950; }
.mdv-alert-important { border-left-color: #a371f7; }
.mdv-alert-warning   { border-left-color: #d29922; }
.mdv-alert-caution   { border-left-color: #f85149; }

.mdv-code {
  padding: .15em .38em; border-radius: 4px; font-size: .88em;
  background: var(--bg-elev); border: 1px solid var(--border);
  font-family: var(--mono, ui-monospace, "SF Mono", Menlo, Consolas, monospace);
}
.mdv-pre {
  margin: 0 0 1em; padding: 12px 14px; overflow-x: auto; border-radius: 8px;
  background: var(--bg-elev); border: 1px solid var(--border);
  font-size: 12.5px; line-height: 1.55; position: relative;
}
.mdv-pre-code { font-family: var(--mono, ui-monospace, "SF Mono", Menlo, Consolas, monospace); white-space: pre; }
.mdv-pre[data-lang]::before {
  content: attr(data-lang); position: absolute; top: 0; right: 0;
  padding: 1px 7px; font-size: 10px; letter-spacing: .04em; text-transform: uppercase;
  color: var(--text-dim); background: var(--bg-elev-2); border-left: 1px solid var(--border);
  border-bottom: 1px solid var(--border); border-radius: 0 7px 0 6px;
}

/* Wide tables scroll inside their own box rather than stretching the modal. */
.mdv-table-wrap { overflow-x: auto; margin: 0 0 1em; }
.mdv-table { border-collapse: collapse; width: 100%; font-size: .95em; }
.mdv-table th, .mdv-table td { padding: 6px 12px; border: 1px solid var(--border); text-align: left; }
.mdv-table th { background: var(--bg-elev); font-weight: 600; }
.mdv-table tbody tr:nth-child(even) { background: color-mix(in srgb, var(--bg-elev) 45%, transparent); }
@supports not (color: color-mix(in srgb, red 50%, blue)) {
  .mdv-table tbody tr:nth-child(even) { background: var(--bg-elev); }
}
.mdv-fm .mdv-fm-key { width: 1%; white-space: nowrap; color: var(--text-dim); font-weight: 500; }
.mdv-chips { display: inline-flex; flex-wrap: wrap; gap: 5px; }
.mdv-chip {
  padding: 1px 8px; border-radius: 999px; font-size: 11.5px;
  background: var(--bg-elev-2); border: 1px solid var(--border); color: var(--text-dim);
}

.mdv-img { max-width: 100%; height: auto; border-radius: 6px; display: block; margin: 0 auto 1em; }
.mdv-noimg {
  display: inline-flex; align-items: center; gap: 4px; padding: 1px 8px; border-radius: 5px;
  font-size: 12px; color: var(--text-dim); background: var(--bg-elev); border: 1px dashed var(--border);
}
.mdv-noimg-alt { font-style: italic; }

.mdv-sup { font-size: .78em; }
.mdv-fnref { color: var(--accent); text-decoration: none; padding: 0 1px; }
.mdv-footnotes { margin: 0; padding-left: 1.5em; font-size: .92em; color: var(--text-dim); }
.mdv-fn { scroll-margin-top: 12px; }
.mdv-fn > :last-child { margin-bottom: 0; }
.mdv-fnback { margin-left: .4em; color: var(--accent); text-decoration: none; }
.mdv-fn-missing { font-style: italic; }
.mdv-doc mark.reader-mark { background: color-mix(in srgb, var(--accent) 35%, transparent); border-radius: 2px; }
@supports not (color: color-mix(in srgb, red 50%, blue)) {
  .mdv-doc mark.reader-mark { background: var(--accent); color: #fff; }
}

@media (max-width: 640px) {
  .fv-preview { padding: 16px 14px; }
  .fv-stats { flex-basis: 100%; text-align: left; }
  .mdv-doc { font-size: 14px; }
}

/* ============ Pause All Media (floating) ============ */
.pause-all-media {
  /* 64px cleared the desktop toolbar; on a notched phone the toolbar is taller
     than that AND the notch is above it, so both have to be accounted for. */
  position: fixed; top: calc(64px + var(--safe-t)); left: 50%; transform: translateX(-50%);
  z-index: 1200; padding: 8px 16px; border-radius: 999px; border: none;
  background: var(--accent); color: #fff; font-size: 13px; font-weight: 600;
  cursor: pointer; box-shadow: 0 6px 20px rgba(0,0,0,0.35);
  display: flex; align-items: center; gap: 6px;
  animation: pause-all-in 0.18s ease;
}
.pause-all-media:hover { filter: brightness(1.08); }
.pause-all-media[hidden] { display: none; }
@keyframes pause-all-in { from { opacity: 0; transform: translate(-50%, -8px); } to { opacity: 1; transform: translate(-50%, 0); } }

/* ============ Worldbuilding entity card (character, location, …) ============ */
.obj-entity {
  background: var(--panel);
  border: 1.5px solid var(--entity-color, var(--accent));
  border-radius: 12px;
  overflow: hidden;
  display: flex; flex-direction: column;
  cursor: grab;
}
.obj-entity .ent-media {
  flex: 1; min-height: 0;
  background: var(--bg-elev-2) center/cover no-repeat;
  display: flex; align-items: center; justify-content: center;
  color: var(--entity-color, var(--accent));
}
.obj-entity .ent-media-ico { font-size: 40px; opacity: 0.55; }
.obj-entity .ent-body {
  flex: 0 0 auto;
  padding: 8px 10px;
  border-top: 1px solid var(--border);
  background: var(--panel);
}
.obj-entity .ent-head { display: flex; align-items: center; gap: 6px; min-width: 0; }
.obj-entity .ent-ico { font-size: 15px; flex: 0 0 auto; }
.obj-entity .ent-name {
  font-weight: 600; font-size: 14px; color: var(--text);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.obj-entity .ent-sub {
  display: flex; gap: 6px; align-items: baseline; min-width: 0;
  margin-top: 2px; font-size: 11.5px; color: var(--text-dim);
  overflow: hidden; white-space: nowrap;
}
.obj-entity .ent-kind {
  color: var(--entity-color, var(--accent)); font-weight: 700;
  text-transform: uppercase; letter-spacing: 0.03em; font-size: 10px;
  flex: 0 0 auto;
}
.obj-entity .ent-meta { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.obj-entity .ent-open {
  margin-top: 4px; font-size: 10.5px; color: var(--text-dim); opacity: 0.7;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* ---- Entity field cards (shown inside an entity's canvas) ---- */
.obj-field {
  background: var(--panel);
  border: 1.5px solid var(--border);
  border-left: 3px solid var(--entity-color, var(--accent));
  border-radius: 10px;
  display: flex; flex-direction: column;
  padding: 8px 10px; gap: 6px;
  cursor: grab; overflow: hidden;
}
.obj-field.is-name { border-left-width: 4px; }
.obj-field .fld-head {
  display: flex; align-items: center; gap: 6px; flex: 0 0 auto;
}
.obj-field .fld-label {
  font-size: 10px; font-weight: 700; letter-spacing: 0.04em;
  text-transform: uppercase; color: var(--entity-color, var(--accent));
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.obj-field .fld-lock { margin-left: auto; font-size: 10px; opacity: 0.45; flex: 0 0 auto; }
.obj-field.is-name .fld-value { font-size: 16px; font-weight: 600; color: var(--text); }
.obj-field .fld-value {
  flex: 1; min-height: 0; overflow: auto;
  font-size: 13px; line-height: 1.4; color: var(--text);
  white-space: pre-wrap; word-break: break-word;
}
.obj-field .fld-value.empty { color: var(--text-faint); font-style: italic; }
.obj-field .fld-input,
.obj-field .fld-edit {
  width: 100%; box-sizing: border-box;
  background: var(--bg-elev-2); color: var(--text);
  border: 1px solid var(--border); border-radius: 6px;
  padding: 5px 7px; font: inherit; font-size: 13px;
}
.obj-field .fld-edit { flex: 1; min-height: 0; resize: none; }
.obj-field .fld-media {
  flex: 1; min-height: 40px; border-radius: 6px;
  background: var(--bg-elev-2) center/cover no-repeat;
  display: flex; align-items: center; justify-content: center;
  color: var(--entity-color, var(--accent));
}
.obj-field .fld-media-ico { font-size: 30px; opacity: 0.5; }
.obj-field .fld-img-btn {
  flex: 0 0 auto; align-self: flex-start;
  background: var(--bg-elev-2); color: var(--text-dim);
  border: 1px solid var(--border); border-radius: 6px;
  padding: 4px 8px; font-size: 11.5px; cursor: pointer;
}
.obj-field .fld-img-btn:hover { color: var(--text); border-color: var(--entity-color, var(--accent)); }
.obj-field .fld-orphan { color: var(--text-faint); font-size: 12px; font-style: italic; }

/* ===================== Command palette (Space) ===================== */
.cmdp-overlay {
  position: fixed; inset: 0; z-index: 5000;
  background: rgba(0, 0, 0, 0.42);
  display: flex; align-items: flex-start; justify-content: center;
  padding-top: 14vh;
  backdrop-filter: blur(2px);
}
.cmdp-box {
  width: min(640px, 92vw);
  max-height: 62vh;
  display: flex; flex-direction: column;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 14px;
  box-shadow: 0 24px 70px rgba(0, 0, 0, 0.45);
  overflow: hidden;
}
.cmdp-input {
  flex: 0 0 auto;
  border: none; outline: none;
  background: transparent; color: var(--text);
  font-size: 20px; padding: 18px 20px;
  border-bottom: 1px solid var(--border);
}
.cmdp-input::placeholder { color: var(--text-faint); }
.cmdp-results { flex: 1; min-height: 0; overflow: auto; padding: 6px; }
.cmdp-group {
  font-size: 10px; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--text-faint); padding: 10px 12px 4px;
}
.cmdp-item {
  display: flex; align-items: center; gap: 12px;
  padding: 9px 12px; border-radius: 9px; cursor: pointer;
}
.cmdp-item.active { background: var(--accent); }
.cmdp-item.active .cmdp-sub { color: rgba(255, 255, 255, 0.8); }
.cmdp-item.active .cmdp-title,
.cmdp-item.active .cmdp-ico { color: #fff; }
.cmdp-ico {
  flex: 0 0 auto; width: 26px; text-align: center; font-size: 18px;
  color: var(--text-dim);
}
.cmdp-text { min-width: 0; flex: 1; }
.cmdp-title { font-size: 14.5px; color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cmdp-sub { font-size: 11.5px; color: var(--text-dim); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cmdp-empty { padding: 24px; text-align: center; color: var(--text-faint); }
.cmdp-hint {
  flex: 0 0 auto; padding: 8px 14px; border-top: 1px solid var(--border);
  font-size: 11px; color: var(--text-faint); text-align: right;
}

/* ===================== @-mention chips + picker ===================== */
.mention {
  display: inline-flex; align-items: center; gap: 3px;
  padding: 0 6px; margin: 0 1px; border-radius: 6px;
  background: color-mix(in srgb, var(--mention-color, var(--accent)) 16%, transparent);
  color: var(--mention-color, var(--accent));
  border: 1px solid color-mix(in srgb, var(--mention-color, var(--accent)) 40%, transparent);
  font-weight: 600; font-size: 0.92em; line-height: 1.5;
  white-space: nowrap; cursor: pointer; vertical-align: baseline;
  user-select: none;
}
.mention .mention-ico { font-size: 0.9em; }
.mention.missing { color: var(--text-faint); border-style: dashed; background: transparent; cursor: default; }
.obj-text.editing .mention,
.fld-editing .mention { cursor: default; }
.fld-value.fld-editing { outline: none; overflow: auto; }

.mention-menu {
  position: fixed; z-index: 5200;
  width: 260px; max-height: 260px; overflow: auto;
  background: var(--panel); border: 1px solid var(--border);
  border-radius: 10px; box-shadow: 0 16px 44px rgba(0, 0, 0, 0.4);
  padding: 5px;
}
.mention-item {
  display: flex; align-items: center; gap: 9px;
  padding: 7px 9px; border-radius: 7px; cursor: pointer;
}
.mention-item.active { background: var(--accent); }
.mention-item.active .mention-name, .mention-item.active .mention-ico { color: #fff; }
.mention-item.active .mention-sub { color: rgba(255, 255, 255, 0.8); }
.mention-item .mention-ico { flex: 0 0 auto; width: 20px; text-align: center; font-size: 16px; color: var(--text-dim); }
.mention-item .mention-text { min-width: 0; flex: 1; }
.mention-item .mention-name { font-size: 13px; color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mention-item .mention-sub { font-size: 11px; color: var(--text-dim); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mention-empty { padding: 14px; text-align: center; color: var(--text-faint); font-size: 12.5px; }

/* ===================== Modal windows ===================== */
.modal-backdrop {
  position: fixed; inset: 0; z-index: 400;
  display: flex; align-items: center; justify-content: center;
  background: rgba(0,0,0,0.45);
  /* A tall dialog centres close to the top edge, so the notch has to be part
     of the inset or its title and close button end up underneath it. */
  padding: calc(24px + var(--safe-t)) calc(24px + var(--safe-r)) calc(24px + var(--safe-b)) calc(24px + var(--safe-l));
  opacity: 0; transition: opacity 0.16s ease;
}
.modal-backdrop.show { opacity: 1; }
.modal-card {
  width: 460px; max-width: 100%; max-height: 86vh;
  display: flex; flex-direction: column;
  background: var(--bg-elev); border: 1px solid var(--border-strong);
  border-radius: 16px; box-shadow: var(--shadow);
  transform: translateY(8px); transition: transform 0.16s ease;
}
.modal-backdrop.show .modal-card { transform: none; }
.modal-card.wide { width: 860px; }
/* Kanban card detail modal is wider than a plain dialog. */
.modal-card:has(.kbm) { width: 619px; }
.modal-head { display: flex; align-items: center; justify-content: space-between; padding: 14px 18px; border-bottom: 1px solid var(--border); flex-shrink: 0; }
.modal-title { font-weight: 650; font-size: 15px; }
.modal-body { padding: 16px 18px; overflow-y: auto; }
.modal-foot { display: flex; justify-content: flex-end; gap: 8px; padding: 12px 18px; border-top: 1px solid var(--border); flex-shrink: 0; }
.modal-foot .mini-btn { flex: 0 0 auto; min-width: 88px; }
.dialog-text { font-size: 13.5px; line-height: 1.5; margin-bottom: 12px; }
/* Secondary line under a dialog message: ids, current state — context that
   helps when reporting a problem but shouldn't compete with the message. */
.dialog-detail { margin-top: 8px; font-size: 12px; line-height: 1.45; opacity: .65; word-break: break-word; }

/* ===================== SVG card (imported vector artwork) ===================== */
.obj-svg { background: transparent; box-shadow: none; overflow: visible; }
.svg-holder { width: 100%; height: 100%; display: block; overflow: hidden; }
.svg-holder svg { width: 100%; height: 100%; display: block; pointer-events: none; }
.svg-broken {
  width: 100%; height: 100%;
  display: flex; align-items: center; justify-content: center;
  color: var(--text-faint); font-size: 12px;
  border: 1px dashed var(--border-strong); border-radius: 10px;
  background: var(--bg-elev);
}

/* ===================== Kanban card detail modal ===================== */
.kbm { display: flex; flex-direction: column; gap: 20px; width: 100%; max-width: 100%; }
.kbm-sec { display: flex; flex-direction: column; gap: 8px; }
.kbm-sec-tight { gap: 4px; margin-top: -8px; }
.kbm-sec-title { gap: 0; }
.kbm-label { font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.05em; color: var(--text-dim); }
.kbm textarea {
  width: 100%; box-sizing: border-box;
  background: var(--bg-elev-2); color: var(--text);
  border: 1px solid var(--border); border-radius: 10px;
  padding: 10px 12px; font: inherit; outline: none;
}
.kbm textarea:focus { border-color: var(--accent); }
/* Title: prominent, borderless until focused, auto-grows. */
.kbm-title {
  font-size: 21px; font-weight: 800; resize: none; overflow: hidden; line-height: 1.25;
  background: transparent; border-color: transparent; padding: 6px 8px;
}
.kbm-title:hover { background: var(--bg-elev-2); }
.kbm-title:focus { background: var(--bg-elev-2); border-color: var(--accent); }

/* Metadata row (chips summarising priority / due / checklist / tags). */
.kbm-meta { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; padding: 0 8px; }
.kbm-meta-empty { color: var(--text-faint); font-size: 12px; }
.kbm-chip {
  display: inline-flex; align-items: center; gap: 4px;
  font-size: 11.5px; font-weight: 600; padding: 2px 9px; border-radius: 999px;
  border: 1px solid var(--border-strong); background: var(--bg-elev-2); color: var(--text); white-space: nowrap;
}
.kbm-chip-pri { background: transparent; }
.kbm-chip.done { border-color: #3fb37a; color: #3fb37a; }
.kbm-due.overdue { border-color: var(--danger); color: var(--danger); }
.kbm-due.soon { border-color: #e0a92e; color: #e0a92e; }
.kbm-due.done { border-color: #3fb37a; color: #3fb37a; }

/* Priority + due controls. */
.kbm-two { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.kbm-select, .kbm-date { height: 38px; }
.kbm-due-row { display: flex; align-items: center; gap: 10px; }
.kbm-check { display: inline-flex; align-items: center; gap: 5px; font-size: 12.5px; color: var(--text-dim); cursor: pointer; white-space: nowrap; }

/* Checklist. */
.kbm-cl-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.kbm-cl-count { font-size: 12px; color: var(--text-dim); font-variant-numeric: tabular-nums; }
.kbm-cl-bar { height: 6px; border-radius: 999px; background: var(--bg-elev-2); overflow: hidden; }
.kbm-cl-fill { height: 100%; width: 0; background: #3fb37a; border-radius: 999px; transition: width 0.2s ease; }
.kbm-checklist { display: flex; flex-direction: column; gap: 4px; }
.kbm-cl-item { display: flex; align-items: center; gap: 8px; }
.kbm-cl-item input[type=checkbox] { flex: 0 0 auto; }
.kbm-cl-text { flex: 1; min-width: 0; background: transparent; border: 1px solid transparent; border-radius: 8px; padding: 5px 8px; color: var(--text); font: inherit; outline: none; }
.kbm-cl-text:hover { background: var(--bg-elev-2); }
.kbm-cl-text:focus { background: var(--bg-elev-2); border-color: var(--accent); }
.kbm-cl-text.done { text-decoration: line-through; color: var(--text-faint); }
.kbm-cl-x { flex: 0 0 auto; background: none; border: none; color: var(--text-faint); cursor: pointer; font-size: 15px; line-height: 1; }
.kbm-cl-x:hover { color: var(--danger); }

/* Description markdown toolbar. */
.kbm-md-toolbar { display: flex; flex-wrap: wrap; gap: 4px; }
.kbm-md-btn {
  min-width: 28px; height: 28px; padding: 0 7px; border: 1px solid var(--border); border-radius: 7px;
  background: var(--bg-elev-2); color: var(--text-dim); font: inherit; font-size: 12.5px; cursor: pointer;
}
.kbm-md-btn:hover { color: var(--text); border-color: var(--accent); }
.kbm-input {
  width: 100%; box-sizing: border-box; background: var(--bg-elev-2); color: var(--text);
  border: 1px solid var(--border); border-radius: 10px; padding: 8px 12px; font: inherit; outline: none;
}
.kbm-input:focus { border-color: var(--accent); }
/* Cover section */
.kbm-cover-prev {
  height: 56px; border-radius: 10px; border: 1px solid var(--border);
  background-size: cover; background-position: center;
  display: flex; align-items: center; justify-content: center;
}
.kbm-cover-prev-color { border: none; }
.kbm-cover-none { color: var(--text-faint); font-size: 12.5px; }
.kbm-cover-row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.kbm-swatches { display: flex; gap: 6px; flex-wrap: wrap; }
.kbm-swatch { width: 24px; height: 24px; border-radius: 6px; border: 1px solid var(--border-strong); cursor: pointer; }
.kbm-swatch:hover { transform: scale(1.1); }
.kbm-cover-color { width: 36px; height: 28px; padding: 0; border: 1px solid var(--border); border-radius: 6px; background: transparent; cursor: pointer; }
.kbm-cover-clear { width: 28px; height: 28px; border: 1px solid var(--border); border-radius: 6px; background: var(--bg-elev-2); color: var(--text-dim); cursor: pointer; }
.kbm-cover-clear:hover { color: var(--danger); border-color: var(--danger); }
.kbm-cover-row2 { display: flex; align-items: center; gap: 8px; margin-top: 8px; }
.kbm-cover-row2 .kbm-cover-url { flex: 1 1 auto; }
.kbm-cover-from { flex: 0 0 auto; white-space: nowrap; padding: 7px 11px; border: 1px solid var(--border); border-radius: 6px; background: var(--bg-elev-2); color: var(--text); font-size: 12.5px; cursor: pointer; }
.kbm-cover-from:hover { border-color: var(--accent); color: var(--accent); }
/* Project-image picker grid (reuse an image already on the board as a cover). */
.img-pick-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)); gap: 10px; max-height: 60vh; overflow-y: auto; }
.img-pick-tile { position: relative; aspect-ratio: 4 / 3; border: 1px solid var(--border); border-radius: 8px; background-size: cover; background-position: center; background-color: var(--bg-elev-2); cursor: pointer; padding: 0; overflow: hidden; }
.img-pick-tile:hover { border-color: var(--accent); transform: translateY(-1px); }
.img-pick-cap { position: absolute; left: 0; right: 0; bottom: 0; padding: 4px 6px; font-size: 11px; color: #fff; text-align: left; background: linear-gradient(transparent, rgba(0,0,0,0.72)); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.img-pick-empty { grid-column: 1 / -1; padding: 24px; text-align: center; color: var(--text-dim); font-size: 13px; }
.kbm-desc, .kbm-cm-input { resize: none; overflow: hidden; line-height: 1.5; }
.kbm-tags { display: flex; flex-wrap: wrap; gap: 6px; }
.kbm-tag {
  font-size: 12px; padding: 4px 10px; border-radius: 999px;
  border: 1px solid var(--border); background: transparent; color: var(--text); cursor: pointer;
}
.kbm-tag.on { font-weight: 700; }
.kbm-tag-add {
  font-size: 12px; padding: 4px 10px; border-radius: 999px;
  border: 1px dashed var(--border-strong); background: transparent; color: var(--text-dim); cursor: pointer;
}
.kbm-tag-add:hover { color: var(--text); border-color: var(--accent); }
.kbm-comments { display: flex; flex-direction: column; gap: 10px; }
.kbm-empty { color: var(--text-faint); font-size: 13px; }
.kbm-cm { display: flex; gap: 10px; background: var(--bg-elev-2); border: 1px solid var(--border); border-radius: 10px; padding: 8px 10px; }
.kbm-cm-main { flex: 1; min-width: 0; }
.kbm-cm-avatar {
  flex: 0 0 auto; width: 28px; height: 28px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  color: #fff; font-weight: 700; font-size: 13px;
}
.kbm-cm-head { display: flex; align-items: center; gap: 8px; font-size: 12px; margin-bottom: 4px; }
.kbm-cm-dot { width: 10px; height: 10px; border-radius: 50%; flex: 0 0 auto; }
.kbm-cm-author { font-weight: 600; color: var(--text); }
.kbm-cm-time { color: var(--text-faint); }
.kbm-cm-x { margin-left: auto; background: none; border: none; color: var(--text-faint); cursor: pointer; font-size: 15px; line-height: 1; padding: 0 2px; }
.kbm-cm-x:hover { color: var(--danger); }
.kbm-cm-text { font-size: 13.5px; line-height: 1.5; color: var(--text); word-break: break-word; }
.kbm-cm-text > :first-child { margin-top: 0; }
.kbm-cm-text > :last-child { margin-bottom: 0; }
.kbm-cm-compose { display: flex; flex-direction: column; gap: 8px; }
.kbm-cm-compose .mini-btn { align-self: flex-end; min-width: 100px; }

/* Reusable progress modal (SD.ui.progress) */
.progress-modal { display: flex; flex-direction: column; gap: 12px; min-width: 320px; }
.pm-msg { font-size: 13.5px; line-height: 1.45; color: var(--text-dim); min-height: 19px; }
.pm-track {
  position: relative; height: 10px; border-radius: 999px;
  background: var(--bg-elev-2); overflow: hidden;
}
.pm-fill {
  height: 100%; width: 0%; border-radius: 999px;
  background: var(--accent); transform: none;
  transition: width 0.25s ease;
}
.pm-track.indeterminate .pm-fill {
  width: 40%; transition: none;
  animation: pm-indeterminate 1.15s ease-in-out infinite;
}
@keyframes pm-indeterminate {
  0%   { transform: translateX(-110%); }
  100% { transform: translateX(320%); }
}
.pm-pct {
  text-align: right; min-height: 14px;
  font-size: 12px; color: var(--text-faint); font-variant-numeric: tabular-nums;
}
.mini-btn.danger-solid { background: var(--danger); border-color: var(--danger); color: #fff; }

/* ---- Send-to-board picker ---- */
.stb-wrap { display: flex; flex-direction: column; gap: 10px; min-width: 340px; }
.stb-search { flex: 0 0 auto; }
.stb-list { display: flex; flex-direction: column; gap: 2px; max-height: 48vh; overflow-y: auto; margin: -2px; padding: 2px; }
.stb-empty { padding: 18px 6px; text-align: center; color: var(--text-dim); font-size: 13px; }
.stb-row { display: flex; align-items: center; gap: 10px; width: 100%; text-align: left; padding: 8px 10px; border: 1px solid transparent; border-radius: 9px; background: transparent; color: var(--text); cursor: pointer; transition: background 0.1s ease, border-color 0.1s ease; }
.stb-row:hover, .stb-row:focus-visible { background: var(--bg-elev-2); border-color: var(--border); outline: none; }
.stb-ico { flex: 0 0 auto; font-size: 17px; width: 22px; text-align: center; }
.stb-main { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.stb-name { font-size: 13.5px; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.stb-path { font-size: 11px; color: var(--text-dim); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.stb-kind { flex: 0 0 auto; font-size: 10.5px; color: var(--text-dim); text-transform: uppercase; letter-spacing: 0.04em; padding: 2px 7px; border: 1px solid var(--border); border-radius: 999px; }
.stb-row.stb-home .stb-kind { color: var(--accent); border-color: var(--accent); }

/* ---- Tier list card ---- */
.obj-tierlist {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 12px;
  box-shadow: var(--shadow);
  overflow: hidden;
  display: flex;
  flex-direction: column;
}
.tl-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; padding: 8px 12px; border-bottom: 1px solid var(--border); background: var(--bg-elev-2); }
.tl-title { font-weight: 700; font-size: 15px; color: var(--text); outline: none; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tl-title[contenteditable="true"] { white-space: normal; cursor: text; }
.tl-menu { flex: 0 0 auto; width: 26px; height: 26px; border: 1px solid var(--border); border-radius: 6px; background: var(--panel); color: var(--text-dim); cursor: pointer; font-size: 15px; line-height: 1; }
.tl-menu:hover { border-color: var(--border-strong); color: var(--text); }
/* Rows divide the card's (resizable) height evenly; tiles fill each row. */
.tl-rows { flex: 1 1 auto; display: flex; flex-direction: column; min-height: 0; }
.tl-row { flex: 1 1 0; display: flex; align-items: stretch; border-bottom: 1px solid var(--border); min-height: 0; }
.tl-row:last-child { border-bottom: none; }
.tl-label { flex: 0 0 68px; display: flex; align-items: center; justify-content: center; padding: 4px; color: #1a1d24; font-weight: 800; font-size: var(--tl-label-font, 22px); line-height: 1.1; text-align: center; user-select: none; overflow: hidden; }
.tl-label-text { outline: none; word-break: break-word; }
.tl-label-unranked { color: #fff; }
/* Compact, tiermaker-style packing: no gaps or padding, tiles fill the row. */
.tl-items { flex: 1 1 auto; display: flex; flex-wrap: wrap; align-content: flex-start; gap: 0; padding: 0; min-width: 0; overflow-y: auto; background: var(--bg); }
.tl-tile {
  position: relative; width: var(--tl-tile, 64px); height: var(--tl-tile, 64px); flex: 0 0 auto;
  box-sizing: border-box; border-radius: 0; overflow: hidden; cursor: grab;
  background: var(--bg-elev-2);
  /* subtle right/bottom edge → grid lines between touching tiles, no spacing */
  box-shadow: inset -1px -1px 0 rgba(0,0,0,0.38);
  display: flex; align-items: center; justify-content: center;
}
.tl-tile:active { cursor: grabbing; }
.tl-tile img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* Flexible-width mode: images keep their aspect ratio at full row height. */
.tl-flex-img .tl-tile-image { width: auto; }
.tl-flex-img .tl-tile-image img { width: auto; height: 100%; object-fit: contain; }
/* Video reference tiles: thumbnail is primary; a small, subtle play glyph in
   the bottom-left just hints it's a video without covering the image. */
.tl-tile-play { position: absolute; left: 3px; bottom: 2px; font-size: 11px; line-height: 1;
  color: #fff; opacity: 0.3; text-shadow: 0 1px 2px rgba(0,0,0,.9); pointer-events: none; }
.tl-flex-img .tl-tile-thumb { width: auto; }
.tl-flex-img .tl-tile-thumb img { width: auto; height: 100%; object-fit: contain; }
.tl-tile-text { font-size: var(--tl-tile-font, 11px); line-height: 1.2; padding: 3px; text-align: center; color: var(--text); overflow: hidden; display: -webkit-box; -webkit-line-clamp: 4; -webkit-box-orient: vertical; word-break: break-word; }
.tl-tile-ico { font-size: 22px; }
.tl-tile-shape .tl-tile-text { color: #10131a; font-weight: 600; }
.tl-tile-dragging { opacity: 0.45; }
.tl-tile-x { position: absolute; top: 2px; right: 2px; width: 16px; height: 16px; border: none; border-radius: 50%; background: rgba(0,0,0,0.6); color: #fff; font-size: 12px; line-height: 15px; text-align: center; padding: 0; cursor: pointer; opacity: 0; transition: opacity 0.1s ease; }
.tl-tile:hover .tl-tile-x { opacity: 1; }
.tl-row-drop { box-shadow: inset 0 0 0 2px var(--accent); }
.tl-row-drop .tl-items { background: var(--accent-soft); }

/* ============ Kanban board (Trello-style; columns × swimlanes) ============
   The board is a bright surface on the canvas (light in either app theme, like
   Trello) with bold colour-bar column headers and white task cards. */
.obj-kanban {
  --kb-surface: #e9edf2;   /* board backdrop behind the columns */
  --kb-col-bg: #f4f6f9;    /* column body */
  --kb-card-bg: #ffffff;   /* task card */
  --kb-ink: #1c2430;       /* card / column text */
  --kb-ink-dim: #6b7688;
  --kb-line: rgba(20,28,42,0.10);
  background: var(--kb-surface);
  border: 1px solid rgba(20,28,42,0.18);
  border-radius: 14px;
  box-shadow: var(--shadow);
  overflow: hidden;
  display: flex;
  flex-direction: column;
}
.kb-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; padding: 10px 14px; }
.kb-head-l { display: flex; align-items: baseline; gap: 10px; min-width: 0; }
.kb-title { font-weight: 800; font-size: var(--kb-head-font, 16px); color: var(--kb-ink); outline: none; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.kb-title[contenteditable="true"] { white-space: normal; cursor: text; }
.kb-total { flex: 0 0 auto; font-size: 11.5px; font-weight: 600; color: var(--kb-ink-dim); }
.kb-head-r { display: flex; align-items: center; gap: 6px; flex: 0 0 auto; }
.kb-badge { font-size: 10.5px; font-weight: 800; color: #fff; background: #3b7cff; border-radius: 20px; padding: 2px 9px; white-space: nowrap; }
.kb-menu { flex: 0 0 auto; width: 26px; height: 26px; border: 1px solid rgba(20,28,42,0.18); border-radius: 7px; background: #fff; color: var(--kb-ink-dim); cursor: pointer; font-size: 15px; line-height: 1; }
.kb-menu:hover { color: var(--kb-ink); border-color: rgba(20,28,42,0.35); }

/* Shared vertical scroll area (column headers + grid) so the headers stick. */
.kb-scroll { flex: 1 1 auto; min-height: 0; overflow-y: auto; overflow-x: hidden; padding: 0 var(--kb-board-pad, 12px) var(--kb-board-pad, 12px); }
.kb-scroll::-webkit-scrollbar { width: 9px; }
.kb-scroll::-webkit-scrollbar-thumb { background: rgba(20,28,42,0.22); border-radius: 9px; }
.kb-scroll::-webkit-scrollbar-thumb:hover { background: rgba(20,28,42,0.38); }
.kb-scroll::-webkit-scrollbar-track { background: transparent; }

/* Column headers — a bold colour bar per column, stuck to the top on scroll. */
.kb-colhead { position: sticky; top: 0; z-index: 3; display: grid; column-gap: var(--kb-col-gap, 12px); padding-top: 4px; background: var(--kb-surface); }
.kb-colh { display: flex; align-items: center; justify-content: space-between; gap: 8px; padding: 9px 12px; border-radius: 10px 10px 0 0; min-width: 0; font-weight: 800; }
.kb-col-title { font-weight: 800; outline: none; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; letter-spacing: 0.2px; }
.kb-col-title[contenteditable="true"] { white-space: normal; cursor: text; }
.kb-colh-r { display: flex; align-items: center; gap: 7px; flex: 0 0 auto; }
.kb-col-count { min-width: 20px; height: 20px; padding: 0 6px; border-radius: 20px; font-size: 11.5px; font-weight: 800; display: inline-flex; align-items: center; justify-content: center; }

/* Body grid: columns are unbroken vertical containers; lanes are the aligned
   rows inside them. Row-gap 0 keeps each column continuous; column-gap
   separates the columns. */
.kb-grid { display: grid; column-gap: var(--kb-col-gap, 12px); row-gap: 0; }
.kb-cell {
  display: flex; flex-direction: column; gap: var(--kb-card-gap, 8px); padding: 8px; min-width: 0; min-height: 46px;
  background: var(--kb-col-bg); border-left: 1px solid var(--kb-line); border-right: 1px solid var(--kb-line);
}
/* Lane divider inside a column (aligned across the grid → reads as a swimlane). */
.kb-grid-lanes .kb-cell:not(.kb-cell-bottom) { border-bottom: 1px dashed var(--kb-line); }
/* First lane tucks under the coloured column header; last lane rounds off. */
.kb-cell-top { border-top: none; }
.kb-cell-bottom { border-bottom: 1px solid var(--kb-line); border-radius: 0 0 10px 10px; box-shadow: 0 2px 6px rgba(20,28,42,0.06); }
.kb-cell-drop { box-shadow: inset 0 0 0 2px #3b7cff; background: #dfeaff; }
/* Per-cell lane label (repeated inside every column, so columns stay whole). */
.kb-cell-lane { display: flex; align-items: center; gap: 6px; padding: 2px 2px 1px; outline: none; }
.kb-cell-lane-dot { flex: 0 0 auto; width: 8px; height: 8px; border-radius: 50%; }
.kb-cell-lane-name { font-weight: 800; font-size: 12px; color: var(--kb-ink); outline: none; word-break: break-word; }
/* "＋ Add a card" at the foot of every cell → adds into that exact intersection. */
.kb-cell-add { flex: 0 0 auto; margin-top: auto; padding: 7px 8px; border: none; border-radius: 6px; background: transparent; color: var(--kb-ink-dim); font-size: 12.5px; font-weight: 600; text-align: left; cursor: pointer; opacity: 0.75; }
.kb-cell-add:hover { background: rgba(20,28,42,0.07); color: var(--kb-ink); opacity: 1; }

/* Task cards — white, soft shadow, footer with id badge + reorder arrows. */
.kb-card {
  position: relative; flex: 0 0 auto; background: var(--kb-card-bg); border: 1px solid rgba(20,28,42,0.10);
  border-radius: var(--kb-card-radius, 8px); padding: var(--kb-card-pad, 9px) calc(var(--kb-card-pad, 9px) + 15px) var(--kb-card-pad, 9px) var(--kb-card-pad, 9px); cursor: grab;
  box-shadow: 0 1px 2px rgba(20,28,42,0.12), 0 2px 6px rgba(20,28,42,0.06); min-height: max(20px, var(--kb-card-minh, 0px));
}
.kb-card:hover { box-shadow: 0 2px 5px rgba(20,28,42,0.16), 0 6px 14px rgba(20,28,42,0.10); }
.kb-card:active { cursor: grabbing; }
.kb-card-dragging { opacity: 0.45; }
.kb-card-urgent { border-left: 4px solid #ff5a52; padding-left: 9px; }
/* Priority-coloured left border (Low/Medium/High/Urgent → colour via --kb-pri). */
.kb-card-pri { border-left: 4px solid var(--kb-pri, #8a93a6); padding-left: 9px; }
.kb-card-ind.kb-due.overdue { color: #d1453b; font-weight: 700; }
.kb-card-ind.kb-due.soon { color: #b7791f; }
.kb-card-ind.kb-due.done, .kb-card-ind.done { color: #2f9e6e; }
.kb-card-body { min-width: 0; }
/* Trello-style cover: bleeds to the card edges (card padding is 9/24/8/11). */
.kb-card-cover { margin: calc(-1 * var(--kb-card-pad, 9px)) calc(-1 * (var(--kb-card-pad, 9px) + 15px)) 8px calc(-1 * var(--kb-card-pad, 9px)); border-radius: 7px 7px 0 0; background-size: cover; background-position: center; }
.kb-card-cover-color { height: 30px; }
.kb-card-cover-image { height: var(--kb-cover-h, 108px); background-color: rgba(20,28,42,0.06); }
.kb-card-text { font-size: var(--kb-title-font, 13px); line-height: 1.4; color: var(--kb-ink); word-break: break-word; outline: none; overflow: hidden; font-weight: 700; text-transform: capitalize; }
.kb-card-desc { font-size: var(--kb-desc-font, 11.5px); line-height: 1.35; color: var(--kb-ink-dim); margin-top: 3px; overflow: hidden; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: var(--kb-desc-lines, 1); word-break: break-word; }
.kb-card-text.empty { color: var(--kb-ink-dim); font-style: italic; }
.kb-card-text[contenteditable="true"] { cursor: text; }
.kb-card-row { display: flex; align-items: center; gap: 7px; min-width: 0; }
.kb-card-ico { flex: 0 0 auto; font-size: 15px; }
.kb-card-dot { flex: 0 0 auto; width: 12px; height: 12px; border-radius: 3px; }
.kb-card-row .kb-card-text { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.kb-card img { width: 100%; max-height: 130px; object-fit: cover; border-radius: 5px; display: block; margin-bottom: 5px; }
/* Footer */
.kb-card-foot { display: flex; align-items: center; justify-content: space-between; gap: 6px; margin-top: 8px; }
.kb-card-foot-l { display: flex; align-items: center; gap: 6px; min-width: 0; }
.kb-card-foot-r { display: flex; align-items: center; gap: 3px; flex: 0 0 auto; opacity: 0; transition: opacity 0.1s ease; }
.kb-card:hover .kb-card-foot-r { opacity: 1; }
.kb-card-id { font-size: 10.5px; font-weight: 800; letter-spacing: 0.4px; border-radius: 5px; padding: 1px 6px; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }
/* Tag colour dots + detail indicators on the mini card. */
.kb-card-tags { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 6px; }
.kb-card-tag { width: 24px; height: 6px; border-radius: 999px; }
.kb-card-ind { font-size: 10.5px; color: var(--kb-ink-dim); display: inline-flex; align-items: center; gap: 2px; white-space: nowrap; }
.kb-card-urg { flex: 0 0 auto; width: 14px; height: 14px; padding: 0; border-radius: 4px; border: 1.5px solid rgba(20,28,42,0.28); background: transparent; cursor: pointer; }
.kb-card-urg:hover { border-color: #ff5a52; }
.kb-card-urg.on { background: #ff5a52; border-color: #ff5a52; box-shadow: 0 0 0 2px rgba(255,90,82,0.25); }
.kb-card-move { flex: 0 0 auto; width: 18px; height: 18px; padding: 0; border: 1px solid rgba(20,28,42,0.14); border-radius: 5px; background: #fff; color: var(--kb-ink-dim); font-size: 8px; line-height: 1; cursor: pointer; }
.kb-card-move:hover { color: var(--kb-ink); border-color: rgba(20,28,42,0.35); background: #eef1f6; }
.kb-card-x { position: absolute; top: 4px; right: 4px; width: 17px; height: 17px; border: none; border-radius: 50%; background: rgba(20,28,42,0.42); color: #fff; font-size: 12px; line-height: 16px; text-align: center; padding: 0; cursor: pointer; opacity: 0; transition: opacity 0.1s ease; }
.kb-card:hover .kb-card-x { opacity: 1; }

/* Header focus button (matches .kb-menu). */
.kb-focus-btn { flex: 0 0 auto; width: 26px; height: 26px; border: 1px solid rgba(20,28,42,0.18); border-radius: 7px; background: #fff; color: var(--kb-ink-dim); cursor: pointer; font-size: 14px; line-height: 1; }
.kb-focus-btn:hover { color: var(--kb-ink); border-color: rgba(20,28,42,0.35); }

/* Kanban focus mode — a distraction-free fullscreen overlay. */
.kbf-overlay {
  /* Above the app chrome (toolbar / inspector / rail / tooltip) but below the
     context menu (500) and modal dialogs (400) so the ⋯ menu and rename prompt
     opened from inside focus mode still layer on top. */
  position: fixed; inset: 0; z-index: 350;
  display: flex; flex-direction: column;
  background: rgba(10,14,22,0.72); backdrop-filter: blur(3px);
  opacity: 0; transition: opacity 0.18s ease;
}
.kbf-overlay.show { opacity: 1; }
.kbf-bar {
  flex: 0 0 auto; display: flex; align-items: center; justify-content: space-between; gap: 12px; color: #fff;
  padding: calc(12px + var(--safe-t)) calc(18px + var(--safe-r)) 12px calc(18px + var(--safe-l));
}
.kbf-heading { display: flex; align-items: center; gap: 10px; min-width: 0; font-weight: 800; font-size: 17px; }
.kbf-ico { font-size: 18px; }
.kbf-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.kbf-close { flex: 0 0 auto; width: 38px; height: 38px; border: 1px solid rgba(255,255,255,0.22); border-radius: 999px; background: rgba(255,255,255,0.10); color: #fff; font-size: 16px; cursor: pointer; }
.kbf-close:hover { background: rgba(255,255,255,0.20); }
.kbf-stage { flex: 1 1 auto; min-height: 0; display: flex; align-items: flex-start; justify-content: center; padding: 8px 24px 28px; overflow: auto; }
/* The Kanban card itself, un-anchored from the canvas and enlarged a touch. */
.kbf-card {
  position: relative !important; left: auto !important; top: auto !important;
  transform: none !important; margin: 0 auto; flex: 0 0 auto;
  box-shadow: 0 24px 60px rgba(0,0,0,0.45);
}
.kbf-card .kb-focus-btn { display: none; }   /* already focused */

/* ---- Tier editor (inspector) ---- */
.tier-editor { display: flex; flex-direction: column; gap: 6px; }
.tier-ed-row { display: flex; align-items: center; gap: 6px; }
.tier-ed-color { flex: 0 0 auto; width: 22px; height: 22px; border-radius: 6px; border: 1px solid var(--border-strong); cursor: pointer; padding: 0; }
.tier-ed-input { flex: 1 1 auto; min-width: 0; }
.tier-ed-btn { flex: 0 0 auto; width: 24px; height: 24px; border: 1px solid var(--border); border-radius: 6px; background: var(--bg-elev-2); color: var(--text-dim); cursor: pointer; font-size: 13px; line-height: 1; padding: 0; }
.tier-ed-btn:hover:not(:disabled) { border-color: var(--border-strong); color: var(--text); }
.tier-ed-btn:disabled { opacity: 0.35; cursor: default; }
.tier-ed-del:hover { border-color: var(--danger); color: var(--danger); }

/* ---- Image grid ---- */
.img-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); gap: 12px; }
.img-cell { border: 1px solid var(--border); border-radius: 10px; overflow: hidden; cursor: pointer; background: var(--bg-elev-2); transition: border-color 0.12s ease, transform 0.06s ease;
  content-visibility: auto; contain-intrinsic-size: auto 154px; }   /* skip rendering off-screen cells */
.img-cell:hover { border-color: var(--accent); transform: translateY(-1px); }
.img-cell img { width: 100%; height: 120px; object-fit: cover; display: block; background: var(--bg); }
.img-cap { padding: 6px 8px; font-size: 11.5px; color: var(--text-dim); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* ---- Board list (view-all + picker) ---- */
.board-list { display: flex; flex-direction: column; gap: 4px; }
.bl-row { display: flex; align-items: center; gap: 12px; padding: 8px 10px; border-radius: 10px; cursor: pointer; }
.bl-row:hover { background: var(--bg-elev-2); }
.bl-row.current { background: var(--accent-soft); }
.bl-thumb { width: 40px; height: 40px; flex: 0 0 40px; border-radius: 8px; border: 1.5px solid var(--border-strong); display: flex; align-items: center; justify-content: center; font-size: 18px; background-size: cover; background-position: center; }
.bl-thumb.has-cover span { display: none; }
.bl-main { min-width: 0; }
.bl-name { font-size: 13.5px; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bl-sub { font-size: 11.5px; color: var(--text-faint); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* ---- Project picker ---- */
.picker-section { font-size: 11px; text-transform: uppercase; letter-spacing: 0.5px; color: var(--text-faint); margin: 14px 0 6px; }
.picker-section:first-child { margin-top: 0; }
.proj-card { display: flex; align-items: center; gap: 12px; padding: 10px 12px; border: 1px solid var(--border); border-radius: 10px; margin-bottom: 6px; }
.proj-card.active { border-color: var(--accent); }
.proj-ico { font-size: 20px; }
.proj-meta { flex: 1; min-width: 0; }
.proj-title { font-size: 13.5px; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.proj-sub { font-size: 11.5px; color: var(--text-faint); }
.proj-card .mini-btn { flex: 0 0 auto; min-width: 100px; }

/* ---- Text style presets (inspector) ---- */
.text-presets { display: flex; gap: 6px; width: 100%; }
.text-style-presets { flex-wrap: wrap; }
.preset-btn {
  flex: 1; padding: 6px 4px;
  border: 1px solid var(--border); border-radius: 8px;
  background: var(--bg-elev-2); color: var(--text);
  font: inherit; font-size: 12.5px; cursor: pointer; white-space: nowrap;
}
.text-style-presets .preset-btn { flex: 1 1 calc(33.333% - 6px); }
.preset-btn:hover { border-color: var(--border-strong); }
.preset-btn.active { border-color: var(--accent); background: var(--accent-soft); color: var(--accent); font-weight: 600; }

/* ---- Segmented control (align, etc.) ---- */
.seg { display: flex; width: 100%; }
.seg-btn {
  flex: 1; padding: 6px 4px;
  border: 1px solid var(--border); border-left-width: 0;
  background: var(--bg-elev-2); color: var(--text-dim);
  font: inherit; font-size: 14px; cursor: pointer; line-height: 1;
}
.seg-btn:first-child { border-left-width: 1px; border-radius: 8px 0 0 8px; }
.seg-btn:last-child { border-radius: 0 8px 8px 0; }
.seg-btn:hover { color: var(--text); }
.seg-btn.active { border-color: var(--accent); color: var(--accent); background: var(--accent-soft); }
.seg-btn.active + .seg-btn { border-left-color: var(--accent); }

/* ---- Board icon picker (inspector) ---- */
.icon-choice { border: 1px solid transparent; border-radius: 6px; background: var(--bg-elev-2); cursor: pointer; font-size: 14px; padding: 3px 0; line-height: 1; }
.icon-choice:hover { border-color: var(--border-strong); }
.icon-choice.active { border-color: var(--accent); background: var(--accent-soft); }

/* Compact icon control in the inspector (opens the picker window). */
.icon-trigger {
  display: flex; align-items: center; gap: 8px; width: 100%;
  background: var(--bg-elev-2); border: 1px solid var(--border);
  border-radius: 8px; padding: 6px 10px; cursor: pointer; color: var(--text);
}
.icon-trigger:hover { border-color: var(--accent); }
.icon-trigger-emoji { font-size: 20px; line-height: 1; }
.icon-trigger-label { font-size: 12.5px; color: var(--text-dim); }

/* Icon picker window (modal). */
.icon-window { display: flex; flex-direction: column; gap: 10px; min-width: min(420px, 80vw); }
.icon-search { width: 100%; box-sizing: border-box; }
.icon-grid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(40px, 1fr));
  gap: 6px; max-height: 46vh; overflow: auto;
}
.icon-grid .icon-choice { font-size: 22px; padding: 8px 0; }

/* Group-resize bounds around a multi-selection. */
.group-bounds {
  position: absolute; z-index: 3000;
  border: calc(1.5px * var(--inv-scale, 1)) dashed var(--accent);
  border-radius: 4px;
  pointer-events: none;   /* only its handles are interactive */
}
.group-bounds .handle { pointer-events: auto; }

/* Thumbnail focal-point pad (inspector). */
.thumbpos-pad {
  position: relative; width: 100%; aspect-ratio: 16 / 10;
  border: 1px solid var(--border); border-radius: 8px;
  background: var(--bg-elev-2) center/cover no-repeat;
  cursor: crosshair; touch-action: none; outline: none; overflow: hidden;
}
.thumbpos-pad:focus { border-color: var(--accent); }
.thumbpos-dot {
  position: absolute; width: 14px; height: 14px; border-radius: 50%;
  background: var(--accent); border: 2px solid #fff;
  transform: translate(-50%, -50%); pointer-events: none;
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.45);
}

/* Auto-arrange dialog. */
.arrange-dialog { display: flex; flex-direction: column; gap: 12px; min-width: min(340px, 80vw); }
.arrange-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.arrange-row .field { width: 90px; }
.arrange-slider-wrap { display: flex; align-items: center; gap: 12px; flex: 1; max-width: 220px; }
.arrange-slider { flex: 1; accent-color: var(--accent); cursor: pointer; height: 22px; }
.arrange-val { min-width: 22px; text-align: right; font-variant-numeric: tabular-nums; font-weight: 650; color: var(--text); }

/* Generate-Transcript (yt-dlp command) modal. */
.tx-modal { display: flex; flex-direction: column; gap: 12px; min-width: 0; }
.tx-help { font-size: 13px; line-height: 1.5; color: var(--text-dim); }
.tx-row { display: grid; grid-template-columns: 92px 1fr; align-items: center; gap: 12px; }
.tx-dir { display: flex; align-items: center; gap: 8px; min-width: 0; }
.tx-dir .field { flex: 1 1 auto; min-width: 0; }
.tx-dir .mini-btn { flex: 0 0 auto; white-space: nowrap; }
.tx-label { font-size: 12.5px; font-weight: 600; color: var(--text-dim); }
.tx-cmd-head { display: flex; align-items: center; justify-content: space-between; gap: 8px 12px; margin-top: 4px; flex-wrap: wrap; }
.tx-cmd-label { font-size: 11px; text-transform: uppercase; letter-spacing: 0.5px; color: var(--text-faint); }
.tx-cmd-head .mini-btn { flex: 0 0 auto; min-width: 64px; padding: 4px 10px; }
.tx-cmd-text {
  display: block; width: 100%; box-sizing: border-box; padding: 9px 11px;
  border: 1px solid var(--border); border-radius: 8px; background: var(--bg); color: var(--text);
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12px;
  line-height: 1.5; white-space: pre-wrap; overflow-wrap: anywhere; user-select: all;
}
.tx-note { font-size: 12px; color: var(--text-faint); }
.tx-paste { width: 100%; box-sizing: border-box; resize: vertical; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12px; }
.tx-paste-actions { display: flex; justify-content: flex-end; gap: 8px; }
/* Download-command output buttons (Copy / .bat / .sh) + option checkboxes */
.tx-out-btns { display: flex; align-items: center; gap: 6px; flex: 0 0 auto; flex-wrap: wrap; justify-content: flex-end; }
.tx-out-btns .mini-btn { min-width: 0; padding: 4px 10px; }
.tx-chk { display: flex; align-items: center; gap: 8px; font-size: 12.5px; color: var(--text-dim); cursor: pointer; min-width: 0; }
.tx-chk input { flex: 0 0 auto; accent-color: var(--accent); cursor: pointer; }
.tx-modal .field { min-width: 0; }

/* ===== SVG freehand drawing (features/svg-drawing) ===== */
/* Pencil cursor while drawing mode is active (crosshair fallback). */
.viewport.drawing {
  cursor: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath d='M3 21l3.2-1L18 8.2l-2.2-2.2L4 17.8z' fill='%23222' stroke='white' stroke-width='1.2' stroke-linejoin='round'/%3E%3Cpath d='M15.8 6l2.2 2.2 1.6-1.6a1 1 0 0 0 0-1.4l-.8-.8a1 1 0 0 0-1.4 0z' fill='%23f5b301' stroke='white' stroke-width='1'/%3E%3C/svg%3E") 3 21, crosshair;
}
/* Shapes are dragged out from a corner, so a crosshair beats the pencil. */
.viewport.drawing.shaping { cursor: crosshair; }

/* Live stroke overlay — sits inside the transformed #world (world coords),
   paints outside its 1px box, and never intercepts pointer input. */
.svg-draw-live { position: absolute; left: 0; top: 0; overflow: visible; pointer-events: none; z-index: 90000; }
/* Committed stroke object: only the drawn line is a hit target, so the large
   transparent bounding box never blocks clicks to objects beneath it. */
/* Borderless: a drawing is a native vector object — no card frame/background/
   shadow. A dashed bounding box appears only when it's selected. */
.obj-drawing { pointer-events: none; background: none; border: none; box-shadow: none; border-radius: 0; }
.obj-drawing .drawing-svg { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; pointer-events: none; display: block; }
/* The card itself is pointer-events:none so only the drawn line is a hit target
   — but its children inherit that, which left the resize handles VISIBLE and
   completely un-grabbable on any selected drawing. */
.obj-drawing .handle { pointer-events: auto; }
.obj-drawing .drawing-svg path { pointer-events: stroke; }
.obj-drawing .drawing-svg .hit { stroke: transparent; fill: none; }
/* A filled shape is clickable across its face; an outline stays click-through
   in the middle so you can still reach whatever is behind it. */
.obj-drawing .drawing-svg .hit.hit-fill { fill: transparent; }
.obj-drawing .drawing-svg .fx-under, .obj-drawing .drawing-svg .fx-core { stroke: var(--bg); }
.obj-drawing.selected { outline: calc(1.5px * var(--inv-scale, 1)) dashed var(--accent); outline-offset: calc(3px * var(--inv-scale, 1)); }

/* The tool sections had no layout of their own, so every row inside them sat
   flush against the next — the panel's `gap` only separates its five direct
   children, not the contents of a section. */
.svg-pen-only, .svg-eraser-only, .svg-shape-only, .svg-common-only {
  display: flex; flex-direction: column; gap: 11px;
}
/* Every section AND the individual rows are toggled with the `hidden` property,
   which a class rule carrying `display` silently overrules — the inactive
   tool's controls would otherwise stay on screen underneath the active one's. */
.svg-pen-only[hidden], .svg-eraser-only[hidden],
.svg-shape-only[hidden], .svg-common-only[hidden],
.svg-brush-row[hidden], .svg-brush-hint[hidden] { display: none; }

/* Shape picker: two flush rows of five, each drawn by the same geometry the
   tool commits, so the button is a true thumbnail of the result. */
.svg-shape-grid { display: grid; grid-template-columns: repeat(5, 1fr); gap: 5px; }
.svg-shape-btn {
  height: 28px; padding: 0; cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  border: 1px solid var(--border); border-radius: 7px;
  background: var(--bg-elev); color: var(--text-dim);
}
.svg-shape-btn:hover { border-color: var(--accent); color: var(--text); }
.svg-shape-btn.on { border-color: var(--accent); background: var(--accent-soft); color: var(--accent); }
.svg-shape-ico { display: block; overflow: visible; pointer-events: none; }

/* Border / Fill each get their own picker and quick strip — they are separate
   settings, not two uses of one "current colour". */
.svg-shape-row { display: grid; grid-template-columns: 48px auto 1fr; align-items: center; gap: 8px; }
.svg-quick { display: flex; gap: 4px; }
.svg-swatch-sm { width: 18px; height: 18px; border-radius: 5px; aspect-ratio: auto; }
/* "No fill": a diagonal bar over the panel colour, the usual way to say empty. */
.svg-swatch-none {
  width: 18px; height: 18px; border-radius: 5px; aspect-ratio: auto;
  background:
    linear-gradient(to top right, transparent calc(50% - 1px), var(--danger) calc(50% - 1px),
                    var(--danger) calc(50% + 1px), transparent calc(50% + 1px)),
    var(--bg-elev);
}

/* Line / Ends / Effect: labelled groups of icon buttons. Each option draws
   itself with the real stroke renderer, so the choice is legible without
   opening anything. */
.svg-brush-groups { display: flex; flex-direction: column; gap: 9px; }
.svg-opt-wrap { display: grid; grid-template-columns: 56px 1fr; align-items: center; gap: 10px; }
.svg-opt-group { display: flex; gap: 4px; }
.svg-opt {
  flex: 1; min-width: 0; height: 26px; padding: 0; cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  border: 1px solid var(--border); border-radius: 7px;
  background: var(--bg-elev); color: var(--text-dim);
}
.svg-opt:hover { border-color: var(--accent); color: var(--text); }
.svg-opt.on { border-color: var(--accent); background: var(--accent-soft); color: var(--accent); }
.svg-opt-ico { display: block; overflow: visible; pointer-events: none; }
/* The thumbnails paint with currentColor so they follow the button state. On a
   board the outline halo and double core are the background colour; in a flat
   icon that would be invisible, so they're drawn as a visible band instead —
   the icon has to communicate the shape, not reproduce the colours. */
.svg-opt-ico .fx-under { stroke: var(--border-strong); }
.svg-opt-ico .fx-core { stroke: var(--bg-elev); }
.svg-opt.on .svg-opt-ico .fx-core { stroke: var(--accent-soft); }
.svg-brush-hint { font-size: 10.5px; color: var(--text-faint); line-height: 1.4; margin-top: 2px; }
.svg-tool-row { display: flex; gap: 6px; }
.svg-tool-btn { width: 34px; height: 34px; border-radius: 9px; border: 1px solid var(--border); background: var(--bg-elev); color: var(--text); cursor: pointer; font-size: 15px; }
.svg-tool-btn:hover { border-color: var(--accent); }
.svg-tool-btn.active { border-color: var(--accent); background: var(--accent-soft); color: var(--accent); }

/* Eraser: hollow circular brush preview that tracks the cursor. */
.viewport.erasing { cursor: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='22' height='22' viewBox='0 0 24 24'%3E%3Crect x='3' y='11' width='13' height='8' rx='2' transform='rotate(-40 9 15)' fill='%23f0a5a5' stroke='white' stroke-width='1.3'/%3E%3C/svg%3E") 4 18, crosshair; }
/* Pen: an outline-only ring under the cursor, the exact on-screen diameter of
   the brush. Kept faint so it guides without competing with the artwork, and
   the dark halo keeps it visible over pale boards and images alike. */
.svg-pen-ring {
  position: absolute; z-index: 45; pointer-events: none;
  border: 1.5px solid var(--accent); border-radius: 50%;
  transform: translate(-50%, -50%); opacity: 0.45;
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.35);
}
.svg-pen-ring[hidden] { display: none; }

.svg-eraser-preview {
  position: absolute; z-index: 45; pointer-events: none;
  border: 1.5px solid var(--accent); background: color-mix(in srgb, var(--accent) 14%, transparent);
  border-radius: 50%; transform: translate(-50%, -50%);
}

/* Vector node-editing overlay (screen space; sits over the canvas) */
.svg-nodeedit { position: absolute; inset: 0; z-index: 40; pointer-events: none; overflow: visible; }
.svg-nodeedit .ne-guide-line { stroke: var(--accent); stroke-width: 1; opacity: 0.5; }
.svg-nodeedit .ne-handle-pt { fill: var(--panel); stroke: var(--accent); stroke-width: 1.5; pointer-events: auto; cursor: grab; }
.svg-nodeedit .ne-anchor-pt { fill: var(--panel); stroke: var(--accent); stroke-width: 1.6; pointer-events: auto; cursor: grab; }
.svg-nodeedit .ne-anchor-pt.sel { fill: var(--accent); }
.svg-nodeedit .ne-seg-hit { fill: none; stroke: transparent; stroke-width: 12; pointer-events: stroke; cursor: copy; }
/* Live selection bounding box (matches the path geometry in real time). */
.svg-nodeedit .ne-bbox { fill: none; stroke: var(--accent); stroke-width: 1; stroke-dasharray: 4 3; opacity: 0.6; pointer-events: none; }
.svg-nodeedit .ne-bbox.snapping { stroke: #22c55e; stroke-width: 2; opacity: 1; }
/* Clutter-free editing: while dragging, hide everything except the active point/handles. */
.svg-nodeedit.dragging .ne-seg-hit,
.svg-nodeedit.dragging .ne-anchor-pt:not(.active),
.svg-nodeedit.dragging .ne-handle-pt:not(.active),
.svg-nodeedit.dragging .ne-guide-line:not(.active) { display: none; }
/* The overlay draws the live box, so suppress the object's static outline while editing. */
.obj-drawing.editing-nodes.selected { outline: none; }

/* Floating brush toolbar */
.svg-brush {
  position: fixed; left: 50%; bottom: 22px; transform: translateX(-50%); z-index: 60;
  width: 260px; max-width: calc(100vw - 24px);
  background: var(--panel); border: 1px solid var(--border); border-radius: 14px;
  box-shadow: var(--shadow); padding: 14px; display: flex; flex-direction: column; gap: 12px;
  /* Roomier rows make the panel tall enough to run off a short window, so it
     scrolls internally rather than putting the tools out of reach. */
  max-height: calc(100vh - 44px); overflow-y: auto; overscroll-behavior: contain;
}
.svg-brush[hidden] { display: none; }
.svg-brush-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.svg-brush-title { font-size: 14px; font-weight: 700; color: var(--text); }
/* Full-width live stroke: shows colour, width, opacity, dash, cap, effect and
   how far smoothing rounds a corner — all of which the old 40px dot could not. */
.svg-brush-preview {
  height: 60px; border-radius: 10px; border: 1px solid var(--border);
  display: flex; align-items: center; justify-content: center;
  overflow: hidden; flex: 0 0 auto;
  /* A faint checker, not flat board colour. Opacity now reads as translucency,
     and the "outline"/"double" effects — whose layers ARE the board colour —
     become visible as opaque bands cutting through it. On a flat background
     they were indistinguishable from "none", which is the exact problem this
     preview exists to solve. */
  --svg-checker: color-mix(in srgb, var(--border) 55%, transparent);
  background-color: var(--bg);
  background-image:
    linear-gradient(45deg, var(--svg-checker) 25%, transparent 25%, transparent 75%, var(--svg-checker) 75%),
    linear-gradient(45deg, var(--svg-checker) 25%, transparent 25%, transparent 75%, var(--svg-checker) 75%);
  background-size: 14px 14px;
  background-position: 0 0, 7px 7px;
}
@supports not (color: color-mix(in srgb, red 50%, blue)) {
  .svg-brush-preview { --svg-checker: var(--border); }
}
.svg-brush-preview-svg { width: 100%; height: 100%; display: block; overflow: visible; }
.svg-brush-preview-svg .fx-under, .svg-brush-preview-svg .fx-core { stroke: var(--bg); }
.svg-brush-row { display: grid; grid-template-columns: 56px 1fr auto; align-items: center; gap: 10px; }
.svg-brush-label { font-size: 12px; color: var(--text-dim); }
.svg-brush-val { font-size: 11.5px; font-weight: 650; color: var(--text); font-variant-numeric: tabular-nums; min-width: 34px; text-align: right; }
.svg-brush-range { width: 100%; accent-color: var(--accent); cursor: pointer; margin: 0; }
/* The custom-colour picker sits on the Recent header as a small tile rather
   than a full-width native bar, so there is one colour area, not two. The
   vendor pseudo-elements strip the browser's own chrome off it. */
.svg-brush-sechead { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.svg-brush-color {
  width: 24px; height: 22px; padding: 0; cursor: pointer; flex: 0 0 auto;
  -webkit-appearance: none; appearance: none;
  border: 1px solid var(--border-strong); border-radius: 6px; background: var(--bg);
}
.svg-brush-color::-webkit-color-swatch-wrapper { padding: 1px; }
.svg-brush-color::-webkit-color-swatch { border: none; border-radius: 4px; }
.svg-brush-color::-moz-color-swatch { border: none; border-radius: 4px; }
.svg-brush-color:hover { border-color: var(--accent); }
.svg-brush-seclabel { font-size: 10.5px; text-transform: uppercase; letter-spacing: 0.5px; color: var(--text-faint); margin-top: 2px; }
/* Fixed 8 columns: both rows stay flush, and 16 presets fill two of them. */
.svg-brush-swatches { display: grid; grid-template-columns: repeat(8, 1fr); gap: 6px; }
.svg-swatch {
  width: 100%; aspect-ratio: 1; border-radius: 6px; padding: 0; cursor: pointer;
  border: 1px solid var(--border-strong);
}
.svg-swatch:hover { border-color: var(--accent); }
/* Selected colour: a ring, drawn outside the tile so it doesn't shrink the
   colour or fight the swatch's own border on dark and light swatches alike. */
.svg-swatch.on { border-color: var(--panel); box-shadow: 0 0 0 2px var(--accent); }
.svg-brush-empty { font-size: 12px; color: var(--text-faint); }
.svg-brush-foot { display: flex; justify-content: flex-end; margin-top: 2px; }
/* Edge drag grips — move the whole panel; interior controls stay interactive. */
.svg-brush-grip { position: absolute; z-index: 1; cursor: move; opacity: 0.35; transition: opacity 0.12s; }
.svg-brush-grip:hover { opacity: 0.8; }
.svg-brush-grip::after { content: ""; position: absolute; inset: 0; margin: auto; background: var(--border-strong); border-radius: 3px; }
.svg-brush-grip-top, .svg-brush-grip-bottom { left: 50%; transform: translateX(-50%); width: 64px; height: 12px; }
.svg-brush-grip-top { top: 1px; } .svg-brush-grip-bottom { bottom: 1px; }
.svg-brush-grip-top::after, .svg-brush-grip-bottom::after { width: 40px; height: 4px; }
.svg-brush-grip-left, .svg-brush-grip-right { top: 50%; transform: translateY(-50%); width: 12px; height: 64px; }
.svg-brush-grip-left { left: 1px; } .svg-brush-grip-right { right: 1px; }
.svg-brush-grip-left::after, .svg-brush-grip-right::after { width: 4px; height: 40px; }

/* Portrait "Set image" picker window. */
.img-picker { display: flex; flex-direction: column; gap: 12px; min-width: min(560px, 82vw); }
.img-picker-actions { display: flex; gap: 8px; }
.img-picker-bar { display: flex; align-items: center; gap: 8px; }
.img-picker-bar label { font-size: 12px; color: var(--text-dim); }
.img-picker-bar .img-sort { flex: 0 0 auto; width: auto; min-width: 150px; }
.img-cell { position: relative; }

/* Sync progress bar under the save status */
.save-status { position: relative; }
.save-status[data-state="saving"]::after {
  content: ""; position: absolute; left: 10px; right: 10px; bottom: 2px; height: 2px;
  background: linear-gradient(to right, var(--accent) var(--sync-pct, 0%), var(--border) var(--sync-pct, 0%));
  border-radius: 2px;
}

/* Scrollbars */
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-thumb { background: var(--border-strong); border-radius: 8px; border: 2px solid transparent; background-clip: content-box; }
::-webkit-scrollbar-track { background: transparent; }

/* Hide button labels on narrow screens */
@media (max-width: 1080px) {
  .tbtn span { display: none; }
  .tbtn { padding: 0 9px; }
}

/* ===================== Phone / narrow-screen layout =====================
   Placed last so these overrides beat the base .inspector / .sidebar-* rules
   (equal specificity → later source wins). */
@media (max-width: 640px) {
  /* Compact, non-overlapping bar: keep just the mark, drop dividers, and let
     what remains scroll horizontally rather than pile up on top of itself. */
  #toolbar { gap: 6px; justify-content: flex-start; flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none; }
  #toolbar::-webkit-scrollbar { display: none; }
  .toolbar-left { gap: 6px; flex: 0 0 auto; }
  .toolbar-right { gap: 4px; flex: 0 0 auto; margin-left: auto; }
  .brand { flex-shrink: 0; white-space: nowrap; }
  .brand-name { display: none; }
  #toolbar .divider { display: none; }
  .breadcrumb { max-width: 40vw; }
  /* Tool rail stays a left column (respecting the safe area) — clear of the
     bottom-center mode toggle and the corner badge. */
  .sidebar-rail { top: calc(58px + var(--safe-t)); left: max(8px, var(--safe-l)); }
  /* Open the panel to the RIGHT of the rail (touch buttons are 50px wide), not
     on top of it — otherwise the Search/Tags/Settings buttons overlap the
     panel they just opened. */
  .sidebar-panel {
    left: calc(max(8px, var(--safe-l)) + 60px); right: max(8px, var(--safe-r));
    width: auto; top: calc(58px + var(--safe-t)); bottom: calc(80px + var(--safe-b)); max-height: none;
  }
  .modal-card, .modal-card.wide { width: 100%; }
  .hint { display: none; }
}

/* ============================================================
   LIVE EDIT — Users panel & Follow Mode
   ============================================================ */
.users-panel { display: flex; flex-direction: column; gap: 4px; min-width: 340px; max-width: 420px; }
.user-row {
  display: flex; align-items: center; gap: 10px;
  padding: 8px 6px; border-radius: 9px; border: 1px solid transparent;
}
.user-row:hover { background: var(--bg-elev-2); }
.user-row.self { background: var(--bg-elev-1); }
.user-dot { flex: 0 0 auto; width: 12px; height: 12px; border-radius: 50%; box-shadow: 0 0 0 3px rgba(255,255,255,0.06); }
.user-meta { flex: 1; min-width: 0; }
.user-name { font-size: 13.5px; font-weight: 600; color: var(--text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.user-sub { font-size: 11.5px; color: var(--text-faint); display: flex; gap: 5px; margin-top: 1px; }
.user-status { color: #43c59e; }
.user-where { color: var(--text-faint); }
.user-actions { flex: 0 0 auto; display: flex; gap: 6px; }
.user-actions .mini-btn { margin: 0; }

/* The colored frame + shield shown while following another user. */
.follow-shield { position: fixed; inset: 0; z-index: 3000; background: transparent; cursor: default; }
body.follow-mode::after {
  content: ""; position: fixed; inset: 0; z-index: 3100; pointer-events: none;
  border: 4px solid var(--follow-color, #4f8cff);
  box-shadow: inset 0 0 22px -6px var(--follow-color, #4f8cff);
}
/* Hide every editing control while following (read-only presentation mode). */
body.follow-mode #toolbar,
body.follow-mode .breadcrumb-bar,
body.follow-mode #sidebar-rail,
body.follow-mode #sidebar-panel,
body.follow-mode .inspector,
body.follow-mode #view-switch,
body.follow-mode .hint,
body.follow-mode .pause-all-media { display: none !important; }

.follow-bar {
  position: fixed; top: calc(14px + var(--safe-t)); left: 50%; transform: translateX(-50%);
  z-index: 3200; display: flex; align-items: center; gap: 10px;
  padding: 7px 8px 7px 14px; border-radius: 999px;
  background: var(--bg-elev, #1a1d24); color: var(--text);
  border: 1px solid var(--follow-color, #4f8cff);
  box-shadow: 0 8px 30px -8px rgba(0,0,0,0.6);
  font-size: 13px; font-weight: 600;
}
.follow-dot { width: 11px; height: 11px; border-radius: 50%; flex: 0 0 auto; }
.follow-text { white-space: nowrap; }
.follow-stop {
  border: none; border-radius: 999px; cursor: pointer;
  background: var(--follow-color, #4f8cff); color: #fff;
  font: inherit; font-weight: 600; font-size: 12.5px; padding: 5px 12px;
}
.follow-stop:hover { filter: brightness(1.08); }

/* Users panel — your profile / display-name section */
.user-profile { display: flex; flex-direction: column; gap: 5px; padding: 2px 2px 10px; margin-bottom: 6px; border-bottom: 1px solid var(--border); }
.profile-label { font-size: 11px; font-weight: 600; letter-spacing: .04em; text-transform: uppercase; color: var(--text-faint); }
.profile-msg { font-size: 11.5px; color: var(--danger); }
.users-list { display: flex; flex-direction: column; gap: 4px; }
.users-list-head { font-size: 11px; font-weight: 600; letter-spacing: .04em; text-transform: uppercase; color: var(--text-faint); margin: 4px 2px; }

/* Account panel (signed-in identity) */
.account-id { display: flex; align-items: center; gap: 12px; }
.account-avatar { width: 42px; height: 42px; border-radius: 50%; background: var(--accent); color: #fff; display: flex; align-items: center; justify-content: center; font-weight: 700; font-size: 18px; flex: 0 0 auto; }
.account-meta { min-width: 0; }
.account-name { font-weight: 600; font-size: 15px; }
.account-role { font-size: 12px; color: var(--text-faint); margin-top: 1px; }
.account-actions { display: flex; gap: 8px; margin-top: 10px; }
.account-perms { margin-top: 10px; }
.account-perms-label { font-size: 11px; font-weight: 600; letter-spacing: .04em; text-transform: uppercase; color: var(--text-faint); margin-bottom: 5px; }
.perm-chips { display: flex; gap: 6px; flex-wrap: wrap; }
.perm-chip { font-size: 12px; font-weight: 600; padding: 3px 9px; border-radius: 999px; border: 1px solid var(--border); }
.perm-chip.on { color: #1f9d55; border-color: #1f9d5566; background: #1f9d5514; }
.perm-chip.off { color: var(--text-faint); }

/* ============================================================
   WEB LINK CARD — rich preview of any web page
   ============================================================ */
.obj-weblink {
  background: var(--panel); border: 1px solid var(--border); border-radius: 12px;
  overflow: hidden; cursor: grab; display: flex; flex-direction: column;
}
.obj-weblink .wl-media {
  position: relative; flex: 1 1 auto; min-height: 0;
  background: var(--bg-elev-2) center/cover no-repeat;
  display: flex; align-items: center; justify-content: center;
}
.obj-weblink .wl-media.has-img { background-color: #0d0f14; }
.wl-media-ph { font-size: 40px; opacity: 0.5; }
.wl-ext {
  position: absolute; top: 7px; right: 7px; width: 22px; height: 22px; border-radius: 6px;
  background: rgba(0,0,0,0.55); color: #fff; font-size: 13px; display: flex; align-items: center; justify-content: center;
}
.wl-spinner { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; }
.wl-spinner::after {
  content: ""; width: 24px; height: 24px; border: 3px solid var(--border-strong);
  border-top-color: var(--accent); border-radius: 50%; animation: sd-spin 0.7s linear infinite;
}
.obj-weblink .wl-body { flex: 0 0 auto; padding: 9px 11px; display: flex; flex-direction: column; gap: 3px; background: var(--panel); }
.wl-title { font-size: 13.5px; font-weight: 600; color: var(--text); line-height: 1.25;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.wl-site { display: flex; align-items: center; gap: 6px; font-size: 11.5px; color: var(--text-faint); }
.wl-fav { width: 14px; height: 14px; flex: 0 0 auto; border-radius: 3px; object-fit: cover; }
.wl-fav.ph { font-size: 11px; line-height: 14px; }
.wl-domain { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.wl-desc { font-size: 11.5px; color: var(--text-dim); line-height: 1.35;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; margin-top: 1px; }

/* ============================================================
   FAMILY TREE (features/family-tree)
   A full-screen genealogy editor. Everything below is scoped to
   .ft-* / .ftn / .ftl so it can never leak onto the board.
   ============================================================ */

/* ---- the board card that opens the panel ---- */
.obj-familytree {
  background: var(--panel); border: 1px solid var(--border); border-left: 3px solid var(--board-color, #43c59e);
  border-radius: 12px; overflow: hidden; cursor: grab; display: flex; flex-direction: column;
}
.obj-familytree .board-header { display: flex; align-items: center; gap: 7px; padding: 9px 11px 7px; font-weight: 650; font-size: 14px; }
.obj-familytree .board-header .ico { font-size: 16px; }
.obj-familytree .board-header .name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.fttree-body { flex: 1; min-height: 0; display: flex; flex-direction: column; justify-content: center; gap: 10px; padding: 4px 11px 11px; }
.fttree-strip { display: flex; align-items: center; }
.fttree-av {
  width: 40px; height: 40px; flex: 0 0 auto; border-radius: 50%; margin-right: -10px;
  border: 2px solid var(--border-strong); background: var(--bg-elev-2) center/cover no-repeat;
  display: flex; align-items: center; justify-content: center; font-size: 17px;
  box-shadow: 0 0 0 2px var(--panel);
}
.fttree-av.more { font-size: 12px; font-weight: 700; color: var(--text-dim); border-color: var(--border); }
.fttree-none { font-size: 12px; color: var(--text-faint); font-style: italic; }
.fttree-stats { font-size: 11.5px; color: var(--text-dim); }

/* ---- overlay shell ---- */
.ft-overlay {
  position: fixed; inset: 0; z-index: 72; background: var(--bg);
  opacity: 0; transition: opacity 0.2s ease; overflow: hidden;
  background-image: radial-gradient(var(--grid-dot) 1px, transparent 1px);
  background-size: 28px 28px;
}
.ft-overlay.show { opacity: 1; }
.ft-stage { position: absolute; inset: 0; overflow: hidden; cursor: grab; touch-action: none; }
.ft-stage.panning { cursor: grabbing; }
.ft-overlay.is-connecting .ft-stage { cursor: crosshair; }
.ft-world { position: absolute; top: 0; left: 0; transform-origin: 0 0; }
.ft-card-layer { position: absolute; top: 0; left: 0; }
.ft-line-layer { position: absolute; top: 0; left: 0; width: 1px; height: 1px; overflow: visible; pointer-events: none; }
.ft-line-layer .ftl-hit { pointer-events: stroke; cursor: pointer; }
.ft-pending { position: fixed; inset: 0; pointer-events: none; z-index: 3; }
.ft-pending line { stroke: var(--accent); stroke-width: 2; stroke-dasharray: 6 5; }
.ft-marquee {
  position: absolute; border: 1px solid var(--accent); background: var(--accent-soft);
  border-radius: 4px; pointer-events: none; z-index: 3;
}

/* ---- top bar ---- */
.ft-bar {
  position: absolute; top: 0; left: 0; right: 0; z-index: 4;
  display: flex; align-items: center; gap: 12px;
  padding: calc(10px + var(--safe-t)) calc(14px + var(--safe-r)) 10px calc(14px + var(--safe-l));
  background: linear-gradient(var(--bg) 62%, transparent);
  pointer-events: none; flex-wrap: wrap;
}
.ft-bar > * { pointer-events: auto; }
.ft-bar-left { display: flex; align-items: center; gap: 10px; min-width: 0; }
.ft-bar-mid { display: flex; align-items: center; gap: 8px; flex: 1; justify-content: center; flex-wrap: wrap; }
.ft-bar-right { display: flex; align-items: center; gap: 5px; }
/* Same gap as its parent, so on a wide bar this nesting is invisible. */
.ft-zoom-group { display: flex; align-items: center; gap: 5px; }
.ft-title {
  display: inline-flex; align-items: center; gap: 8px; height: 34px; padding: 0 10px;
  background: var(--bg-elev); border: 1px solid var(--border); border-radius: 9px;
  color: var(--text); font-size: 14px; font-weight: 650; cursor: pointer; font-family: inherit;
  max-width: 260px;
}
.ft-title:hover { border-color: var(--accent); }
.ft-title-ico { font-size: 16px; }
.ft-title-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ft-caret { color: var(--text-faint); font-size: 11px; }
.ft-count { font-size: 12px; color: var(--text-dim); white-space: nowrap; }
.ft-search { width: 190px; max-width: 34vw; }
.ft-add { white-space: nowrap; }
.ft-ctl {
  height: 34px; min-width: 34px; padding: 0 9px; border-radius: 9px;
  background: var(--bg-elev); border: 1px solid var(--border); color: var(--text-dim);
  font-size: 14px; cursor: pointer; font-family: inherit; display: inline-flex;
  align-items: center; justify-content: center;
}
.ft-ctl:hover { border-color: var(--accent); color: var(--accent); }
.ft-zoom-label { font-size: 12px; font-variant-numeric: tabular-nums; min-width: 52px; }
.ft-close:hover { border-color: var(--danger); color: var(--danger); }
.ft-sep { width: 1px; height: 22px; background: var(--border); margin: 0 3px; }

/* ---- person card ---- */
.ftn {
  position: absolute; top: 0; left: 0;
  display: flex; align-items: stretch;
  background: var(--panel); border: 1px solid var(--border);
  border-left: 3px solid var(--ftn-accent, var(--accent));
  border-radius: 14px; box-shadow: var(--obj-shadow);
  cursor: grab; user-select: none;
  transition: box-shadow 0.16s ease, border-color 0.16s ease, opacity 0.16s ease;
}
.ftn.animate { transition: transform 0.32s cubic-bezier(0.22, 0.61, 0.36, 1), box-shadow 0.16s ease, opacity 0.16s ease; }
.ftn:hover { border-color: var(--border-strong); box-shadow: var(--shadow-sm); z-index: 2; }
.ftn.is-selected { border-color: var(--accent); box-shadow: 0 0 0 2px var(--accent-soft), var(--shadow-sm); z-index: 3; }
.ftn.is-dim { opacity: 0.22; }
.ftn.is-source { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); }
.ftn.is-target:hover { border-color: #43c59e; box-shadow: 0 0 0 3px rgba(67, 197, 158, 0.28); }
.ftn.deceased .ftn-portrait { filter: grayscale(0.65); }
.ftn-inner { display: flex; align-items: center; gap: 10px; padding: 9px 11px 9px 9px; width: 100%; min-width: 0; position: relative; }
.ftn-portrait {
  width: 54px; height: 54px; flex: 0 0 auto; border-radius: 12px;
  border: 2px solid var(--border-strong); background: var(--bg-elev-2) center/cover no-repeat;
  display: flex; align-items: center; justify-content: center; font-size: 24px;
}
.ftn-body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 3px; }
.ftn-name {
  font-size: 14px; font-weight: 650; color: var(--text); line-height: 1.2;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.ftn-meta { display: flex; flex-wrap: wrap; gap: 4px; }
.ftn-chip {
  font-size: 10.5px; line-height: 1; padding: 3px 6px; border-radius: 999px;
  background: var(--bg-elev-2); color: var(--text-dim); border: 1px solid var(--border);
  max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.ftn-house { display: flex; align-items: center; gap: 4px; font-size: 11px; color: var(--text-faint); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ftn-house-ico { opacity: 0.7; }
.ftn-rip { position: absolute; top: 6px; right: 8px; font-size: 12px; color: var(--text-faint); }
.ftn-note-dot { position: absolute; bottom: 6px; right: 8px; font-size: 11px; color: var(--text-faint); cursor: help; }

/* connect handles — appear on hover, drag one onto another card */
.ftn-anchor {
  position: absolute; width: 15px; height: 15px; border-radius: 50%;
  background: var(--accent); border: 2px solid var(--panel);
  opacity: 0; transition: opacity 0.13s ease, transform 0.13s ease; cursor: crosshair; z-index: 4;
}
.ftn:hover .ftn-anchor, .ftn.is-selected .ftn-anchor { opacity: 1; }
.ftn-anchor:hover { transform: scale(1.3); }
.ftn-anchor.top { top: -8px; left: 50%; margin-left: -7px; }
.ftn-anchor.bottom { bottom: -8px; left: 50%; margin-left: -7px; }
.ftn-anchor.left { left: -8px; top: 50%; margin-top: -7px; }
.ftn-anchor.right { right: -8px; top: 50%; margin-top: -7px; }

/* ---- relationship lines ---- */
.ftl .ftl-wire { transition: stroke-width 0.14s ease; }
.ftl.is-hot .ftl-wire { stroke-width: 3.4; }
.ftl:hover .ftl-wire { stroke-width: 3.2; }

/* ---- legend ---- */
.ft-legend {
  position: absolute; left: 14px; bottom: 14px; z-index: 4; width: 172px;
  background: var(--panel); border: 1px solid var(--border); border-radius: 12px;
  box-shadow: var(--shadow-sm); padding: 9px 11px; font-size: 12px;
}
.ft-legend-head { display: flex; align-items: center; justify-content: space-between; font-weight: 650; color: var(--text); margin-bottom: 7px; }
.ft-legend-x { border: none; background: transparent; color: var(--text-dim); cursor: pointer; font-size: 15px; line-height: 1; padding: 0 4px; }
.ft-legend.collapsed .ft-legend-body { display: none; }
.ft-legend-body { display: flex; flex-direction: column; gap: 4px; }
.ft-legend-row { display: flex; align-items: center; gap: 8px; color: var(--text-dim); font-size: 11.5px; }
.ft-legend-swatch { width: 34px; height: 10px; flex: 0 0 auto; }

/* ---- minimap ---- */
.ft-minimap {
  position: absolute; right: 14px; bottom: 14px; z-index: 4;
  background: var(--panel); border: 1px solid var(--border); border-radius: 10px;
  box-shadow: var(--shadow-sm); cursor: pointer;
}
.ft-minimap.hidden { display: none; }
.ft-mm-rect { position: absolute; z-index: 5; border: 1.5px solid var(--accent); background: var(--accent-soft); border-radius: 3px; pointer-events: none; }
.ft-minimap.hidden ~ .ft-mm-rect { display: none; }

/* ---- inspector ---- */
.ft-inspector {
  position: absolute; top: 62px; right: 14px; bottom: 156px; width: 320px; max-width: calc(100vw - 28px);
  z-index: 5; display: flex; flex-direction: column;
  background: var(--panel); border: 1px solid var(--border); border-radius: 14px;
  box-shadow: var(--shadow); overflow: hidden;
  transform: translateX(16px); opacity: 0; transition: transform 0.18s ease, opacity 0.18s ease;
}
.ft-inspector[hidden] { display: none; }
.ft-inspector.show { transform: none; opacity: 1; }
.ft-ins-head { display: flex; align-items: center; gap: 11px; padding: 14px; border-bottom: 1px solid var(--border); }
.ft-ins-avatar {
  width: 46px; height: 46px; flex: 0 0 auto; border-radius: 11px; border: 2px solid var(--border-strong);
  background: var(--bg-elev-2) center/cover no-repeat; display: flex; align-items: center; justify-content: center; font-size: 21px;
}
.ft-ins-htext { flex: 1; min-width: 0; }
.ft-ins-name { font-size: 15.5px; font-weight: 700; color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ft-ins-type { display: flex; align-items: center; gap: 6px; margin-top: 3px; font-size: 12px; color: var(--text-dim); }
.ft-ins-dot { width: 9px; height: 9px; border-radius: 50%; }
.ft-ins-x { border: none; background: transparent; color: var(--text-dim); cursor: pointer; font-size: 15px; width: 26px; height: 26px; border-radius: 7px; flex: 0 0 auto; }
.ft-ins-x:hover { background: var(--bg-elev); color: var(--text); }
.ft-ins-facts { padding: 12px 14px; border-bottom: 1px solid var(--border); max-height: 40%; overflow-y: auto; }
.ft-fact { margin-bottom: 9px; }
.ft-fact:last-child { margin-bottom: 0; }
.ft-fact-k { font-size: 10.5px; text-transform: uppercase; letter-spacing: 0.04em; color: var(--text-faint); font-weight: 650; margin-bottom: 2px; }
.ft-fact-v { font-size: 12.5px; color: var(--text); line-height: 1.45; }
.ft-ins-rels { flex: 1; padding: 12px 14px; overflow-y: auto; }
.ft-ins-none { font-size: 12.5px; color: var(--text-dim); line-height: 1.5; }
.ft-rel-h { display: flex; align-items: center; gap: 7px; font-size: 12.5px; font-weight: 650; color: var(--text); margin: 12px 0 7px; }
.ft-rel-h:first-child { margin-top: 0; }
.ft-rel-dot { width: 10px; height: 10px; border-radius: 50%; flex: 0 0 auto; }
.ft-rel-n { margin-left: auto; font-size: 11px; color: var(--text-dim); background: var(--bg-elev); border-radius: 999px; padding: 1px 8px; }
.ft-rel-chips { display: flex; flex-wrap: wrap; gap: 6px; }
.ft-chip {
  display: inline-flex; align-items: center; gap: 6px; max-width: 100%;
  padding: 5px 9px; border-radius: 999px; font-size: 12px; font-family: inherit;
  background: var(--bg-elev); border: 1px solid var(--border); color: var(--text); cursor: pointer;
}
.ft-chip:hover { border-color: var(--accent); background: var(--accent-soft); }
.ft-chip-ico { flex: 0 0 auto; font-size: 12px; }
.ft-ins-actions { padding: 12px 14px; border-top: 1px solid var(--border); display: flex; flex-direction: column; gap: 6px; }

/* ---- empty state ---- */
.ft-empty {
  position: absolute; inset: 0; z-index: 2; display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: 10px; text-align: center;
  color: var(--text-dim); pointer-events: none;
}
.ft-empty[hidden] { display: none; }
.ft-empty-ico { font-size: 54px; opacity: 0.55; }
.ft-empty-title { font-size: 18px; font-weight: 650; color: var(--text); }
.ft-empty-sub { font-size: 13px; max-width: 380px; line-height: 1.55; }
.ft-empty-actions { display: flex; gap: 8px; margin-top: 8px; pointer-events: auto; }

/* ---- person editor modal ---- */
.ft-editor { display: flex; gap: 18px; align-items: flex-start; }
.ft-editor-side { flex: 0 0 auto; }
.ft-editor-main { flex: 1; min-width: 0; }
.ft-portrait-box { display: flex; flex-direction: column; align-items: center; gap: 8px; }
.ft-portrait-prev {
  width: 120px; height: 120px; border-radius: 16px; border: 2px solid var(--border-strong);
  background: var(--bg-elev-2) center/cover no-repeat;
  display: flex; align-items: center; justify-content: center; font-size: 48px; color: var(--text-faint);
}
.ft-form { display: grid; grid-template-columns: 1fr 1fr; gap: 10px 14px; }
.ft-form-row { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.ft-form-row.wide { grid-column: 1 / -1; }
.ft-form-lab { font-size: 11px; font-weight: 650; letter-spacing: 0.03em; text-transform: uppercase; color: var(--text-faint); }
.ft-area { resize: vertical; min-height: 60px; font-family: inherit; }

/* ---- relationship picker modal ---- */
.ft-pick-wrap { display: flex; flex-direction: column; gap: 14px; }
.ft-pick-head { display: flex; align-items: center; justify-content: center; gap: 12px; flex-wrap: wrap; }
.ft-pick-who {
  padding: 6px 12px; border-radius: 999px; background: var(--bg-elev);
  border: 1px solid var(--border); font-size: 13px; font-weight: 650; color: var(--text);
  max-width: 40%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.ft-pick-swap {
  width: 32px; height: 32px; border-radius: 50%; border: 1px solid var(--border);
  background: var(--bg-elev); color: var(--text-dim); cursor: pointer; font-size: 15px; font-family: inherit;
}
.ft-pick-swap:hover { border-color: var(--accent); color: var(--accent); }
.ft-pick-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(196px, 1fr)); gap: 8px; }
.ft-pick {
  display: grid; grid-template-columns: auto 1fr; grid-template-rows: auto auto; gap: 1px 10px;
  align-items: center; text-align: left; padding: 10px 12px; border-radius: 11px;
  background: var(--bg-elev); border: 1px solid var(--border); cursor: pointer; font-family: inherit;
  transition: border-color 0.13s ease, background 0.13s ease, transform 0.13s ease;
}
.ft-pick:hover { border-color: var(--ft-pick-color, var(--accent)); background: var(--bg-elev-2); transform: translateY(-1px); }
.ft-pick-ico { grid-row: 1 / span 2; font-size: 20px; }
.ft-pick-lab { font-size: 13px; font-weight: 650; color: var(--text); }
.ft-pick-sub { font-size: 11px; color: var(--text-faint); line-height: 1.3; }
.ft-pick-hint { min-height: 18px; text-align: center; font-size: 12.5px; color: var(--text-dim); }

/* ---- add-existing picker ---- */
.ft-picker { display: flex; flex-direction: column; gap: 10px; }
.ft-picklist { max-height: 46vh; overflow-y: auto; display: flex; flex-direction: column; gap: 3px; }
.ft-pickrow {
  display: flex; align-items: center; gap: 9px; padding: 8px 10px; border-radius: 9px;
  background: transparent; border: 1px solid transparent; cursor: pointer; font-family: inherit; text-align: left;
}
.ft-pickrow:hover { background: var(--bg-elev); }
.ft-pickrow.on { border-color: var(--accent); background: var(--accent-soft); }
.ft-pickrow-ico { flex: 0 0 auto; font-size: 15px; }
.ft-pickrow-name { flex: 1; min-width: 0; font-size: 13px; color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ft-pickrow-type { font-size: 11px; color: var(--text-faint); }

/* ---- layout settings ---- */
.ft-knobs { display: flex; flex-direction: column; gap: 12px; min-width: 300px; }
.ft-knob { display: flex; flex-direction: column; gap: 4px; }
.ft-knob-labs { display: flex; align-items: baseline; justify-content: space-between; font-size: 12px; color: var(--text-dim); }
.ft-knob-val { font-size: 11.5px; font-weight: 650; color: var(--text); font-variant-numeric: tabular-nums; }
.ft-knob-range { width: 100%; accent-color: var(--accent); cursor: pointer; margin: 0; }

/* ---- narrow screens ---- */
@media (max-width: 620px) {
  .ft-editor { flex-direction: column; align-items: stretch; }
  .ft-form { grid-template-columns: 1fr; }
}
@media (max-width: 900px) {
  /* Tighten the padding but KEEP the insets — a plain `padding: 8px 10px`
     here silently wiped them out, and this bar is the only thing standing
     between its controls and the Dynamic Island. */
  .ft-bar {
    gap: 8px; row-gap: 8px;
    padding: calc(8px + var(--safe-t)) calc(10px + var(--safe-r)) 8px calc(10px + var(--safe-l));
  }
  /* Deliberate rows — title + undo/redo/more/close, then zoom, then the
     actions — instead of ten items wrapping wherever they happen to run out of
     room, which left the close button stranded on a line of its own.

     Eight 44px buttons need 408px and a 393px phone has 373 to give, so the
     right-hand group has to break somewhere; breaking it around the grouped
     zoom cluster is the one split that reads as intentional. The separators go:
     they are decoration, and at this width they cost a whole button's room.
     Nothing is dropped — undo, redo and zoom all stay reachable, which matters
     more on a touch screen than on a desktop with keyboard shortcuts. */
  .ft-bar-right { order: 2; flex: 1 1 auto; justify-content: flex-end; flex-wrap: wrap; }
  .ft-zoom-group { order: 3; width: 100%; justify-content: flex-start; }
  .ft-bar-mid { order: 4; width: 100%; justify-content: flex-start; }
  .ft-sep { display: none; }
  /* Add / link / search / filter share their row: the two buttons keep their
     size, the text fields take what is left. Left to itself the search field
     claimed the whole width and pushed the filter button onto a fourth row. */
  .ft-add { flex: 1 1 auto; }
  .ft-search { flex: 1 1 110px; width: auto; min-width: 0; max-width: none; }
  .ft-legend { display: none; }
  .ft-inspector { top: auto; left: 14px; right: 14px; bottom: 14px; width: auto; max-height: 52vh; }
  .ft-minimap { display: none; }
  .ft-mm-rect { display: none; }
}
