/* Volty UI — palette carried over verbatim from the previous orchestrator's
   index.html, deliberately. The migration changes the runtime underneath, not
   the way the thing looks: the point of building our own UI instead of adopting
   OpenClaw's was to keep this. */

:root {
  /* Dark mode's palette is custom CSS, but the browser still needs to be told
     the PAGE is dark, or native controls (the composer textarea above all)
     render with light-mode defaults: near-black text on a near-black --bg.
     Text you type is there, just invisible. */
  color-scheme: light dark;

  --bg:#fbfaf6; --ink:#1a1a1a; --dim:#8a8a8a; --line:#e4e4e4; --line2:#d8d8d8;
  --run:#2563eb; --wait:#c2560c; --done:#3f7d3f; --err:#b42318; --idle:#8a8a8a;
  --hover:#0000000a;
  --surface:#fafafa;
  /* Text ON TOP of a solid --ink fill. NOT a fixed white: --ink itself flips in
     dark mode, so anything filled with --ink needs --inv to flip with it. */
  --inv:#fff;
  --btn-bg:#f4f4f4;
  --tint-err:#fdf2f2; --tint-done:#eafaea; --tint-run:#eef4ff;

  --serif:"Iowan Old Style",Georgia,"Times New Roman",serif;
  --mono:ui-monospace,SFMono-Regular,Menlo,monospace;
  --chat:system-ui,-apple-system,"Segoe UI",sans-serif;
}

