Files
Socrates/apps/web/styles/base.css
dtoro 4b8e3f04ee MVP M7: Socrates can propose model changes — review impact + Accept/Reject
The dock now has a "propose" button that asks Socrates to suggest one
high-value structural change. He returns a structured payload (reasoning
+ ops + impactSummary) which renders as a ProposalCard inline in the
thread. Accept routes the ops through the same useApply() pipeline as
user-originated edits (optimistic local + persisted POST + server
reconciliation).

apps/web/lib/llm/prompts/socrates/propose.md
- Promoted verbatim from phase-0/src/prompts/.

apps/web/lib/llm/proposeChange.ts
- Server-side proposeChange() — calls the LLM with character + propose
  prompts + trimmed model + active issues. JSON Schema uses oneOf per
  op kind (Phase 0 lesson: small models need this to produce real op
  shapes instead of cramming everything into the kind name).
- normalizeOps() converts the LLM-emitted op shapes into canonical
  ModelOp[] (fills in property ids/multiplicity defaults, expands
  satisfiedBy[] into RequirementRelation[], splits PropertyType union
  per kind).

apps/web/lib/sysml/impact.ts
- Pure pre-apply impact analysis. Runs the ops through the same
  applyOps() reducer locally, then diffs:
    - structural: added / removed / changed elements (per kind)
    - validation: issues created vs resolved (by canonical issue key)
    - dep-graph blast radius (closure of touched element ids on the
      post-apply graph)
  Headline stats summarized as deltas (+1 block, −1 assoc, etc.) for
  the proposal card.

apps/web/app/api/projects/[projectId]/socrates/propose
- POST: returns { reasoning, ops, impactSummary, meta }. Pure read of
  the model — does not apply anything; client must POST /apply with the
  same ops to commit.

apps/web/components/socrates/ProposalCard.tsx
- In-dock card: PROPOSAL tag + delta stats / reasoning / collapsible
  ops list / Impact section (added/removed/changed) / Validation diff
  (resolves ✓ / creates ⚠) / Accept + Reject. Disabled when impact
  analysis flagged the apply as illegal.

apps/web/components/socrates/SocratesDock.tsx
- New "propose" button between the input field and send. Renders
  ProposalCard for assistant turns of role "proposal". Accept calls
  useApply(); on success the card collapses to a "✓ Applied" system
  bubble. On apply failure the dock shows the structured error
  messages.
- New "system" bubble role for apply confirmations + dismissals.

apps/web/components/diagram-canvas/DiagramCanvas.tsx
- Bug fix: new blocks/constraints arriving via the model→RF sync (e.g.
  from accepted proposals) are now positioned to the right of the
  existing layout instead of stacking at (0, 0) offscreen.

apps/web/lib/sync/ModelStore.tsx
- Bug fix / observability: background-POST failures and version
  mismatches now log to the console with structured context instead of
  being silently swallowed. The server's authoritative state still
  replaces the optimistic local state on response, but the user can now
  see why their accept appeared to do nothing (typically: page tab
  was at version N but server had advanced to N+1).
2026-04-30 00:27:29 +02:00

1248 lines
37 KiB
CSS

