/* FoloForge — light theme only. */

:root{
  --ink:#16151b;
  --ink-2:#56525f;
  --ink-3:#8d8996;
  --line:#e6e3de;
  --line-soft:#f0edE8;
  --panel:#ffffff;
  --canvas:#eceae4;
  --accent:#5a3ff5;
  --accent-hover:#4a31d8;
  --accent-soft:#eeeaff;
  --good:#127a52;
  --bad:#b3261e;
  /* Amber: attention needed, not failure. Seven rules further down already lean on this exact
     literal as a `var(--warn, #9a6700)` fallback; defining it here is what makes them resolve to
     a token, and it renders the identical colour. */
  --warn:#9a6700;
  --shadow-sm:0 1px 2px rgba(22,21,27,.06), 0 1px 1px rgba(22,21,27,.04);
  --shadow-md:0 8px 24px -8px rgba(22,21,27,.16), 0 2px 6px rgba(22,21,27,.06);
  --font:"Plus Jakarta Sans", ui-sans-serif, -apple-system, sans-serif;
  --mono:"JetBrains Mono", ui-monospace, SFMono-Regular, monospace;
}

*{box-sizing:border-box}
/* a display: rule on a component would otherwise beat the UA's [hidden] */
[hidden]{display:none !important}
html,body{height:100%;overflow:hidden}
body{
  margin:0; font-family:var(--font); color:var(--ink); background:var(--panel);
  -webkit-font-smoothing:antialiased; display:flex; flex-direction:column; overflow:hidden;
}
button,select,textarea,input{font:inherit;color:inherit}
a{color:inherit;text-decoration:none}
.ico{width:15px;height:15px;fill:none;stroke:currentColor;stroke-width:1.7;stroke-linecap:round;stroke-linejoin:round;flex:none}

/* ─────────────────────────────── topbar ─────────────────────────────── */
.topbar{
  min-height:56px; flex:none; display:flex; align-items:center; gap:12px;
  padding:8px 16px; border-bottom:1px solid var(--line); background:var(--panel); z-index:20;
  /* Wraps to a second row rather than pushing the page sideways. At 1024 the single row
     wanted 1324px, which put "New project" 300px past the edge and made the whole window
     scroll horizontally to reach the app's primary action. */
  flex-wrap:wrap; row-gap:8px;
}
.brand{display:flex;align-items:center;gap:9px;flex:none}
.brand-mark{
  width:28px;height:28px;border-radius:8px;display:grid;place-items:center;flex:none;
  background:var(--ink); color:#fff;
}
.brand-name{font-size:14.5px;font-weight:500;letter-spacing:-.015em;color:var(--ink-2);white-space:nowrap}
.brand-name b{font-weight:800;color:var(--ink)}

.kit-chip{
  display:flex;align-items:center;gap:7px;padding:5px 11px 5px 8px;border:1px solid var(--line);
  border-radius:999px;font-size:12.5px;font-weight:600;color:var(--ink-2);background:#faf9f7;
  flex:0 4 auto;min-width:74px;
}
.kit-chip>span:last-child{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}
.kit-chip-ico{display:grid;place-items:center;color:var(--accent)}
.kit-chip-ico svg{width:15px;height:15px;fill:none;stroke:currentColor;stroke-width:1.6;stroke-linecap:round;stroke-linejoin:round}

/* Which product this is. Quiet, but it carries the name and it is the rename control, so
   it has to read as pressable at rest — not only once a pointer happens to cross it. */
