@jossuealcala/madre 0.3.3 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/CHANGELOG.md +497 -3
  2. package/CONTRIBUTING.md +3 -1
  3. package/README.md +68 -186
  4. package/SECURITY.md +2 -1
  5. package/bin/madre.mjs +56 -13
  6. package/docs/INTERNALS.md +16 -0
  7. package/docs/REFERENCE.md +249 -0
  8. package/docs/SDK.md +121 -0
  9. package/docs/room.png +0 -0
  10. package/docs/sdk/hello-module.mjs +51 -0
  11. package/package.json +9 -1
  12. package/public/app.js +3979 -867
  13. package/public/es.js +2258 -0
  14. package/public/i18n.js +66 -0
  15. package/public/index.html +96 -15
  16. package/public/inquiry.js +220 -0
  17. package/public/resay.js +77 -0
  18. package/public/styles.css +622 -65
  19. package/public/troubleshooting.js +255 -46
  20. package/src/adapters/claude.mjs +2 -1
  21. package/src/adapters/codex.mjs +2 -1
  22. package/src/adapters/gemini.mjs +6 -5
  23. package/src/adapters/opencode.mjs +2 -1
  24. package/src/adapters/process.mjs +79 -20
  25. package/src/asking.mjs +128 -0
  26. package/src/auth-probe.mjs +58 -1
  27. package/src/chats.mjs +193 -0
  28. package/src/checkpoint.mjs +1 -1
  29. package/src/cold.mjs +56 -0
  30. package/src/commands.mjs +6 -0
  31. package/src/conversation-context.mjs +35 -3
  32. package/src/credentials.mjs +145 -0
  33. package/src/dataset.mjs +56 -4
  34. package/src/distiller.mjs +12 -5
  35. package/src/event-store.mjs +14 -8
  36. package/src/exam.mjs +240 -0
  37. package/src/extensions.mjs +3 -2
  38. package/src/eyecat-watch.mjs +100 -0
  39. package/src/eyecat.mjs +169 -0
  40. package/src/i18n.mjs +47 -0
  41. package/src/image-studio.mjs +2 -0
  42. package/src/launch.mjs +61 -0
  43. package/src/maturity.mjs +94 -0
  44. package/src/mcp/image-server.mjs +36 -3
  45. package/src/mcp/memory-server.mjs +1 -1
  46. package/src/memory.mjs +325 -17
  47. package/src/modules/ahp.mjs +9 -7
  48. package/src/modules/ash.mjs +36 -0
  49. package/src/modules/git-pulse.mjs +5 -3
  50. package/src/modules/helpers.mjs +31 -0
  51. package/src/modules/image-studio.mjs +10 -4
  52. package/src/modules/index.mjs +141 -9
  53. package/src/modules/ollama.mjs +66 -10
  54. package/src/modules/playwright.mjs +44 -23
  55. package/src/modules/ripley.mjs +5 -3
  56. package/src/modules/sdk.mjs +93 -2
  57. package/src/modules/updates.mjs +81 -0
  58. package/src/ollama.mjs +5 -2
  59. package/src/outbound.mjs +297 -0
  60. package/src/privacy.mjs +54 -7
  61. package/src/room/context.mjs +4 -4
  62. package/src/room/economy.mjs +161 -0
  63. package/src/room/prompt.mjs +118 -46
  64. package/src/room.mjs +443 -44
  65. package/src/runtime-detection.mjs +27 -8
  66. package/src/sentinel-errors.mjs +19 -1
  67. package/src/server.mjs +709 -71
  68. package/src/setup.mjs +1 -1
  69. package/src/updates.mjs +4 -2
  70. package/src/usage-sentinel.mjs +13 -8
  71. package/src/verdict.mjs +74 -0
  72. package/src/ashcode.mjs +0 -64
  73. package/src/modules/ashcode.mjs +0 -28
package/public/styles.css CHANGED
@@ -17,7 +17,7 @@
17
17
  --text-3: #626a5f;
18
18
 
19
19
  --phosphor: #9bff66; /* MOTHER */
20
- --brew: #28fe14; /* ORDER 937: Terminal.app 'Homebrew' green */
20
+ --brew: #28fe14; /* Terminal.app 'Homebrew' green */
21
21
  --brew-ink: #041004;
22
22
  --phosphor-ink: #0a1405;
23
23
  --accent: var(--phosphor);
@@ -117,7 +117,7 @@ body {
117
117
  /* The bar is the ship's console: wider than the thread so the project root, the crew and the alerts share one line. */
118
118
  .shell > .bar { width: min(1280px, 100% - 40px); }
119
119
  .shell > .bar { grid-row: 1; } .shell > .onboarding { grid-row: 2; } .shell > .thread { grid-row: 3; } .shell > .composer { grid-row: 4; } .shell > .safety { grid-row: 5; }
120
- .shell > .bar, .shell > .onboarding, .shell > .thread, .shell > .composer, .shell > .safety, .shell > .tree { grid-column: 1; }
120
+ .shell > .bar, .shell > .onboarding, .shell > .thread, .shell > .composer, .shell > .safety, .shell > .tree, .shell > .chats-button { grid-column: 1; }
121
121
  .visually-hidden { position: absolute !important; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
122
122
 
123
123
  /* ================= bar ================= */
@@ -129,6 +129,7 @@ body {
129
129
  color: var(--text-2);
130
130
  }
131
131
  .bar-left { display: flex; align-items: center; gap: 14px; min-width: 0; flex: 1 1 auto; }
132
+ .bar-left .project + .project { margin-left: -6px; }
132
133
  .brand { display: flex; align-items: center; gap: 10px; color: var(--text); font-weight: 700; letter-spacing: .22em; font-size: 11px; text-decoration: none; cursor: pointer; }
133
134
  .brand:hover .brand-mark { filter: drop-shadow(0 0 10px var(--phosphor)); } .brand:hover .brand-word { color: var(--phosphor); }
134
135
  .brand-mark { width: 64px; height: 18px; color: var(--phosphor); overflow: visible; filter: drop-shadow(0 0 6px color-mix(in srgb, var(--phosphor) 55%, transparent)); }
@@ -144,7 +145,7 @@ body {
144
145
  .brand-mark .beat-dot { animation: none; opacity: 1; }
145
146
  .brand-word { animation: none; opacity: 1; transform: none; }
146
147
  }
147
- .project { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--text-3); }
148
+ .project { flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--text-3); }
148
149
  .update-command { display: flex; align-items: center; gap: 10px; margin: 8px 0; padding: 8px 10px; border: 1px solid var(--line); border-radius: 10px; background: color-mix(in srgb, var(--text) 4%, transparent); }
149
150
  .update-command code { flex: 1; min-width: 0; overflow-x: auto; white-space: nowrap; font-family: var(--mono); font-size: 11.5px; color: var(--text); }
150
151
  .update-command button { font: inherit; font-family: var(--mono); font-size: 9.5px; letter-spacing: .14em; text-transform: uppercase; border: 1px solid var(--line); background: transparent; color: var(--text-2); border-radius: 8px; padding: 4px 10px; cursor: pointer; }