@media (prefers-color-scheme: dark) {
  :root { --bg:#1c1c1a; --ink:#ececea; --dim:#9a9a94; --line:#333330; --line2:#414138;
          --hover:#ffffff14; --surface:#141412; --inv:#1c1c1a; --btn-bg:#282825;
          --tint-err:#3a1f1c; --tint-done:#1c3320; --tint-run:#1c2b45; }
}
/* data-theme is set explicitly by JS, so it must win over the media query in
   BOTH directions — hence both overrides, not just the dark one. */
:root[data-theme="dark"] { color-scheme:dark;
  --bg:#1c1c1a; --ink:#ececea; --dim:#9a9a94; --line:#333330; --line2:#414138;
  --hover:#ffffff14; --surface:#141412; --inv:#1c1c1a; --btn-bg:#282825;
  --tint-err:#3a1f1c; --tint-done:#1c3320; --tint-run:#1c2b45; }
:root[data-theme="light"] { color-scheme:light;
  --bg:#fbfaf6; --ink:#1a1a1a; --dim:#8a8a8a; --line:#e4e4e4; --line2:#d8d8d8;
  --hover:#0000000a; --surface:#fafafa; --inv:#fff; --btn-bg:#f4f4f4;
  --tint-err:#fdf2f2; --tint-done:#eafaea; --tint-run:#eef4ff; }

* { box-sizing:border-box; }

/* [hidden] must actually hide. The UA stylesheet sets display:none for it, but
   any class-based display (.body is display:flex) is MORE specific and silently
   wins — which showed the chat and the board stacked on top of each other. */
[hidden] { display:none !important; }
html, body { height:100%; }
body {
  margin:0; background:var(--bg); color:var(--ink);
  font:14px/1.5 var(--chat);
  -webkit-font-smoothing:antialiased;
}

/* ---- shell ---------------------------------------------------------------- */
.app { display:flex; flex-direction:column; height:100dvh; }

header.top {
  display:flex; align-items:center; gap:12px;
  padding:10px 14px; border-bottom:1px solid var(--line);
  font:12px var(--mono); color:var(--dim); flex:0 0 auto;
}
header.top .brand { color:var(--ink); letter-spacing:.14em; }
header.top .spacer { flex:1; }

.dot { width:7px; height:7px; border-radius:50%; background:var(--idle); display:inline-block; }
.dot.ok { background:var(--done); }
.dot.err { background:var(--err); }
.dot.run { background:var(--run); }

button {
  font:inherit; color:var(--ink); background:var(--btn-bg);
  border:1px solid var(--line2); border-radius:2px; padding:5px 10px; cursor:pointer;
}
button:hover { background:var(--hover); }
button.primary { background:var(--ink); color:var(--inv); border-color:var(--ink); }

/* ---- main split: sessions | chat | preview -------------------------------- */
.body { display:flex; flex:1; min-height:0; }

aside.sessions {
  width:230px; flex:0 0 auto; border-right:1px solid var(--line);
  overflow-y:auto; padding:8px;
}
aside.sessions .s {
  padding:7px 8px; border-radius:2px; cursor:pointer;
  display:flex; align-items:center; gap:7px; font-size:13px;
}
aside.sessions .s:hover { background:var(--hover); }
aside.sessions .s.active { background:var(--tint-run); }
aside.sessions .s .label { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }

main.chat { flex:1; min-width:0; display:flex; flex-direction:column; }
.transcript { flex:1; overflow-y:auto; padding:18px 20px; }

/* The preview pane sits LEFT of nothing and RIGHT of chat on desktop, and is
   never stacked above the chat — that stacking was the specific thing rejected
   in the previous UI. On mobile it collapses to a mini strip (see below). */
section.preview {
  width:min(46%, 620px); flex:0 0 auto;
  border-left:1px solid var(--line); display:flex; flex-direction:column;
}
section.preview .head {
  padding:8px 10px; border-bottom:1px solid var(--line);
  font:11px var(--mono); color:var(--dim);
  display:flex; align-items:center; gap:8px;
}
section.preview .content { flex:1; overflow:auto; padding:12px; }

/* ---- transcript rows ------------------------------------------------------ */
.row { margin:0 0 14px; }
.row .who { font:11px var(--mono); color:var(--dim); margin-bottom:3px; }
.row.user .who { color:var(--run); }
.row .text { white-space:pre-wrap; word-wrap:break-word; }
.row.life { color:var(--dim); font-size:13px; }
.row.err .text { color:var(--err); }

.doc-link { color:var(--run); text-decoration:underline; text-underline-offset:2px; cursor:pointer; }

pre, code { font-family:var(--mono); font-size:12.5px; }
pre { background:var(--surface); padding:10px; border-radius:2px; overflow-x:auto; }

/* ---- composer ------------------------------------------------------------- */
.composer { border-top:1px solid var(--line); padding:10px; display:flex; gap:8px; flex:0 0 auto; }
.composer textarea {
  flex:1; resize:none; font:inherit; color:var(--ink);
  background:var(--bg); border:1px solid var(--line2); border-radius:2px;
  padding:8px; min-height:38px; max-height:180px;
}
/* Stop reads as destructive because it is: it kills work in flight. Aligned to
   the bottom so it sits level with a one-line composer and does not jump around
   as the textarea grows. */
.composer #stopRun {
  align-self:flex-end; flex:0 0 auto; font:inherit; cursor:pointer;
  color:var(--err); background:var(--bg);
  border:1px solid var(--err); border-radius:2px;
  padding:8px 12px; min-height:38px;
}
.composer #stopRun:disabled { opacity:.5; cursor:default; }

/* ---- mobile --------------------------------------------------------------- */
@media (max-width: 820px) {
  aside.sessions { display:none; }
  aside.sessions.open { display:block; position:absolute; inset:41px auto 0 0; z-index:5;
                        background:var(--bg); height:calc(100dvh - 41px); }

  /* Preview never stacks above the chat on mobile. It becomes a small strip the
     chat can scroll past, and opens fullscreen on tap. */
  section.preview { display:none; }
  .previewmini {
    display:flex; align-items:center; gap:8px; cursor:pointer;
    padding:6px 10px; border-top:1px solid var(--line);
    font:11px var(--mono); color:var(--dim);
  }
  .previewmini .thumb { width:52px; height:36px; object-fit:cover; border:1px solid var(--line); }
  body.previewfull section.preview {
    display:flex; position:fixed; inset:0; z-index:20; width:100%;
    background:var(--bg); border-left:0;
  }
}
@media (min-width: 821px) { .previewmini { display:none; } }

/* ---- home: the board ------------------------------------------------------
   Carried over from the previous UI, including the details that were argued
   about there: the serif project title, the checkbox (not the drag grip) as the
   first in-flow element so it lines up with the project name, and generous
   column gaps so the board reads as columns rather than a table. */

.board { display:grid; gap:48px; align-items:start; padding:24px 26px; }
.col { display:flex; flex-direction:column; gap:34px; }

.pcardhead {
  display:flex; align-items:baseline; gap:10px;
  padding-bottom:8px; border-bottom:1px solid var(--ink); margin-bottom:4px;
}
.pcardhead .pname { font-family:var(--serif); font-size:21px; }
.pcardhead .count { margin-left:auto; font:11px var(--mono); color:var(--dim); }

.todo {
  display:flex; align-items:flex-start; gap:8px;
  padding:7px 0; border-bottom:1px solid var(--line); position:relative;
}
.todo:last-child { border-bottom:0; }
.todo .cb {
  width:14px; height:14px; min-width:14px; max-width:14px;
  min-height:14px; max-height:14px; margin:1px 0 0; padding:0;
  border:1.5px solid var(--line2); border-radius:3px; background:transparent;
  cursor:pointer; position:relative; flex:0 0 14px; align-self:flex-start;
  box-sizing:border-box;
}
/* doing = black border + a black dot. The dot only ANIMATES while the linked
   session is actually running (.working); otherwise it sits still, so the board
   does not shimmer at you for work that is merely started. */
.todo .cb.doing { border-color:var(--ink); background:transparent; }
.todo .cb.doing::after {
  content:''; position:absolute; top:50%; left:50%; width:5px; height:5px;
  border-radius:50%; background:var(--ink); transform:translate(-50%,-50%);
}

/* While the session works, the dot cycles through 6 hand-drawn-looking blobs as
   an INSTANT jump-cut every 300ms — no smooth morph. Driven by a JS class swap,
   NOT a CSS animation: Chrome interpolates clip-path polygons frame by frame
   even under steps(), so a @keyframes version produced dozens of real in-between
   shapes instead of 6 held ones. Verified empirically in the previous UI; the
   finding is carried over rather than re-discovered. */
.todo .cb.doing.working::after { width:10px; height:10px; border-radius:0; }
.scribble-0::after { clip-path:polygon(80.3% 52.7%, 77.2% 70.4%, 43.8% 91.7%, 22.9% 66.2%, 19.5% 51.5%, 17.9% 27.6%, 40.2% 17.9%, 80.7% 24.6%) !important; }
.scribble-1::after { clip-path:polygon(90.0% 42.9%, 66.8% 75.0%, 41.9% 91.8%, 29.2% 75.4%, 15.4% 39.8%, 22.6% 34.0%, 58.9% 10.1%, 85.8% 25.9%) !important; }
.scribble-2::after { clip-path:polygon(97.5% 51.1%, 80.3% 76.0%, 41.5% 90.3%, 15.7% 71.4%, 19.4% 46.0%, 21.2% 29.7%, 41.1% 17.0%, 67.8% 19.9%) !important; }
.scribble-3::after { clip-path:polygon(86.4% 53.1%, 75.7% 71.9%, 56.8% 96.4%, 18.5% 76.2%, 7.8% 58.8%, 19.1% 30.0%, 62.6% 10.4%, 81.0% 21.2%) !important; }
.scribble-4::after { clip-path:polygon(93.0% 59.4%, 75.0% 67.6%, 54.0% 84.6%, 23.3% 88.7%, 15.3% 41.6%, 26.4% 21.3%, 59.9% 13.0%, 70.5% 22.3%) !important; }
.scribble-5::after { clip-path:polygon(84.7% 51.3%, 80.9% 84.3%, 52.1% 83.9%, 15.1% 67.8%, 20.2% 57.8%, 14.6% 28.8%, 56.9% 13.0%, 68.0% 17.8%) !important; }

/* done = filled box with a real tick. The fill alone was not enough: without
   the ✓ a done row just looked like a dark square. */
.todo .cb.done { border-color:#4a4a4a; background:#4a4a4a; }
.todo .cb.done::after {
  content:'✓'; color:#fff; font-size:10px; line-height:12px;
  display:block; text-align:center;
}
.todo .txt { flex:1; cursor:pointer; }
.todo.done .txt { color:var(--dim); text-decoration:line-through; }
.todo .txt:hover { color:var(--run); }

.ghost-input {
  width:100%; border:0; background:transparent; color:var(--dim);
  font:inherit; padding:7px 0; outline:none;
}
.ghost-input:focus { color:var(--ink); }

/* Settings, and anything that is not the home page, live behind a link rather
   than cluttering it — the home page stays minimal on purpose. */
.settings { padding:24px 26px; max-width:680px; }
.settings h2 { font-family:var(--serif); font-weight:400; }
.settings a, .settings .link {
  color:var(--run); cursor:pointer; text-decoration:underline; text-underline-offset:2px;
}
.settings .row { padding:10px 0; border-bottom:1px solid var(--line); }

@media (max-width: 820px) {
  .board { grid-template-columns:1fr !important; gap:30px; padding:16px; }
}

/* Drag handle sits OUTSIDE the row, overflowing into the card's left margin, so
   the checkbox stays the first in-flow element and lines up with the project
   title. Carried over from the previous UI, where that alignment was an
   explicit request. */
.todo .grip {
  position:absolute; left:-20px; top:9px; width:14px;
  cursor:grab; color:var(--dim); opacity:0; transition:opacity .12s;
  user-select:none; font-size:12px; line-height:12px; text-align:center;
}
.todo:hover .grip { opacity:.55; }
.todo .grip:hover { opacity:1; }
.todo.dragging { opacity:.4; }
.todo.drop-above { box-shadow:inset 0 2px 0 var(--run); }
.todo.drop-below { box-shadow:inset 0 -2px 0 var(--run); }

/* ---- approvals -------------------------------------------------------------
   The human-in-the-loop gate. This is the one thing that must NEVER be subtle:
   a session waiting on a decision and showing nothing is indistinguishable from
   a session that has hung, which is exactly how the gap was found. */

.approvals { border-bottom:1px solid var(--wait); background:var(--tint-err); }
.approval {
  padding:10px 14px; display:flex; gap:10px; align-items:flex-start;
  border-bottom:1px solid var(--line);
}
.approval:last-child { border-bottom:0; }
.approval .body { flex:1; min-width:0; }
.approval .what { font:12px var(--mono); word-break:break-all; }
.approval .meta { font-size:11px; color:var(--dim); margin-top:2px; }
.approval .acts { display:flex; gap:6px; flex-shrink:0; }
.approval .acts button { padding:4px 8px; font-size:12px; }
.approval .acts .deny { color:var(--err); border-color:var(--err); }

/* A task whose session is waiting on you. Distinct from "running": running is
   patience, waiting is a request. */
.todo .cb.turn { border-color:var(--wait); background:transparent; }
.todo .cb.turn::after {
  content:'?'; color:var(--wait); font-size:10px; font-weight:600;
  line-height:12px; display:block; text-align:center;
}

/* ---- context menu + confirm ------------------------------------------------ */
.ctxmenu {
  position:fixed; z-index:40; min-width:170px;
  background:var(--bg); border:1px solid var(--line2); border-radius:2px;
  box-shadow:0 6px 24px #0003; padding:4px 0; font-size:13px;
}
.ctxmenu div { padding:6px 12px; cursor:pointer; }
.ctxmenu div:hover { background:var(--tint-run); }
.ctxmenu div.danger { color:var(--err); }
.ctxmenu .sep { height:1px; padding:0; margin:4px 0; background:var(--line); cursor:default; }

.modal {
  position:fixed; inset:0; z-index:50; display:grid; place-items:center;
  background:#0006;
}
.modal .card {
  background:var(--bg); border:1px solid var(--line2); border-radius:3px;
  padding:18px 20px; min-width:min(400px, 92vw);
}
.modal .card p { margin:0 0 14px; }
.modal .acts { display:flex; gap:8px; justify-content:flex-end; }