.project-chip{
  display:flex;align-items:center;gap:7px;min-width:112px;max-width:240px;cursor:pointer;flex:0 2 auto;
  padding:5px 11px;border:1px solid var(--line);border-radius:999px;background:var(--panel);
  font-size:12.5px;font-weight:700;color:var(--ink);transition:border-color .14s,background .14s;
}
.project-chip .ico{color:var(--ink-3)}
.project-chip span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.project-chip:hover{background:#faf9f7;border-color:#d6d2cb}
.project-chip:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
.btn-stop{color:var(--bad);border-color:#f0d4d2}
.btn-stop:hover{background:#fdf3f2;border-color:#e4b4b0;color:var(--bad)}

.topbar-actions{margin-inline-start:auto;display:flex;align-items:center;gap:6px;flex:none;position:relative}
.divider{width:1px;height:22px;background:var(--line);margin:0 4px}

.field{display:flex;align-items:center;gap:7px}
.field label{font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.06em;color:var(--ink-3)}
select,.f input,.f textarea{
  appearance:none;border:1px solid var(--line);background:var(--panel);border-radius:9px;
  padding:7px 10px;font-size:12.5px;font-weight:500;
}
.field select,#test-preset,#forge-rounds{
  padding-inline-end:26px;cursor:pointer;
  background-image:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='none' stroke='%238d8996' stroke-width='1.8' stroke-linecap='round'><path d='M4.5 6.5l3.5 3.5 3.5-3.5'/></svg>");
  background-repeat:no-repeat;background-position:right 7px center;
}
.field select:hover,#test-preset:hover,#forge-rounds:hover{border-color:#d6d2cb}
select:focus-visible,input:focus-visible,textarea:focus-visible{outline:2px solid var(--accent);outline-offset:1px}

.btn{
  display:inline-flex;align-items:center;gap:6px;border:1px solid var(--line);background:var(--panel);
  border-radius:9px;padding:7px 11px;font-size:12.5px;font-weight:600;cursor:pointer;
  color:var(--ink-2);transition:background .14s, border-color .14s, color .14s;white-space:nowrap;
}
.btn:hover{background:#faf9f7;border-color:#d6d2cb;color:var(--ink)}
.btn:active{transform:translateY(.5px)}
.btn[aria-pressed="true"]{background:var(--accent-soft);border-color:#d9cffd;color:var(--accent)}
.btn-primary{background:var(--ink);border-color:var(--ink);color:#fff}
.btn-primary:hover{background:#2a2833;border-color:#2a2833;color:#fff}
.btn-sm{padding:5px 9px;font-size:12px}
.btn:disabled{opacity:.45;cursor:not-allowed}
/* .proj-open and .recent-card are the two ways into a project and had no ring at all: the
   whole project grid could be tabbed through with nothing on screen saying where you were. */
.btn:focus-visible,.chip:focus-visible,.send:focus-visible,.kit-card:focus-visible,
.proj-open:focus-visible,.recent-card:focus-visible{outline:2px solid var(--accent);outline-offset:2px}

/* ──────────────────────────────── views ─────────────────────────────── */
.view{flex:1;min-height:0}
.view-gallery{overflow-y:auto;background:#faf9f7}
.gallery-inner{max-width:1080px;margin:0 auto;padding:54px 28px 72px}
.maker-inner{max-width:760px;margin:0 auto;padding:36px 28px 80px}
.view h1{font-size:30px;font-weight:800;letter-spacing:-.03em;margin:0 0 10px}
.lede{font-size:14px;line-height:1.65;color:var(--ink-2);margin:0 0 30px;max-width:64ch}
.lede b{font-weight:700;color:var(--ink)}
.back{display:inline-block;font-size:12.5px;font-weight:600;color:var(--ink-3);margin-bottom:18px}
.back:hover{color:var(--accent)}

/* ─────────────────────────────── gallery ────────────────────────────── */
.kit-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(248px,1fr));gap:14px}
.kit-card{
  position:relative;display:flex;flex-direction:column;gap:9px;text-align:start;cursor:pointer;
  border:1px solid var(--line);background:var(--panel);border-radius:14px;padding:18px 18px 16px;
  transition:border-color .16s, box-shadow .16s, transform .16s;
}
.kit-card:hover{border-color:#d3cec5;box-shadow:var(--shadow-md);transform:translateY(-2px)}
.kit-card-ico{
  width:38px;height:38px;border-radius:11px;display:grid;place-items:center;
  background:var(--tint,#eeeaff);color:var(--tone,#5a3ff5);
}
.kit-card-ico svg{width:20px;height:20px;fill:none;stroke:currentColor;stroke-width:1.6;stroke-linecap:round;stroke-linejoin:round}
.kit-card h2{font-size:15px;font-weight:700;letter-spacing:-.015em;margin:2px 0 0}
.kit-card p{font-size:12.5px;line-height:1.5;color:var(--ink-2);margin:0;flex:1}
.kit-meta{display:flex;flex-wrap:wrap;gap:5px;margin-top:4px}
.tag{
  font-size:10.5px;font-weight:600;letter-spacing:.02em;color:var(--ink-3);
  border:1px solid var(--line);border-radius:6px;padding:2px 6px;background:#fbfaf8;
}
.tag-own{color:var(--accent);border-color:#d9cffd;background:var(--accent-soft)}
.kit-edit{
  position:absolute;top:12px;right:12px;opacity:1;transition:opacity .14s;
  border:1px solid var(--line);background:var(--panel);border-radius:7px;padding:3px 7px;
  font-size:11px;font-weight:600;color:var(--ink-2);cursor:pointer;
}
.kit-card:hover .kit-edit,.kit-edit:focus-visible{border-color:#d6d2cb;color:var(--ink)}
.kit-edit:hover{border-color:#d6d2cb;color:var(--ink)}
.kit-new{border-style:dashed;background:transparent;align-items:flex-start;justify-content:center}
.kit-new:hover{border-color:var(--accent);background:var(--accent-soft)}

/* ───────────────────────────── kit builder ──────────────────────────── */
.draft-box{border:1px solid var(--line);background:var(--panel);border-radius:14px;padding:14px;margin-bottom:26px}
.draft-box textarea{
  width:100%;border:0;resize:none;font-size:14px;line-height:1.55;padding:2px;background:transparent;
}
.draft-box textarea:focus{outline:none}
.draft-bar{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-top:10px}
.draft-hint{font-size:11.5px;color:var(--ink-3)}

.kit-form{display:flex;flex-direction:column;gap:14px}
.row-3{display:grid;grid-template-columns:repeat(3,1fr);gap:12px}
.f{display:flex;flex-direction:column;gap:6px;border:0;padding:0;margin:0;min-width:0}
.f > span{font-size:11.5px;font-weight:700;color:var(--ink-2);letter-spacing:.005em}
.f > span em{font-style:normal;font-weight:500;color:var(--ink-3)}
.f input,.f textarea{width:100%;font-weight:400;font-size:13px;line-height:1.6}
.f textarea{resize:vertical;font-family:var(--mono);font-size:11.5px;line-height:1.7}
.f input[type="color"]{padding:3px;height:34px;cursor:pointer}
.presets > div{display:flex;flex-direction:column;gap:7px;margin-bottom:8px}
.preset-row{display:grid;grid-template-columns:1fr 72px 72px 96px 32px;gap:7px;align-items:center}
.preset-row button{
  border:1px solid var(--line);background:var(--panel);border-radius:8px;height:32px;cursor:pointer;
  color:var(--ink-3);font-size:15px;line-height:1;
}
.preset-row button:hover{border-color:#e7c9c6;color:var(--bad)}
.form-msg{margin-inline-start:auto;font-size:12px;color:var(--ink-3);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:40%}
.form-msg.bad{color:var(--bad);font-weight:600}
.form-msg.good{color:var(--good);font-weight:600}
/* A run that HALTED — discovery asked something nobody has answered — is neither good nor bad.
   Reading it as either is the misreport this whole increment is against. */
.form-msg.warn{color:#8a5a00;font-weight:600}

/* ────────────────────────────── workspace ───────────────────────────── */
.view-work,.view-maker{
  overflow:hidden; display:grid;
  grid-template-columns:372px minmax(0,1fr);
  grid-template-rows:minmax(0,1fr);   /* without this the grid item grows to its content */
}

.chat{display:flex;flex-direction:column;min-height:0;min-width:0;border-inline-end:1px solid var(--line);background:var(--panel)}
.chat-log{flex:1;overflow-y:auto;padding:20px 18px 8px;display:flex;flex-direction:column;gap:14px;scrollbar-width:thin}
.chat-log::before{content:"";margin-top:auto}

.empty h1{font-size:19px;font-weight:800;letter-spacing:-.02em;margin:4px 0 6px}
.empty p{font-size:13px;line-height:1.55;color:var(--ink-2);margin:0 0 14px}
.empty .hint{font-size:12px;color:var(--ink-3);margin-top:14px;line-height:1.6}
.empty .hint em{font-style:normal;color:var(--ink-2);background:var(--line-soft);padding:1px 5px;border-radius:5px}
.chips{display:flex;flex-direction:column;gap:6px}
.chip{
  text-align:start;border:1px solid var(--line);background:var(--panel);border-radius:10px;
  padding:9px 11px;font-size:12.5px;font-weight:500;color:var(--ink-2);cursor:pointer;line-height:1.4;
  transition:background .14s,border-color .14s,color .14s;
}
.chip:hover{background:var(--accent-soft);border-color:#d9cffd;color:var(--accent)}

.msg{display:flex;flex-direction:column;gap:5px;animation:rise .22s ease}
@keyframes rise{from{opacity:0;transform:translateY(5px)}to{opacity:1;transform:none}}
.msg-role{font-size:10.5px;font-weight:700;text-transform:uppercase;letter-spacing:.07em;color:var(--ink-3)}
.msg-body{font-size:13px;line-height:1.55;border-radius:12px;padding:10px 12px;white-space:pre-wrap;word-break:break-word}
.msg.user .msg-body{background:var(--ink);color:#fff;border-bottom-right-radius:4px;align-self:flex-end;max-width:92%}
.msg.user{align-items:flex-end}
.msg.bot .msg-body{background:#f6f5f2;border:1px solid var(--line);color:var(--ink-2);border-bottom-left-radius:4px}
.msg.bot.error .msg-body{background:#fdf0ef;border-color:#f3cfcb;color:var(--bad);font-family:var(--mono);font-size:11.5px}
.msg-note{display:flex;gap:8px;align-items:center;font-size:11px;color:var(--ink-3);padding-inline-start:2px}
.msg-note b{font-weight:600;color:var(--ink-2)}
.dot{width:3px;height:3px;border-radius:50%;background:#c9c5bd;flex:none}

.composer{flex:none;border-top:1px solid var(--line);padding:12px 14px 14px;background:var(--panel)}
.composer textarea{
  width:100%;border:1px solid var(--line);border-radius:12px;padding:11px 12px;resize:none;
  font-size:13.5px;line-height:1.5;background:var(--panel);
  /* height is set from content by fitComposer() — a kit placeholder can wrap to 2-3 lines */
  min-height:44px;max-height:180px;
}
.composer textarea::placeholder{color:var(--ink-3)}
.composer textarea:focus{outline:none;border-color:var(--accent);box-shadow:0 0 0 3px var(--accent-soft)}
.composer-bar{display:flex;align-items:center;justify-content:space-between;margin-top:9px}
.composer-hint{font-size:11px;color:var(--ink-3)}
kbd{font-family:var(--mono);font-size:10px;background:var(--line-soft);border:1px solid var(--line);border-bottom-width:2px;border-radius:5px;padding:1px 4px;color:var(--ink-2)}
.send{
  width:34px;height:34px;border-radius:10px;border:0;background:var(--accent);color:#fff;
  display:grid;place-items:center;cursor:pointer;transition:background .14s,opacity .14s;
}
.send:hover{background:var(--accent-hover)}
.send:disabled{opacity:.4;cursor:not-allowed}
.send .ico{width:17px;height:17px;stroke-width:2}

/* ──────────────────────────── kit studio ───────────────────────────── */
.stage.stage-def{background:#faf9f7}
.stage-title{font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.06em;color:var(--ink-3);flex:none}
.def-scroll{flex:1;min-height:0;overflow-y:auto;padding:20px 22px 40px}
.def-scroll .kit-form{max-width:720px}

/* tabs — Definition | Live test */
.tabs{display:flex;gap:2px;padding:2px;background:#eeece7;border-radius:9px;flex:none}
.tab{
  border:0;background:transparent;border-radius:7px;padding:5px 11px;cursor:pointer;
  font-size:12px;font-weight:600;color:var(--ink-3);transition:background .14s,color .14s;
}
.tab:hover{color:var(--ink-2)}
.tab[aria-selected="true"]{background:var(--panel);color:var(--ink);box-shadow:var(--shadow-sm)}
.tab:focus-visible{outline:2px solid var(--accent);outline-offset:1px}

/* auto-test switch */
.switch{display:flex;align-items:center;gap:6px;flex:none;cursor:pointer;user-select:none}
.switch span{font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.06em;color:var(--ink-3)}
.switch input{
  appearance:none;width:30px;height:17px;border-radius:999px;background:#d9d5cd;
  position:relative;cursor:pointer;transition:background .16s;flex:none;margin:0;
}
.switch input::after{
  content:"";position:absolute;top:2px;left:2px;width:13px;height:13px;border-radius:50%;
  background:#fff;transition:transform .16s;box-shadow:var(--shadow-sm);
}
.switch input:checked{background:var(--accent)}
.switch input:checked::after{transform:translateX(13px)}
.switch input:focus-visible{outline:2px solid var(--accent);outline-offset:2px}

/* per-field AI action */
.f-head{display:flex;align-items:center;gap:7px;font-size:11.5px;font-weight:700;color:var(--ink-2)}
.f-head label{cursor:pointer}
.f-head em{font-style:normal;font-weight:500;color:var(--ink-3)}
.f-ai{
  flex:none;border:1px solid var(--line);background:var(--panel);border-radius:6px;
  width:20px;height:20px;display:grid;place-items:center;cursor:pointer;
  font-size:11px;line-height:1;color:var(--ink-3);
  opacity:1;transition:opacity .14s,border-color .14s,color .14s,background .14s;
}
.f:hover .f-ai{border-color:#d9cffd;color:var(--accent)}
.f-ai:hover{border-color:#d9cffd;background:var(--accent-soft);color:var(--accent)}
.f-ai.on{border-color:#d9cffd;background:var(--accent-soft);color:var(--accent)}
.f-ai:disabled{opacity:1;cursor:wait;color:var(--accent)}
.f-ai.busy{animation:pulse .9s ease-in-out infinite}
@keyframes pulse{50%{opacity:.35}}

/* the inline one-line prompt a ✦ opens */
.f-prompt{display:flex;gap:6px;margin-bottom:2px}
.f-prompt input{
  flex:1;min-width:0;border:1px solid var(--accent);background:var(--panel);border-radius:9px;
  padding:7px 10px;font-size:12.5px;box-shadow:0 0 0 3px var(--accent-soft);
}
.f-prompt input:focus{outline:none}
.f-prompt button{
  flex:none;border:1px solid var(--line);background:var(--panel);border-radius:9px;
  padding:0 11px;font-size:12px;font-weight:600;color:var(--ink-2);cursor:pointer;
}
.f-prompt button:hover{border-color:#d6d2cb;color:var(--ink)}

/* ── live test pane ── */
.test-pane{flex:1;min-height:0;display:flex;flex-direction:column;background:var(--canvas)}
.test-bar{
  flex:none;display:flex;align-items:flex-end;gap:10px;padding:12px 16px;
  border-bottom:1px solid #dedbd4;background:rgba(255,255,255,.72);
}
.test-brief{flex:1;min-width:0;display:flex;flex-direction:column;gap:5px}
.test-brief span{font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.06em;color:var(--ink-3)}
.test-brief input{
  width:100%;border:1px solid var(--line);background:var(--panel);border-radius:9px;
  padding:7px 10px;font-size:12.5px;
}
.test-brief input:focus{outline:none;border-color:var(--accent);box-shadow:0 0 0 3px var(--accent-soft)}
#test-preset{flex:none;max-width:190px}
.test-bar .btn{flex:none;height:33px}
.test-canvas{flex:1;min-height:0}

/* ───────────────────────────── forge (level 3) ───────────────────────── */
.forge-pane{flex:1;min-height:0;display:flex;flex-direction:column;background:var(--canvas)}
.forge-taste{flex:1.2}
#forge-rounds{flex:none;max-width:120px}
.forge-scroll{flex:1;min-height:0;overflow-y:auto;padding:18px 20px 28px;scrollbar-width:thin}

.forge-empty{max-width:560px;margin:14px auto 0;text-align:center;color:var(--ink-2)}
.forge-empty h2{font-size:17px;font-weight:800;letter-spacing:-.02em;color:var(--ink);margin:0 0 8px}
.forge-empty p{font-size:13px;line-height:1.65;margin:0 0 10px}
.forge-empty .hint{font-size:12px;color:var(--ink-3)}

/* auto margins, not flex centering: a card wider than the pane still scrolls into
   reach instead of being clipped at the left edge. */
.forge-list{list-style:none;margin:0 auto;padding:0;display:flex;flex-direction:column;gap:12px;max-width:760px}
.forge-round{
  border:1px solid var(--line);border-radius:12px;background:var(--panel);
  padding:13px 15px 14px;box-shadow:var(--shadow-sm);
}
.forge-round.running{border-color:#d9cffd;background:var(--accent-soft)}
.forge-round.pass{border-color:#bfe0d2}

.forge-head{display:flex;align-items:center;gap:9px;flex-wrap:wrap}
.forge-n{font-size:12px;font-weight:700;color:var(--ink)}
.forge-score{
  font-size:11px;font-weight:700;padding:2px 8px;border-radius:99px;
  background:var(--canvas);color:var(--ink-2);font-variant-numeric:tabular-nums;
}
.forge-score.pass{background:#e2f2ea;color:var(--good)}
.forge-score.weak{background:#fbe9e7;color:var(--bad)}
.forge-meta{margin-inline-start:auto;font-size:11px;color:var(--ink-3);font-variant-numeric:tabular-nums}
.forge-summary{margin:8px 0 0;font-size:12.5px;line-height:1.6;color:var(--ink-2)}

.forge-findings{list-style:none;margin:9px 0 0;padding:0;display:flex;flex-direction:column;gap:5px}
.forge-findings li{
  display:flex;gap:7px;align-items:flex-start;font-size:12px;line-height:1.55;color:var(--ink-2);
}
.forge-sev{
  flex:none;margin-top:5px;width:6px;height:6px;border-radius:99px;background:var(--ink-3);
}
.forge-sev.high{background:var(--bad)}
.forge-sev.medium{background:#c98a00}
.forge-cause{
  flex:none;font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:.05em;
  padding:1px 5px;border-radius:4px;background:var(--canvas);color:var(--ink-3);margin-top:1px;
}
.forge-cause.kit{background:var(--accent-soft);color:var(--accent)}

.forge-fix{
  margin-top:10px;padding:9px 11px;border-radius:9px;background:var(--accent-soft);
  font-size:12px;line-height:1.55;color:var(--ink-2);
}
.forge-fix b{color:var(--accent);font-weight:700}
.forge-actions{display:flex;gap:7px;margin-top:11px}
.forge-actions .btn{height:28px;font-size:11.5px;padding:0 10px}

/* ─────────────────────────────── stage ──────────────────────────────── */
.stage{position:relative;min-width:0;min-height:0;display:flex;flex-direction:column;background:var(--canvas)}
.stage-bar{
  flex:none;min-height:48px;display:flex;align-items:center;gap:14px;padding:8px 16px;
  border-bottom:1px solid #dedbd4;background:rgba(255,255,255,.72);backdrop-filter:blur(8px);
  position:relative;z-index:5;
  /* Wraps at any width, not only on a phone. A fixed height with nowrap gave the stage a
     714px minimum, which with the 372px composer forced a 1086px floor on the whole page:
     at 1024 the window scrolled sideways and the zoom control sat past the right edge. */
  flex-wrap:wrap;row-gap:8px;
}
.versions{display:flex;align-items:center;gap:8px;min-width:0;overflow:hidden;flex:none;max-width:224px}
.versions-label{font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.06em;color:var(--ink-3);flex:none}
.versions-list{display:flex;gap:5px;overflow-x:auto;padding:2px;scrollbar-width:none;min-width:0}
.versions-list::-webkit-scrollbar{display:none}
.ver{
  border:1px solid var(--line);background:var(--panel);border-radius:8px;padding:5px 9px;
  font-size:11.5px;font-weight:600;color:var(--ink-2);cursor:pointer;white-space:nowrap;max-width:150px;min-width:64px;
  overflow:hidden;text-overflow:ellipsis;
}
.ver:hover{border-color:#cfcac2}
.ver.active{background:var(--ink);border-color:var(--ink);color:#fff}

.zoom{margin-inline-start:auto;display:flex;align-items:center;border:1px solid var(--line);background:var(--panel);border-radius:9px;overflow:hidden;flex:none}
.zbtn{width:28px;height:30px;border:0;background:transparent;cursor:pointer;color:var(--ink-2);font-size:15px;line-height:1}
.zbtn:hover{background:#f4f2ef}
.zval{min-width:52px;height:30px;border:0;border-inline:1px solid var(--line);background:transparent;font-size:11.5px;font-weight:600;color:var(--ink-2);cursor:pointer}
.zval:hover{background:#f4f2ef}

.stage-canvas{
  flex:1;min-height:0;position:relative;display:flex;flex-direction:column;
  /* Deliberately NOT align/justify: center.  A CENTRED flex item that outgrows an
     overflow:auto box is clipped on its top and left with no way to reach it —
     scrollTop/scrollLeft are already 0 at that edge.  Auto margins centre the
     same way when there is room and collapse to 0 when there is not, so zooming
     in past Fit stays fully scrollable. */
  align-items:flex-start; justify-content:flex-start;
  gap:14px;padding:26px 20px;overflow:auto;
  background-image:radial-gradient(circle, #d8d4cc 1px, transparent 1px);
  background-size:22px 22px;
}
.frame-wrap{flex:none;margin:auto auto 0}
.frame-label{
  flex:none;margin:0 auto auto;font-size:11.5px;font-weight:600;color:var(--ink-3);
  letter-spacing:.01em;
}

/* ── preview frames.  The padding/chrome numbers here are mirrored in app.js
      (FRAME_METRICS) so the fit-to-canvas maths matches what is drawn. ── */
/* max-content: .frame-wrap has an explicit scaled width, so a plain block .frame would
   inherit it and overflow:hidden would crop the iframe. Size to the iframe instead. */
.frame{position:relative;transform-origin:top left;width:max-content}
.frame iframe{display:block;border:0;background:#fff}

.frame[data-frame="phone"]{
  background:#0f0f12;border-radius:46px;padding:11px;
  box-shadow:0 26px 60px -22px rgba(20,18,28,.5), 0 0 0 1.5px rgba(255,255,255,.12) inset, 0 2px 5px rgba(0,0,0,.25);
}
.frame[data-frame="phone"]::after{
  content:"";position:absolute;inset:4px;border-radius:42px;pointer-events:none;
  box-shadow:0 0 0 1px rgba(255,255,255,.07) inset;
}
.frame[data-frame="phone"] iframe{border-radius:36px}
.frame[data-frame="phone"] .frame-island{display:block}
.frame[data-frame="phone"] .frame-chrome{display:none}

.frame[data-frame="browser"]{
  background:#fff;border:1px solid #d5d1c9;border-radius:13px;overflow:hidden;
  box-shadow:0 22px 50px -20px rgba(20,18,28,.32), 0 2px 6px rgba(20,18,28,.06);
}
.frame[data-frame="browser"] .frame-chrome{display:flex}
.frame[data-frame="browser"] .frame-island{display:none}

.frame[data-frame="card"]{
  background:#fff;border-radius:14px;overflow:hidden;
  box-shadow:0 22px 50px -20px rgba(20,18,28,.32), 0 2px 6px rgba(20,18,28,.06);
}
.frame[data-frame="card"] .frame-chrome,.frame[data-frame="card"] .frame-island{display:none}

.frame-chrome{
  height:38px;align-items:center;gap:7px;padding:0 14px;background:#f6f5f3;
  border-bottom:1px solid #e4e0d9;
}
.frame-chrome span[class^="dot-"]{width:10px;height:10px;border-radius:50%;flex:none}
.dot-r{background:#f05c52}.dot-y{background:#f5bc3e}.dot-g{background:#4fc14f}
.frame-url{
  margin-inline-start:10px;flex:1;height:22px;border-radius:6px;background:#fff;border:1px solid #e4e0d9;
  display:flex;align-items:center;padding:0 9px;font-size:11px;color:var(--ink-3);
  overflow:hidden;white-space:nowrap;text-overflow:ellipsis;
}
.frame-island{
  position:absolute;top:22px;left:50%;transform:translateX(-50%);width:98px;height:28px;
  background:#0f0f12;border-radius:16px;z-index:3;pointer-events:none;
}

.working{
  position:absolute;bottom:18px;left:50%;transform:translateX(-50%);
  display:flex;align-items:center;gap:9px;background:var(--ink);color:#fff;
  padding:8px 14px;border-radius:999px;font-size:12px;font-weight:600;box-shadow:var(--shadow-md);z-index:6;
}
.spinner{width:12px;height:12px;border-radius:50%;border:2px solid rgba(255,255,255,.28);border-top-color:#fff;animation:spin .7s linear infinite}
/* Shown only when past runs give a real basis for a percentage. With no history the text
   says so and no bar appears — a bar at an invented position is worse than no bar. */
.work-bar{width:74px;height:4px;border-radius:999px;background:rgba(255,255,255,.22);overflow:hidden;flex:none}
.work-bar i{display:block;height:100%;width:0;border-radius:999px;background:#fff;transition:width .45s ease}
@keyframes spin{to{transform:rotate(360deg)}}

/* ─────────────────────────────── drawer ─────────────────────────────── */
.drawer{
  position:absolute;inset:48px 0 0 auto;width:min(560px,60%);background:var(--panel);
  border-inline-start:1px solid var(--line);display:flex;flex-direction:column;z-index:8;box-shadow:-18px 0 40px -24px rgba(22,21,27,.3);
  animation:slide .2s ease;
}
@keyframes slide{from{transform:translateX(14px);opacity:0}to{transform:none;opacity:1}}
.drawer-bar{flex:none;height:44px;display:flex;align-items:center;gap:10px;padding:0 12px;border-bottom:1px solid var(--line);font-size:12.5px}
.drawer-bar strong{font-family:var(--mono);font-weight:500;font-size:12px}
.drawer-meta{font-size:11px;color:var(--ink-3);margin-inline-start:auto}
.drawer-code{flex:1;margin:0;overflow:auto;padding:14px 16px;background:#fbfaf8}
.drawer-code code{font-family:var(--mono);font-size:11.5px;line-height:1.65;white-space:pre;color:#3c3846}

/* ─────────────────────────────── toast ──────────────────────────────── */
.toast{
  position:fixed;bottom:22px;left:50%;transform:translateX(-50%);background:var(--ink);color:#fff;
  padding:9px 16px;border-radius:999px;font-size:12.5px;font-weight:600;z-index:60;box-shadow:var(--shadow-md);
  animation:rise .18s ease;
}

@media (max-width:1100px){
  .view-work{grid-template-columns:320px minmax(0,1fr)}
  .row-3{grid-template-columns:1fr}
}

/* ───────────────────────────── discovery plan ─────────────────────────── */
/* Sits inside a bot message, so it inherits the column and needs no width of its own. */
.plan{display:flex;flex-direction:column;gap:10px;margin-top:8px;padding:12px;
  border:1px solid var(--line);border-radius:12px;background:var(--panel)}
.plan-understood{margin:0;font-size:12.5px;line-height:1.5;color:var(--ink);font-weight:600}
/* The canvas the plan moved to. Named on the card so the size change is explained. */
.plan-surface{align-self:flex-start;font-size:11px;font-weight:600;padding:4px 9px;
  border-radius:999px;background:var(--accent-soft);color:var(--accent)}
.plan-label{font-size:10.5px;font-weight:700;text-transform:uppercase;letter-spacing:.06em;
  color:var(--ink-3)}
.plan-screens{display:flex;flex-wrap:wrap;gap:6px}
.plan-screen{font:inherit;font-size:11.5px;line-height:1.2;padding:6px 10px;cursor:pointer;
  border:1px solid var(--line);border-radius:999px;background:#f6f5f2;color:var(--ink-2)}
.plan-screen:hover:not(:disabled){background:var(--accent-soft);border-color:#d9cffd;color:var(--accent)}
/* The first screen is already on the canvas — shown for context, not offered again. */
.plan-screen:disabled{cursor:default;background:var(--accent-soft);border-color:#d9cffd;
  color:var(--accent);font-weight:600}
.plan-screen:disabled::before{content:"✓ ";}
.plan-q{display:flex;flex-direction:column;gap:5px;padding-top:10px;border-top:1px solid var(--line-soft)}
.plan-q-text{margin:0;font-size:12.5px;line-height:1.45;color:var(--ink);font-weight:600}
.plan-q-why{margin:0;font-size:11.5px;line-height:1.45;color:var(--ink-3)}
.plan-q-form{display:flex;gap:6px}
.plan-q-form input{flex:1;min-width:0;font:inherit;font-size:12px;padding:7px 10px;color:var(--ink);
  border:1px solid var(--line);border-radius:8px;background:#fbfaf8}
.plan-q-form input:focus{outline:2px solid var(--accent);outline-offset:-1px;border-color:transparent}

/* ────────────────────────────── projects ────────────────────────────── */
.view-projects{overflow-y:auto;background:#faf9f7}
.projects-count{color:var(--ink-3);font-weight:600}

.recent{margin:0 0 34px}
.recent h2{font-size:13px;font-weight:700;letter-spacing:-.01em;color:var(--ink-2);margin:0 0 10px}
.recent-list{display:grid;grid-template-columns:repeat(auto-fill,minmax(212px,1fr));gap:10px}
.recent-card{
  display:flex;align-items:center;gap:10px;flex-wrap:wrap;
  border:1px solid var(--line);background:var(--panel);border-radius:12px;padding:11px 13px;
  transition:border-color .16s,box-shadow .16s;
}
.recent-card:hover{border-color:#d3cec5;box-shadow:var(--shadow-sm)}
.recent-card b{font-size:13px;font-weight:700;letter-spacing:-.01em;flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.recent-card span:last-child{flex-basis:100%;font-size:11.5px;color:var(--ink-3);font-weight:600}
.recent-card.is-running span:last-child{color:var(--tone,var(--accent))}
.recent-ico{width:26px;height:26px;border-radius:8px;display:grid;place-items:center;background:#f4f2ee;color:var(--tone,var(--accent));flex:none}
.recent-ico svg{width:15px;height:15px;fill:none;stroke:currentColor;stroke-width:1.6;stroke-linecap:round;stroke-linejoin:round}
.recent-all{display:inline-block;margin-top:12px;font-size:12.5px;font-weight:600;color:var(--ink-3)}
.recent-all:hover{color:var(--accent)}

.projects-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(296px,1fr));gap:14px}
.proj{
  display:flex;flex-direction:column;border:1px solid var(--line);background:var(--panel);
  border-radius:14px;transition:border-color .16s,box-shadow .16s;
}
.proj:hover{border-color:#d3cec5;box-shadow:var(--shadow-md)}
.proj.is-running{border-color:var(--tone)}
.proj-open{display:flex;flex-direction:column;gap:7px;padding:16px 16px 12px;flex:1}
.proj-kit{display:inline-flex;align-items:center;gap:6px;font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.06em;color:var(--tone)}
.proj-kit-ico{display:grid;place-items:center}
.proj-kit-ico svg{width:14px;height:14px;fill:none;stroke:currentColor;stroke-width:1.7;stroke-linecap:round;stroke-linejoin:round}
.proj h3{font-size:15.5px;font-weight:700;letter-spacing:-.015em;margin:0;color:var(--ink);overflow-wrap:anywhere}
.proj-last{font-size:12.5px;line-height:1.5;color:var(--ink-2);margin:0;overflow-wrap:anywhere}

.proj-live{display:flex;align-items:center;gap:9px;padding:0 16px 10px}
.proj-live .bar{flex:none;width:64px;height:4px;border-radius:999px;background:var(--line);overflow:hidden}
.proj-live .bar i{display:block;height:100%;width:0;border-radius:999px;background:var(--tone);transition:width .45s ease}
.proj-live-text{font-size:11.5px;font-weight:600;color:var(--tone)}

.proj-foot{
  display:flex;align-items:center;gap:8px;flex-wrap:wrap;
  padding:10px 16px;border-top:1px solid var(--line-soft);
}
.proj-stats{font-size:11.5px;font-weight:600;color:var(--ink-3);flex:1;min-width:0}
.proj-acts{display:flex;gap:6px}

.projects-empty{
  border:1px dashed var(--line);border-radius:14px;padding:30px;text-align:center;background:var(--panel);
}
.projects-empty p{font-size:13.5px;line-height:1.6;color:var(--ink-2);margin:0 0 16px;max-width:52ch;margin-inline:auto}

/* The ring belongs inside the card: .proj-open fills the card's top, so an outward offset
   draws a rectangle that crosses the card's own rounded border. */
.proj-open:focus-visible{outline-offset:-3px;border-radius:12px}

/* Search, kit, sort, empty-draft toggle. Geometry lifted from .org-row so this reads as the
   same kind of control strip as the ones on the organization page, and the two selects inherit
   `.field select` so the product has one select and not two. It lives above #projects-grid and
   never inside it — paintProjects owns the inside of that grid on a three-second poll. */
.proj-bar{display:flex;flex-wrap:wrap;align-items:center;gap:8px;margin:0 0 16px}
/* The base control rule is `select,.f input,.f textarea`, which never reaches an input outside
   a .f: left alone, a type=search renders as the browser's own pill and reads as a widget
   borrowed from another application. */
.proj-q{
  flex:1 1 220px;min-width:0;appearance:none;
  border:1px solid var(--line);background:var(--panel);border-radius:9px;
  padding:7px 11px;font:inherit;font-size:12.5px;font-weight:500;color:var(--ink);
}
.proj-q::placeholder{color:var(--ink-3)}
.proj-q:hover{border-color:#d6d2cb}
.proj-bar .field,.proj-bar .btn{flex:none}

/* A filter matching nothing is not an empty account, so it must not borrow the dashed first-run
   box: this is one line carrying the filter's own sentence and the way back out of it. */
#projects-none{
  display:flex;flex-wrap:wrap;align-items:center;gap:10px;
  margin:0 0 16px;padding:14px 16px;
  border:1px solid var(--line);border-radius:12px;background:var(--panel);
  font-size:13px;line-height:1.6;color:var(--ink-2);
}

/* A project whose record could not be read still has every version file intact on disk, and
   the card beside this pill says so in words. Amber and quiet, never red: "error" would tell
   the owner the one thing that is not true here. */
.proj-state{
  display:inline-flex;align-items:center;gap:5px;flex:none;align-self:start;
  border:1px solid var(--line);border-radius:999px;background:#f4f2ee;
  padding:2px 8px;font-size:10.5px;font-weight:700;
  text-transform:uppercase;letter-spacing:.06em;color:var(--ink-2);
}
/* Panel behind the amber rather than a tint: at 10.5px the token needs a white ground to clear
   4.5:1, and the border carries the signal on its own — the shape .msg-retry below already
   uses for a failure you can walk back from. */
.proj-state.corrupt{
  border-color:color-mix(in srgb, var(--warn) 42%, var(--line));
  background:var(--panel);color:var(--warn);
}
/* Dashed, like the empty-state box, and deliberately not red: a deleted project is not an
   error either — it is a thing being held for you. */
.proj-state.trashed{border-style:dashed;background:var(--panel);color:var(--ink-2)}

/* Recently deleted is an .org-card, not another grid of cards: these are not things you open,
   they are things you put back. */
.proj-trash{margin-block-start:26px}
.proj-trash h2{display:flex;align-items:center;gap:7px;flex-wrap:wrap}
.proj-trash-list{display:flex;flex-direction:column;gap:8px;margin-block-start:12px}
/* The name takes a line of its own. A model-written title can be a whole sentence, and making
   it share one row with two buttons is what turns a long name into a one-word-per-line column
   the moment the window narrows. */
.proj-trash-row{
  display:flex;flex-wrap:wrap;align-items:center;gap:6px 10px;
  border:1px solid var(--line);border-radius:10px;background:var(--panel);padding:10px 12px;
}
.proj-trash-row > div:not(:is(.proj-acts,.proj-trash-acts)){
  flex:1 1 240px;min-width:0;display:flex;flex-wrap:wrap;align-items:center;gap:4px 10px;
}
.proj-trash-row b{
  flex:1 1 100%;min-width:0;overflow-wrap:anywhere;
  font-size:13px;font-weight:700;letter-spacing:-.01em;color:var(--ink);
}
/* --ink-2, not the --ink-3 used for a card's stats line: this is the panel's own body register
   (see .org-note), and when it was deleted is the fact you read to decide whether to restore. */
.proj-trash-row span,.proj-trash-row time{
  min-width:0;overflow-wrap:anywhere;font-size:11.5px;font-weight:600;color:var(--ink-2);
}
/* Actions end the row and drop to a line of their own before they start squeezing the date —
   whether app.js groups them or emits them as siblings. */
.proj-trash-row :is(.proj-acts,.proj-trash-acts){display:flex;flex-wrap:wrap;gap:6px;margin-inline-start:auto}
.proj-trash-row > .btn:first-of-type{margin-inline-start:auto}

/* A failed turn carries its own way out. Quiet, but present at rest — a control you only
   discover by hovering is not a control when the thing you are recovering from is an error. */
.msg-retry{
  display:inline-flex;align-items:center;gap:6px;margin-top:9px;cursor:pointer;
  border:1px solid #e4c9c6;background:var(--panel);border-radius:8px;padding:5px 10px;
  font-size:12px;font-weight:600;color:var(--bad);transition:background .14s,border-color .14s;
}
.msg-retry:hover{background:#fdf3f2;border-color:#d8a9a4}
.msg-retry:disabled{opacity:.5;cursor:not-allowed}
.msg-retry:focus-visible{outline:2px solid var(--accent);outline-offset:2px}

/* Project and artifact names come from the model and are as often Arabic as English. Without
   this, an RTL name in an LTR container truncates from the wrong end ("…ات للمحاماة") and the
   leading number on a version chip jumps to the wrong side. `plaintext` makes the browser read
   the direction from the text's own first strong character, per element, which is exactly the
   "I do not know which language this is" case. */
.project-chip span,
.proj h3,
.proj-last,
.recent-card b,
.proj-trash-row b,
.proj-state,
/* The query too: it is typed by the person searching, and an Arabic project name searched for
   in an LTR field puts the cursor and the first character on the wrong side. */
.proj-q,
.ver{unicode-bidi:plaintext}

/* ── Identity: which organization this work is going into ────────────────────── */
/* Sits next to the brand rather than off in a corner: with two organizations open in
   two tabs, "which one am I editing?" is a question with an expensive wrong answer. */
.identity {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  /* No margin of its own: the bar's own `gap` already separates it from the wordmark, and
     the extra 10px was double-spacing that also pushed the 1440px bar onto a second row by
     just over 5px. */
  padding-inline: 10px;
  padding-block: 5px;
  border: 1px solid var(--line, #e6e4ea);
  border-radius: 999px;
  font-size: 12.5px;
  white-space: nowrap;
  flex: 0 1 auto;
  /* min-content, not 0: at 1180px a `min-width:0` let the chip shrink under its own children
     and "Sign out" ended up clipped behind the kit chip. The organization name inside has
     `min-width:0`, so the shrink lands on the name -- which ellipsizes -- and never on the
     two controls. */
  min-width: auto;
  /* NOT overflow:hidden -- a scroll container's automatic minimum size is zero, which is
     what defeated the min-width above on the first attempt. Each child clips its own text. */
  overflow: visible;
}
/* Enough of the organization name to tell two of them apart, before the ellipsis starts. */
.identity-org { font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; max-width: 210px; }
.identity-role {
  padding-inline: 7px;
  padding-block: 1px;
  border-radius: 999px;
  background: var(--ground, #f2f1f5);
  color: var(--muted, #6b6875);
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: .04em;
}
.identity-out {
  border: 0;
  background: none;
  padding: 0;
  font: inherit;
  font-size: 12px;
  color: var(--muted, #6b6875);
  cursor: pointer;
  text-decoration: underline;
  padding-block: 5px;
  padding-inline: 2px;
  flex: none;
}
.identity-out:hover { color: var(--ink, #16151b); }


/* ───────────────────────── organization ─────────────────────────────── */
/* Logical properties only: Arabic and RTL are a planned phase, and a physical
   margin written today is a bug filed against that phase. */
.view-org{overflow-y:auto}
.org-card{
  border:1px solid var(--line); border-radius:10px; background:var(--panel);
  padding:16px 18px; margin-block-end:14px; box-shadow:var(--shadow-sm);
}
.org-card h2{font-size:13px;font-weight:650;letter-spacing:.01em;margin:0 0 4px}
.org-card h3{font-size:12px;font-weight:650;margin:14px 0 4px}
.org-note{font-size:12px;line-height:1.65;color:var(--ink-2);margin:6px 0 0;max-width:74ch}
.org-row{display:flex;flex-wrap:wrap;align-items:center;gap:8px;margin-block-start:12px}
.org-row label{font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.06em;color:var(--ink-3)}
.org-row input,.org-row select{
  padding:6px 9px;border:1px solid var(--line);border-radius:7px;background:var(--panel);
  font-size:12.5px;color:var(--ink);min-width:0;
}
.org-row input[type="number"]{width:104px}
.org-row input[type="email"],.org-row input[type="text"],.org-row input[type="password"]{min-width:172px;flex:1 1 172px}
.org-row input:focus-visible,.org-row select:focus-visible{outline:2px solid var(--accent);outline-offset:1px}

.org-table{width:100%;border-collapse:collapse;margin-block-start:10px;font-size:12.5px}
.org-table th{
  text-align:start; font-size:10.5px; font-weight:650; text-transform:uppercase;
  letter-spacing:.06em; color:var(--ink-3); padding:0 10px 6px 0; border-block-end:1px solid var(--line);
}
.org-table td{padding:8px 10px 8px 0;border-block-end:1px solid var(--line-soft);vertical-align:middle}
.org-table td:last-child,.org-table th:last-child{padding-inline-end:0;text-align:end}
.org-table select{padding:4px 7px;border:1px solid var(--line);border-radius:6px;font-size:12px;background:var(--panel)}
.org-you{font-size:10.5px;font-weight:650;color:var(--accent);background:var(--accent-soft);border-radius:4px;padding:1px 5px;margin-inline-start:6px}
.org-audit td{font-size:11.5px;color:var(--ink-2)}
.org-audit td:first-child{font-family:var(--mono);font-size:10.5px;white-space:nowrap;color:var(--ink-3)}
.org-empty{font-size:12px;color:var(--ink-3);padding:10px 0}
/* The absolute path an owner pastes into a terminal when they are recovering a project by hand.
   Mono and selectable, and allowed to break anywhere: a relocated data root nested six
   directories deep must never be the reason this page scrolls sideways. */
.org-path{
  flex:1 1 260px;min-width:0;max-width:74ch;margin:0;
  font-family:var(--mono);font-size:11.5px;line-height:1.6;color:var(--ink-2);
  word-break:break-all;
  background:#fbfaf8;border:1px solid var(--line-soft);border-radius:8px;padding:7px 9px;
}

.org-danger{border-color:#f0d4d2;background:#fdfaf9}
.org-danger h2{color:var(--bad)}
.org-banner{
  border:1px solid #f0d4d2; background:#fdf3f2; color:var(--bad);
  border-radius:9px; padding:11px 14px; font-size:12.5px; line-height:1.6; margin-block-end:14px;
}
.org-banner.ok{border-color:#c9e6d9;background:#f2faf6;color:var(--good)}
.identity-link{
  font-size:11px;font-weight:650;color:var(--ink-3);border:1px solid var(--line);
  border-radius:6px;padding:5px 7px;margin-inline-start:7px;background:var(--panel);cursor:pointer;flex:none;
}
.identity-link:hover{color:var(--accent);border-color:#d9cffd;background:var(--accent-soft)}

/* ───────────────────────── addressable sections ──────────────────────────
   A generated document's regions, and which one the next message will change.
   Logical properties only, so the Arabic pass is a `dir` attribute. */

.sections{display:flex; align-items:center; gap:8px; overflow:hidden;
  /* Basis 0, not auto. With the bar allowed to wrap, an `auto` basis means the browser
     sizes this group at its full content width (~1000px of chips) and wraps it onto a
     line of its own BEFORE any shrinking happens -- flex-shrink only acts within a line.
     From zero it stays on the first line and grows into whatever the other groups leave.

     The minimum is the label plus one chip. Without it the group collapsed to nothing on a
     narrow bar and clipped its own label to "SECTIC" with no chips beside it -- an
     affordance that cannot be used, which is worse than not offering it. Clamped like this
     it takes a second row instead. */
  flex:1 1 0; min-width:170px}
.sections-list{display:flex; gap:6px; overflow-x:auto; scrollbar-width:thin; padding-block:2px; min-width:0}
.sections-list .sec{
  flex:none; font-weight:500; font-size:12px; line-height:1; color:var(--ink-3);
  border:1px solid var(--line); background:var(--panel);
  padding:6px 10px; border-radius:999px; cursor:pointer; white-space:nowrap;
}
.sections-list .sec:hover{color:var(--ink); border-color:var(--ink)}
.sections-list .sec[aria-pressed="true"]{color:var(--ink); border-color:var(--ink); background:var(--canvas); font-weight:600}
.sections-list .sec .sec-kind{
  font-size:10px; text-transform:uppercase; letter-spacing:.04em; opacity:.6; margin-inline-start:6px;
}
.sections-list .sec[data-depth="1"]{margin-inline-start:10px}
.sections-list .sec[data-depth="1"]::before{content:"·"; margin-inline-end:6px; opacity:.5}

/* ─────────────────────── what the checks found ────────────────────────────
   A disclosure, not a dialog: it opens under its own pill, closes on Escape or an
   outside click, and takes no focus away from the composer. It DOES overlay the canvas
   below it — a dropdown in a toolbar cannot do otherwise — so what matters is that all
   of it is readable and one keystroke dismisses it.

   Anchored to the inline END of the strip, because the strip sits at the far right of a
   1440px toolbar: anchored to the start, the panel's right edge landed 44px past the
   viewport and the evidence text was unreadable. Severity is carried by the pill's
   colour and by a rule on the inline start of each row, so it survives being read in
   Arabic. */

.checks{display:flex; align-items:center; gap:8px; position:relative; flex:none}
.check-pill{
  font-weight:600; font-size:12px; line-height:1; padding:6px 11px; border-radius:999px; cursor:pointer;
  border:1px solid var(--line); background:var(--panel); color:var(--ink-3); white-space:nowrap;
}
.check-pill:hover{border-color:var(--ink)}
.check-pill[data-state="good"]{color:var(--good); border-color:#c9e6d9; background:#f2faf6}
.check-pill[data-state="warn"]{color:#8a5a00; border-color:#ecdcb8; background:#fdf8ee}
.check-pill[data-state="bad"]{color:var(--bad); border-color:#eccbc8; background:#fdf4f3}
.check-panel{
  position:absolute; inset-block-start:calc(100% + 8px); inset-inline-end:0; inset-inline-start:auto; z-index:40;
  width:min(420px, calc(100vw - 32px)); max-height:min(52vh, 460px); overflow-y:auto;
  background:var(--panel); border:1px solid var(--line); border-radius:12px;
  box-shadow:var(--shadow-md); padding:12px 14px;
}
/* Sticky so the two actions cannot be scrolled away from — the panel is a scroller and the
   findings under it are unbounded. The negative margins + padding let the background span the
   panel's own padding, or the rows show through beside it as they scroll past. */
.check-head{
  display:flex; align-items:center; justify-content:space-between; gap:10px;
  position:sticky; inset-block-start:0; z-index:1;
  margin:-12px -14px 10px; padding:12px 14px 9px;
  background:var(--panel); border-block-end:1px solid var(--line-soft);
}
.check-head strong{font-size:12.5px}
.check-acts{display:flex; align-items:center; gap:6px; flex-wrap:wrap; justify-content:flex-end}
.check-section{margin-block-start:12px; padding-block-start:11px; border-block-start:1px solid var(--line)}
.check-sub{display:block; font-size:12.5px; margin-block-end:8px}
.check-again{
  font-weight:600; font-size:11.5px; line-height:1; color:var(--ink-3); background:none; border:1px solid var(--line);
  border-radius:999px; padding:5px 10px; cursor:pointer;
}
.check-again:hover:not(:disabled){color:var(--ink); border-color:var(--ink)}
.check-again:disabled{opacity:.6; cursor:default}
.check-row{
  padding:9px 0 9px 11px; border-inline-start:3px solid var(--line);
  border-block-start:1px solid var(--line-soft);
}
.check-row:first-child{border-block-start:0}
.check-row[data-sev="high"]{border-inline-start-color:var(--bad)}
.check-row[data-sev="medium"]{border-inline-start-color:#d9a63a}
.check-row[data-sev="low"]{border-inline-start-color:var(--line)}
.check-row.clickable{cursor:pointer}
.check-row.clickable:hover{background:var(--canvas)}
.check-what{margin:0; font-size:13px; font-weight:600; color:var(--ink)}
.check-meta{margin:3px 0 0; font-size:11px; color:var(--ink-3); font-variant-numeric:tabular-nums}
.check-why{margin:5px 0 0; font-size:11.5px; line-height:1.5; color:var(--ink-2)}
.check-none{margin:0; font-size:12.5px; color:var(--ink-3)}
.check-foot{
  margin:10px 0 0; padding-block-start:9px; border-block-start:1px solid var(--line-soft);
  font-size:11px; line-height:1.5; color:var(--ink-3);
}
/* The strip's position in the toolbar flips with the layout, so the panel's anchor has to flip
   with it. Measured at both ends: end-anchored at 390px ran from -200px to 158px, and
   start-anchored at 1440px ran to 1521px — each leaving two thirds of every finding unreachable
   at the other width. So: anchored to the END where the strip sits right (wide), to the START
   where it sits left (narrow), and `position:absolute` in both cases.
   NOT `position:fixed` — an ancestor of the stage carries a transform for the zoom, which makes
   it the containing block for a fixed element, so a viewport-pinned sheet resolved against the
   wrong box and sat 3px above the top of the screen. */
@media (max-width:860px){
  .check-panel{ inset-inline-start:0; inset-inline-end:auto }
}
/* Opened upwards when there is more room above the pill than below it. The height itself is
   measured in placeCheckPanel() — see the note there for why no vh rule can do this job. */
.check-panel.above{ inset-block-start:auto; inset-block-end:calc(100% + 8px) }
/* ── what a real browser saw ────────────────────────────────────────────────
   A fourth pill state, "part", and it is the one that matters most: checks ran, nothing failed,
   and the behaviour was NOT verified. Green there would be the exact lie this whole subsystem
   exists to stop — an unverified interaction is not a passing one. */
.check-pill[data-state="part"]{color:var(--ink-2); border-color:#d8d4ca; background:#faf9f6}
.behave{margin-block-start:0}
.behave-state{margin:0 0 8px; font-size:11.5px; line-height:1.5; color:var(--ink-3)}
.behave-row{
  padding:9px 0 9px 11px; border-inline-start:3px solid var(--line);
  border-block-start:1px solid var(--line-soft);
}
.behave-row:first-child{border-block-start:0}
.behave-row[data-status="fail"]{border-inline-start-color:var(--bad)}
.behave-row[data-status="pass"]{border-inline-start-color:var(--good)}
.behave-row[data-status="unverified"]{border-inline-start-color:#d9a63a}
.behave-row.clickable{cursor:pointer}
.behave-row.clickable:hover{background:var(--canvas)}
.behave-what{margin:0; display:flex; align-items:baseline; gap:7px; font-size:13px; font-weight:600; color:var(--ink)}
.behave-chip{
  flex:none; font-weight:700; font-size:9.5px; line-height:1; letter-spacing:.07em; text-transform:uppercase;
  padding:3px 6px; border-radius:4px; border:1px solid var(--line); color:var(--ink-3); background:var(--panel);
}
.behave-row[data-status="fail"] .behave-chip{color:var(--bad); border-color:#eccbc8; background:#fdf4f3}
.behave-row[data-status="pass"] .behave-chip{color:var(--good); border-color:#c9e6d9; background:#f2faf6}
.behave-row[data-status="unverified"] .behave-chip{color:#8a5a00; border-color:#ecdcb8; background:#fdf8ee}
.behave-why{margin:5px 0 0; font-size:11.5px; line-height:1.5; color:var(--ink-2)}
.behave-said{margin:4px 0 0; font-size:11px; line-height:1.5; color:var(--ink-3)}
.behave-act{margin-block-start:7px; display:flex; gap:7px; flex-wrap:wrap}
.behave-fix{
  font-weight:600; font-size:11.5px; line-height:1; color:var(--bad); background:var(--panel);
  border:1px solid #eccbc8; border-radius:999px; padding:5px 10px; cursor:pointer;
}
.behave-fix:hover:not(:disabled){background:#fdf4f3; border-color:var(--bad)}
.behave-fix:disabled{opacity:.6; cursor:default}

.changed.findings{display:flex; align-items:center; gap:8px; flex-wrap:wrap}
.findings-open{
  font-weight:600; font-size:11.5px; line-height:1; color:var(--ink); background:none; border:1px solid var(--line);
  border-radius:999px; padding:4px 9px; cursor:pointer;
}
.findings-open:hover{border-color:var(--ink); background:var(--canvas)}

.target{
  display:flex; align-items:center; gap:8px; margin-block-end:8px;
  font-size:12px; color:var(--ink);
  border:1px solid var(--ink); border-radius:999px; padding:5px 6px 5px 12px;
  background:var(--canvas); align-self:flex-start; max-width:100%;
}
.target-what{color:var(--ink-3)}
.target strong{overflow:hidden; text-overflow:ellipsis; white-space:nowrap}
.target-size{color:var(--ink-3); font-variant-numeric:tabular-nums}
.target button{
  flex:none; width:20px; height:20px; border-radius:50%; border:0; cursor:pointer;
  background:var(--ink); color:#fff; font-size:13px; line-height:1;
}

/* What an edit actually did, stated rather than implied. */
.changed{margin-block-start:6px; font-size:12px; color:var(--ink-3)}
.changed b{color:var(--ink); font-weight:600}

/* The kit's one behavioural flag, as a real control rather than a hand-edited JSON field. */
.field-check{margin-block-start:4px}
.field-check label{display:flex; gap:10px; align-items:flex-start; cursor:pointer}
.field-check input{margin-block-start:3px; flex:none; accent-color:var(--accent)}
.field-check strong{display:block; font-size:13px; font-weight:600}
.field-check em{display:block; margin-block-start:3px; font-size:12px; font-style:normal; color:var(--ink-3); line-height:1.5}

/* ═════════════════════════ published generators ═══════════════════════════
   Logical properties only, and every width is a max rather than a fixed size, so the
   whole surface collapses to one column on a phone without a second set of rules. */

.tool-grid{display:grid;gap:12px;grid-template-columns:repeat(auto-fill,minmax(min(100%,300px),1fr));margin-block-end:20px}
.tool-card{
  display:flex;flex-direction:column;gap:6px;text-align:start;
  border:1px solid var(--line);background:var(--panel);border-radius:12px;padding:14px 16px;
  box-shadow:var(--shadow-sm);text-decoration:none;color:inherit;
}
.tool-card:hover{border-color:var(--ink);box-shadow:var(--shadow-md)}
.tool-card strong{font-size:14px;font-weight:650}
.tool-card .tool-addr{font-family:var(--mono);font-size:11.5px;color:var(--ink-3);word-break:break-all}
.tool-card .tool-meta{display:flex;flex-wrap:wrap;gap:6px;align-items:center;font-size:11.5px;color:var(--ink-2)}
.tool-state{font-size:10.5px;font-weight:650;border-radius:4px;padding:1px 6px;white-space:nowrap}
.tool-state.live{color:#0b5c3c;background:#e6f4ec}
.tool-state.off{color:#8a5a00;background:#fdf3e2}
.tool-state.draft{color:var(--ink-3);background:var(--canvas)}

/* `.view-tool` SCROLLS, like .view-gallery / .view-projects / .view-org.
 *
 * This rule was here once, was measured, and was removed as "a fix for nothing". The measurement
 * was wrong, and the way it was wrong is worth keeping: it checked whether **#tool-save** was
 * reachable, and #tool-save lives inside `.tool-actions`, which is `position:sticky;bottom:0`. A
 * sticky element is reachable whether or not anything scrolls. So the probe returned "reachable:
 * true in both" and could not have returned anything else.
 *
 * What it missed, measured in Chrome at 1440x720 with the rule absent: `#view-tool` is 664px tall
 * with a scrollHeight of **6171px**, every ancestor up to `body` is `overflow:visible`, and `body`
 * is `overflow:hidden`. `window.scrollBy(0, 2000)` left scrollY at 0 and setting `scrollTop` did
 * nothing, because a `visible` box is not a scroll container. The product card sat at y=3713 and
 * the report card at y=4334 — **both permanently off screen**. An owner could not reach their own
 * product's access policy, plan, invite codes, deployment hostname, logo, knowledge grants or usage
 * report. Only the top ~664px and the sticky bar were ever usable.
 *
 * Both sticky children depend on this too: `position:sticky` needs a scrolling ancestor, and
 * without one they pin to the viewport instead of to the panel they belong to.
 *
 * The guard: `scripts/ui-audit.mjs` now measures the DEEPEST element of a view rather than a
 * convenient one, for exactly this reason. */
.view-tool{overflow-y:auto}
.tool-layout{
  display:grid;gap:20px;align-items:start;
  grid-template-columns:minmax(0,1fr) minmax(0,420px);
  max-width:1280px;margin-inline:auto;padding:24px 20px 56px;
}
.tool-form{min-width:0;display:flex;flex-direction:column;gap:14px}
.tool-form h1{font-size:22px;margin:0;letter-spacing:-.01em}
.tool-back{font-size:12px;color:var(--ink-3);text-decoration:none;width:fit-content}
.tool-back:hover{color:var(--ink)}
.tool-field{display:block;margin-block-start:12px}
.tool-field>span{
  display:block;font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.06em;
  color:var(--ink-3);margin-block-end:5px;text-transform:none;letter-spacing:0;font-size:12px;
}
.tool-field>span em{font-style:normal;color:var(--ink-3);font-weight:400}
.tool-field textarea,.tool-field input{
  width:100%;font:inherit;font-size:13px;color:inherit;background:var(--panel);
  border:1px solid var(--line);border-radius:8px;padding:8px 10px;resize:vertical;
}
.tool-field textarea:focus-visible,.tool-field input:focus-visible{outline:2px solid var(--accent);outline-offset:1px}
.tool-slug-prefix{font-family:var(--mono);font-size:12px;color:var(--ink-3)}

.tool-modes{display:flex;flex-direction:column;gap:8px;margin-block-start:12px}
.tool-mode{display:flex;gap:9px;align-items:flex-start;cursor:pointer;border:1px solid var(--line);border-radius:9px;padding:10px 12px}
.tool-mode:hover{border-color:var(--ink-3)}
.tool-mode:has(input:checked){border-color:var(--ink);background:var(--canvas)}
.tool-mode input{margin-block-start:2px;flex:none;accent-color:var(--accent)}
.tool-mode strong{display:block;font-size:13px;font-weight:650}
.tool-mode em{display:block;margin-block-start:2px;font-size:12px;font-style:normal;color:var(--ink-2);line-height:1.55}

.tool-actions{display:flex;flex-wrap:wrap;gap:8px;align-items:center;position:sticky;bottom:0;
  background:linear-gradient(to top,var(--canvas) 62%,transparent);padding-block:12px 4px;z-index:2}
/* THE FUNNEL. A bar per stage, drawn as a proportion of opens rather than of the step above —
   "62% of the previous step" hides which step is actually losing people. An optional stage is
   marked, because a product whose document is optional should not read as losing everyone who
   did not attach one. */
.funnel{list-style:none;margin:10px 0 0;padding:0;display:flex;flex-direction:column;gap:7px}
.funnel li{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:4px 12px;align-items:baseline}
.funnel .f-label{font-size:12.5px;color:var(--ink);min-width:0}
.funnel .f-label em{font-style:normal;color:var(--ink-3);font-weight:400}
.funnel .f-n{font-size:12.5px;font-variant-numeric:tabular-nums;color:var(--ink-2);white-space:nowrap}
.funnel .f-bar{grid-column:1/-1;height:5px;border-radius:999px;background:var(--line);overflow:hidden}
.funnel .f-bar i{display:block;height:100%;background:var(--accent);border-radius:999px}
.funnel li.optional .f-bar i{background:var(--ink-3)}

.tool-msg{font-size:12px;color:var(--ink-2)}
.tool-msg.good{color:var(--good);font-weight:600}
.tool-msg.bad{color:var(--bad);font-weight:600}
.tool-warn-list{margin:8px 0 0;padding-inline-start:18px;font-size:12.5px;line-height:1.6;color:var(--ink-2)}
.tool-warn-list li{margin-block-end:8px}
.tool-warn-list b{color:var(--ink)}

.tool-preview{
  position:sticky;top:16px;display:flex;flex-direction:column;gap:8px;
  border:1px solid var(--line);background:var(--panel);border-radius:12px;padding:12px;min-width:0;
}
.tool-preview-bar{display:flex;gap:8px;align-items:center;font-size:13px}
.tool-preview-bar strong{margin-inline-end:auto}
#tool-frame{width:100%;height:min(72vh,720px);border:1px solid var(--line);border-radius:8px;background:#fff}

@media (max-width:900px){
  .tool-layout{grid-template-columns:minmax(0,1fr);padding-inline:16px}
  .tool-preview{position:static;order:-1}
  #tool-frame{height:min(56vh,520px)}
  .tool-actions{position:static;background:none}
}

/* ═══════════════════════════════ narrow screens ════════════════════════════
   The studio was built for a desktop and only ever narrowed its columns, so below about
   800px the two-pane layouts squeezed the preview to nothing and — worse — the top bar did
   not wrap, so its buttons ran off the right edge. At 390px that put Save kit, Publish for
   customers and the model selector past the viewport with no way to scroll to them: the
   product was not merely cramped on a phone, its primary actions were unreachable.

   One breakpoint, logical properties only, and nothing hidden that cannot be reached
   another way. */

/* Wide: the group is just part of the bar and the toggle does not exist. */
.more-wrap{display:contents}
.more-group{display:contents}
.btn-more{display:none}

/* Unfolded, the bar needs ~1790px: a 732px left cluster and a 1027px action row. Above that
   everything is on show; below it the four artifact actions fold into a menu anchored to
   their own button. The breakpoint is measured, not guessed -- at 1520 the bar was still
   wrapping to two rows at 1600 and 1440, and the row it wrapped to cost 38px of canvas. */
@media (min-width:861px) and (max-width:1790px){
  .more-wrap{display:inline-flex; position:relative; flex:none}
  .more-group{display:none}
  .more-group.open{
    display:flex; flex-direction:column; align-items:stretch; gap:4px;
    position:absolute; inset-block-start:calc(100% + 8px); inset-inline-end:0; z-index:60;
    min-width:198px; padding:7px; background:var(--panel);
    border:1px solid var(--line); border-radius:12px; box-shadow:var(--shadow-md);
  }
  .more-group.open .btn{width:100%; justify-content:flex-start}
  .btn-more{display:inline-flex}
  .btn-more[aria-expanded="true"]{background:var(--canvas); border-color:var(--ink-3)}
  /* The select already reads "Sonnet 5 — balanced". The word above it does not earn 48px. */
  .topbar-actions .field label{display:none}
  /* At 1440 the folded bar still wanted 1493px of 1440. These two give back 58px, which is
     what a single row costs there; the project name is also on the version chip beside it
     and in full on the Projects screen. When a longer name does push past the edge the bar
     wraps to two rows, which is a layout, not a break. */
  .topbar{gap:10px}
  .project-chip{max-width:190px}
}

@media (max-width: 860px) {
  /* Six rows of buttons on a phone is not a toolbar, it is a wall. The artifact actions fold
     behind one control; nothing is removed and nothing becomes unreachable. */
  .more-wrap{display:contents}
  .more-group{display:none}
  .more-group.open{display:flex; flex-wrap:wrap; gap:7px; width:100%; position:static; padding:0; border:0; box-shadow:none; background:none}
  .btn-more{display:inline-flex}
  .btn-more[aria-expanded="true"]{background:var(--canvas); border-color:var(--ink-3)}

  /* The bar becomes as many rows as it needs. */
  .topbar{
    height:auto; flex-wrap:wrap; row-gap:8px; padding-block:9px;
    position:sticky; inset-block-start:0;
  }
  .topbar-actions{margin-inline-start:0; width:100%; flex-wrap:wrap; gap:7px}
  /* The mark keeps the identity; the wordmark is the first thing that can go. */
  .brand-name{display:none}
  .divider{display:none}
  .kit-chip,.project-chip{max-width:100%}

  /* Two panes become two rows. The preview goes first and the composer ends up at the foot
     of the page, which is where a thumb is. */
  .view-work,.view-maker{
    grid-template-columns:minmax(0,1fr);
    grid-template-rows:auto minmax(0,1fr);
    overflow-y:auto;
  }
  .chat{border-inline-end:0; border-block-start:1px solid var(--line); order:2; min-height:46vh}
  .chat-log{max-height:52vh}
  .stage,.maker-panel{order:1}
  .composer{position:sticky; inset-block-end:0}

  /* Both toolbars wrap rather than clip what is past the edge. */
  .stage-bar{height:auto; flex-wrap:wrap; row-gap:8px; padding-block:9px}
  .zoom{margin-inline-start:0}
  .versions,.sections{max-width:100%}

  /* The code drawer covers the stage instead of taking a slice of it. */
  .drawer{inset-inline:0; width:auto}

  /* A table cannot reflow, so it gets its own scroller instead of pushing the page wide. */
  .org-table{display:block; overflow-x:auto; white-space:nowrap}
  .org-card{padding-inline:14px}
  .gallery-inner{padding-inline:16px}

  /* Four columns of numbers do not fit a phone; two do. */
  .preset-row{grid-template-columns:1fr 1fr; gap:6px}
  .preset-row .p-label{grid-column:1 / -1}
  .preset-row button{grid-column:1 / -1; justify-self:end}
}

@media (max-width: 520px) {
  .tool-actions{gap:6px}
  .tool-actions .btn{flex:1 1 auto; justify-content:center}
  .org-row input,.org-row select{flex:1 1 100%; min-width:0}
  .org-row label{flex:0 0 100%}
  #tool-frame{height:min(64vh,560px)}
  .chat-log{padding-inline:14px}
  .composer{padding-inline:12px}

  /* The toolbar becomes a stack. A search field, two selects and a toggle cannot share a row at
     this width without the selects shrinking in under their own chevron. */
  .proj-q,.proj-bar .field,.proj-bar .btn{flex:1 1 100%}
  .proj-bar .field select{flex:1 1 auto;min-width:0}
  .proj-bar .btn{justify-content:center}
  .org-path{flex:1 1 100%}
  /* Restore and Delete permanently get the full width rather than a sliver each. */
  .proj-trash-row :is(.proj-acts,.proj-trash-acts){flex:1 1 100%;margin-inline-start:0}
  .proj-trash-row .btn{flex:1 1 auto;justify-content:center}
  .proj-trash-row > .btn:first-of-type{margin-inline-start:0}
}

/* ───────────────────── workflow, trace and knowledge ───────────────────── */
/* The three surfaces the harness added. Sized off the same tokens as .check-* so a trace row and
   a findings row read as the same kind of thing — they are both "what the platform observed". */

.wf-list { list-style: none; margin: 0 0 10px; padding: 0; display: grid; gap: 6px; }
.wf-row {
  display: grid; grid-template-columns: 22px 1fr auto; gap: 10px; align-items: start;
  padding: 8px 10px; border: 1px solid var(--line); border-radius: 10px; background: var(--card);
}
.wf-row > .wf-n {
  width: 22px; height: 22px; border-radius: 999px; display: grid; place-items: center;
  font: 600 11px/1 var(--mono, ui-monospace, monospace); color: var(--ink-3);
  background: var(--surface-2); border: 1px solid var(--line);
}
.wf-row strong { display: block; font-size: 13px; font-weight: 600; }
.wf-row em { display: block; font-style: normal; font-size: 12px; color: var(--ink-3); margin-top: 2px; }
.wf-tags { display: flex; gap: 5px; flex-wrap: wrap; margin-top: 4px; }
.wf-tag {
  font-size: 10.5px; line-height: 1; padding: 3px 6px; border-radius: 5px;
  background: var(--surface-2); color: var(--ink-3); border: 1px solid var(--line);
}
.wf-drop {
  border: 0; background: none; cursor: pointer; color: var(--ink-3);
  font-size: 18px; line-height: 1; padding: 2px 4px; border-radius: 6px;
}
.wf-drop:hover { color: var(--danger, #b3261e); background: var(--surface-2); }
.wf-add { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.wf-add select { flex: 1 1 220px; min-width: 0; }
.wf-add input { flex: 1 1 220px; min-width: 0; }
.wf-add + .wf-add { margin-top: 8px; }
.wf-note { font-size: 12px; color: var(--ink-3); margin: 8px 0 0; min-height: 1em; }
.wf-note.bad { color: var(--danger, #b3261e); }
.wf-empty { font-size: 12px; color: var(--ink-3); margin: 0 0 10px; }

/* THE INPUT CONTRACT EDITOR. Deliberately the same rhythm as .wf-list: a step this generator
   performs and a question it asks are both parts of one declaration, and making them look like
   two unrelated features would hide that reading one explains the other. What differs is the
   controls inside a row — a field has a type, a required flag and sometimes a choice list, and
   those are edited in place rather than in a dialog. */
.in-list { list-style: none; margin: 0 0 10px; padding: 0; display: grid; gap: 6px; }
.in-row {
  display: grid; grid-template-columns: 22px 1fr auto; gap: 10px; align-items: start;
  padding: 8px 10px; border: 1px solid var(--line); border-radius: 10px; background: var(--card);
}
.in-row > .wf-n { width: 22px; }
.in-main { min-width: 0; display: grid; gap: 6px; }
.in-main > input[type="text"] { font-size: 13px; font-weight: 600; }
.in-opts { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; }
.in-opts select { flex: 0 1 150px; min-width: 0; }
.in-opts input { flex: 1 1 160px; min-width: 0; font-size: 12px; }
.in-opts label { display: inline-flex; gap: 5px; align-items: center; font-size: 12px; color: var(--ink-2); white-space: nowrap; }
.in-help { font-size: 12px; }
.in-move { display: grid; gap: 2px; }
.in-move button { border: 0; background: none; cursor: pointer; color: var(--ink-3); font-size: 11px; line-height: 1; padding: 2px 4px; border-radius: 5px; }
.in-move button:hover:not(:disabled) { color: var(--ink); background: var(--surface-2); }
.in-move button:disabled { opacity: 0.3; cursor: default; }
/* What was asked for and cannot be collected. Never silently dropped: the whole point of the
   capability registry is that an owner is TOLD, so this is a visible block and not a log line. */
.in-refused { margin: 8px 0 0; padding: 8px 10px; border-radius: 8px; font-size: 12px;
  border: 1px solid color-mix(in srgb, var(--warn, #9a6700) 35%, var(--line));
  background: color-mix(in srgb, var(--warn, #9a6700) 8%, transparent); color: var(--ink-2); }
.in-refused strong { display: block; margin-bottom: 4px; color: var(--ink); }
.in-refused ul { margin: 0; padding-inline-start: 18px; }
@media (max-width: 720px) {
  .in-row { grid-template-columns: 22px 1fr; }
  .in-move { grid-column: 1 / -1; display: flex; justify-content: flex-end; }
}

/* The test pane's copy of a customer's form. Denser than the published page's: this is an owner
   checking a generator, not a customer filling one in, and the point is to see every answer the
   test submits at once rather than to make filling it in pleasant. */
.test-inputs { border-bottom: 1px solid var(--line); padding: 10px 14px 12px; background: var(--surface-2); }
.test-inputs-note { font-size: 12px; color: var(--ink-3); margin: 0 0 8px; }
.test-fields { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: 8px 12px; }
.test-field { min-width: 0; display: grid; gap: 3px; }
.test-field.wide { grid-column: 1 / -1; }
.test-field > span { font-size: 11px; font-weight: 600; color: var(--ink-2); }
.test-field > span .req { color: var(--danger, #b3261e); margin-inline-start: 2px; }
.test-field input, .test-field select, .test-field textarea { width: 100%; min-width: 0; font-size: 12px; }
.test-field textarea { min-height: 56px; resize: vertical; }
.test-field .picks { display: flex; gap: 4px 10px; flex-wrap: wrap; }
.test-field .picks label { display: inline-flex; gap: 4px; align-items: center; font-size: 12px; color: var(--ink-2); }
.test-field .one { display: inline-flex; gap: 5px; align-items: center; font-size: 12px; color: var(--ink-2); }

/* The execution trace. Deliberately the same row rhythm as .wf-row: the plan and what happened
   are the same list, seen before and after. */
.trace { list-style: none; margin: 0; padding: 10px 14px; display: grid; gap: 4px; }
.trace-row {
  display: grid; grid-template-columns: 20px 130px 1fr auto; gap: 8px; align-items: baseline;
  padding: 5px 8px; border-radius: 8px; font-size: 12px;
}
.trace-row + .trace-row { border-top: 1px solid var(--line); }
.trace-n { color: var(--ink-3); font: 600 11px/1.6 var(--mono, ui-monospace, monospace); }
.trace-step { font-weight: 600; }
.trace-detail { color: var(--ink-2); overflow-wrap: anywhere; }
.trace-ms { color: var(--ink-3); font: 11px/1.6 var(--mono, ui-monospace, monospace); white-space: nowrap; }
.trace-row[data-status="completed"] .trace-step { color: var(--ok, #0f7b4f); }
.trace-row[data-status="failed"] { background: color-mix(in srgb, var(--danger, #b3261e) 7%, transparent); }
.trace-row[data-status="failed"] .trace-step { color: var(--danger, #b3261e); }
.trace-row[data-status="unverified"] .trace-step,
.trace-row[data-status="unsupported"] .trace-step { color: var(--warn, #9a6700); }
/* A STOPPED STEP IS NOT A FAILED ONE. Amber like the other "we could not establish this" statuses
   rather than red: the product did not do anything wrong, it ran past its ceiling. Reading it as a
   defect is exactly what the separate status exists to prevent — see lib/budget.js. */
.trace-row[data-status="timedout"] { background: color-mix(in srgb, var(--warn, #9a6700) 8%, transparent); }
.trace-row[data-status="timedout"] .trace-step { color: var(--warn, #9a6700); }
.trace-row[data-status="skipped"] { opacity: 0.62; }
.trace-plan { padding: 0; }
.trace-plan .trace-row { grid-template-columns: 20px 150px 1fr; }

/* Knowledge: a grant is a checkbox, because that is what it is. */
.know-head { font-size: 12px; font-weight: 600; color: var(--ink-3); margin: 14px 0 6px; }
.know-list { display: grid; gap: 6px; }
.know-row {
  display: grid; grid-template-columns: auto 1fr auto; gap: 10px; align-items: start;
  padding: 8px 10px; border: 1px solid var(--line); border-radius: 10px; background: var(--card);
}
.know-row input[type="checkbox"] { margin-top: 3px; }
.know-row strong { display: block; font-size: 13px; }
.know-row .know-meta { display: block; font-size: 11.5px; color: var(--ink-3); margin-top: 2px; }
.know-row .know-body { display: block; font-size: 12px; color: var(--ink-2); margin-top: 4px;
  max-height: 3.4em; overflow: hidden; }
.know-row[data-enabled="false"] { opacity: 0.58; }
.know-acts { display: flex; gap: 4px; align-items: center; }
.know-add { margin-top: 14px; }
.know-form { display: grid; gap: 10px; padding-block-start: 10px; }

@media (max-width: 720px) {
  .trace-row { grid-template-columns: 18px 1fr; grid-template-areas: "n step" ". detail" ". ms"; }
  .trace-n { grid-area: n; } .trace-step { grid-area: step; }
  .trace-detail { grid-area: detail; } .trace-ms { grid-area: ms; }
  .trace-plan .trace-row { grid-template-columns: 18px 1fr; }
  .wf-row { grid-template-columns: 22px 1fr auto; }
  .know-row { grid-template-columns: auto 1fr; }
  .know-acts { grid-column: 1 / -1; justify-content: flex-end; }
}


/* ── the product report: six numbers and the reasons, not a chart ───────────
   An owner asking "is this working" needs the figures and the refusal reasons at a glance. A graph
   of a fortnight on a product with nine visits is decoration taking the space the reasons need. */
.report-grid{display:grid; grid-template-columns:repeat(auto-fit, minmax(150px, 1fr)); gap:10px; margin-block:12px}
.report-grid > div{border:1px solid var(--line); border-radius:10px; padding:10px 12px; background:var(--surface)}
.report-k{display:block; font-size:11px; text-transform:uppercase; letter-spacing:.06em; color:var(--muted); margin-block-end:4px}
.report-grid strong{font-size:17px; font-variant-numeric:tabular-nums}
/* The logo the owner pasted, beside the box they pasted it into. Sized here, never by the artwork. */
.mark-preview{display:inline-flex; width:34px; height:34px; align-items:center; justify-content:center;
  border:1px solid var(--line); border-radius:8px; background:var(--surface); flex:0 0 34px}
.mark-preview svg{width:24px; height:24px; display:block}

/* A FORM THAT CONTRADICTS THE BUSINESS'S OWN KNOWLEDGE — shown on the draft, before a publish.
   Amber, not red: the product is not broken and the owner may have meant it. The softer variant is a
   scoped or partial refusal, which is something to look at rather than something that is wrong. */
#tool-report-top li.conflict{
  border-inline-start:3px solid var(--warn,#9a6700);
  padding-inline-start:.6rem;
  background:color-mix(in srgb, var(--warn,#9a6700) 7%, transparent);
}
#tool-report-top li.conflict.soft{
  border-inline-start-color:#ecdcb8;
  background:none;
}

/* The platform's own account of an edit, beside the model's. Shown only when the kit came back
   unchanged — see the `changed` channel on /api/kits/draft. */
.msg-flat {
  margin-block: 8px 0;
  font-size: 12px;
  font-weight: 600;
  color: var(--ink-2, #4a5160);
}

/* ───────────────────────── reduced motion ─────────────────────────────────
   Two of the four animations here carry information: the spinner and the ✦ pulse are the only
   thing in the product that says the machine is working, so they are slowed rather than
   removed — taking them away leaves a busy state that looks idle. Everything else is entrance
   decoration, and a card that lifts or slides under the pointer is precisely the movement this
   preference is asking us to stop. */
@media (prefers-reduced-motion: reduce){
  /* rise — every chat message, and the toast. slide — the code drawer. */
  .msg,.toast,.drawer{animation:none}
  /* spin and pulse: kept, slowed. */
  .spinner,.f-ai.busy{animation-duration:2.4s}
  /* The project transitions: a border tint and a shadow do not need animating, and a progress
     bar is read for where it is, not for the travel getting there. */
  .recent-card,.proj{transition:none}
  .proj-live .bar i,.work-bar i{transition:none}
}