@@ -215,7 +216,9 @@ code { font-family: var(--mono); font-size: .9em; color: var(--phosphor); backgr
215
216
  .row.compact .avatar { visibility: hidden; }
216
217
  .row.user { grid-template-columns: 1fr; align-self: flex-end; justify-items: end; }
217
218
  .row .col { display: grid; gap: 6px; min-width: 0; }
218
- .who { font-family: var(--mono); font-size: 10px; letter-spacing: .12em; text-transform: uppercase; color: var(--agent, var(--text-2)); margin: 0 0 2px 12px; display: flex; gap: 10px; align-items: baseline; }
219
+ .who { font-family: var(--mono); font-size: 10px; letter-spacing: .12em; text-transform: uppercase; color: var(--agent, var(--text-2)); margin: 0 0 2px 12px; display: flex; gap: 10px; align-items: baseline; min-width: 0; }
220
+ /* The quoted line gives way first: the cost and who spoke are short and must not be pushed out. */
221
+ .who .reply { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
219
222
  .user .who { color: var(--text-3); margin: 0 12px 2px 0; justify-content: flex-end; }
220
223
  .user .who b { color: var(--phosphor); font-weight: 600; }
221
224
  .user.expendable .who b { color: var(--crit); }
@@ -269,6 +272,10 @@ code { font-family: var(--mono); font-size: .9em; color: var(--phosphor); backgr
269
272
  @keyframes status-in { from { opacity: 0; transform: translateY(3px); } to { opacity: 1; transform: none; } }
270
273
  @keyframes cursor-blink { 50% { opacity: 0; } }
271
274
  .row.thinking .elapsed { font-family: var(--mono); font-size: 10px; letter-spacing: .08em; color: var(--text-3); margin-left: 8px; }
275
+ /* What a turn is reading, while it reads it, and what it was charged once it is done. */
276
+ .row.thinking .tokens { font-family: var(--mono); font-size: 10px; letter-spacing: .08em; color: var(--text-3); margin-left: 8px; opacity: .75; }
277
+ /* What a reply cost, on the same line as everything else above the bubble: no pill, no wrap. */
278
+ .who .spent { flex: none; margin-left: auto; padding: 0; border: 0; background: none; font-family: var(--mono); font-size: 10px; letter-spacing: .04em; color: var(--text-3); white-space: nowrap; }
272
279
  .row.thinking .elapsed.late { color: var(--warn); }
273
280
  .row.thinking i { width: 7px; height: 7px; border-radius: 50%; background: var(--agent, var(--text-2)); animation: bounce 1.2s infinite; opacity: .5; }
274
281
  .row.thinking i:nth-child(2) { animation-delay: .18s; } .row.thinking i:nth-child(3) { animation-delay: .36s; }
@@ -289,21 +296,27 @@ code { font-family: var(--mono); font-size: .9em; color: var(--phosphor); backgr
289
296
  .system.memory .brief { text-transform: none; letter-spacing: 0; color: var(--text-2); }
290
297
 
291
298
  /* NOSTROMO: the archive as a small solar system. One colour per kind of memory. */
292
- :root { --mem-decision: #ffb000; --mem-fact: #9bff66; --mem-preference: #d78cff; --mem-question: #5fd6ff; --nostromo-sun: #ff2a1f; }
299
+ :root { --mem-decision: #ffdc3c; --mem-fact: #dfeeff; --mem-preference: #5cf07a; --mem-question: #3dc6ff; --mem-aberration: #b14cff; --nostromo-sun: #ff2a1f; }
293
300
  .nostromo-button { color: var(--terror); border-color: color-mix(in srgb, var(--terror) 55%, transparent); }
294
301
  .nostromo-button:hover { background: color-mix(in srgb, var(--terror) 14%, transparent); }
295
302
  dialog.nostromo { color: #eef1ea; }
296
303
  dialog.nostromo::backdrop { background: #000; }
297
304
  .nostromo-frame { --ph: #ffb4ad; --ph-dim: rgba(255, 42, 31, .55); --ph-faint: rgba(255, 42, 31, .16); position: relative; height: 100%; background: #030203; font-family: var(--mono); overflow: hidden; display: grid; grid-template-rows: auto 1fr; }
298
- .nostromo-head { display: grid; grid-template-columns: auto 1fr auto auto auto; gap: 18px; align-items: center; padding: 18px 26px 14px; border-bottom: 1px solid var(--ph-faint); color: var(--ph); position: relative; z-index: 2; }
305
+ .nostromo-head { display: grid; grid-template-columns: auto 1fr; grid-auto-flow: column; grid-auto-columns: max-content; gap: 18px; align-items: center; padding: 18px 26px 14px; border-bottom: 1px solid var(--ph-faint); color: var(--ph); position: relative; z-index: 2; }
299
306
  .nostromo-head .mother-title { color: var(--nostromo-sun); text-shadow: 0 0 18px color-mix(in srgb, var(--nostromo-sun) 60%, transparent); }
300
- .nostromo-head .mother-sub { color: var(--ph-dim); }
301
- .nostromo-legend { display: flex; gap: 8px; }
302
- .nostromo-legend .k { font-size: 9px; letter-spacing: .16em; padding: 3px 8px; border: 1px solid; border-radius: 999px; }
303
- .nostromo-legend .k.decision { color: var(--mem-decision); } .nostromo-legend .k.fact { color: var(--mem-fact); } .nostromo-legend .k.preference { color: var(--mem-preference); } .nostromo-legend .k.question { color: var(--mem-question); }
307
+ .nostromo-head .mother-sub { color: var(--ph-dim); min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
308
+ .nostromo-legend { display: flex; gap: 16px; align-items: center; }
309
+ .nostromo-legend .k { display: inline-flex; align-items: center; gap: 6px; font-size: 9px; letter-spacing: .16em; }
310
+ .nostromo-legend .k canvas { display: block; }
311
+ .nostromo-legend .k i { font-style: normal; }
312
+ .nostromo-legend .k.decision { color: var(--mem-decision); } .nostromo-legend .k.fact { color: var(--mem-fact); } .nostromo-legend .k.preference { color: var(--mem-preference); } .nostromo-legend .k.question { color: var(--mem-question); } .nostromo-legend .k.aberration { color: var(--mem-aberration); }
304
313
  .nostromo-frame .mother-close { color: var(--ph); border-color: var(--ph-dim); }
305
314
  .nostromo-frame .mother-close:hover { background: var(--ph-faint); }
306
315
  .nostromo-canvas { display: block; width: 100%; height: 100%; cursor: crosshair; }
316
+ /* The map takes focus when NOSTROMO opens, so that Enter does not land on LEAVE. It is not in
317
+ the tab order (tabindex="-1"), so the ring the browser draws around it points at nothing and
318
+ boxes the constellation in blue. Nothing is hidden by removing it: there is nowhere to tab. */
319
+ .nostromo-canvas:focus, .nostromo-canvas:focus-visible { outline: none; }
307
320
  .nostromo-canvas.over { cursor: pointer; }
308
321
  .nostromo-canvas.dragging { cursor: grabbing; }
309
322
  .nostromo-help { position: absolute; left: 26px; bottom: 16px; margin: 0; font-size: 9px; letter-spacing: .2em; color: var(--ph-dim); pointer-events: none; }
@@ -325,16 +338,192 @@ dialog.nostromo::backdrop { background: #000; }
325
338
  .nostromo-card .text { margin: 0; font-family: -apple-system, BlinkMacSystemFont, "SF Pro Text", Inter, sans-serif; font-size: 14px; line-height: 1.5; color: #eef1ea; }
326
339
  .nostromo-card .meta { margin: 0; display: grid; grid-template-columns: auto 1fr; gap: 4px 14px; font-size: 10px; letter-spacing: .1em; }
327
340
  .nostromo-card .meta dt { color: var(--ph-dim); } .nostromo-card .meta dd { margin: 0; color: #c9c2bf; }
341
+ /* What this memory is wired to, and what has actually passed between it and the room. The list
342
+ is the same set of wires the map draws: hovering a row lights its filament out there. */
343
+ .nostromo-card .wire { display: grid; gap: 7px; border-top: 1px solid rgba(255, 255, 255, .08); padding-top: 11px; }
344
+ .nostromo-card .wire h6 { margin: 0; font-size: 9px; font-weight: 400; letter-spacing: .22em; color: var(--ph-dim); display: flex; align-items: center; gap: 8px; }
345
+ .nostromo-card .wire h6 .n { color: #c9c2bf; }
346
+ .nostromo-card .wire h6 .live { color: var(--kind, var(--ph)); font-size: 7px; animation: blink 2.4s steps(1) infinite; margin-left: auto; }
347
+ .nostromo-card .exchange { margin: 0; white-space: pre-line; font-size: 10.5px; line-height: 1.6; letter-spacing: .06em; color: #c9c2bf; }
348
+ .nostromo-card .wires { margin: 0; padding: 0; list-style: none; display: grid; gap: 4px; max-height: 148px; overflow-y: auto; }
349
+ .nostromo-card .wires .none { font-size: 10px; letter-spacing: .08em; color: var(--ph-dim); padding: 2px 0; }
350
+ .nostromo-card .wire-row { display: grid; grid-template-columns: 8px minmax(0, 1fr) auto; gap: 8px; align-items: center; padding: 4px 6px 4px 2px; cursor: pointer; border: 1px solid transparent; }
351
+ .nostromo-card .wire-row:hover { border-color: rgba(255, 255, 255, .16); background: rgba(255, 255, 255, .04); }
352
+ .nostromo-card .wire-row i { width: 7px; height: 7px; border-radius: 50%; background: var(--kind, var(--ph)); box-shadow: 0 0 8px var(--kind, var(--ph)); }
353
+ .nostromo-card .wire-row .what { font-family: -apple-system, BlinkMacSystemFont, "SF Pro Text", Inter, sans-serif; font-size: 11.5px; line-height: 1.4; color: #d7d2cf; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
354
+ .nostromo-card .wire-row b { font-family: var(--mono); font-size: 9px; font-weight: 600; letter-spacing: .08em; color: var(--ph-dim); }
355
+ .nostromo-card .wire-row.refuted b { color: var(--nostromo-sun); }
356
+ /* Company strong enough that recalling one now brings the other: the association is live. */
357
+ /* What to ask next: a panel on the other side of the map from the memory card, so the two can
358
+ be open one after the other and never fight for the same corner. */
359
+ .nostromo-card.asks { right: auto; left: 26px; width: min(420px, calc(100vw - 52px)); border-color: rgba(255, 255, 255, .14); }
360
+ .nostromo-card.asks .kind { color: #d7d2cf; text-shadow: none; }
361
+ .nostromo-card.asks .foot { margin: 0; }
362
+ .asks-list { margin: 0; padding: 0; list-style: none; display: grid; gap: 10px; max-height: min(58vh, 520px); overflow-y: auto; }
363
+ .asks-list .none { font-size: 11px; letter-spacing: .06em; color: var(--ph-dim); }
364
+ .asks-list .ask { display: grid; gap: 6px; padding: 11px 12px; border: 1px solid rgba(255, 255, 255, .1); border-left: 2px solid var(--well, rgba(255, 255, 255, .3)); background: rgba(255, 255, 255, .03); }
365
+ .asks-list .ask-open { --well: #3dc6ff; }
366
+ .asks-list .ask-cold { --well: #96b0c6; }
367
+ .asks-list .ask-thin { --well: #ffdc3c; }
368
+ .asks-list .ask .q { margin: 0; font-family: -apple-system, BlinkMacSystemFont, "SF Pro Text", Inter, sans-serif; font-size: 13px; line-height: 1.5; color: #eef1ea; }
369
+ .asks-list .ask .why { margin: 0; font-family: var(--mono); font-size: 8.5px; letter-spacing: .12em; line-height: 1.5; color: var(--ph-dim); }
370
+ .asks-list .ask .actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 2px; }
371
+ .asks-list .ask button { border: 1px solid rgba(255, 255, 255, .22); background: transparent; color: #d7d2cf; font: inherit; font-size: 9px; letter-spacing: .16em; padding: 6px 10px; cursor: pointer; }
372
+ .asks-list .ask button.primary { border-color: var(--well); color: var(--well); }
373
+ .asks-list .ask button:hover { background: rgba(255, 255, 255, .06); }
374
+ .asks-list .ask button:disabled { opacity: .45; cursor: default; }
375
+ .nostromo-head .mother-close.ask { color: #d7d2cf; border-color: rgba(255, 255, 255, .28); }
376
+ .nostromo-head .mother-close.ask[aria-pressed="true"] { background: rgba(255, 255, 255, .12); color: #fff; }
377
+
378
+ /* ================= the core =================
379
+ Inside the star, not looking at it: the field burns from the edges inward and breathes, and
380
+ the console sits on top of it in the panel's own phosphor. Nothing here is a new language —
381
+ the frame, the folds, the bars and the type are the ones MADRE already speaks. */
382
+ /* The core opens from inside NOSTROMO, on top of the star itself: the field behind this frame is
383
+ the real one, still turning and still breathing. A second sun drawn here would be two. */
384
+ .core::backdrop { background: radial-gradient(circle at 50% 50%, rgba(30, 2, 0, .58), rgba(0, 0, 0, .86) 72%); }
385
+ .core .mother-frame { position: relative; z-index: 1; border-color: color-mix(in srgb, var(--nostromo-sun) 45%, var(--ph-dim)); box-shadow: 0 0 60px rgba(226, 40, 22, .22), 0 0 0 1px rgba(0, 0, 0, .6); }
386
+ .core .mother-title { color: var(--nostromo-sun); text-shadow: 0 0 20px color-mix(in srgb, var(--nostromo-sun) 55%, transparent); }
387
+ .core .mother-foot a { color: var(--ph-dim); text-decoration: none; }
388
+ .core .mother-foot a:hover { color: var(--ph); }
389
+ .core .mother-frame { display: grid; grid-template-rows: auto auto 1fr; overflow: hidden; padding-bottom: 18px; }
390
+ .core .mother-boot { margin: 12px 0 6px; min-height: 2.4em; font-size: 11px; }
391
+ .core-body { display: grid; grid-template-rows: auto auto minmax(0, 1fr) auto; gap: 10px; min-height: 0; }
392
+
393
+ /* The strip: who it goes to, at what mode, and around what. One line each, never a paragraph. */
394
+ .core-strip { display: grid; gap: 6px; border-bottom: 1px solid var(--ph-faint); padding-bottom: 10px; }
395
+ .core-pick { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 10px; }
396
+ .core-pick .k, .core-asking .k { font-size: 8.5px; letter-spacing: .22em; color: var(--ph-dim); }
397
+ .core-agents { display: flex; flex-wrap: wrap; gap: 4px; }
398
+ .core-agent { border: 1px solid var(--ph-faint); background: transparent; color: var(--ph-dim); font: inherit; font-size: 10px; letter-spacing: .12em; padding: 3px 8px; cursor: pointer; }
399
+ .core-agent:hover { border-color: var(--ph-dim); color: var(--ph); }
400
+ .core-agent.on { border-color: var(--ph); color: var(--ph); background: var(--ph-faint); }
401
+ .core-agent.cold { opacity: .55; }
402
+ .core-pick select { border: 1px solid var(--ph-faint); background: #000; color: var(--ph); font: inherit; font-size: 10px; letter-spacing: .12em; padding: 3px 6px; }
403
+ .core-ceiling { font-family: var(--mono); font-size: 8.5px; letter-spacing: .14em; color: var(--warn); }
404
+ .core-asking { display: grid; grid-template-columns: auto 1fr auto; align-items: center; gap: 10px; }
405
+ .core-asking textarea { width: 100%; resize: none; border: 0; border-bottom: 1px solid var(--ph-faint); background: transparent; color: var(--ph); font: inherit; font-family: var(--mono); font-size: 11.5px; line-height: 1.6; padding: 3px 0; }
406
+ .core-asking textarea::placeholder { color: var(--ph-dim); opacity: .7; }
407
+ .core-asking textarea:focus { outline: 0; border-bottom-color: var(--ph); }
408
+ .core-asking .note { font-size: 8px; letter-spacing: .14em; color: var(--ph-dim); white-space: nowrap; }
409
+
410
+ /* Four panes, one at a time, each tab carrying what is behind it. */
411
+ .core-tabs { display: flex; flex-wrap: wrap; gap: 2px; }
412
+ .core-tab { display: grid; gap: 1px; text-align: left; border: 1px solid transparent; border-bottom: 1px solid var(--ph-faint); background: transparent; color: var(--ph-dim); font: inherit; padding: 6px 12px; cursor: pointer; }
413
+ .core-tab b { font-size: 10px; font-weight: 600; letter-spacing: .18em; }
414
+ .core-tab .meta { font-size: 8.5px; letter-spacing: .1em; opacity: .8; }
415
+ .core-tab:hover { color: var(--ph); }
416
+ .core-tab.on { color: var(--ph); border-color: var(--ph-faint); border-bottom-color: transparent; background: rgba(0, 0, 0, .45); }
417
+ .core-panes { overflow: auto; min-height: 0; padding: 4px 2px 8px; transition: opacity .18s ease; }
418
+ .core-panes.reading { opacity: .45; }
419
+ .core-pane { display: grid; gap: 10px; align-content: start; min-width: 0; }
420
+ .core-pane > *, .core-blocks, .core-block { min-width: 0; }
421
+
422
+ /* `==> TITLE meta`, and facts in two columns: how the terminal this frame is dressed as prints. */
423
+ .brew-head { margin: 6px 0 0; display: flex; flex-wrap: wrap; align-items: baseline; gap: 10px; font-family: var(--mono); font-size: 9px; letter-spacing: .14em; text-transform: uppercase; }
424
+ .brew-head b { color: var(--nostromo-sun); text-shadow: none; letter-spacing: 0; }
425
+ .brew-head .title { color: var(--ph); font-size: 10px; letter-spacing: .2em; }
426
+ .brew-head .meta { color: var(--ph-dim); }
427
+ .brew-rows { display: grid; gap: 4px; }
428
+ .brew-row { display: grid; grid-template-columns: 190px minmax(0, 1fr); gap: 14px; align-items: baseline; }
429
+ .brew-row .k { font-family: var(--mono); font-size: 9px; letter-spacing: .14em; color: var(--ph-dim); text-transform: uppercase; }
430
+ .brew-row .v { font-family: var(--mono); font-size: 10.5px; line-height: 1.6; color: rgba(210, 255, 190, .88); text-shadow: none; }
431
+ .brew-row .v .where { color: var(--ph-dim); }
432
+
433
+ .core-doc, .core-launch, .core-outbound { display: grid; gap: 10px; }
434
+ .core-blocks { display: grid; gap: 1px; }
435
+ .core-block { border: 1px solid transparent; }
436
+ .core-block > summary { display: grid; grid-template-columns: 14px 148px 72px minmax(140px, 1.1fr) minmax(0, 1.4fr); gap: 14px; align-items: center; padding: 5px 6px; cursor: pointer; list-style: none; }
437
+ .core-block > summary::-webkit-details-marker { display: none; }
438
+ .core-block:hover { border-color: var(--ph-faint); }
439
+ .core-block[open] { border-color: var(--ph-faint); background: rgba(0, 0, 0, .35); }
440
+ .core-block > summary .sign { font-style: normal; font-size: 11px; color: var(--nostromo-sun); text-shadow: none; }
441
+ .core-block > summary b { font-size: 9.5px; font-weight: 600; letter-spacing: .16em; color: var(--ph); text-shadow: none; }
442
+ .core-block > summary .n { font-family: var(--mono); font-size: 9px; letter-spacing: .1em; color: var(--ph-dim); text-align: right; }
443
+ .core-block > summary .weigh { height: 6px; background: var(--ph-faint); position: relative; }
444
+ .core-block > summary .weigh::after { content: ''; position: absolute; inset: 0 auto 0 0; width: var(--fill, 0%); background: color-mix(in srgb, var(--nostromo-sun) 55%, var(--ph)); }
445
+ .core-block > summary .when { font-size: 8.5px; letter-spacing: .1em; color: var(--ph-dim); text-transform: uppercase; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
446
+ .core-text { position: relative; padding: 4px 8px 12px; }
447
+ .core-from { margin: 0 0 8px; font-family: var(--mono); font-size: 9px; letter-spacing: .12em; line-height: 1.7; color: var(--ph-dim); text-transform: uppercase; }
448
+ .core-from b { color: var(--ph); letter-spacing: .2em; margin-right: 8px; }
449
+ .core-from .where { color: rgba(210, 255, 190, .72); }
450
+ .core-text pre { margin: 0; max-height: 40vh; overflow: auto; padding: 12px 14px; border: 1px solid var(--ph-faint); background: #000; font-family: var(--mono); font-size: 11.5px; line-height: 1.65; white-space: pre-wrap; overflow-wrap: anywhere; color: rgba(210, 255, 190, .92); text-shadow: none; }
451
+ .core-copy-one { position: absolute; top: 12px; right: 18px; border: 1px solid var(--ph-faint); background: #000; color: var(--ph-dim); font: inherit; font-size: 8.5px; letter-spacing: .16em; padding: 4px 8px; cursor: pointer; }
452
+ .core-copy-one:hover { color: var(--ph); border-color: var(--ph-dim); }
453
+ .core-fixed { margin: 0; font-family: var(--mono); font-size: 9px; letter-spacing: .12em; line-height: 1.7; color: var(--ph-dim); border-left: 2px solid color-mix(in srgb, var(--nostromo-sun) 60%, transparent); padding-left: 12px; }
454
+ .core-pane .note { margin: 0; font-size: 8.5px; letter-spacing: .12em; line-height: 1.7; color: var(--ph-dim); }
455
+
456
+ /* THE LAUNCH: the command under the document, printed as it would be run. */
457
+ .core-launch .mother-cmd { margin: 0; }
458
+ .core-launch .mother-cmd pre { max-height: 32vh; overflow: auto; font-size: 11px; color: rgba(210, 255, 190, .92); text-shadow: none; }
459
+ .core-server .tools { display: inline-flex; flex-wrap: wrap; gap: 4px; margin-left: 6px; vertical-align: middle; }
460
+ .core-server .tools i { font-style: normal; font-size: 8.5px; letter-spacing: .1em; color: var(--ph-dim); border: 1px solid var(--ph-faint); padding: 1px 5px; }
461
+
462
+ /* WHAT LEFT THIS MACHINE: the declaration, and the log that checks it. */
463
+ .core-egress { display: grid; gap: 2px; }
464
+ .egress { display: grid; grid-template-columns: 52px minmax(120px, 1fr) auto; gap: 4px 12px; align-items: baseline; padding: 8px; border: 1px solid transparent; }
465
+ .egress:hover { border-color: var(--ph-faint); background: rgba(0, 0, 0, .3); }
466
+ .egress .state { font-family: var(--mono); font-size: 8.5px; letter-spacing: .16em; color: var(--nostromo-sun); text-shadow: none; }
467
+ .egress.off .state { color: var(--ph-dim); }
468
+ .egress.local .state { color: #96b0c6; }
469
+ .egress > b { font-size: 10px; letter-spacing: .1em; color: var(--ph); text-shadow: none; }
470
+ .egress .n { font-family: var(--mono); font-size: 8.5px; letter-spacing: .12em; color: var(--ph-dim); text-align: right; white-space: nowrap; }
471
+ .egress .what, .egress .when { grid-column: 2 / -1; margin: 0; font-family: var(--mono); font-size: 10.5px; line-height: 1.6; color: rgba(210, 255, 190, .86); text-shadow: none; }
472
+ .egress .when { font-size: 9px; letter-spacing: .1em; color: var(--ph-dim); text-transform: uppercase; }
473
+ .egress .when i { font-style: normal; }
474
+ .egress .when .where { color: rgba(210, 255, 190, .72); }
475
+ .egress.off { opacity: .55; }
476
+ .egress.off:hover { opacity: 1; }
477
+ pre.core-egress-log { margin: 0; max-height: 34vh; overflow: auto; padding: 10px 12px; border: 1px solid var(--ph-faint); background: #000; font-family: var(--mono); font-size: 10.5px; line-height: 1.6; white-space: pre; color: rgba(210, 255, 190, .92); text-shadow: none; }
478
+
479
+ /* The prompt: always at the bottom, the way through all four panes. */
480
+ .console-log { margin: 0; font-family: var(--mono); font-size: 11.5px; line-height: 1.6; white-space: pre-wrap; overflow-wrap: anywhere; color: rgba(210, 255, 190, .92); text-shadow: none; }
481
+ .console-log .said { color: var(--ph-dim); }
482
+ .console-log .refused { color: var(--warn); }
483
+ .console-line { display: flex; align-items: center; gap: 8px; border-top: 1px solid var(--ph-dim); padding-top: 10px; }
484
+ .console-line .prompt { font-family: var(--mono); font-size: 13px; color: var(--nostromo-sun); text-shadow: none; }
485
+ .console-line input { flex: 1; min-width: 0; border: 0; background: transparent; color: var(--ph); font: inherit; font-family: var(--mono); font-size: 12px; letter-spacing: .06em; text-transform: uppercase; padding: 2px 0; }
486
+ .console-line input:focus { outline: 0; }
487
+ .console-line input::placeholder { color: var(--ph-dim); opacity: .7; letter-spacing: .14em; }
488
+ .console-line input:disabled { opacity: .4; }
489
+ .console-strikes { display: inline-flex; gap: 3px; }
490
+ .console-strikes i { font-style: normal; font-size: 9px; color: var(--ph-faint); }
491
+ .console-strikes i.on { color: var(--warn); }
492
+ .console-help { border: 1px solid var(--ph-faint); background: transparent; color: var(--ph-dim); font: inherit; font-size: 8.5px; letter-spacing: .16em; padding: 3px 8px; cursor: pointer; }
493
+ .console-help:hover { color: var(--ph); border-color: var(--ph-dim); }
494
+
495
+ @media (max-width: 720px) {
496
+ .core-block > summary { grid-template-columns: 14px 1fr auto; }
497
+ .core-block > summary .weigh, .core-block > summary .when { display: none; }
498
+ .brew-row { grid-template-columns: 1fr; gap: 2px; }
499
+ .egress { grid-template-columns: 52px 1fr; }
500
+ .egress .n { grid-column: 1 / -1; text-align: left; }
501
+ .core-asking { grid-template-columns: 1fr; }
502
+ .core-asking .note { display: none; }
503
+ }
504
+
505
+ /* The cold zone: a colour that is not a memory kind and not the sun, so it never reads as life. */
506
+ .nostromo-head .mother-close.cold { color: #96b0c6; border-color: color-mix(in srgb, #96b0c6 45%, transparent); }
507
+ .nostromo-head .mother-close.cold[aria-pressed="true"] { background: color-mix(in srgb, #96b0c6 16%, transparent); color: #cfe0ec; }
508
+ .nostromo-card .wire-row.carries b { color: var(--kind, var(--ph)); }
509
+ .nostromo-card .wire-row.carries b::after { content: ' ⇢'; }
328
510
  .nostromo-card .forget { border: 1px solid var(--nostromo-sun); background: transparent; color: var(--nostromo-sun); font: inherit; font-size: 10px; letter-spacing: .18em; padding: 9px 12px; cursor: pointer; transition: background .2s; }
329
511
  .nostromo-card .forget:hover { background: color-mix(in srgb, var(--nostromo-sun) 14%, transparent); }
330
512
  .nostromo-card .forget.confirm { background: var(--nostromo-sun); color: #fff3f1; animation: blink 1s steps(1) infinite; }
331
513
  .nostromo-card .forget:disabled { opacity: .5; cursor: default; animation: none; }
332
514
  .nostromo-card .foot { margin: 0; font-size: 9px; letter-spacing: .1em; color: var(--ph-dim); line-height: 1.5; }
333
515
  .nostromo-empty { position: absolute; inset: 50% 0 auto 0; margin: 0; text-align: center; font-size: 11px; letter-spacing: .22em; color: var(--ph-dim); }
334
- @media (max-width: 720px) { .nostromo-head { grid-template-columns: auto 1fr auto; } .nostromo-legend { display: none; } .nostromo-card { right: 16px; left: 16px; width: auto; } }
516
+ @media (max-width: 720px) { .nostromo-head { grid-template-columns: auto 1fr; gap: 10px; } .nostromo-legend { display: none; } .nostromo-card { right: 16px; left: 16px; width: auto; } }
335
517
  .system.memory.forgotten { color: var(--terror); }
336
518
  .system.memory.privacy { color: var(--warn); }
337
- .privacy-form textarea { min-height: 64px; resize: vertical; }
519
+ /* The list of terms is the subject of this form, so it gets the whole width instead of a cell
520
+ beside the marker, where it was three lines wide and unreadable. */
521
+ .privacy-form { grid-template-columns: 1fr minmax(160px, 260px); }
522
+ .privacy-form > label:first-child { grid-column: 1 / -1; }
523
+ .privacy-form textarea { min-height: 76px; resize: vertical; line-height: 1.5; }
524
+ .privacy-form .toggle { align-items: flex-start; line-height: 1.5; text-transform: none; letter-spacing: .1em; }
525
+ .privacy-form .toggle input { margin: 2px 0 0; flex: none; }
526
+ @media (max-width: 720px) { .privacy-form { grid-template-columns: 1fr; } }
338
527
  .system.mother-alert { display: grid; gap: 6px; justify-items: center; max-width: min(720px, 92%); padding: 12px 16px; border: 1px solid color-mix(in srgb, var(--terror) 60%, transparent); border-radius: 12px; background: color-mix(in srgb, var(--terror) 7%, transparent); color: var(--terror); text-shadow: 0 0 8px color-mix(in srgb, var(--terror) 45%, transparent); }
339
528
  .system.mother-alert .head b { color: var(--terror); font-weight: 700; }
340
529
  .system.mother-alert .clear { color: var(--text); text-shadow: none; text-transform: none; letter-spacing: 0; font-size: 12px; }
@@ -346,6 +535,13 @@ dialog.nostromo::backdrop { background: #000; }
346
535
  .memory-hint .pill b { font-family: var(--mono); font-size: 9px; letter-spacing: .16em; text-transform: uppercase; color: var(--kind); }
347
536
  .memory-hint .pill:hover { background: color-mix(in srgb, var(--kind) 18%, transparent); color: var(--text); }
348
537
  .memory-hint .more { font-family: var(--mono); font-size: 9.5px; color: var(--text-3); }
538
+ /* Memory used is not memory saved: saving is an event, using happens on most turns. Same shape so
539
+ there is one thing to learn, quieter so it never competes with the answer it helped write. */
540
+ .memory-hint.used { margin-top: 4px; opacity: .78; animation: none; }
541
+ .memory-hint.used:hover { opacity: 1; }
542
+ .memory-hint.used .lead { color: var(--text-3); }
543
+ .memory-hint.used .pill { animation: none; box-shadow: none; font-size: 10.5px; padding: 2px 9px; }
544
+ .memory-hint.used .pill.carried b::after { content: ' ⇢'; }
349
545
  @keyframes hint-in { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; transform: none; } }
350
546
  @keyframes hint-glow { 0% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--kind) 55%, transparent); } 40% { box-shadow: 0 0 0 6px color-mix(in srgb, var(--kind) 0%, transparent), 0 0 18px color-mix(in srgb, var(--kind) 45%, transparent); } 100% { box-shadow: 0 0 0 0 transparent; } }
351
547
  @media (prefers-reduced-motion: reduce) { .memory-hint, .memory-hint .pill { animation: none; } }
@@ -369,11 +565,6 @@ dialog.nostromo::backdrop { background: #000; }
369
565
  .onboarding-eyebrow { margin: 0 0 10px; font-family: var(--mono); font-size: 10px; letter-spacing: .18em; color: var(--phosphor); }
370
566
  .onboarding h2 { margin: 0 0 6px; font-size: 18px; letter-spacing: -.01em; font-weight: 600; }
371
567
  .onboarding p { margin: 0 0 14px; color: var(--text-2); font-size: 13px; line-height: 1.5; }
372
- .onboarding-list { list-style: none; margin: 0 0 12px; padding: 0; display: grid; gap: 6px; }
373
- .onboarding-list li { display: grid; grid-template-columns: 28px 100px 1fr; gap: 12px; align-items: center; padding: 8px 10px; border: 1px solid var(--line); border-radius: 12px; font-size: 13px; }
374
- .onboarding-list .name { font-weight: 600; color: var(--text); }
375
- .onboarding-list .state { color: var(--text-2); font-size: 12.5px; }
376
- .onboarding-list .state b { color: var(--text); font-weight: 600; }
377
568
  .onboarding .hint { margin: 0; font-size: 12px; color: var(--text-3); }
378
569
 
379
570
  /* ================= composer ================= */
@@ -416,8 +607,6 @@ textarea::placeholder { color: var(--text-3); }
416
607
  .row { max-width: 92%; }
417
608
  .who .reply { max-width: 150px; }
418
609
  .system .gauge { width: 160px; }
419
- .onboarding-list li { grid-template-columns: 28px 1fr; }
420
- .onboarding-list .state { grid-column: 2; }
421
610
  }
422
611
 
423
612
  /* ================= MU/TH/UR: troubleshooting ================= */
@@ -428,13 +617,32 @@ textarea::placeholder { color: var(--text-3); }
428
617
  font-family: var(--mono); font-size: 10px; letter-spacing: .18em; cursor: pointer;
429
618
  }
430
619
  .mother-button:hover { color: var(--phosphor); border-color: var(--phosphor); box-shadow: 0 0 14px color-mix(in srgb, var(--phosphor) 30%, transparent); }
620
+ /* Light mode: a halo that reads as light on a dark ground reads as a smudge on a pale one. The
621
+ border and the colour already say the button is live, so the light around it stays slight. */
622
+ @media (prefers-color-scheme: light) {
623
+ :root:not([data-theme="dark"]) .mother-button:hover,
624
+ :root:not([data-theme="dark"]) .tree-button:hover,
625
+ :root:not([data-theme="dark"]) .tree-button[aria-pressed="true"],
626
+ :root:not([data-theme="dark"]) .stop-all:hover,
627
+ :root:not([data-theme="dark"]) .chats-button:hover,
628
+ :root:not([data-theme="dark"]) .chats-button[aria-pressed="true"] { box-shadow: 0 0 5px color-mix(in srgb, var(--phosphor) 12%, transparent); }
629
+ }
630
+ :root[data-theme="light"] .mother-button:hover,
631
+ :root[data-theme="light"] .tree-button:hover,
632
+ :root[data-theme="light"] .tree-button[aria-pressed="true"],
633
+ :root[data-theme="light"] .stop-all:hover,
634
+ :root[data-theme="light"] .chats-button:hover,
635
+ :root[data-theme="light"] .chats-button[aria-pressed="true"] { box-shadow: 0 0 5px color-mix(in srgb, var(--phosphor) 12%, transparent); }
636
+
637
+ .lang-button { font-family: var(--mono); font-size: 9.5px; font-weight: 600; letter-spacing: .16em; padding: 0 9px; }
431
638
  .mother-glyph { width: 8px; height: 8px; border: 1px solid currentColor; }
432
639
  .mother-count { min-width: 16px; height: 16px; padding: 0 4px; border-radius: 8px; display: inline-grid; place-items: center; background: var(--danger); color: #fff; font-size: 9px; letter-spacing: 0; }
433
640
 
434
- dialog.mother { border: 0; padding: 0; margin: 0; width: 100vw; max-width: 100vw; height: 100dvh; max-height: 100dvh; background: transparent; color: #9bff66; }
641
+ dialog.mother { border: 0; padding: 0; margin: 0; width: 100vw; max-width: 100vw; height: 100dvh; max-height: 100dvh; background: transparent; color: var(--brew); }
435
642
  dialog.mother::backdrop { background: rgba(0, 0, 0, .82); backdrop-filter: blur(6px); }
436
643
  .mother-frame {
437
- --ph: #9bff66; --ph-dim: rgba(155, 255, 102, .45); --ph-faint: rgba(155, 255, 102, .14);
644
+ /* Terminal.app's Homebrew profile, which is what a room like this should read as. */
645
+ --ph: #28fe14; --ph-dim: rgba(40, 254, 20, .52); --ph-faint: rgba(40, 254, 20, .13);
438
646
  position: relative; height: 100%; overflow-y: auto;
439
647
  padding: 26px clamp(16px, 5vw, 64px) 40px;
440
648
  background:
@@ -467,6 +675,39 @@ dialog.mother::backdrop { background: rgba(0, 0, 0, .82); backdrop-filter: blur(
467
675
  .mother-section h3.toggle:not(:first-child) { margin-top: 40px; }
468
676
  .settings .room-form, .settings .conn-grid, .settings .mother-grid { margin-bottom: 8px; }
469
677
  .mother-section h3::before { content: "▌ "; color: var(--ph); }
678
+
679
+ /* A section that folds. The summary carries the same header it always had, plus a mark on the
680
+ right when there is something behind it worth opening. */
681
+ .mother-section .fold { margin: 0; }
682
+ .mother-section .fold + .fold { margin-top: 26px; }
683
+ .mother-section .fold > summary { display: flex; align-items: baseline; gap: 10px; cursor: pointer; list-style: none; }
684
+ .mother-section .fold > summary::-webkit-details-marker { display: none; }
685
+ /* The button is a word, not a glyph: the panel already said EXPAND and COLLAPSE elsewhere. */
686
+ .mother-section .fold > summary .caret { margin-left: auto; font-family: var(--mono); font-size: 9px; letter-spacing: .18em; color: var(--ph); white-space: nowrap; }
687
+ .mother-section .fold > summary:hover h3 { color: var(--ph); }
688
+ .mother-section .fold > summary h3 { margin: 0 0 12px; }
689
+ .mother-section .fold > summary h3:not(:first-child) { margin-top: 0; padding-top: 0; border-top: 0; }
690
+ .fold-badge { font-family: var(--mono); font-size: 9px; letter-spacing: .12em; padding: 2px 7px; border-radius: 999px; border: 1px solid var(--ph-dim); color: var(--ph-dim); white-space: nowrap; }
691
+ /* Red, and only red, when there is something new: that is the whole message. */
692
+ .fold-badge.urgent { border-color: var(--danger); background: var(--danger); color: #fff; }
693
+ .fold-body { padding-top: 2px; }
694
+ .mother-section .note.lead { color: rgba(210, 255, 190, .9); letter-spacing: .12em; line-height: 1.7; }
695
+
696
+ /* Inside MU/TH/UR everything is phosphor. A command in the panel was drawn in the page's own
697
+ text colour, which on this ground is all but invisible. */
698
+ .mother-section .update-command { border-color: var(--ph-dim); border-radius: 0; background: var(--ph-faint); }
699
+ .mother-section .update-command code { color: var(--ph); }
700
+ .mother-section .update-command button { border-color: var(--ph-dim); color: var(--ph-dim); }
701
+ .mother-section .update-command button:hover { color: var(--ph); border-color: var(--ph); }
702
+
703
+ /* The steps were numbered by the browser, at the panel's own size, which made them shout. They
704
+ are a quiet index in the margin now, like everything else here. */
705
+ .mother-section .train-steps { list-style: none; padding-left: 26px; counter-reset: step; }
706
+ .mother-section .train-steps > li { counter-increment: step; position: relative; margin: 14px 0; }
707
+ .mother-section .train-steps > li::before { content: counter(step); position: absolute; left: -26px; top: 1px; width: 18px; text-align: right; font-family: var(--mono); font-size: 9px; letter-spacing: .1em; color: var(--ph-dim); opacity: .8; }
708
+ .mother-section .train-steps > li.note { counter-increment: none; margin-left: 0; }
709
+ .mother-section .train-steps > li.note::before { content: none; }
710
+ .mother-section .train-step-label { color: var(--ph-dim); }
470
711
  .mother-grid { display: grid; gap: 12px; grid-template-columns: repeat(auto-fill, minmax(min(100%, 380px), 1fr)); }
471
712
  .mother-card { border: 1px solid var(--ph-dim); padding: 14px 16px; background: rgba(2, 6, 2, .6); display: grid; gap: 8px; align-content: start; }
472
713
  .mother-card.hit { border-color: var(--ph); box-shadow: inset 0 0 0 1px var(--ph-faint), 0 0 22px rgba(155, 255, 102, .12); }
@@ -488,11 +729,13 @@ dialog.mother::backdrop { background: rgba(0, 0, 0, .82); backdrop-filter: blur(
488
729
  .mother-card.hit p.remedy { box-shadow: 0 0 18px rgba(155, 255, 102, .18); animation: remedy-in .6s ease; }
489
730
  @keyframes remedy-in { from { background: rgba(155, 255, 102, .35); } to { background: var(--ph-faint); } }
490
731
  .mother-cmd { position: relative; margin: 4px 0 0; border: 1px solid var(--ph-faint); background: #000; padding: 10px 12px; }
491
- .mother-cmd pre { margin: 0; font: inherit; font-size: 12px; line-height: 1.6; white-space: pre-wrap; overflow-wrap: anywhere; color: var(--ph); }
732
+ .mother-cmd pre { margin: 0; padding-right: 30px; font: inherit; font-size: 12px; line-height: 1.6; white-space: pre-wrap; overflow-wrap: anywhere; color: var(--ph); }
492
733
  .mother-cmd .c { color: var(--ph-dim); }
493
- .mother-cmd .copy { position: absolute; top: 6px; right: 6px; border: 1px solid var(--ph-dim); background: #000; color: var(--ph-dim); font: inherit; font-size: 9.5px; letter-spacing: .16em; padding: 3px 7px; cursor: pointer; }
734
+ .mother-cmd .copy { position: absolute; top: 6px; right: 6px; display: inline-flex; align-items: center; justify-content: center; width: 22px; height: 22px; border: 1px solid var(--ph-faint); border-radius: 3px; background: #000; color: var(--ph-dim); padding: 0; cursor: pointer; }
735
+ .mother-cmd .copy:hover { color: var(--ph); border-color: var(--ph-dim); }
736
+ .mother-cmd .copy.done { color: var(--ph); border-color: var(--ph); }
494
737
  .mother-cmd .copy:hover { color: var(--ph); border-color: var(--ph); }
495
- .mother-record { display: grid; grid-template-columns: 64px 92px 1fr auto; gap: 12px; align-items: baseline; padding: 8px 0; border-bottom: 1px solid var(--ph-faint); font-size: 12px; }
738
+ .mother-record { display: grid; grid-template-columns: 64px 92px 1fr auto 20px; gap: 14px; align-items: baseline; padding: 10px 0; border-bottom: 1px solid var(--ph-faint); font-size: 12px; }
496
739
  .mother-record .t { color: var(--ph-dim); }
497
740
  .mother-record .a { color: var(--agent, var(--ph)); text-transform: uppercase; letter-spacing: .1em; }
498
741
  .mother-record .e { color: rgba(210, 255, 190, .85); text-shadow: none; overflow-wrap: anywhere; }
@@ -527,15 +770,32 @@ dialog.mother::backdrop { background: rgba(0, 0, 0, .82); backdrop-filter: blur(
527
770
  /* ================= MODULES ================= */
528
771
  .modules-glyph { border-radius: 50%; }
529
772
  .modules-count { min-width: 16px; height: 16px; padding: 0 4px; border-radius: 8px; display: inline-grid; place-items: center; background: var(--phosphor); color: var(--phosphor-ink); font-size: 9px; letter-spacing: 0; }
530
- .module-card { border: 1px solid var(--ph-dim); padding: 18px 20px; background: rgba(2, 6, 2, .6); display: grid; gap: 12px; max-width: 760px; min-width: 0; overflow-wrap: anywhere; }
773
+ /* A module card is floors, not a stack of paragraphs: who it is, what it does, what it touches,
774
+ what it lets you set, and the switch. Each floor owns its own padding and is separated by a
775
+ hairline, so nothing can crowd anything else. The floors stack from the top and the switch is
776
+ pushed to the bottom — as a stretched grid the card used to spread the spare height of the
777
+ tallest card in the row between its own rows, which is why the text drifted apart on some
778
+ cards and not on others. */
779
+ .module-card { border: 1px solid var(--ph-dim); background: rgba(2, 6, 2, .6); display: flex; flex-direction: column; padding: 0; max-width: 760px; min-width: 0; overflow-wrap: anywhere; }
780
+ /* The switch sits on the floor of the card, not wherever the text happens to end: cards in a row
781
+ are the same height, so their buttons line up along the bottom edge. */
782
+ .module-card > .actions { margin-top: auto; }
783
+ .module-card:not(.dev-card) > * { padding: 16px 20px; }
784
+ .module-card:not(.dev-card) > * + * { border-top: 1px solid var(--ph-faint); }
531
785
  .mother-grid > * { min-width: 0; }
532
- .module-card .head { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 12px; align-items: start; }
786
+ .module-card .head { display: grid; gap: 7px; }
787
+ .module-card .head .top { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 12px; align-items: baseline; }
533
788
  .module-card h4 { margin: 0; font-size: 16px; letter-spacing: .1em; text-transform: uppercase; }
534
- .module-card .vendor { font-size: 10px; letter-spacing: .18em; color: var(--ph-dim); }
535
- .module-card .state { font-size: 10px; letter-spacing: .18em; padding: 4px 8px; border: 1px solid var(--ph-dim); color: var(--ph-dim); white-space: nowrap; }
536
- .module-card .detail { font-size: 10px; letter-spacing: .12em; color: var(--ph-dim); }
537
- .module-card .state.installed { color: var(--ph); border-color: var(--ph); }
538
- .module-card .state.running { color: #ffd166; border-color: #ffd166; animation: blink 1s infinite; }
789
+ /* The state is a state, not a button: a word and a dot, never a box you want to press. */
790
+ .module-card .state { font-family: var(--mono); font-size: 9.5px; letter-spacing: .18em; color: var(--ph-dim); white-space: nowrap; display: inline-flex; align-items: center; gap: 6px; }
791
+ .module-card .state::before { content: ''; width: 6px; height: 6px; border-radius: 50%; background: currentColor; opacity: .5; }
792
+ .module-card .state.on { color: var(--ph); }
793
+ .module-card .state.on::before { opacity: 1; box-shadow: 0 0 8px var(--ph); }
794
+ .module-card .state.running { color: #ffd166; animation: blink 1s infinite; }
795
+ /* Switched off, the whole card steps back — except the switch that turns it on again. */
796
+ .module-card.off { border-color: var(--ph-faint); }
797
+ .module-card.off > *:not(.actions) { opacity: .52; }
798
+ .module-card .detail { font-size: 10px; letter-spacing: .12em; color: var(--ph-dim); line-height: 1.5; }
539
799
  .module-card p { margin: 0; font-size: 12.5px; line-height: 1.6; color: rgba(210, 255, 190, .85); text-shadow: none; }
540
800
  .module-card ul { margin: 0; padding-left: 18px; font-size: 12px; line-height: 1.7; color: rgba(210, 255, 190, .8); text-shadow: none; }
541
801
  .module-card .actions { display: flex; gap: 10px; flex-wrap: wrap; align-items: center; }
@@ -609,14 +869,17 @@ dialog.mother::backdrop { background: rgba(0, 0, 0, .82); backdrop-filter: blur(
609
869
  .conn-card button.primary, .settings .room-form button.primary { background: var(--ph); color: #041004; text-shadow: none; border-color: var(--ph); }
610
870
  .conn-card button:disabled, .settings .room-form button:disabled { opacity: .4; cursor: default; }
611
871
  .conn-card label, .settings .room-form label { display: grid; gap: 4px; font-size: 9.5px; letter-spacing: .16em; color: var(--ph-dim); }
612
- .conn-card input, .settings .room-form input, .settings .room-form select { border: 1px solid var(--ph-dim); background: #000; color: var(--ph); font: inherit; font-size: 12px; padding: 6px 8px; letter-spacing: .04em; min-width: 0; }
613
- .conn-card input:focus, .settings .room-form input:focus { outline: 1px solid var(--ph); }
872
+ /* A textarea is a field like any other here. It was left out of this rule, so what you typed
873
+ into it was drawn in the page's own colour: invisible on this ground. */
874
+ .conn-card input, .conn-card select, .settings .room-form input, .settings .room-form select, .settings .room-form textarea { border: 1px solid var(--ph-dim); background: #000; color: var(--ph); font: inherit; font-size: 12px; padding: 6px 8px; letter-spacing: .04em; min-width: 0; }
875
+ .settings .room-form textarea::placeholder { color: var(--ph-dim); }
876
+ .conn-card input:focus, .settings .room-form input:focus, .settings .room-form textarea:focus { outline: 1px solid var(--ph); }
614
877
  .conn-card .login-log { max-height: 140px; overflow-y: auto; border: 1px solid var(--ph-faint); background: #000; padding: 8px 10px; font-size: 11px; line-height: 1.5; white-space: pre-wrap; overflow-wrap: anywhere; }
615
878
  .conn-card .login-log a { color: var(--ph); }
616
879
  .settings .room-form { border: 1px solid var(--ph-dim); padding: 14px 16px; display: grid; gap: 12px; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); align-items: end; }
617
880
  .settings .room-form .full { grid-column: 1 / -1; display: flex; gap: 10px; align-items: center; flex-wrap: wrap; }
618
881
  .settings .room-form .toggle { display: flex; align-items: center; gap: 8px; font-size: 10px; letter-spacing: .16em; color: var(--ph); cursor: pointer; }
619
- .settings .room-form .toggle input { width: auto; accent-color: #9bff66; }
882
+ .settings .room-form .toggle input { width: auto; accent-color: var(--brew); }
620
883
  .settings .note { font-size: 10px; letter-spacing: .1em; color: var(--ph-dim); }
621
884
  .system.login { color: var(--phosphor); }
622
885
  .system.login.failed { color: var(--danger); }
@@ -708,6 +971,9 @@ dialog.override[open] { display: grid; place-items: center; }
708
971
  .override-reply { min-height: 22px; letter-spacing: .14em; }
709
972
  .override-reply.denied { color: #ff7b72; text-shadow: 0 0 10px rgba(255, 123, 114, .6); animation: terror-in .8s steps(3, end) 1; }
710
973
  .override-reply.granted { color: var(--ph); text-shadow: 0 0 10px rgba(155, 255, 102, .6); }
974
+ /* MOTHER answering a name that is not this project's: her lines, and the count. */
975
+ .override-reply .line { display: block; line-height: 1.6; }
976
+ .override-reply .strike { display: block; margin-top: 6px; font-size: 9px; letter-spacing: .18em; color: var(--warn); text-shadow: none; }
711
977
  .override-frame.shake { animation: override-shake .35s ease; }
712
978
  @keyframes override-shake { 0%, 100% { transform: none; } 25% { transform: translateX(-6px); } 50% { transform: translateX(6px); } 75% { transform: translateX(-3px); } }
713
979
  .model-menu { position: fixed; z-index: 40; width: min(320px, calc(100vw - 24px)); padding: 10px; border: 1px solid var(--line-2); border-radius: 14px; background: var(--panel); box-shadow: var(--shadow); font-family: var(--mono); font-size: 11px; letter-spacing: .04em; color: var(--text); }
@@ -813,35 +1079,45 @@ dialog.viewer::backdrop { background: rgba(0,0,0,.7); backdrop-filter: blur(4px)
813
1079
  .system.lease.missing button:hover { background: color-mix(in srgb, var(--warn) 20%, transparent); }
814
1080
  .system.lease.missing button:disabled { opacity: .6; cursor: default; }
815
1081
  .conn-card .scope.sub { margin-left: 22px; font-size: 9px; margin-top: -2px; }
816
- .conn-card .ceiling { display: grid; grid-template-columns: auto auto; gap: 8px 12px; align-items: center; margin-top: 6px; padding-top: 12px; border-top: 1px solid var(--ph-faint); }
1082
+ /* One column for the label, the same width on every row and in every card, so MAX MODE and
1083
+ DEFAULT MODE start at the same place instead of each beginning where its own words end. */
1084
+ .conn-card .ceiling { display: grid; grid-template-columns: 104px 1fr; gap: 8px 12px; align-items: center; margin-top: 6px; padding-top: 12px; border-top: 1px solid var(--ph-faint); }
1085
+ .conn-card .ceiling .seg { justify-self: start; }
817
1086
  .conn-card .ceiling .k { font-size: 9.5px; letter-spacing: .16em; color: var(--ph-dim); }
818
1087
  .conn-card .ceiling .why { grid-column: 1 / -1; font-size: 9px; letter-spacing: .08em; color: var(--ph-dim); text-transform: none; line-height: 1.5; }
819
1088
  .conn-card .seg { display: inline-flex; border: 1px solid var(--ph-dim); }
820
1089
  .conn-card .seg-option { border: 0; border-right: 1px solid var(--ph-faint); background: transparent; color: var(--ph-dim); font: inherit; font-size: 10px; letter-spacing: .1em; padding: 4px 9px; cursor: pointer; }
821
1090
  .conn-card .seg-option:last-child { border-right: 0; }
822
1091
  .conn-card .seg-option:hover { background: var(--ph-faint); color: var(--ph); }
823
- .conn-card .seg-option.current { background: var(--ph); color: #041004; font-weight: 700; }
824
- .conn-card .seg-option.o3.current { background: #ff7b72; }
1092
+ /* A chosen mode wears its own colour, the same one the composer uses for it. It used to be
1093
+ phosphor for every mode but #3, which made one card look like a warning and the rest like
1094
+ settings, when all five are the same kind of choice. */
1095
+ .conn-card .seg-option.current { background: var(--mode, var(--ph)); color: var(--mode-ink, #041004); font-weight: 700; }
1096
+ .conn-card .seg-option.o0 { --mode: var(--mode-0); --mode-ink: var(--mode-0-ink); }
1097
+ .conn-card .seg-option.o1 { --mode: var(--mode-1); --mode-ink: var(--mode-1-ink); }
1098
+ .conn-card .seg-option.o2 { --mode: var(--mode-2); --mode-ink: var(--mode-2-ink); }
1099
+ .conn-card .seg-option.o3 { --mode: var(--mode-3); --mode-ink: var(--mode-3-ink); }
1100
+ .conn-card .seg-option.o4 { --mode: var(--mode-4); --mode-ink: var(--mode-4-ink); }
825
1101
  .conn-card .seg-option:disabled { opacity: .35; cursor: default; }
826
1102
  .create-toggle[aria-pressed="true"] .lock.closed { display: none; }
827
1103
  .create-toggle[aria-pressed="true"] .lock.open { display: block; }
828
- /* ORDER 937: a bash prompt on a black terminal, Homebrew green. */
1104
+ /* Ash: a bash prompt on a black terminal, Homebrew green. */
829
1105
  /* Off: a dormant prompt, grey like the other idle controls. On: the terminal lights up. */
830
- .ashcode-toggle { height: 30px; padding: 0 10px 0 9px; border-radius: 7px; border: 1px dashed var(--line-2); background: transparent; color: var(--text-3); font-family: var(--mono); font-size: 10px; font-weight: 500; letter-spacing: .06em; text-transform: none; cursor: pointer; align-self: center; display: inline-flex; align-items: center; gap: 6px; opacity: .8; transition: color .2s, border-color .2s, box-shadow .2s, background .2s, opacity .2s; }
831
- .ashcode-toggle::before { content: '$'; color: var(--text-3); font-weight: 700; }
832
- .ashcode-toggle::after { content: ''; width: 7px; height: 12px; background: currentColor; opacity: .25; margin-left: 1px; }
833
- .ashcode-toggle:hover { opacity: 1; color: var(--text-2); border-color: var(--text-3); }
834
- .ashcode-toggle[aria-pressed="true"] { opacity: 1; border-style: solid; background: #070907; color: var(--brew); border-color: var(--brew); font-weight: 700; box-shadow: 0 0 0 1px color-mix(in srgb, var(--brew) 25%, transparent), 0 0 18px color-mix(in srgb, var(--brew) 40%, transparent), inset 0 0 12px color-mix(in srgb, var(--brew) 14%, transparent); text-shadow: 0 0 8px color-mix(in srgb, var(--brew) 70%, transparent); }
835
- .ashcode-toggle[aria-pressed="true"]::before { color: color-mix(in srgb, var(--brew) 75%, transparent); }
836
- .ashcode-toggle[aria-pressed="true"]::after { animation: cursor-blink 1s steps(1) infinite; opacity: 1; }
837
- .ashcode-toggle[aria-pressed="true"]:hover { color: var(--brew); border-color: var(--brew); }
838
- :root[data-theme="light"] .ashcode-toggle, .ashcode-toggle { /* stays a black terminal in every theme */ }
839
-
840
- /* With ORDER 937 lit, the field widens like CREATE and glows Homebrew green. */
841
- .composer.ordering { width: min(1180px, 100% - 40px); }
842
- .composer.ordering .field { position: relative; border-color: color-mix(in srgb, var(--brew) 70%, transparent); box-shadow: 0 0 0 3px color-mix(in srgb, var(--brew) 14%, transparent); }
843
- .composer.ordering .crew-label { color: var(--brew); text-shadow: 0 0 8px color-mix(in srgb, var(--brew) 55%, transparent); }
844
- .composer.ordering .editor textarea::placeholder { color: color-mix(in srgb, var(--brew) 60%, transparent); }
1106
+ .ash-toggle { height: 30px; padding: 0 10px 0 9px; border-radius: 7px; border: 1px dashed var(--line-2); background: transparent; color: var(--text-3); font-family: var(--mono); font-size: 10px; font-weight: 500; letter-spacing: .06em; text-transform: none; cursor: pointer; align-self: center; display: inline-flex; align-items: center; gap: 6px; opacity: .8; transition: color .2s, border-color .2s, box-shadow .2s, background .2s, opacity .2s; }
1107
+ .ash-toggle::before { content: '$'; color: var(--text-3); font-weight: 700; }
1108
+ .ash-toggle::after { content: ''; width: 7px; height: 12px; background: currentColor; opacity: .25; margin-left: 1px; }
1109
+ .ash-toggle:hover { opacity: 1; color: var(--text-2); border-color: var(--text-3); }
1110
+ .ash-toggle[aria-pressed="true"] { opacity: 1; border-style: solid; background: #070907; color: var(--brew); border-color: var(--brew); font-weight: 700; box-shadow: 0 0 0 1px color-mix(in srgb, var(--brew) 25%, transparent), 0 0 18px color-mix(in srgb, var(--brew) 40%, transparent), inset 0 0 12px color-mix(in srgb, var(--brew) 14%, transparent); text-shadow: 0 0 8px color-mix(in srgb, var(--brew) 70%, transparent); }
1111
+ .ash-toggle[aria-pressed="true"]::before { color: color-mix(in srgb, var(--brew) 75%, transparent); }
1112
+ .ash-toggle[aria-pressed="true"]::after { animation: cursor-blink 1s steps(1) infinite; opacity: 1; }
1113
+ .ash-toggle[aria-pressed="true"]:hover { color: var(--brew); border-color: var(--brew); }
1114
+ :root[data-theme="light"] .ash-toggle, .ash-toggle { /* stays a black terminal in every theme */ }
1115
+
1116
+ /* With Ash lit, the field widens like CREATE and glows Homebrew green. */
1117
+ .composer.ash-on { width: min(1180px, 100% - 40px); }
1118
+ .composer.ash-on .field { position: relative; border-color: color-mix(in srgb, var(--brew) 70%, transparent); box-shadow: 0 0 0 3px color-mix(in srgb, var(--brew) 14%, transparent); }
1119
+ .composer.ash-on .crew-label { color: var(--brew); text-shadow: 0 0 8px color-mix(in srgb, var(--brew) 55%, transparent); }
1120
+ .composer.ash-on .editor textarea::placeholder { color: color-mix(in srgb, var(--brew) 60%, transparent); }
845
1121
  .composer.creating.ordering .field { border-color: var(--mode-2); box-shadow: 0 0 0 3px color-mix(in srgb, var(--mode-2) 18%, transparent), inset 0 0 0 1px color-mix(in srgb, var(--brew) 45%, transparent); }
846
1122
 
847
1123
  /* The wink to MOTHER when the order is given: a CRT sweep and scanlines that fade. */
@@ -882,10 +1158,9 @@ dialog.viewer::backdrop { background: rgba(0,0,0,.7); backdrop-filter: blur(4px)
882
1158
 
883
1159
  .who .badge.ash { color: var(--phosphor); border-color: color-mix(in srgb, var(--phosphor) 50%, transparent); }
884
1160
  .who .badge.ash.skipped { color: var(--text-3); border-color: var(--line-2); }
885
- .ash-original { margin-top: 10px; border-top: 1px dashed var(--line-2); padding-top: 8px; }
886
- .ash-original summary { color: var(--text-3); font-family: var(--mono); font-size: 10px; cursor: pointer; }
887
- .ash-original pre { white-space: pre-wrap; overflow-wrap: anywhere; margin: 8px 0 0; font: inherit; color: var(--text-2); }
888
- .ash-beta { color: var(--warn); font-size: 12px; }
1161
+ .ash-was { margin-top: 10px; border-top: 1px dashed var(--line-2); padding-top: 8px; }
1162
+ .ash-was summary { color: var(--text-3); font-family: var(--mono); font-size: 10px; cursor: pointer; }
1163
+ .ash-was pre { white-space: pre-wrap; overflow-wrap: anywhere; margin: 8px 0 0; font: inherit; color: var(--text-2); }
889
1164
  .create-scopes { display: inline-flex; gap: 4px; margin-left: 4px; }
890
1165
  .create-scopes .cap { font-size: 8.5px; padding: 1px 5px; }
891
1166
  .create-scopes .cap.on { color: var(--warn); border-color: color-mix(in srgb, var(--warn) 55%, transparent); }
@@ -956,7 +1231,7 @@ dialog.viewer::backdrop { background: rgba(0,0,0,.7); backdrop-filter: blur(4px)
956
1231
  .conn-card .scope .assist { margin-left: 6px; border: 1px solid var(--ph-dim); background: transparent; color: var(--ph); font: inherit; font-size: 8.5px; letter-spacing: .14em; padding: 1px 6px; cursor: pointer; text-transform: uppercase; }
957
1232
  .conn-card .scope .assist:hover { background: var(--ph-faint); }
958
1233
  .conn-card .scope { display: flex; align-items: center; gap: 8px; font-size: 10px; letter-spacing: .12em; color: var(--ph); cursor: pointer; }
959
- .conn-card .scope input { accent-color: #9bff66; width: auto; }
1234
+ .conn-card .scope input { accent-color: var(--brew); width: auto; }
960
1235
  .conn-card .scope.unavailable { color: var(--ph-dim); cursor: not-allowed; }
961
1236
  .conn-card .scope .why { margin-left: auto; font-size: 9px; letter-spacing: .08em; color: var(--ph-dim); text-transform: none; }
962
1237
 
@@ -980,6 +1255,25 @@ dialog.viewer::backdrop { background: rgba(0,0,0,.7); backdrop-filter: blur(4px)
980
1255
  .editor .highlight .chip.cmd { --agent: var(--phosphor); font-family: var(--mono); font-size: 13px; letter-spacing: .04em; }
981
1256
  .editor .highlight .chip.file { --agent: var(--text-2); font-family: var(--mono); font-size: 13px; letter-spacing: 0; font-weight: 500; }
982
1257
  .editor .highlight .chip.unknown { --agent: var(--text-3); text-decoration: underline dotted; font-weight: 500; }
1258
+ /* A mention is a word in the sentence you are writing, so it is only ever coloured. A pill drawn
1259
+ round it turns the line into a form to fill in and breaks the rhythm of reading it back. */
1260
+ .editor .highlight .chip.agent { background: transparent; box-shadow: none; }
1261
+ /* What you are answering, inside the field and above the line you write on. It takes a row of
1262
+ its own, so the box grows by exactly its height and the writing space is never eaten. Two
1263
+ points smaller than what you type, in a badge faint enough not to compete with it. */
1264
+ .reply-quote {
1265
+ grid-column: 1 / -1;
1266
+ display: flex; align-items: baseline; gap: 7px;
1267
+ margin: 2px 0 0 -6px; padding: 6px 8px 6px 12px; border-radius: 12px;
1268
+ background: color-mix(in srgb, var(--text-3) 9%, transparent);
1269
+ font-size: 13px; line-height: 1.4; color: var(--text-3);
1270
+ }
1271
+ /* With something quoted the field gives you room to answer in rather than one cramped line. */
1272
+ .field.quoting textarea, .field.quoting .highlight { min-height: 42px; }
1273
+ .reply-quote b { flex: none; color: var(--text-2); font-weight: 600; }
1274
+ .reply-quote .said { min-width: 0; overflow: hidden; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; }
1275
+ .reply-quote .drop { flex: none; margin-left: auto; width: 18px; height: 18px; display: grid; place-items: center; border: 0; border-radius: 50%; background: transparent; color: var(--text-3); font-size: 14px; line-height: 1; cursor: pointer; }
1276
+ .reply-quote .drop:hover { background: color-mix(in srgb, var(--text-3) 14%, transparent); color: var(--text); }
983
1277
  .slash-menu { position: absolute; left: -12px; bottom: calc(100% + 14px); z-index: 30; min-width: 280px; max-width: min(460px, 90vw); border: 1px solid var(--line-2); background: var(--panel); border-radius: 14px; box-shadow: 0 14px 40px rgba(0, 0, 0, .35); padding: 6px; display: grid; gap: 2px; }
984
1278
  .slash-menu .item { display: grid; grid-template-columns: auto 1fr; gap: 10px; align-items: baseline; padding: 7px 10px; border-radius: 9px; cursor: pointer; border: 0; background: transparent; color: var(--text); text-align: left; font: inherit; }
985
1279
  .slash-menu .item .key { font-family: var(--mono); font-size: 12.5px; color: var(--agent, var(--phosphor)); font-weight: 600; white-space: nowrap; }
@@ -993,6 +1287,33 @@ dialog.viewer::backdrop { background: rgba(0,0,0,.7); backdrop-filter: blur(4px)
993
1287
  .theme-button svg { display: none; }
994
1288
  .theme-button[data-theme="auto"] .t-auto, .theme-button[data-theme="light"] .t-light, .theme-button[data-theme="dark"] .t-dark { display: block; }
995
1289
  .tree-button:hover, .tree-button[aria-pressed="true"] { color: var(--phosphor); border-color: var(--phosphor); box-shadow: 0 0 14px color-mix(in srgb, var(--phosphor) 30%, transparent); }
1290
+ /* The conversations of this project, on the other side of the canvas from the files: the same
1291
+ panel, so there is one shape to learn, and never both edges at once on a narrow screen. */
1292
+ .tree.chats { justify-self: start; margin: 18px 0 10px 20px; grid-template-rows: auto minmax(0, 1fr) auto auto; }
1293
+ .chats-new { padding: 10px 12px; border-top: 1px solid var(--line); }
1294
+ .chats-new-button { display: flex; align-items: center; justify-content: center; gap: 8px; width: 100%; border: 1px dashed var(--line-2); border-radius: 10px; background: transparent; color: var(--text-2); font-family: var(--mono); font-size: 10px; letter-spacing: .16em; padding: 9px 10px; cursor: pointer; }
1295
+ .chats-new-button svg { width: 14px; height: 14px; }
1296
+ .chats-new-button:hover { color: var(--phosphor); border-color: var(--phosphor); background: color-mix(in srgb, var(--phosphor) 8%, transparent); }
1297
+ .chats-body { padding: 10px 8px; display: grid; gap: 6px; align-content: start; }
1298
+ .chats-body .chat { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 10px; align-items: baseline; width: 100%; border: 0; background: transparent; color: var(--text-2); font: inherit; text-align: left; padding: 11px 12px; border-radius: 10px; cursor: pointer; }
1299
+ .chats-body .chat:hover { background: color-mix(in srgb, var(--phosphor) 10%, transparent); color: var(--text); }
1300
+ .chats-body .chat.on { background: color-mix(in srgb, var(--phosphor) 14%, transparent); color: var(--text); }
1301
+ .chats-body .chat .name { font-family: var(--sans, inherit); font-size: 12.5px; line-height: 1.4; color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
1302
+ .chats-body .chat .when { font-family: var(--mono); font-size: 9px; letter-spacing: .08em; color: var(--text-3); white-space: nowrap; }
1303
+ .chats-body .chat-row { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center; gap: 4px; }
1304
+ .chats-body .chat-drop { border: 0; background: transparent; color: var(--text-3); font: inherit; font-size: 13px; line-height: 1; padding: 4px 7px; border-radius: 7px; cursor: pointer; opacity: 0; }
1305
+ .chats-body .chat-row:hover .chat-drop, .chats-body .chat-drop:focus-visible { opacity: 1; }
1306
+ .chats-body .chat-drop:hover { color: var(--danger); background: color-mix(in srgb, var(--danger) 12%, transparent); }
1307
+ .chats-body .chat-drop.sure { opacity: 1; color: #fff; background: var(--danger); }
1308
+ .project.chat-here { flex: 0 1 auto; max-width: 340px; color: var(--text-2); }
1309
+ .chats-foot { padding: 10px 14px; border-top: 1px solid var(--line); font-family: var(--mono); font-size: 8.5px; letter-spacing: .12em; color: var(--text-3); }
1310
+ /* Its handle sits in the canvas, under the bar, where the conversation it opens begins. */
1311
+ .chats-button { grid-row: 3; justify-self: start; align-self: start; z-index: 6; margin: 18px 0 0 20px; display: inline-flex; align-items: center; justify-content: center; width: 40px; height: 36px; border-radius: 11px; border: 1px solid var(--line-2); background: var(--panel); color: var(--text-2); cursor: pointer; padding: 0; }
1312
+ .chats-button svg { width: 21px; height: 21px; }
1313
+ .chats-button:hover, .chats-button[aria-pressed="true"] { color: var(--phosphor); border-color: var(--phosphor); box-shadow: 0 0 14px color-mix(in srgb, var(--phosphor) 30%, transparent); }
1314
+ .chats-button:active { transform: translateY(1px); }
1315
+ @media (max-width: 1100px) { .chats-button { display: none; } }
1316
+
996
1317
  .tree { grid-row: 3; justify-self: end; align-self: stretch; z-index: 5; width: clamp(240px, calc((100% - var(--column)) / 2 - 36px), 420px); margin: 18px 20px 10px 0; min-height: 0; display: grid; grid-template-rows: auto minmax(0, 1fr); border: 1px solid var(--line-2); border-radius: 18px; background: var(--panel); box-shadow: 0 10px 30px rgba(0, 0, 0, .06); overflow: hidden; animation: tree-in .22s ease; }
997
1318
  @keyframes tree-in { from { opacity: 0; transform: translateX(8px); } to { opacity: 1; transform: none; } }
998
1319
  .tree-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 12px 12px 10px 16px; border-bottom: 1px solid var(--line); font-family: var(--mono); font-size: 10px; letter-spacing: .18em; color: var(--text-2); }
@@ -1019,6 +1340,23 @@ dialog.viewer::backdrop { background: rgba(0,0,0,.7); backdrop-filter: blur(4px)
1019
1340
 
1020
1341
  .command-card { margin: 10px 0 6px; max-width: min(860px, 100%); border: 1px solid var(--line-2); border-radius: 12px; background: color-mix(in srgb, var(--panel) 80%, transparent); overflow: hidden; }
1021
1342
  .command-card.failed { border-color: color-mix(in srgb, var(--crit) 60%, transparent); }
1343
+
1344
+ /* EYECAT: a question the room is asking the human, so it reads as one thing until it is answered. */
1345
+ .command-card.eyecat { border-color: color-mix(in srgb, var(--mem-aberration) 55%, transparent); }
1346
+ .command-card.eyecat .head b { color: var(--mem-aberration); }
1347
+ .command-card.eyecat > div:not(.head):not(.eyecat-actions):not(.eyecat-settled) { padding: 10px 12px; border-bottom: 1px solid var(--line-2); }
1348
+ .command-card.eyecat b { display: block; font-family: var(--mono); font-size: 9px; letter-spacing: .18em; color: var(--text-2); margin-bottom: 4px; }
1349
+ .command-card.eyecat p { margin: 0; font-size: 13.5px; line-height: 1.5; }
1350
+ .command-card.eyecat .eyecat-claim p { color: var(--mem-aberration); }
1351
+ .command-card.eyecat .eyecat-correction p { color: var(--ok, #9bff66); }
1352
+ .eyecat-actions { display: flex; gap: 8px; padding: 10px 12px; }
1353
+ .eyecat-actions button { font-family: var(--mono); font-size: 10px; letter-spacing: .16em; padding: 6px 12px; border-radius: 999px; border: 1px solid var(--line-2); background: transparent; color: var(--text-2); cursor: pointer; }
1354
+ .eyecat-actions button.primary { border-color: color-mix(in srgb, var(--mem-aberration) 70%, transparent); color: var(--mem-aberration); }
1355
+ .eyecat-actions button:hover:not(:disabled) { background: var(--ph-faint); }
1356
+ .eyecat-actions button:disabled { opacity: .45; cursor: default; }
1357
+ .eyecat-settled { padding: 9px 12px; font-family: var(--mono); font-size: 9.5px; letter-spacing: .16em; color: var(--text-2); }
1358
+ .command-card.eyecat.filed { border-color: color-mix(in srgb, var(--mem-aberration) 75%, transparent); }
1359
+ .command-card.eyecat.stands { border-color: var(--line-2); opacity: .7; }
1022
1360
  .command-card .head { display: flex; gap: 10px; align-items: baseline; padding: 8px 12px; font-family: var(--mono); font-size: 10.5px; letter-spacing: .14em; color: var(--text-2); border-bottom: 1px solid var(--line-2); }
1023
1361
  .command-card .head b { color: var(--phosphor); font-weight: 600; }
1024
1362
  .command-card .head .args { color: var(--text-3); letter-spacing: .04em; }
@@ -1054,7 +1392,7 @@ dialog.viewer::backdrop { background: rgba(0,0,0,.7); backdrop-filter: blur(4px)
1054
1392
  :root[data-scheme="light"] .composer.control .field { box-shadow: 0 0 0 2px color-mix(in srgb, var(--terror) 30%, transparent); }
1055
1393
  :root[data-scheme="light"] .composer.airlock .field { box-shadow: 0 0 0 2px color-mix(in srgb, var(--mode-4) 35%, transparent); }
1056
1394
  :root[data-scheme="light"] .composer.creating .field, :root[data-scheme="light"] .composer.creating.ordering .field { box-shadow: 0 0 0 2px color-mix(in srgb, var(--mode-2) 30%, transparent); }
1057
- :root[data-scheme="light"] .composer.ordering .field { box-shadow: 0 0 0 2px color-mix(in srgb, var(--brew) 22%, transparent); }
1395
+ :root[data-scheme="light"] .composer.ash-on .field { box-shadow: 0 0 0 2px color-mix(in srgb, var(--brew) 22%, transparent); }
1058
1396
  :root[data-scheme="light"] .composer.dropping .field { box-shadow: 0 0 0 2px color-mix(in srgb, var(--phosphor) 35%, transparent); }
1059
1397
  :root[data-scheme="light"] .memory-hint .pill { animation: none; box-shadow: none; }
1060
1398
  :root[data-scheme="light"] .mode-menu .mode-option .tag .dot { box-shadow: none; }
@@ -1067,22 +1405,241 @@ dialog.viewer::backdrop { background: rgba(0,0,0,.7); backdrop-filter: blur(4px)
1067
1405
  /* MU/TH/UR: the sentinel section. */
1068
1406
  .mother-section.sentinel .sentinel-controls, .mother-section.update .sentinel-controls { display: flex; flex-wrap: wrap; align-items: center; gap: 14px; margin: 6px 0 12px; }
1069
1407
  .mother-section.sentinel .toggle, .mother-section.update .toggle { display: flex; align-items: center; gap: 8px; font-size: 10px; letter-spacing: .16em; color: var(--ph-dim); cursor: pointer; }
1070
- .mother-section.sentinel .toggle input, .mother-section.update .toggle input { accent-color: #9bff66; }
1408
+ .mother-section.sentinel .toggle input, .mother-section.update .toggle input { accent-color: var(--brew); margin: 0; flex: none; }
1409
+ /* The row starts where the text above it starts, so the column reads straight down. */
1410
+ .mother-section.sentinel .sentinel-controls, .mother-section.update .sentinel-controls { margin-left: 0; padding-left: 0; }
1071
1411
  .mother-section.sentinel .toggle input:disabled + * , .mother-section.sentinel .toggle:has(input:disabled) { opacity: .6; cursor: default; }
1072
1412
  .mother-section.sentinel button, .mother-section.update button { border: 1px solid var(--ph-dim); background: transparent; color: var(--ph); font: inherit; font-size: 9.5px; letter-spacing: .16em; padding: 6px 10px; cursor: pointer; }
1073
1413
  .mother-section.sentinel button:hover, .mother-section.update button:hover { background: var(--ph-faint); }
1074
1414
  .mother-section.sentinel .sentinel-report.sent { opacity: .65; }
1075
1415
  .settings .memory-form .toggle { display: inline-flex; align-items: center; gap: 6px; margin-right: 14px; font-size: 9.5px; letter-spacing: .14em; color: var(--ph-dim); cursor: pointer; text-transform: uppercase; }
1076
- .settings .memory-form .toggle input { accent-color: #9bff66; width: auto; }
1416
+ .settings .memory-form .toggle input { accent-color: var(--brew); width: auto; }
1077
1417
  .settings .memory-form .full .note { margin-right: 12px; }
1078
1418
  .settings .memory-form .dataset-row { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; padding-top: 8px; border-top: 1px solid var(--ph-faint); }
1079
- .module-card .ollama-status { margin: 0 0 10px; display: grid; grid-template-columns: auto 1fr; gap: 3px 12px; font-size: 10px; letter-spacing: .1em; }
1419
+ .module-card .ollama-status { margin: 0; display: grid; grid-template-columns: auto 1fr; gap: 3px 12px; font-size: 10px; letter-spacing: .1em; }
1080
1420
  .module-card .ollama-status dt { color: var(--ph-dim); } .module-card .ollama-status dd { margin: 0; color: rgba(210, 255, 190, .85); overflow-wrap: anywhere; }
1081
1421
  .module-card .actions .toggle { display: inline-flex; align-items: center; gap: 6px; font-size: 9.5px; letter-spacing: .14em; color: var(--ph-dim); cursor: pointer; }
1082
- .module-card .actions .toggle input { accent-color: #9bff66; }
1422
+ .module-card .actions .toggle input { accent-color: var(--brew); }
1083
1423
  .mother-section.sentinel .note, .mother-section.update .note { margin: 0 0 10px; font-size: 10px; letter-spacing: .12em; line-height: 1.7; color: var(--ph-dim); }
1084
1424
 
1085
1425
  /* Motion of controls: every colour change eases over 220 ms. Nothing snaps. */
1086
- .mother-button, .tree-button, .stop-all, .mother-close, .mother-section button, .seg-option, .mode-chip, .model-chip, .bubble-actions .act, .update-command button, .codeblock .copy { transition: background-color .22s ease, color .22s ease, border-color .22s ease, box-shadow .22s ease, opacity .22s ease, transform .16s ease; }
1426
+ .mother-button, .tree-button, .chats-button, .stop-all, .mother-close, .mother-section button, .seg-option, .mode-chip, .model-chip, .bubble-actions .act, .update-command button, .codeblock .copy { transition: background-color .22s ease, color .22s ease, border-color .22s ease, box-shadow .22s ease, opacity .22s ease, transform .16s ease; }
1087
1427
  .mother-button:active, .tree-button:active, .mother-close:active, .mother-section button:active, .update-command button:active { transform: scale(.98); }
1088
- @media (prefers-reduced-motion: reduce) { .mother-button, .tree-button, .stop-all, .mother-close, .mother-section button, .seg-option, .mode-chip, .model-chip, .bubble-actions .act, .update-command button, .update-pill, .update-restart { transition: none; } }
1428
+ @media (prefers-reduced-motion: reduce) { .mother-button, .tree-button, .chats-button, .stop-all, .mother-close, .mother-section button, .seg-option, .mode-chip, .model-chip, .bubble-actions .act, .update-command button, .update-pill, .update-restart { transition: none; } }
1429
+
1430
+ /* Developer card in MODULES and the four-step tour */
1431
+ .dev-card { display: grid; grid-template-columns: 64px 1fr; gap: 18px; align-items: start; padding: 18px 20px; }
1432
+ .dev-glyph { font-family: var(--mono); font-size: 26px; font-weight: 700; letter-spacing: .08em; color: var(--ph); text-shadow: 0 0 10px rgba(155, 255, 102, .45); line-height: 1; padding-top: 4px; }
1433
+ .dev-body h4 { margin: 0 0 8px; font-size: 13px; letter-spacing: .18em; color: var(--ph); }
1434
+ .dev-body p { margin: 0 0 10px; font-size: 11.5px; line-height: 1.65; letter-spacing: .04em; color: var(--ph-dim); text-transform: none; }
1435
+ .dev-where { margin: 0 0 12px; font-size: 10.5px; letter-spacing: .04em; color: var(--ph-dim); }
1436
+ .dev-where code { color: var(--ph); background: var(--ph-faint); padding: 2px 6px; }
1437
+ .dev-row { display: flex; flex-wrap: wrap; gap: 10px; margin-bottom: 8px; }
1438
+ .dev-row a.mother-close { text-decoration: none; display: inline-flex; align-items: center; }
1439
+ .dev-fail { color: var(--warn); } .dev-fail code { color: var(--warn); background: color-mix(in srgb, var(--warn) 12%, transparent); padding: 1px 5px; }
1440
+ .tour-button { min-width: 30px; padding: 6px 0; text-align: center; font-weight: 700; }
1441
+ .tour-frame { width: min(620px, 92vw); padding: 26px 30px 22px; }
1442
+ .tour-body { min-height: 190px; padding: 12px 0 6px; }
1443
+ .tour-step h3 { margin: 6px 0 14px; font-size: 11px; letter-spacing: .28em; color: var(--ph); font-weight: 700; }
1444
+ .tour-step p { margin: 0 0 10px; font-size: 12.5px; line-height: 1.7; letter-spacing: .03em; color: var(--ph); text-transform: none; }
1445
+ .tour-foot { display: flex; align-items: center; justify-content: space-between; gap: 14px; padding-top: 14px; border-top: 1px solid var(--ph-dim); }
1446
+ .tour-dots { display: inline-flex; gap: 8px; }
1447
+ .tour-dots .dot { width: 7px; height: 7px; border-radius: 50%; background: var(--ph-faint); border: 1px solid var(--ph-dim); transition: background-color .22s ease; }
1448
+ .tour-dots .dot.on { background: var(--ph); box-shadow: 0 0 8px rgba(155, 255, 102, .5); }
1449
+ .tour-nav { display: inline-flex; gap: 8px; }
1450
+ .tour .mother-close.primary { background: var(--ph-faint); }
1451
+
1452
+ /* DEV modules and proposals */
1453
+ .module-card .dev-tag { font-family: var(--mono); font-size: 8.5px; letter-spacing: .18em; color: var(--warn); border: 1px solid color-mix(in srgb, var(--warn) 55%, transparent); padding: 2px 7px; margin-left: 8px; align-self: center; }
1454
+ .module-card .note { font-size: 9.5px; letter-spacing: .1em; color: var(--ph-dim); text-transform: none; }
1455
+ .dev-yours { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin-top: 6px; }
1456
+ .dev-chip { display: inline-flex; align-items: center; gap: 6px; border: 1px solid var(--ph-dim); padding: 4px 8px; font-size: 10px; letter-spacing: .08em; color: var(--ph); text-transform: none; }
1457
+ .dev-chip .act-link, .module-proposed .act-link { border: 0; background: transparent; color: var(--phosphor); font: inherit; font-size: inherit; letter-spacing: .12em; text-transform: uppercase; cursor: pointer; padding: 0; text-decoration: underline; text-underline-offset: 3px; }
1458
+ .dev-chip .act-link { color: var(--warn); }
1459
+ .system.module-proposed { color: var(--text-3); } .system.module-proposed .who { color: var(--agent, var(--text-2)); }
1460
+
1461
+ /* The local model saying whether it has been measured against this project, and the one button
1462
+ that measures it. Same system line as the rest, with room for an answer and an action. */
1463
+ .system.local-model { color: var(--text-3); display: grid; gap: 8px; justify-items: center; }
1464
+ .system.local-model .who { color: var(--agent, var(--text-2)); }
1465
+ .system.local-model .says { margin: 0; max-width: 52ch; font-size: 11px; letter-spacing: .04em; text-transform: none; line-height: 1.65; color: var(--text-2); }
1466
+ .system.local-model .acts { display: flex; flex-wrap: wrap; justify-content: center; align-items: center; gap: 8px; }
1467
+ .system.local-model button { border: 1px solid var(--line-2); border-radius: 999px; background: transparent; color: var(--text-2); font: inherit; font-size: 9.5px; letter-spacing: .16em; text-transform: uppercase; padding: 5px 12px; cursor: pointer; }
1468
+ .system.local-model button:hover:not(:disabled) { border-color: var(--phosphor); color: var(--phosphor); }
1469
+ .system.local-model button:disabled { opacity: .5; cursor: default; }
1470
+ .system.local-model button.primary { border-color: var(--phosphor); color: var(--phosphor); }
1471
+ .system.local-model .note { font-size: 8.5px; letter-spacing: .14em; color: var(--text-3); }
1472
+
1473
+ /* THE BRIDGE: first contact, one card per agent with the one action it needs now. */
1474
+ .bridge { max-width: 640px; }
1475
+ .bridge h2 { margin: 0 0 8px; }
1476
+ .bridge-crew { display: grid; gap: 10px; width: 100%; margin: 18px 0 6px; text-align: left; }
1477
+ .bridge-card { border: 1px solid var(--line-2); border-radius: 14px; padding: 14px 16px; background: var(--panel); display: grid; gap: 10px; }
1478
+ .bridge-card .head { display: flex; align-items: center; gap: 10px; }
1479
+ .bridge-card .title { display: grid; gap: 2px; min-width: 0; flex: 1; }
1480
+ .bridge-card .title b { font-size: 13px; letter-spacing: .02em; color: var(--text); }
1481
+ .bridge-card .vendor { font-family: var(--mono); font-size: 9px; letter-spacing: .16em; text-transform: uppercase; color: var(--text-3); }
1482
+ .bridge-card .state { font-family: var(--mono); font-size: 9px; letter-spacing: .16em; text-transform: uppercase; color: var(--text-3); border: 1px solid var(--line-2); border-radius: 999px; padding: 3px 9px; white-space: nowrap; }
1483
+ .bridge-card .state.ready { color: var(--phosphor); border-color: color-mix(in srgb, var(--phosphor) 55%, transparent); }
1484
+ .bridge-card .state.signed-out { color: var(--warn); border-color: color-mix(in srgb, var(--warn) 50%, transparent); }
1485
+ .bridge-card .detail { font-size: 11.5px; line-height: 1.5; color: var(--text-2); }
1486
+ .bridge-card .actions { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
1487
+ .bridge-card .actions button { font-family: var(--mono); font-size: 10px; letter-spacing: .14em; text-transform: uppercase; border: 1px solid var(--line-2); background: transparent; color: var(--text-2); border-radius: 8px; padding: 7px 12px; cursor: pointer; transition: background-color .22s ease, color .22s ease, border-color .22s ease; }
1488
+ .bridge-card .actions button:hover { color: var(--text); border-color: var(--text-3); }
1489
+ .bridge-card .actions button.primary { border-color: var(--phosphor); color: var(--phosphor); background: color-mix(in srgb, var(--phosphor) 10%, transparent); }
1490
+ .bridge-card .actions button.primary:hover { background: color-mix(in srgb, var(--phosphor) 20%, transparent); }
1491
+ .bridge-card .actions button:disabled { opacity: .5; cursor: default; }
1492
+ .bridge-card .actions .note { font-family: var(--mono); font-size: 9.5px; letter-spacing: .12em; text-transform: uppercase; color: var(--text-3); }
1493
+ .bridge-log { margin: 0; max-height: 140px; overflow-y: auto; font-family: var(--mono); font-size: 10.5px; line-height: 1.55; color: var(--text-2); background: var(--bg); border: 1px solid var(--line); border-radius: 10px; padding: 8px 10px; white-space: pre-wrap; }
1494
+ .bridge-log a { color: var(--phosphor); }
1495
+
1496
+ .bridge-card .account { font-size: 11px; line-height: 1.5; color: var(--text-3); }
1497
+ .bridge-card .account .free { font-family: var(--mono); font-size: 8.5px; letter-spacing: .16em; color: var(--phosphor); border: 1px solid color-mix(in srgb, var(--phosphor) 45%, transparent); border-radius: 999px; padding: 2px 7px; margin-right: 8px; }
1498
+ .conn-card .meta .account { margin-top: 4px; color: var(--ph-dim); }
1499
+ #bridge-close { margin-top: 4px; }
1500
+ .empty-starts { display: grid; gap: 6px; width: 100%; margin-top: 4px; }
1501
+ .empty-start { font: inherit; font-size: 12.5px; line-height: 1.45; text-align: left; color: var(--text-2); background: transparent; border: 1px solid var(--line-2); border-radius: 12px; padding: 9px 12px; cursor: pointer; transition: background-color .22s ease, color .22s ease, border-color .22s ease; }
1502
+ .empty-start:hover { color: var(--text); border-color: var(--text-3); background: color-mix(in srgb, var(--text) 4%, transparent); }
1503
+ .bridge-close { font-family: var(--mono); font-size: 10px; letter-spacing: .16em; text-transform: uppercase; border: 1px solid var(--line-2); background: transparent; color: var(--text-2); border-radius: 8px; padding: 7px 12px; cursor: pointer; }
1504
+ .bridge-close:hover { color: var(--text); border-color: var(--text-3); }
1505
+
1506
+ /* Pasting a provider key: a plain form, the value never comes back. */
1507
+ .key-slot { display: grid; gap: 8px; }
1508
+ .key-form { display: grid; gap: 8px; }
1509
+ .key-form label { display: grid; gap: 4px; }
1510
+ .key-form .k { font-family: var(--mono); font-size: 9px; letter-spacing: .16em; text-transform: uppercase; color: var(--text-3); }
1511
+ .key-form input, .key-form select { font: inherit; font-size: 12.5px; color: var(--text); background: var(--bg); border: 1px solid var(--line-2); border-radius: 10px; padding: 8px 10px; width: 100%; }
1512
+ .key-form input:focus, .key-form select:focus { outline: none; border-color: var(--phosphor); }
1513
+ .key-row { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
1514
+ .key-row button { font-family: var(--mono); font-size: 10px; letter-spacing: .14em; text-transform: uppercase; border: 1px solid var(--phosphor); color: var(--phosphor); background: color-mix(in srgb, var(--phosphor) 10%, transparent); border-radius: 8px; padding: 7px 12px; cursor: pointer; }
1515
+ .key-row button:disabled { opacity: .5; cursor: default; }
1516
+ .key-link { font-family: var(--mono); font-size: 9.5px; letter-spacing: .14em; color: var(--text-3); text-decoration: none; }
1517
+ .key-link:hover { color: var(--phosphor); }
1518
+ .key-note { margin: 0; font-size: 10.5px; line-height: 1.5; color: var(--text-3); }
1519
+ .key-said { margin: 0; min-height: 1em; font-family: var(--mono); font-size: 10px; letter-spacing: .1em; color: var(--text-3); }
1520
+ .key-said.bad { color: var(--danger); text-transform: none; letter-spacing: 0; font-family: inherit; font-size: 11.5px; line-height: 1.5; }
1521
+ /* The key form is shared: it is the bridge's own, on the page's light ground, and it is also
1522
+ inside a connection card, where the ground is black and everything is phosphor. It carried the
1523
+ page's palette into both, so a key field sat there white and rounded among terminal fields.
1524
+ Inside the panel it wears the panel's clothes. */
1525
+ .conn-card .key-form { margin-bottom: 10px; }
1526
+ .conn-card .key-form .k, .conn-card .key-link, .conn-card .key-note, .conn-card .key-said { color: var(--ph-dim); }
1527
+ .conn-card .key-form input, .conn-card .key-form select { color: var(--ph); background: #000; border: 1px solid var(--ph-dim); border-radius: 0; font-size: 12px; padding: 6px 8px; }
1528
+ .conn-card .key-form input::placeholder { color: var(--ph-dim); opacity: .7; }
1529
+ .conn-card .key-form input:focus, .conn-card .key-form select:focus { outline: 1px solid var(--ph); border-color: var(--ph); }
1530
+ .conn-card .key-row button { border: 1px solid var(--ph); color: #041004; background: var(--ph); border-radius: 0; }
1531
+ .conn-card .key-row button:hover:not(:disabled) { background: var(--ph-dim); color: #041004; }
1532
+ .conn-card .key-link:hover { color: var(--ph); }
1533
+ .conn-card .key-said.bad { color: var(--terror); }
1534
+ .bridge-card.bridge-warn { border-color: color-mix(in srgb, var(--warn) 45%, transparent); background: color-mix(in srgb, var(--warn) 6%, transparent); }
1535
+ .bridge-card.bridge-warn b { color: var(--warn); }
1536
+
1537
+ /* A module card, floor by floor: what it is, what it does, what it touches, how it is doing, and
1538
+ then the switch. Everything inside a card wears the panel's phosphor, never the page's palette,
1539
+ and every number is given room so it can never land on its own label. */
1540
+ /* The version line: who made it, what version is running, the button that goes and looks for a
1541
+ newer one, and what the last look found. */
1542
+ .module-card .vendor { font-size: 9.5px; letter-spacing: .2em; color: var(--ph-dim); line-height: 1.4; }
1543
+ /* The version, the button that goes and looks, and what the last look found. */
1544
+ .module-card .version { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; }
1545
+ .module-card .version b { font-family: var(--mono); font-size: 12px; font-weight: 600; letter-spacing: .04em; color: var(--ph); text-shadow: none; }
1546
+ .module-card .version .check { border: 1px solid var(--ph-faint); background: transparent; color: var(--ph-dim); font: inherit; font-size: 11px; line-height: 1; letter-spacing: 0; padding: 3px 7px; cursor: pointer; }
1547
+ .module-card .version .check:hover { border-color: var(--ph); color: var(--ph); }
1548
+ .module-card .version .check:disabled { opacity: .4; cursor: default; }
1549
+ .mother-head .mother-close.add { color: var(--ph); border-color: var(--ph-dim); }
1550
+ .mother-head .mother-close.add:hover { background: color-mix(in srgb, var(--ph) 14%, transparent); }
1551
+ .module-card .version .get { border: 1px solid var(--warn); background: color-mix(in srgb, var(--warn) 12%, transparent); color: var(--warn); font: inherit; font-size: 8.5px; letter-spacing: .16em; padding: 3px 8px; cursor: pointer; }
1552
+ .module-card .version .get:hover { background: color-mix(in srgb, var(--warn) 22%, transparent); }
1553
+ .module-card .version .get:disabled { opacity: .45; cursor: default; }
1554
+ .module-card .version .update { font-family: var(--mono); font-size: 8.5px; letter-spacing: .14em; color: var(--ph-dim); text-shadow: none; }
1555
+ .module-card .version .update:empty { display: none; }
1556
+ .module-card .version .update.new { color: var(--warn); }
1557
+ .module-card .card-panel { display: grid; gap: 14px; }
1558
+ .module-card .card-panel:empty, .module-card .actions:empty { display: none; }
1559
+ .module-card .card-block { display: grid; gap: 8px; }
1560
+ .module-card .card-block h5 { margin: 0; font-size: 9px; font-weight: 400; letter-spacing: .22em; color: var(--ph-dim); border: 0; padding: 0; }
1561
+ .module-card .card-toggles { display: flex; flex-wrap: wrap; gap: 10px 18px; align-items: center; }
1562
+ .module-card .card-toggles .control { display: inline-flex; align-items: center; gap: 8px; font-size: 9.5px; letter-spacing: .14em; color: var(--ph-dim); }
1563
+ .module-card .card-toggles .control input[type="text"] { border: 1px solid var(--ph-dim); background: #000; color: var(--ph); font: inherit; font-size: 10.5px; letter-spacing: .04em; padding: 5px 8px; }
1564
+ .module-card .card-toggles .toggle { display: inline-flex; align-items: center; gap: 6px; font-size: 9.5px; letter-spacing: .14em; color: var(--ph-dim); cursor: pointer; }
1565
+ .module-card .card-toggles .toggle input { accent-color: var(--brew); }
1566
+
1567
+ /* A fold inside a card: the section name, how many lines are behind it, and its own button. */
1568
+ .card-fold > summary { display: flex; align-items: center; gap: 10px; cursor: pointer; list-style: none; }
1569
+ .card-fold > summary::-webkit-details-marker { display: none; }
1570
+ .card-fold > summary b { font-size: 9px; font-weight: 400; letter-spacing: .22em; color: var(--ph-dim); }
1571
+ .card-fold > summary:hover b { color: var(--ph); }
1572
+ .card-fold > summary .caret { margin-left: auto; font-family: var(--mono); font-size: 8.5px; letter-spacing: .16em; color: var(--ph); white-space: nowrap; }
1573
+ .card-fold > summary .count { font-family: var(--mono); font-size: 9px; letter-spacing: .1em; color: var(--ph-dim); }
1574
+ .card-fold-body { padding-top: 10px; }
1575
+ .module-card .card-fold ul { margin: 0; padding: 0; list-style: none; display: grid; gap: 6px; }
1576
+ .module-card .card-fold li { display: grid; grid-template-columns: 52px minmax(0, 1fr); gap: 10px; align-items: baseline; font-size: 11.5px; line-height: 1.55; color: rgba(210, 255, 190, .82); text-shadow: none; }
1577
+ .module-card .card-fold li i { font-style: normal; font-family: var(--mono); font-size: 8.5px; letter-spacing: .16em; color: var(--ph-dim); }
1578
+ .module-card .card-fold ul.card-commands { gap: 4px; }
1579
+ .module-card .card-fold ul.card-commands li { grid-template-columns: 1fr; font-family: var(--mono); font-size: 11px; letter-spacing: .04em; color: var(--ph); }
1580
+
1581
+ /* Numbers read at a glance: the figure, then its name under it, each in its own cell. */
1582
+ .module-card .metrics { display: grid; grid-template-columns: repeat(auto-fit, minmax(92px, 1fr)); gap: 12px 14px; }
1583
+ .module-card .metric { display: grid; gap: 3px; align-content: start; min-width: 0; }
1584
+ .module-card .metric b { font-family: var(--mono); font-size: 15px; font-weight: 600; line-height: 1.15; letter-spacing: .01em; color: var(--ph); text-shadow: none; overflow-wrap: anywhere; }
1585
+ .module-card .metric span { font-family: var(--mono); font-size: 8.5px; line-height: 1.4; letter-spacing: .14em; color: var(--ph-dim); text-shadow: none; }
1586
+
1587
+ /* ASH · what each block of the briefing weighs against the heaviest one. */
1588
+ .ash-blocks { display: grid; gap: 5px; }
1589
+ .ash-block { display: grid; grid-template-columns: 96px minmax(50px, 1fr) auto; gap: 10px; align-items: center; }
1590
+ .ash-block b { font-family: var(--mono); font-size: 8.5px; letter-spacing: .14em; color: var(--ph-dim); font-weight: 600; text-shadow: none; overflow: hidden; text-overflow: ellipsis; }
1591
+ .ash-block i { height: 5px; background: var(--ph-faint); position: relative; }
1592
+ .ash-block i::after { content: ''; position: absolute; inset: 0 auto 0 0; width: var(--fill, 0%); background: color-mix(in srgb, var(--ph) 65%, transparent); }
1593
+ .ash-block span { font-family: var(--mono); font-size: 8.5px; letter-spacing: .1em; color: var(--ph-dim); white-space: nowrap; text-shadow: none; }
1594
+ @media (max-width: 720px) { .ash-block { grid-template-columns: 1fr; } .ash-block i { display: none; } }
1595
+
1596
+ /* How grown the archive is. Six readings, the weakest marked, and what would raise it. */
1597
+ /* The three tests. A verdict that can say NOT YET is worth more than a bar that only fills. */
1598
+ .exams { display: grid; gap: 10px; margin: 18px 0 4px; }
1599
+ .exams h4 { margin: 0; font-size: 10px; font-weight: 400; letter-spacing: .22em; color: var(--ph); }
1600
+ .exams .exam { display: grid; gap: 6px; padding: 12px 14px; border: 1px solid var(--ph-faint); border-left: 2px solid var(--ph-dim); }
1601
+ .exams .exam.passed { border-left-color: var(--ph); }
1602
+ .exams .exam.failed { border-left-color: var(--warn); }
1603
+ .exams .exam-head { display: flex; align-items: baseline; gap: 12px; }
1604
+ .exams .exam-head b { font-family: var(--mono); font-size: 9.5px; font-weight: 600; letter-spacing: .14em; color: rgba(210, 255, 190, .9); }
1605
+ .exams .verdict { margin-left: auto; font-family: var(--mono); font-size: 9px; letter-spacing: .18em; color: var(--ph-dim); white-space: nowrap; }
1606
+ .exams .exam.passed .verdict { color: var(--ph); }
1607
+ .exams .exam.failed .verdict { color: var(--warn); }
1608
+ .exams .about { text-transform: none; letter-spacing: .02em; line-height: 1.6; }
1609
+ .exams .said { margin: 0; font-size: 12px; line-height: 1.55; color: #eaffdf; text-shadow: none; }
1610
+ .exams .actions { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; margin-top: 2px; }
1611
+ .exams button { border: 1px solid var(--ph); background: transparent; color: var(--ph); font: inherit; font-size: 10px; letter-spacing: .18em; padding: 7px 12px; cursor: pointer; }
1612
+ .exams button.primary { background: var(--ph); color: #041004; text-shadow: none; }
1613
+ .exams button:disabled { opacity: .4; cursor: default; }
1614
+
1615
+ .maturity { display: grid; gap: 10px; margin: 12px 0 4px; }
1616
+ .maturity > .card-fold { border-top: 1px solid var(--ph-faint); padding-top: 10px; }
1617
+ .maturity .maturity-next { margin: 0; font-family: var(--mono); font-size: 10.5px; line-height: 1.8; letter-spacing: .1em; text-transform: uppercase; color: #eaffdf; text-shadow: 0 0 6px rgba(155, 255, 102, .18); padding: 11px 13px; border: 1px solid var(--ph); border-left-width: 4px; background: var(--ph-faint); }
1618
+ .maturity .maturity-next b { font-size: 9px; letter-spacing: .22em; color: var(--ph); margin-right: 10px; }
1619
+ .maturity .maturity-next .where { font-size: 9px; letter-spacing: .16em; color: var(--ph); white-space: nowrap; }
1620
+ .maturity-head { display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap; }
1621
+ .maturity-head b { font-family: var(--mono); font-size: 11px; letter-spacing: .24em; }
1622
+ .maturity-head span { font-family: var(--mono); font-size: 9.5px; letter-spacing: .12em; line-height: 1.7; text-transform: uppercase; color: var(--ph-dim); }
1623
+ .maturity-head b.stage.empty, .maturity-head b.stage.sparse { color: var(--ph-dim); }
1624
+ .maturity-head b.stage.forming { color: var(--ph-dim); }
1625
+ .maturity-head b.stage.working, .maturity-head b.stage.mature { color: var(--ph); }
1626
+ .maturity-row { display: grid; grid-template-columns: 210px 90px 1fr; gap: 12px; align-items: center; }
1627
+ .maturity-row b { font-family: var(--mono); font-size: 9px; letter-spacing: .14em; color: var(--ph-dim); font-weight: 400; }
1628
+ .maturity-row i { height: 5px; background: var(--ph-faint); position: relative; }
1629
+ .maturity-row i::after { content: ''; position: absolute; inset: 0 auto 0 0; width: var(--fill, 0%); background: var(--ph-dim); }
1630
+ .maturity-row.weakest i::after { background: var(--ph); }
1631
+ .maturity-row.weakest b { color: var(--ph); }
1632
+ .maturity-row span { font-family: var(--mono); font-size: 9px; letter-spacing: .08em; color: var(--ph-dim); opacity: .8; }
1633
+ .maturity-next { color: var(--ph); }
1634
+ @media (max-width: 860px) { .maturity-row { grid-template-columns: 1fr; gap: 2px; } .maturity-row i { display: none; } }
1635
+
1636
+ /* A record you have dealt with: the way out sits at the end of its own row. */
1637
+ .mother-record .drop { justify-self: end; background: none; border: 0; color: var(--ph-faint); cursor: pointer; font: inherit; font-size: 14px; padding: 0; line-height: 1; }
1638
+ .mother-record .drop:hover { color: var(--warn); }
1639
+ .mother-card .tags .code { color: var(--ph); border: 1px solid var(--ph-faint); border-radius: 3px; padding: 0 4px; letter-spacing: .1em; }
1640
+ .mother-clear { display: inline-block; margin: 8px 10px 10px 0; background: none; border: 1px solid var(--ph-faint); border-radius: 4px; color: var(--ph-dim); cursor: pointer; font: inherit; font-size: 9px; letter-spacing: .16em; padding: 4px 10px; }
1641
+ .mother-clear:hover { color: var(--ph); border-color: var(--ph-dim); }
1642
+
1643
+ /* A remedy this room can carry out itself: the button goes above the commands it replaces. */
1644
+ .mother-goto { display: block; margin: 6px 0 0; border: 1px solid var(--ph-dim); border-radius: 4px; background: transparent; color: var(--ph); cursor: pointer; font: inherit; font-size: 10px; letter-spacing: .14em; padding: 6px 12px; text-transform: uppercase; }
1645
+ .mother-goto:hover { background: color-mix(in srgb, var(--ph) 12%, transparent); }