/* ─── Base shared styles (theme variables defined per-aesthetic in HTML) ─── */
* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; height: 100%; }
body {
font-family: var(--font-body);
color: var(--fg);
background: var(--bg);
/* Direct children fill the viewport */
display: flex;
flex-direction: column;
min-height: 100dvh;
}
body > * { flex: 1; min-height: 0; }
button { font-family: inherit; }
/* ─── Shell ─── */
.shell {
width: 100%;
height: 100dvh;
min-height: 0;
display: flex;
flex-direction: column;
background: var(--bg);
color: var(--fg);
font-family: var(--font-body);
font-size: 13.5px;
line-height: 1.55;
overflow: hidden;
letter-spacing: var(--tracking);
}
.shell-density-compact { font-size: 12.5px; line-height: 1.45; }
/* ─── Top bar ─── */
.topbar {
display: flex;
align-items: center;
justify-content: space-between;
height: 44px;
padding: 0 16px;
background: var(--surface);
border-bottom: 1px solid var(--border);
flex-shrink: 0;
}
.topbar-left { display: flex; align-items: center; gap: 14px; }
.brand { display: flex; align-items: center; gap: 8px; }
.brand-name { font-family: var(--font-display); font-weight: 600; font-size: 15px; letter-spacing: 0.01em; }
.breadcrumbs { display: flex; align-items: center; gap: 6px; font-size: 12.5px; color: var(--muted); font-family: var(--font-mono); }
.bc-sep { opacity: 0.45; }
.bc-active { color: var(--fg); }
.bc-branch { margin-left: 10px; padding: 2px 8px; border-radius: 4px; background: var(--accent-soft); color: var(--accent-strong); font-size: 11.5px; }
.bc-branch-glyph { margin-right: 4px; }
.topbar-right { display: flex; align-items: center; gap: 10px; }
.sync-pill { display: flex; align-items: center; gap: 6px; font-size: 11.5px; color: var(--muted); font-family: var(--font-mono); }
.sync-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--ok); box-shadow: 0 0 0 3px var(--ok-soft); }
.topbar-divider { width: 1px; height: 18px; background: var(--border); }
.icon-btn {
width: 26px; height: 26px; border-radius: 5px; border: 1px solid transparent;
background: transparent; color: var(--muted); font-size: 13px;
display: inline-flex; align-items: center; justify-content: center; cursor: pointer;
font-family: var(--font-mono);
}
.icon-btn:hover { background: var(--surface-2); color: var(--fg); border-color: var(--border); }
.avatar {
width: 26px; height: 26px; border-radius: 50%;
background: var(--accent); color: var(--accent-on);
font-size: 11px; font-weight: 600;
display: inline-flex; align-items: center; justify-content: center;
font-family: var(--font-display); letter-spacing: 0.02em;
}
/* ─── Body layout ─── */
.shell-body {
flex: 1;
display: grid;
grid-template-columns: 320px 184px 1fr;
min-height: 0;
}
.shell-density-compact .shell-body { grid-template-columns: 296px 168px 1fr; }
.shell-presence-prominent .shell-body { grid-template-columns: 360px 184px 1fr; }
.shell-presence-subtle .shell-body { grid-template-columns: 0px 184px 1fr; }
/* ─── Left rail ─── */
.leftrail {
border-right: 1px solid var(--border);
border-left: 1px solid var(--border);
background: var(--surface);
padding: 8px 8px;
overflow-y: auto;
font-size: 11.5px;
position: relative;
}
.leftrail-collapsed {
padding: 14px 0;
display: flex; flex-direction: column; align-items: center; gap: 14px;
width: 36px;
min-width: 36px;
}
.rail-collapsed-stack {
display: flex; flex-direction: column; gap: 18px;
margin-top: 8px;
}
.rail-collapsed-tag {
font-family: var(--font-mono); font-size: 9.5px; letter-spacing: 0.08em;
color: var(--muted); writing-mode: vertical-rl; text-orientation: mixed;
cursor: pointer;
}
.rail-collapsed-tag:hover { color: var(--fg); }
.rail-section-head {
display: flex; align-items: center; gap: 5px;
width: 100%;
background: transparent; border: none; padding: 0 4px;
cursor: pointer;
margin-bottom: 4px;
color: inherit;
height: 20px;
}
.rail-section-head:hover .rail-label { color: var(--fg); }
.rail-caret { display: none; }
.rail-section .rail-label { margin-bottom: 0; padding: 0; }
.rail-collapse-btn {
position: absolute; top: 8px; right: 6px;
width: 18px; height: 18px; border-radius: 4px;
border: none; background: transparent; color: var(--muted);
cursor: pointer; font-size: 11px; line-height: 1;
display: flex; align-items: center; justify-content: center;
}
.rail-collapse-btn:hover { background: var(--surface-2); color: var(--fg); }
.leftrail-collapsed .rail-collapse-btn { position: static; }
.rail-collapse-btn-bottom { display: none; }
.shell-density-compact .leftrail { padding: 10px 10px; font-size: 11.5px; }
.rail-section { margin-bottom: 10px; }
.rail-label {
text-transform: uppercase;
font-size: 9.5px;
font-family: var(--font-mono);
letter-spacing: 0.10em;
color: var(--muted);
margin-bottom: 4px;
padding: 0 4px;
}
.rail-list { list-style: none; padding: 0; margin: 0; }
.rail-item {
padding: 2px 6px; border-radius: 3px; cursor: pointer; color: var(--fg);
display: flex; align-items: center; gap: 6px;
font-size: 11.5px; line-height: 1.35;
}
.rail-item-text { flex: 1; }
.rail-count {
font-family: var(--font-mono); font-size: 9.5px;
padding: 1px 5px; border-radius: 3px;
background: var(--surface-2); color: var(--muted-strong);
letter-spacing: 0.02em;
flex-shrink: 0;
}
.rail-count-asm { background: var(--c-prop-bg); color: var(--c-prop-fg); }
.rail-count-req { background: var(--c-block-bg); color: var(--c-block-fg); }
.rail-count-q { background: var(--accent-soft); color: var(--accent-strong); }
.rail-count-risk { background: var(--warn-soft); color: var(--warn-strong); }
.rail-item:hover { background: var(--surface-2); }
.rail-item-active { background: var(--accent-soft); color: var(--accent-strong); }
.rail-item-muted { color: var(--muted); }
.rail-blocks { display: flex; flex-direction: column; gap: 1px; }
.rail-block {
display: flex; align-items: center; gap: 6px;
padding: 2px 6px; border-radius: 3px; cursor: pointer;
font-family: var(--font-mono); font-size: 10.5px;
line-height: 1.4;
}
.rail-block:hover { background: var(--surface-2); }
.rail-block-active { background: var(--accent-soft); color: var(--accent-strong); }
.rail-block-glyph { color: var(--muted); width: 12px; }
.rail-constraint .rail-block-glyph { color: var(--warn); }
.rail-actor .rail-block-glyph { color: var(--info); }
.rail-block-label { flex: 1; }
.rail-block-count {
font-size: 10px; color: var(--muted);
background: var(--surface-2); padding: 1px 5px; border-radius: 3px;
display: inline-flex; align-items: center; gap: 3px;
cursor: help;
}
.rail-block-count-glyph { opacity: 0.6; }
.rail-req {
display: flex; align-items: center; justify-content: space-between;
padding: 2px 6px; font-family: var(--font-mono); font-size: 10px; color: var(--muted-strong);
}
.req-tag { letter-spacing: 0.04em; }
.req-status { width: 6px; height: 6px; border-radius: 50%; }
.req-traced { background: var(--ok); }
.req-untraced { background: var(--warn); }
/* ─── Canvases ─── */
.canvases { display: grid; grid-template-columns: 1fr 1px 1fr; min-height: 0; background: var(--bg); }
.canvas { display: flex; flex-direction: column; min-height: 0; min-width: 0; }
.canvas-divider { background: var(--border); }
.canvas-header {
height: auto; min-height: 44px; padding: 8px 16px; flex-shrink: 0;
display: flex; align-items: center; justify-content: space-between;
border-bottom: 1px solid var(--border); background: var(--surface);
}
.shell-density-compact .canvas-header { min-height: 38px; padding: 6px 12px; }
.canvas-title { font-family: var(--font-display); font-weight: 600; font-size: 12.5px; letter-spacing: 0.02em; line-height: 1.3; }
.canvas-sub { font-size: 10.5px; color: var(--muted); font-family: var(--font-mono); line-height: 1.3; margin-top: 2px; }
.canvas-actions { display: flex; gap: 2px; background: var(--surface-2); padding: 2px; border-radius: 5px; }
.canvas-mode-pill {
font-size: 10.5px; padding: 3px 8px; border-radius: 4px;
color: var(--muted); cursor: pointer; font-family: var(--font-mono);
}
.canvas-mode-active { background: var(--surface); color: var(--fg); box-shadow: 0 1px 2px var(--shadow); }
.canvas-scroll { flex: 1; overflow-y: auto; min-height: 0; position: relative; }
.canvas-scroll-diagram { overflow: hidden; }
/* ─── Text canvas ─── */
.text-canvas {
max-width: 720px;
margin: 0 auto;
font-family: var(--font-prose);
color: var(--fg);
}
.t-h1 {
font-family: var(--font-display);
font-weight: 600;
font-size: 22px;
letter-spacing: -0.01em;
margin: 14px 0 16px 0;
color: var(--fg);
}
.t-h2 {
font-family: var(--font-display);
font-weight: 600;
font-size: 15px;
letter-spacing: 0.01em;
margin: 24px 0 8px 0;
color: var(--fg);
}
.t-p {
margin: 0 0 14px 0;
font-size: 14.5px;
line-height: 1.65;
color: var(--prose);
text-wrap: pretty;
}
.shell-density-compact .t-p { font-size: 13px; line-height: 1.55; margin-bottom: 10px; }
.shell-density-compact .t-h1 { font-size: 19px; }
.shell-density-compact .t-h2 { font-size: 13.5px; }
/* ─── Chips ─── */
.chip {
display: inline-flex; align-items: baseline; gap: 3px;
padding: 1px 6px; border-radius: 4px; cursor: pointer;
font-family: var(--font-mono);
font-size: 0.86em;
vertical-align: baseline;
transition: background .12s, box-shadow .12s;
white-space: nowrap;
}
.chip-glyph { font-size: 0.85em; opacity: 0.75; }
.chip-label { font-weight: 500; }
.chip-focus { box-shadow: 0 0 0 1.5px var(--accent); }
/* pill (default) */
.chip-style-pill { background: var(--chip-bg); color: var(--chip-fg); }
.chip-style-pill:hover { background: var(--chip-bg-hover); }
/* color-coded by kind */
.chip-style-color.chip-block { background: var(--c-block-bg); color: var(--c-block-fg); }
.chip-style-color.chip-property { background: var(--c-prop-bg); color: var(--c-prop-fg); }
.chip-style-color.chip-association { background: var(--c-assoc-bg); color: var(--c-assoc-fg); }
.chip-style-color.chip-requirement { background: var(--c-req-bg); color: var(--c-req-fg); }
/* underline */
.chip-style-underline {
background: transparent;
padding: 0 1px;
border-bottom: 1.5px solid var(--accent);
border-radius: 0;
color: var(--fg);
}
.chip-style-underline.chip-block { border-bottom-color: var(--c-block-fg); }
.chip-style-underline.chip-property { border-bottom-color: var(--c-prop-fg); }
.chip-style-underline.chip-association { border-bottom-color: var(--c-assoc-fg); }
.chip-style-underline.chip-requirement { border-bottom-color: var(--c-req-fg); }
.chip-style-underline:hover { background: var(--chip-bg); }
/* bracket */
.chip-style-bracket {
background: transparent;
color: var(--muted-strong);
padding: 0 1px;
}
.chip-style-bracket .chip-bracket { color: var(--muted); opacity: 0.6; }
.chip-style-bracket .chip-kind { color: var(--accent); margin-right: 3px; }
.chip-style-bracket .chip-label { color: var(--fg); }
.chip-style-bracket:hover { background: var(--chip-bg); border-radius: 3px; }
/* ─── Margin note ─── */
.margin-note {
display: flex; gap: 12px;
margin: 24px 0 8px 0;
padding: 12px 14px;
background: var(--note-bg);
border-left: 2px solid var(--accent);
border-radius: 0 6px 6px 0;
font-family: var(--font-prose);
font-size: 13px;
color: var(--prose);
}
.margin-note-glyph {
font-family: var(--font-display); font-weight: 600;
color: var(--accent); font-size: 17px; line-height: 1;
flex-shrink: 0;
}
.margin-note-who {
display: block;
font-family: var(--font-mono); font-size: 10.5px;
color: var(--accent); letter-spacing: 0.04em; text-transform: uppercase;
margin-bottom: 4px;
}
.margin-note-text { line-height: 1.5; }
/* ─── Diagram ─── */
.diagram-grid {
position: absolute; inset: 0;
background-image: radial-gradient(var(--grid-dot) 1px, transparent 1px);
background-size: 18px 18px;
background-position: 0 0;
opacity: 0.7;
pointer-events: none;
}
.diagram-legend {
position: absolute; bottom: 12px; left: 12px;
display: flex; gap: 14px;
background: var(--surface);
border: 1px solid var(--border);
padding: 6px 10px;
border-radius: 5px;
font-family: var(--font-mono);
font-size: 10.5px;
color: var(--muted);
}
.legend-sw { display: inline-block; width: 10px; height: 10px; border-radius: 3px; margin-right: 5px; vertical-align: -1px; }
.legend-block { background: var(--block-bg); border: 1px solid var(--block-border); }
.legend-actor { background: var(--block-actor-bg); border: 1px solid var(--block-border); }
.legend-constraint { background: var(--block-constraint-bg); border: 1px dashed var(--block-border); }
/* ─── Socrates dock ─── */
.dock {
border-right: 1px solid var(--border);
background: var(--surface);
display: flex; flex-direction: column;
overflow: hidden;
font-size: 12.5px;
min-height: 0;
}
.dock-header {
display: flex; align-items: center; gap: 8px;
padding: 8px 10px;
border-bottom: 1px solid var(--border);
flex-shrink: 0;
}
.dock-header-text { flex: 1; min-width: 0; }
.dock-header-action {
width: 22px; height: 22px; border-radius: 4px;
border: 1px solid var(--border-strong);
background: transparent; color: var(--muted);
cursor: pointer; font-size: 13px; line-height: 1;
display: flex; align-items: center; justify-content: center;
}
.dock-header-action:hover { background: var(--surface-2); color: var(--fg); }
.dock-thread-wrap {
flex: 1;
min-height: 0;
overflow-y: auto;
padding: 10px 10px 6px;
display: flex; flex-direction: column;
}
.dock-section-label-inline {
margin-bottom: 8px;
flex-shrink: 0;
}
.dock-thread {
display: flex; flex-direction: column; gap: 8px;
flex: 1;
}
.bubble {
padding: 8px 10px; border-radius: 8px;
font-family: var(--font-prose); font-size: 12.5px; line-height: 1.5;
display: flex; gap: 8px;
align-items: flex-start;
}
.bubble-body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 8px; }
.bubble-text { display: block; }
.dock-input-wrap {
flex-shrink: 0;
padding: 8px 10px;
border-top: 1px solid var(--border);
background: var(--surface);
}
.dock-input { margin-top: 0; }
/* Numbered options inside a bubble */
.bubble-options {
display: flex; flex-direction: column; gap: 3px;
margin-top: 2px;
}
.bubble-option {
display: flex; align-items: center; gap: 8px;
padding: 4px 7px;
background: var(--surface);
border: 1px solid var(--border-strong);
border-radius: 5px;
cursor: pointer;
text-align: left;
font-family: var(--font-prose);
color: var(--fg);
width: 100%;
transition: background .12s, border-color .12s;
}
.bubble-option:hover {
background: var(--surface-2);
border-color: var(--accent);
}
.bubble-option-num {
width: 15px; height: 15px; flex-shrink: 0;
border-radius: 3px;
background: var(--accent-soft); color: var(--accent-strong);
font-family: var(--font-mono); font-size: 9.5px; font-weight: 600;
display: flex; align-items: center; justify-content: center;
}
.bubble-option-text { flex: 1; min-width: 0; display: flex; align-items: baseline; gap: 6px; }
.bubble-option-label {
font-size: 11.5px; font-weight: 500; color: var(--fg);
line-height: 1.25;
}
.bubble-option-sub {
font-family: var(--font-mono); font-size: 9.5px;
color: var(--muted);
line-height: 1.25;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.bubble-option-key { display: none; }
.bubble-options-hint {
font-family: var(--font-mono); font-size: 9.5px;
color: var(--muted);
margin-top: 3px;
padding: 0 2px;
}
.bubble-options-hint kbd {
font-family: var(--font-mono); font-size: 9px;
padding: 0 3px; border-radius: 2px;
background: var(--surface-2); color: var(--muted-strong);
border: 1px solid var(--border);
margin: 0 1px;
}
.dock-subtle {
position: fixed; bottom: 24px; right: 24px;
width: 50px; height: 50px;
border-radius: 50%; border: 1px solid var(--border);
background: var(--surface);
display: flex; align-items: center; justify-content: center;
box-shadow: 0 6px 20px var(--shadow-strong);
cursor: pointer;
z-index: 10;
}
.dock-subtle-count {
position: absolute; top: -4px; right: -4px;
width: 18px; height: 18px; border-radius: 50%;
background: var(--accent); color: var(--accent-on);
font-size: 10px; font-weight: 600;
display: flex; align-items: center; justify-content: center;
font-family: var(--font-mono);
}
.dock-header {
display: flex; align-items: center; gap: 8px;
padding: 8px 10px;
border-bottom: 1px solid var(--border);
}
.dock-name { font-family: var(--font-display); font-weight: 600; font-size: 12.5px; }
.dock-status { display: flex; align-items: center; gap: 4px; font-size: 10px; color: var(--muted); font-family: var(--font-mono); }
.dock-dot { width: 5px; height: 5px; border-radius: 50%; background: var(--ok); box-shadow: 0 0 0 2px var(--ok-soft); }
.dock-section { padding: 8px 10px; border-bottom: 1px solid var(--border); }
.dock-section:last-child { border-bottom: none; }
.dock-section-label {
text-transform: uppercase;
font-family: var(--font-mono); font-size: 9.5px; letter-spacing: 0.10em;
color: var(--muted); margin-bottom: 6px;
}
.dock-section-label-2 { margin-top: 16px; }
.dock-thread { display: flex; flex-direction: column; gap: 8px; }
.bubble-socrates {
background: var(--bubble-socrates);
color: var(--prose);
border-left: 2px solid var(--accent);
border-radius: 2px 8px 8px 2px;
padding-left: 10px;
}
.bubble-socrates .bubble-sigil {
display: inline-block;
font-family: var(--font-display); font-weight: 600;
color: var(--accent); margin-right: 6px;
flex-shrink: 0;
}
.bubble-user {
background: var(--bubble-user);
color: var(--fg);
align-self: flex-end;
max-width: 88%;
}
.dock-input {
display: flex; align-items: center; gap: 6px;
padding: 7px 10px;
border: 1px solid var(--border-strong);
border-radius: 6px;
font-family: var(--font-mono);
font-size: 11.5px;
color: var(--muted);
background: var(--bg);
}
.dock-input-prompt { color: var(--accent); }
.dock-input-placeholder { flex: 1; }
.dock-input-shortcut { font-size: 10px; opacity: 0.55; }
/* Proposal */
.proposal {
background: var(--surface-2);
border: 1px solid var(--border);
border-radius: 6px;
padding: 10px;
}
.proposal-title {
font-family: var(--font-display); font-weight: 600; font-size: 12.5px;
color: var(--fg); margin-bottom: 8px;
line-height: 1.35;
}
.proposal-meta { display: flex; flex-direction: column; gap: 4px; margin-bottom: 10px; }
.proposal-row { display: flex; gap: 8px; font-family: var(--font-mono); font-size: 10.5px; align-items: baseline; }
.proposal-key { color: var(--muted); width: 70px; flex-shrink: 0; }
.proposal-val { display: flex; gap: 4px; flex-wrap: wrap; }
.proposal-pill {
background: var(--accent-soft); color: var(--accent-strong);
padding: 1px 6px; border-radius: 3px; font-size: 10.5px;
}
.proposal-pill-req { background: var(--c-req-bg); color: var(--c-req-fg); }
.proposal-actions { display: flex; gap: 6px; }
.btn {
flex: 1;
padding: 6px 10px;
border-radius: 5px;
border: 1px solid var(--border-strong);
background: var(--surface);
color: var(--fg);
font-family: var(--font-mono);
font-size: 11.5px;
cursor: pointer;
}
.btn:hover { background: var(--surface-2); }
.btn-primary { background: var(--accent); color: var(--accent-on); border-color: var(--accent); }
.btn-primary:hover { background: var(--accent-strong); }
.btn-ghost { background: transparent; border-color: transparent; color: var(--muted); flex: 0; padding: 6px 8px; }
/* Watch list */
.watch { display: flex; flex-direction: column; gap: 6px; }
.watch-row {
display: flex; gap: 8px; align-items: flex-start;
font-size: 11.5px; line-height: 1.4; color: var(--prose);
}
.watch-tag {
font-family: var(--font-mono); font-size: 9.5px; letter-spacing: 0.04em;
background: var(--c-prop-bg); color: var(--c-prop-fg);
padding: 2px 5px; border-radius: 3px;
flex-shrink: 0; margin-top: 1px;
}
.watch-tag-risk { background: var(--warn-soft); color: var(--warn-strong); }
.watch-validated .watch-tag { background: var(--ok-soft); color: var(--ok-strong); }
.watch-text { flex: 1; }
.watch-risk-high .watch-text { color: var(--fg); }
/* ─── Status bar ─── */
.statusbar {
height: 24px; flex-shrink: 0;
display: flex; align-items: center; gap: 12px;
padding: 0 16px;
font-family: var(--font-mono); font-size: 10.5px; color: var(--muted);
border-top: 1px solid var(--border);
background: var(--surface);
}
.status-spacer { flex: 1; }
/* ─── Sigil ─── */
.sigil { position: relative; }
.sigil-pulse {
animation: sigilpulse 2.4s ease-in-out infinite;
transform-origin: center;
}
@keyframes sigilpulse {
0%, 100% { opacity: 0.3; r: 2; }
50% { opacity: 1; r: 3.5; }
}
/* ─── Seed screen ─── */
.seed-screen {
width: 100%; height: 100dvh; min-height: 0;
display: flex; flex-direction: column;
background: var(--bg);
font-family: var(--font-body);
color: var(--fg);
overflow: hidden;
}
.seed-top {
display: flex; align-items: center; justify-content: space-between;
padding: 12px 20px;
border-bottom: 1px solid var(--border);
background: var(--surface);
flex-shrink: 0;
}
.seed-top-left { display: flex; align-items: center; gap: 10px; }
.seed-brand { font-family: var(--font-display); font-weight: 600; font-size: 16px; }
.seed-pip { color: var(--muted); }
.seed-step { font-family: var(--font-mono); font-size: 11.5px; color: var(--muted); }
.seed-top-right { display: flex; gap: 2px; background: var(--surface-2); padding: 2px; border-radius: 5px; }
.seed-mode-pill {
font-family: var(--font-mono); font-size: 11px; padding: 4px 12px;
border-radius: 4px; color: var(--muted); cursor: pointer;
}
.seed-mode-active { background: var(--surface); color: var(--fg); box-shadow: 0 1px 2px var(--shadow); }
.seed-body {
flex: 1;
display: grid;
grid-template-columns: minmax(280px, 380px) 1fr;
min-height: 0;
}
.seed-left {
border-right: 1px solid var(--border);
background: var(--surface);
padding: 22px 22px 18px;
overflow-y: auto;
}
.seed-section-label {
text-transform: uppercase;
font-family: var(--font-mono); font-size: 10px; letter-spacing: 0.14em;
color: var(--muted); margin-bottom: 12px;
}
.seed-section-label-2 { margin-top: 24px; }
.seed-fields { display: flex; flex-direction: column; gap: 14px; }
.seed-field {
padding-left: 12px;
border-left: 2px solid var(--border-strong);
}
.seed-field-inferred { border-left-color: var(--accent); border-left-style: dashed; }
.seed-field-label {
font-family: var(--font-mono); font-size: 10.5px;
color: var(--muted); letter-spacing: 0.04em;
margin-bottom: 3px;
display: flex; gap: 8px; align-items: baseline;
}
.seed-conf { font-size: 9.5px; color: var(--accent); opacity: 0.85; }
.seed-field-value {
font-family: var(--font-prose);
font-size: 13.5px; line-height: 1.5;
color: var(--fg);
text-wrap: pretty;
}
.seed-mini-graph {
display: grid;
grid-template-columns: 1fr;
gap: 8px;
padding: 14px;
background: var(--bg);
border: 1px solid var(--border);
border-radius: 6px;
position: relative;
}
.mini-block {
border: 1px solid var(--block-border);
background: var(--block-bg);
padding: 8px 10px;
border-radius: 6px;
display: flex; flex-direction: column; gap: 1px;
}
.mini-block-3 { background: var(--block-constraint-bg); border-style: dashed; }
.mini-block-focus {
border-color: var(--accent); box-shadow: 0 0 0 2px var(--accent-soft);
}
.mini-stereo { font-family: var(--font-mono); font-size: 9.5px; color: var(--muted); letter-spacing: 0.04em; }
.mini-name { font-family: var(--font-display); font-weight: 600; font-size: 13px; }
.mini-prop { font-family: var(--font-mono); font-size: 11px; color: var(--muted-strong); margin-top: 2px; }
.mini-edge {
height: 14px;
background: linear-gradient(to bottom, var(--edge), var(--edge)) center/1px 100% no-repeat;
margin-left: 18px;
position: relative;
}
.mini-edge::after {
content: ""; position: absolute; left: -3px; bottom: 0;
border-left: 4px solid transparent; border-right: 4px solid transparent;
border-top: 5px solid var(--edge);
}
.seed-confidence { margin-top: 18px; }
.seed-confidence-row {
display: flex; justify-content: space-between;
font-family: var(--font-mono); font-size: 11px; color: var(--muted);
margin-bottom: 5px;
}
.seed-confidence-bar {
height: 4px; border-radius: 2px;
background: var(--surface-2);
overflow: hidden;
}
.seed-confidence-fill {
height: 100%; background: var(--accent);
}
.seed-confidence-hint {
margin-top: 6px;
font-size: 11px; color: var(--muted); font-family: var(--font-prose);
font-style: italic;
}
.seed-right {
display: flex; flex-direction: column;
min-height: 0;
background: var(--bg);
}
.seed-thread {
flex: 1; min-height: 0;
overflow-y: auto;
padding: 22px 36px;
display: flex; flex-direction: column; gap: 18px;
max-width: 760px;
margin: 0 auto;
width: 100%;
}
.seed-bubble {
display: flex; gap: 14px;
}
.seed-bubble-user { justify-content: flex-end; }
.seed-bubble-avatar { flex-shrink: 0; padding-top: 2px; }
.seed-bubble-body { max-width: 78%; }
.seed-bubble-user .seed-bubble-body {
background: var(--bubble-user);
padding: 10px 14px;
border-radius: 12px 12px 2px 12px;
color: var(--fg);
}
.seed-bubble-who {
font-family: var(--font-mono); font-size: 10.5px;
color: var(--muted); letter-spacing: 0.04em;
text-transform: uppercase; margin-bottom: 4px;
}
.seed-bubble-user .seed-bubble-who { display: none; }
.seed-bubble-text {
font-family: var(--font-prose);
font-size: 14.5px; line-height: 1.6;
color: var(--prose);
text-wrap: pretty;
}
.seed-bubble-user .seed-bubble-text { color: var(--fg); }
.seed-bubble-typing .seed-bubble-text { color: var(--muted); }
.seed-typing { display: flex; align-items: center; gap: 6px; font-size: 12.5px; color: var(--muted); font-family: var(--font-mono); }
.seed-typing span {
width: 4px; height: 4px; border-radius: 50%;
background: var(--accent); opacity: 0.35;
animation: typing 1.4s ease-in-out infinite;
}
.seed-typing span:nth-child(2) { animation-delay: 0.2s; }
.seed-typing span:nth-child(3) { animation-delay: 0.4s; }
@keyframes typing {
0%, 80%, 100% { opacity: 0.25; transform: translateY(0); }
40% { opacity: 1; transform: translateY(-2px); }
}
.seed-input-row {
border-top: 1px solid var(--border);
background: var(--surface);
padding: 14px 36px;
max-width: 760px; margin: 0 auto; width: 100%;
display: flex; flex-direction: column; gap: 10px;
flex-shrink: 0;
}
.seed-input {
display: flex; align-items: baseline; gap: 8px;
padding: 10px 14px;
background: var(--bg);
border: 1px solid var(--border-strong);
border-radius: 8px;
font-family: var(--font-prose);
font-size: 14px;
line-height: 1.5;
color: var(--fg);
}
.seed-input-prompt { color: var(--accent); font-family: var(--font-mono); }
.seed-input-text { flex: 1; }
.seed-input-caret {
display: inline-block;
width: 1.5px; height: 16px;
background: var(--accent);
animation: caret 1s steps(1) infinite;
vertical-align: -3px;
}
@keyframes caret { 50% { opacity: 0; } }
.seed-input-actions { display: flex; justify-content: flex-end; gap: 8px; }
.seed-btn {
padding: 6px 12px;
border-radius: 5px;
border: 1px solid var(--border-strong);
background: var(--surface);
color: var(--fg);
font-family: var(--font-mono);
font-size: 11.5px;
cursor: pointer;
}
.seed-btn-primary { background: var(--accent); color: var(--accent-on); border-color: var(--accent); }
/* ─── Slash menu (M2) ─── */
.slash-menu {
background: var(--surface);
border: 1px solid var(--border-strong);
border-radius: 6px;
box-shadow: 0 8px 24px var(--shadow-strong);
padding: 4px;
min-width: 280px;
display: flex; flex-direction: column; gap: 2px;
}
.slash-menu-empty {
padding: 10px 12px;
font-family: var(--font-mono);
font-size: 11.5px;
color: var(--muted);
}
.slash-menu-item {
display: grid;
grid-template-columns: 20px 1fr auto auto;
align-items: center;
gap: 8px;
padding: 6px 8px;
border-radius: 4px;
background: transparent;
border: none;
cursor: pointer;
text-align: left;
font-family: inherit;
color: var(--fg);
}
.slash-menu-item:hover,
.slash-menu-item-active {
background: var(--accent-soft);
color: var(--accent-strong);
}
.slash-menu-glyph {
font-family: var(--font-mono);
font-size: 13px;
color: var(--muted);
text-align: center;
}
.slash-menu-glyph-block { color: var(--c-block-fg); }
.slash-menu-glyph-property { color: var(--c-prop-fg); }
.slash-menu-glyph-association { color: var(--c-assoc-fg); }
.slash-menu-glyph-requirement { color: var(--c-req-fg); }
.slash-menu-label {
font-family: var(--font-display);
font-size: 13px;
font-weight: 500;
}
.slash-menu-hint {
font-family: var(--font-mono);
font-size: 10.5px;
color: var(--muted);
margin-right: 4px;
}
.slash-menu-key {
font-family: var(--font-mono);
font-size: 10px;
background: var(--surface-2);
color: var(--muted-strong);
border: 1px solid var(--border);
padding: 1px 5px;
border-radius: 3px;
}
/* TipTap-rendered narrative root */
.tiptap { outline: none; }
.tiptap:focus { outline: none; }
.tiptap p.t-p,
.tiptap > p {
margin: 0 0 14px 0;
font-size: 14.5px;
line-height: 1.65;
color: var(--prose);
text-wrap: pretty;
}
.tiptap > h1 {
font-family: var(--font-display);
font-weight: 600;
font-size: 22px;
margin: 14px 0 16px 0;
}
.tiptap > h2 {
font-family: var(--font-display);
font-weight: 600;
font-size: 15px;
margin: 24px 0 8px 0;
}
/* Make ProseMirror's inline-atom selection look like our chip-focus state. */
.tiptap .ProseMirror-selectednode .chip {
box-shadow: 0 0 0 1.5px var(--accent);
}
/* ─── Issues panel (M4) ─── */
.issues-panel {
position: fixed;
bottom: 32px;
right: 16px;
width: 380px;
max-height: 50vh;
background: var(--surface);
border: 1px solid var(--border-strong);
border-radius: 6px;
box-shadow: 0 8px 24px var(--shadow-strong);
display: flex;
flex-direction: column;
z-index: 50;
font-family: var(--font-body);
overflow: hidden;
}
.issues-panel-clean {
width: auto;
min-width: 200px;
}
.issues-panel-clean .issues-panel-head { color: var(--ok-strong); }
.issues-panel-clean-glyph {
color: var(--ok);
font-size: 14px;
font-weight: 600;
}
.issues-panel-collapsed {
max-height: none;
}
.issues-panel-head {
display: flex; align-items: center; gap: 10px;
padding: 8px 12px;
background: transparent;
border: none;
border-bottom: 1px solid var(--border);
cursor: pointer;
font-family: inherit;
color: var(--fg);
width: 100%;
text-align: left;
}
.issues-panel-collapsed .issues-panel-head { border-bottom: none; }
.issues-panel-head:hover { background: var(--surface-2); }
.issues-panel-title {
font-family: var(--font-display);
font-weight: 500;
font-size: 13px;
flex: 1;
}
.issues-panel-counts { display: flex; gap: 8px; }
.issues-count {
font-family: var(--font-mono);
font-size: 11px;
font-weight: 500;
}
.issues-count-error { color: var(--warn-strong); }
.issues-count-warning { color: var(--accent-strong); }
.issues-count-soft { color: var(--muted); }
.issues-panel-caret { font-size: 9px; color: var(--muted); }
.issues-panel-list {
list-style: none;
margin: 0;
padding: 4px;
overflow-y: auto;
flex: 1;
}
.issues-item {
display: grid;
grid-template-columns: 16px auto 1fr;
gap: 8px;
padding: 6px 10px;
border-radius: 4px;
cursor: pointer;
font-size: 12.5px;
line-height: 1.35;
}
.issues-item:hover { background: var(--surface-2); }
.issues-item-error .issues-item-sev { color: var(--warn-strong); }
.issues-item-warning .issues-item-sev { color: var(--accent); }
.issues-item-soft .issues-item-sev { color: var(--muted); }
.issues-item-sev { font-size: 11px; line-height: 1.6; text-align: center; }
.issues-item-code {
font-family: var(--font-mono);
font-size: 10.5px;
color: var(--muted-strong);
background: var(--surface-2);
padding: 1px 5px;
border-radius: 3px;
align-self: start;
margin-top: 1px;
}
.issues-item-msg { color: var(--prose); }
/* Issue dots in the rail */
.rail-issue-dot {
width: 6px; height: 6px;
border-radius: 50%;
margin-left: auto;
flex-shrink: 0;
margin-right: 4px;
}
.rail-issue-dot-error { background: var(--warn-strong); box-shadow: 0 0 0 2px var(--warn-soft); }
.rail-issue-dot-warning { background: var(--accent); box-shadow: 0 0 0 2px var(--accent-soft); }
.rail-issue-dot-soft { background: var(--muted); }
/* Diagram node issue ring */
.sysml-node-issue-error {
border-color: var(--warn-strong) !important;
box-shadow: 0 0 0 2px var(--warn-soft), 0 2px 4px var(--shadow);
}
.sysml-node-issue-warning {
border-color: var(--accent) !important;
box-shadow: 0 0 0 2px var(--accent-soft), 0 2px 4px var(--shadow);
}
.sysml-node-issue-soft {
border-style: dotted !important;
}
/* ─── Chip inline rename (M5) ─── */
.chip-rename {
font-family: inherit;
font-size: 0.86em;
color: inherit;
background: var(--surface);
border: none;
border-bottom: 1px solid var(--accent);
outline: none;
padding: 0 1px;
min-width: 50px;
font-weight: 500;
}
.chip-rename:focus { background: var(--surface-2); }
.chip.chip-editing { box-shadow: 0 0 0 1.5px var(--accent); }
/* ─── Dock input (M6) ─── */
.dock-input { display: flex; align-items: center; gap: 6px; }
.dock-input-field {
flex: 1;
border: none;
background: transparent;
outline: none;
font: inherit;
font-family: var(--font-prose);
font-size: 12.5px;
color: var(--fg);
min-width: 0;
}
.dock-input-field::placeholder { color: var(--muted); }
.dock-input-field:disabled { color: var(--muted); cursor: not-allowed; }
.dock-input-send {
background: transparent;
border: 1px solid var(--border-strong);
border-radius: 4px;
padding: 2px 6px;
font-family: var(--font-mono);
font-size: 10px;
color: var(--muted);
cursor: pointer;
}
.dock-input-send:hover:not(:disabled) {
background: var(--accent);
color: var(--accent-on);
border-color: var(--accent);
}
.dock-input-send:disabled {
opacity: 0.4;
cursor: not-allowed;
}
/* ─── Proposal card (M7) ─── */
.proposal-card {
background: var(--surface-2);
border: 1px solid var(--border-strong);
border-radius: 8px;
padding: 10px 12px;
margin: 6px 0;
display: flex; flex-direction: column; gap: 8px;
font-family: var(--font-prose);
font-size: 12.5px;
color: var(--prose);
}
.proposal-card-pending { opacity: 0.6; }
.proposal-card-head {
display: flex; align-items: baseline; gap: 8px;
border-bottom: 1px solid var(--border);
padding-bottom: 4px;
}
.proposal-card-tag {
font-family: var(--font-mono);
font-size: 9px;
letter-spacing: 0.10em;
background: var(--accent);
color: var(--accent-on);
padding: 1px 6px;
border-radius: 3px;
}
.proposal-card-stats {
font-family: var(--font-mono);
font-size: 10.5px;
color: var(--muted-strong);
flex: 1;
text-align: right;
}
.proposal-card-reasoning {
line-height: 1.5;
color: var(--fg);
}
.proposal-card-ops {
font-family: var(--font-mono);
font-size: 10.5px;
color: var(--muted-strong);
}
.proposal-card-ops summary {
cursor: pointer;
padding: 2px 0;
color: var(--muted);
}
.proposal-card-ops ul {
margin: 4px 0 0 0;
padding-left: 14px;
}
.proposal-card-ops li { margin-bottom: 2px; }
.proposal-card-section {
display: flex; flex-direction: column; gap: 4px;
}
.proposal-card-section-label {
font-family: var(--font-mono);
font-size: 9.5px;
letter-spacing: 0.10em;
text-transform: uppercase;
color: var(--muted);
}
.proposal-card-impact-grid {
display: flex; flex-direction: column; gap: 3px;
}
.proposal-card-impact-row {
display: grid;
grid-template-columns: 80px 1fr;
gap: 8px;
font-family: var(--font-mono);
font-size: 10.5px;
}
.proposal-card-impact-key { color: var(--muted-strong); }
.proposal-card-impact-val { color: var(--fg); word-break: break-word; }
.proposal-card-issue-list {
margin: 0;
padding-left: 14px;
font-size: 11.5px;
}
.proposal-card-issue-list li { line-height: 1.4; }
.proposal-card-issues-resolved li { color: var(--ok-strong); }
.proposal-card-issues-created li { color: var(--warn-strong); }
.proposal-card-error { color: var(--warn-strong); }
.proposal-card-actions {
display: flex; gap: 6px;
border-top: 1px solid var(--border);
padding-top: 6px;
}
.proposal-card-btn {
padding: 4px 12px;
border-radius: 4px;
border: 1px solid var(--border-strong);
background: var(--surface);
color: var(--fg);
font-family: var(--font-mono);
font-size: 11px;
cursor: pointer;
}
.proposal-card-btn:hover:not(:disabled) { background: var(--surface-2); }
.proposal-card-btn:disabled { opacity: 0.4; cursor: not-allowed; }
.proposal-card-btn-primary {
background: var(--accent);
color: var(--accent-on);
border-color: var(--accent);
}
.proposal-card-btn-primary:hover:not(:disabled) {
background: var(--accent-strong);
border-color: var(--accent-strong);
}
.proposal-card-meta {
font-family: var(--font-mono);
font-size: 9.5px;
color: var(--muted);
text-align: right;
}
/* System bubble (apply confirmation, dismissals) */
.bubble.bubble-system {
background: transparent;
color: var(--muted-strong);
font-family: var(--font-mono);
font-size: 10.5px;
font-style: italic;
padding: 2px 8px;
border: none;
text-align: center;
}
/* Propose button next to send */
.dock-input-propose {
background: transparent;
border: 1px solid var(--border-strong);
border-radius: 4px;
padding: 2px 8px;
font-family: var(--font-mono);
font-size: 10.5px;
color: var(--muted-strong);
cursor: pointer;
white-space: nowrap;
}
.dock-input-propose:hover:not(:disabled) {
background: var(--accent-soft);
color: var(--accent-strong);
border-color: var(--accent);
}
.dock-input-propose:disabled {
opacity: 0.4;
cursor: not-allowed;
}