@echomem/mcp 1.4.43 → 1.4.44

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 (48) hide show
  1. package/README.md +25 -28
  2. package/dist/config-files.js +63 -0
  3. package/dist/context-analysis/claude-native-canonical.js +2 -2
  4. package/dist/context-analysis/vendored-canonical.js +2 -2
  5. package/dist/context-analysis/workspace-report.js +3 -3
  6. package/dist/hud/hooks.js +43 -31
  7. package/dist/index.js +15 -79
  8. package/dist/local-jsonl.js +87 -0
  9. package/dist/migrate.js +6 -4
  10. package/dist/onboarding-stats.js +16 -0
  11. package/dist/save-checkpoint-hook.js +1 -1
  12. package/dist/setup-page/client-core.js +18 -372
  13. package/dist/setup-page/client-extraction.js +33 -188
  14. package/dist/setup-page/client-lifecycle.js +31 -101
  15. package/dist/setup-page/client.js +0 -2
  16. package/dist/setup-page/styles-extraction.js +1 -31
  17. package/dist/setup-page/styles-foundation.js +0 -89
  18. package/dist/setup-page/styles-mvp.js +10 -155
  19. package/dist/setup-page/styles-website-alignment.js +0 -204
  20. package/dist/setup-page/styles.js +0 -4
  21. package/dist/setup-page.js +4 -4
  22. package/dist/setup-preview.js +4 -212
  23. package/dist/setup.js +314 -667
  24. package/dist/v1-contract.js +0 -8
  25. package/package.json +9 -7
  26. package/dist/city/README.md +0 -9
  27. package/dist/city/echo-ai-city-only.html +0 -2232
  28. package/dist/city/echo-extraction-plate.html +0 -330
  29. package/dist/city/echo-face-cutout.png +0 -0
  30. package/dist/city/personality_stickers/bossy.png +0 -0
  31. package/dist/city/personality_stickers/ghosty.png +0 -0
  32. package/dist/city/personality_stickers/loopy.png +0 -0
  33. package/dist/city/personality_stickers/lusty.png +0 -0
  34. package/dist/city/personality_stickers/maxxy.png +0 -0
  35. package/dist/city/personality_stickers/tabby.png +0 -0
  36. package/dist/city/vendor/OrbitControls.js +0 -1417
  37. package/dist/city/vendor/RoundedBoxGeometry.js +0 -155
  38. package/dist/city/vendor/echo_general-file-21.riv +0 -0
  39. package/dist/city/vendor/rive.js +0 -8139
  40. package/dist/city/vendor/rive.wasm +0 -0
  41. package/dist/city/vendor/three.module.min.js +0 -6
  42. package/dist/forensics.js +0 -1531
  43. package/dist/report.js +0 -721
  44. package/dist/setup-page/client-report-audit.js +0 -819
  45. package/dist/setup-page/client-report-city.js +0 -356
  46. package/dist/setup-page/client-report.js +0 -6
  47. package/dist/setup-page/styles-city-report.js +0 -880
  48. package/dist/setup-page/styles-context-audit.js +0 -470
@@ -1,8 +1,6 @@
1
1
  // Generated by splitting the previous inline setup page. Keep this source readable: it is emitted into one browser IIFE.
2
- export const SETUP_PAGE_CLIENT_LIFECYCLE = String.raw ` /* ---------- scan-first poll for the forensic report ---------- */
3
- function renderLocalScanConsent() {
4
- setScanMode(false);
5
- setCityMode(false);
2
+ export const SETUP_PAGE_CLIENT_LIFECYCLE = String.raw ` /* ---------- local-history permission and setup routing ---------- */
3
+ function renderLocalHistoryConsent() {
6
4
  setExtractMode(false);
7
5
  setReadyMode(true);
8
6
  setHead("Local file access", "Consent");
@@ -14,15 +12,15 @@ export const SETUP_PAGE_CLIENT_LIFECYCLE = String.raw ` /* ---------- scan-
14
12
  '<img class="mvpConsentEcho" src="/hud-assets/echo-face-cutout.png" alt="" />' +
15
13
  '<div class="mvpConsentPitch">' +
16
14
  '<h2 id="mvpConsentTitle">Start with the context you’ve already built.</h2>' +
17
- '<p class="mvpConsentIntro">See what you keep re-explaining—and what’s worth carrying into future sessions.</p>' +
15
+ '<p class="mvpConsentIntro">Choose which local coding conversations to bring into EchoMem.</p>' +
18
16
  '</div>' +
19
17
  '</div>' +
20
- '<p class="mvpConsentTrustLine">Your history stays on your Mac until you choose what to import. ' + dataHandlingLink("Data handling") + '</p>' +
21
- '<button type="button" class="primary mvpPrimary" id="allowLocalScan">Build my private report <span aria-hidden="true">→</span></button>' +
18
+ '<p class="mvpConsentTrustLine">Your history stays on this computer until you choose what to import. ' + dataHandlingLink("Data handling") + '</p>' +
19
+ '<button type="button" class="primary mvpPrimary" id="allowLocalHistory">Continue <span aria-hidden="true">→</span></button>' +
22
20
  '<p class="consentFine" id="consentStatus" role="status" aria-live="polite"></p>' +
23
21
  '</section>';
24
- var allow = document.getElementById("allowLocalScan");
25
- if (allow) allow.onclick = allowLocalScan;
22
+ var allow = document.getElementById("allowLocalHistory");
23
+ if (allow) allow.onclick = allowLocalHistory;
26
24
  }
27
25
  function setConsentStatus(message, tone) {
28
26
  var node = document.getElementById("consentStatus");
@@ -31,18 +29,26 @@ export const SETUP_PAGE_CLIENT_LIFECYCLE = String.raw ` /* ---------- scan-
31
29
  node.classList.toggle("is-required", tone === "required");
32
30
  }
33
31
  }
34
- async function allowLocalScan() {
35
- var allow = document.getElementById("allowLocalScan");
32
+ function continueAfterLocalHistoryConsent() {
33
+ if (connected) {
34
+ try { void refreshBillingStatus(); } catch (_) {}
35
+ void waitForStats();
36
+ return;
37
+ }
38
+ rememberSetupPlanChoice("");
39
+ renderLocalLogin("");
40
+ }
41
+ async function allowLocalHistory() {
42
+ var allow = document.getElementById("allowLocalHistory");
36
43
  if (allow) allow.disabled = true;
37
- setConsentStatus("Starting local scan...");
44
+ setConsentStatus("Saving your choice...");
38
45
  try {
39
- await postJson("/report-consent", { allowed: true }, 5000);
46
+ await postJson("/local-history-consent", { allowed: true }, 5000);
40
47
  localHistoryConsentGranted = true;
41
- await showReport();
48
+ continueAfterLocalHistoryConsent();
42
49
  } catch (error) {
43
50
  // The localhost bridge may have accepted consent even if the browser loses the POST
44
- // response while it swaps connections. Recover from the server's authoritative state
45
- // instead of leaving the user forever on "Starting local scan...".
51
+ // response while it swaps connections. Recover from the server's authoritative state.
46
52
  try {
47
53
  var configResponse = await fetchWithTimeout(
48
54
  "/config?nonce=" + encodeURIComponent(nonce),
@@ -52,80 +58,14 @@ export const SETUP_PAGE_CLIENT_LIFECYCLE = String.raw ` /* ---------- scan-
52
58
  var config = configResponse.ok ? await configResponse.json() : null;
53
59
  if (config && config.consentGranted === true) {
54
60
  localHistoryConsentGranted = true;
55
- await showReport();
61
+ continueAfterLocalHistoryConsent();
56
62
  return;
57
63
  }
58
64
  } catch (_) {}
59
- renderReportIssue("CONSENT_SAVE_FAILED", error && error.message ? error.message : "Could not start the local scan.");
65
+ if (allow) allow.disabled = false;
66
+ setConsentStatus(error && error.message ? error.message : "Could not save local-history access.", "required");
60
67
  }
61
68
  }
62
- async function showReport() {
63
- if (reportPollStarted) return;
64
- reportPollStarted = true;
65
- if (!report) renderLoading();
66
- try {
67
- var fails = 0;
68
- for (;;) {
69
- if (decisionMade) return;
70
- var res;
71
- try {
72
- res = await fetchWithTimeout("/report?nonce=" + encodeURIComponent(nonce), { credentials: "omit", cache: "no-store" }, 12000);
73
- } catch (_) {
74
- // Only a RUN of consecutive failures is a real bridge problem. A long scan that keeps
75
- // answering valid 202 envelopes must never trip this.
76
- fails++;
77
- if (fails >= 8) { renderBridgeIssue(); return; }
78
- await sleep(700);
79
- continue;
80
- }
81
- if (res.status === 403) {
82
- renderReportIssue("SETUP_SESSION_INVALID", "This setup link is missing or has an invalid local session nonce.");
83
- return;
84
- }
85
- var envelope;
86
- try {
87
- envelope = await res.json();
88
- } catch (_) {
89
- renderReportIssue("REPORT_RESPONSE_INVALID", "The local bridge returned a report response that could not be decoded.");
90
- return;
91
- }
92
- try {
93
- validateProductionReportEnvelope(envelope);
94
- } catch (_) {
95
- renderReportIssue("REPORT_SCHEMA_INVALID", "The local report failed its provenance or ledger consistency checks.");
96
- return;
97
- }
98
- if (res.status === 202 && envelope.kind === "scanning") {
99
- fails = 0;
100
- reportEnvelope = envelope;
101
- scheduleReportToCity();
102
- renderLoading();
103
- await sleep(700);
104
- continue;
105
- }
106
- if (res.status === 200 && envelope.kind === "empty") {
107
- renderEmptyReport();
108
- return;
109
- }
110
- if (res.status === 200 && envelope.kind === "ready") {
111
- reportEnvelope = envelope;
112
- report = envelope.report;
113
- try {
114
- renderForensicReport();
115
- } catch (_) {
116
- renderReportIssue("RENDER_FAILED", "Your validated local report could not be rendered safely.");
117
- }
118
- return;
119
- }
120
- if (envelope.kind === "failed") {
121
- renderReportIssue(envelope.error.code, envelope.error.message);
122
- return;
123
- }
124
- renderReportIssue("REPORT_STATE_MISMATCH", "The local bridge returned a report state that did not match its HTTP status.");
125
- return;
126
- }
127
- } finally { reportPollStarted = false; }
128
- }
129
69
 
130
70
  window.addEventListener("message", function (event) {
131
71
  if (event.origin !== location.origin) return;
@@ -140,27 +80,22 @@ export const SETUP_PAGE_CLIENT_LIFECYCLE = String.raw ` /* ---------- scan-
140
80
  rememberSetupPlanChoice("");
141
81
  try { void refreshBillingStatus(); } catch (_) {}
142
82
  void waitForStats();
143
- } else if (data.type === "echomem:city-ready" && data.nonce === nonce) {
144
- scheduleReportToCity();
145
- } else if (data.type === "echomem:connect-click") {
146
- onConnectClick();
147
83
  }
148
84
  });
149
85
  async function init() {
150
86
  if (!nonce) throw new Error("Missing setup nonce. Re-run echomem-mcp init.");
151
- // Older builds cached a report under the nonce. It is never an authority now; the current
152
- // server envelope and scan identity are the only inputs allowed to unlock personalized UI.
153
- try { sessionStorage.removeItem("echomem:setup-report:" + nonce); } catch (_) {}
154
87
  var configLoaded = false;
155
88
  var consentRequired = true;
156
89
  var consentGranted = false;
157
- // Learn the connection state and server-side consent gate before routing.
158
90
  try {
159
91
  var cfg = await getJson("/config");
160
92
  configLoaded = true;
161
93
  connected = !!cfg.connected;
162
94
  setupFlow = cfg.flow === "login" ? "login" : "onboarding";
163
95
  workspacePath = cfg.workspacePath || workspacePath;
96
+ setupPlatform = cfg.platform || setupPlatform;
97
+ canOpenClaudeDesktop = !!(cfg.capabilities && cfg.capabilities.openClaudeDesktop);
98
+ canOpenAgentSessions = !!(cfg.capabilities && cfg.capabilities.openAgentSessions);
164
99
  consentRequired = cfg.consentRequired !== false;
165
100
  consentGranted = cfg.consentGranted === true;
166
101
  localHistoryConsentGranted = consentGranted;
@@ -173,32 +108,27 @@ export const SETUP_PAGE_CLIENT_LIFECYCLE = String.raw ` /* ---------- scan-
173
108
  if (!connected) rememberSetupPlanChoice("");
174
109
  if (params.get("connected") === "1") {
175
110
  if (!configLoaded || (consentRequired && !consentGranted)) {
176
- renderLocalScanConsent();
111
+ renderLocalHistoryConsent();
177
112
  setConsentStatus("Local history access is required before you can connect EchoMem or continue setup.", "required");
178
113
  return;
179
114
  }
180
115
  connected = true;
181
116
  if (notifyOpenerConnected()) {
182
- // Popup flow: hand the main tab back control, then this popup closes.
183
117
  setHead("EchoMem connected", "Done");
184
118
  app.className = "notice";
185
119
  app.innerHTML = '<h2 style="color:#203820">This device is connected</h2><p style="margin-top:10px">Returning to the local setup page...</p>';
186
- // Fallback: if window.close() was blocked (auth opened as a tab, or the browser refused),
187
- // this window would otherwise hang on "Returning..." forever. After a beat, take over here
188
- // so the user always lands on the dashboard instead of a dead page.
189
120
  setTimeout(function () { if (!window.closed) void waitForStats(); }, 1500);
190
121
  } else {
191
- // Same-tab fallback (popup was blocked): go straight to the extraction dashboard.
192
122
  try { void refreshBillingStatus(); } catch (_) {}
193
123
  void waitForStats();
194
124
  }
195
125
  return;
196
126
  }
197
127
  if (consentGranted) {
198
- await showReport();
128
+ continueAfterLocalHistoryConsent();
199
129
  return;
200
130
  }
201
- renderLocalScanConsent();
131
+ renderLocalHistoryConsent();
202
132
  }
203
133
  __ECHO_SETUP_STARTUP__;
204
134
  `;
@@ -1,11 +1,9 @@
1
1
  import { SETUP_PAGE_CLIENT_CORE } from "./client-core.js";
2
- import { SETUP_PAGE_CLIENT_REPORT } from "./client-report.js";
3
2
  import { SETUP_PAGE_CLIENT_EXTRACTION } from "./client-extraction.js";
4
3
  import { SETUP_PAGE_CLIENT_LIFECYCLE } from "./client-lifecycle.js";
5
4
  export function renderSetupPageClient(previewState, startupScript) {
6
5
  const body = [
7
6
  SETUP_PAGE_CLIENT_CORE,
8
- SETUP_PAGE_CLIENT_REPORT,
9
7
  SETUP_PAGE_CLIENT_EXTRACTION,
10
8
  SETUP_PAGE_CLIENT_LIFECYCLE,
11
9
  ].join("\n")
@@ -598,13 +598,8 @@ export const SETUP_PAGE_STYLES_EXTRACTION = String.raw ` /* ---- dashboard (p
598
598
  .consentActions .primary { width: 100%; }
599
599
  .localAuthActions .primary,
600
600
  .localAuthForm > .primary { width: 100%; }
601
- .cityHero { min-height: auto; }
602
- .cityShell {
603
- width: 100vw;
604
- height: 100vh;
605
- }
606
601
  }
607
- /* extraction dashboard — plate layout */
602
+ /* extraction dashboard */
608
603
  .exHero { display: grid; grid-template-columns: minmax(0, 1fr) minmax(300px, 0.95fr); gap: clamp(20px, 3vw, 40px); align-items: center; margin-top: 8px; }
609
604
  /* Centered composition: mascot on top, then everything stacked and centered on one axis. */
610
605
  .readyDecision {
@@ -674,7 +669,6 @@ export const SETUP_PAGE_STYLES_EXTRACTION = String.raw ` /* ---- dashboard (p
674
669
  justify-items: stretch;
675
670
  margin: 0 auto;
676
671
  }
677
- .exHeroResult .exPlate { display: none; }
678
672
  .exHeroResult .exCopy { width: 100%; margin: 0 auto; }
679
673
  .exCopy h2 { max-width: 620px; margin: 8px 0 0; font-size: clamp(34px, 4.8vw, 58px); line-height: 1.01; }
680
674
  .exCopy .exSub { margin: 14px 0 0; max-width: 590px; font-size: clamp(15px, 1.3vw, 17px); line-height: 1.6; color: var(--echo-ink-mute); }
@@ -896,19 +890,6 @@ export const SETUP_PAGE_STYLES_EXTRACTION = String.raw ` /* ---- dashboard (p
896
890
  font-size: 12px;
897
891
  line-height: 1.35;
898
892
  }
899
- .exPlate { width: 100%; }
900
- /* WebGL clay city (echo-extraction-plate.html) — the only plate; no SVG layer */
901
- .plateStack { position: relative; width: 100%; aspect-ratio: 640 / 470; }
902
- .plateStack .plateGl { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; background: transparent; }
903
- .exHeroActive {
904
- grid-template-columns: minmax(0, 640px);
905
- justify-content: center;
906
- justify-items: stretch;
907
- width: 100%;
908
- max-width: min(920px, calc(100vw - clamp(36px, 8vw, 120px)));
909
- margin: 0 auto;
910
- }
911
- .exHeroActive .exPlate { display: none; }
912
893
  .exHeroActive .exCopy { width: 100%; }
913
894
  .workbench { display: grid; gap: 14px; max-width: 640px; }
914
895
  .workbench h2 {
@@ -2538,15 +2519,4 @@ export const SETUP_PAGE_STYLES_EXTRACTION = String.raw ` /* ---- dashboard (p
2538
2519
  .sessionPickerFoot button { width: 100%; }
2539
2520
  }
2540
2521
 
2541
- /* ===== workspace readout dashboard (Part 2 — replaces explainPage) ===== */
2542
- @keyframes rdoRise { from { opacity: 0; translate: 0 14px; } to { opacity: 1; translate: 0 0; } }
2543
- @keyframes rdoGrow { from { transform: scaleY(0); } to { transform: scaleY(1); } }
2544
- @keyframes rdoDraw { from { transform: scaleX(0); } to { transform: scaleX(1); } }
2545
- @keyframes rdoWipe { from { clip-path: inset(0 100% 0 0); } to { clip-path: inset(0 0 0 0); } }
2546
- /* Pull the report up toward the city figure — the canvas bottom fades out now,
2547
- so the document can tuck into that empty band without touching the board. */
2548
- body.cityMode .detailsWrap { background: var(--echo-paper-canvas); margin-top: clamp(-120px, -7vw, -56px); }
2549
-
2550
- /* Context audit — a printed report sheet on the desk: serif document type, tight rhythm. */
2551
- .context-audit {
2552
2522
  `;
@@ -105,89 +105,6 @@ export const SETUP_PAGE_STYLES_FOUNDATION = String.raw ` :root {
105
105
  font-size: 16px;
106
106
  text-align: center;
107
107
  }
108
- body.scanMode {
109
- background: var(--echo-paper-canvas);
110
- }
111
- body.scanMode main {
112
- width: 100%;
113
- min-height: 100vh;
114
- margin: 0;
115
- padding: clamp(32px, 7vw, 96px);
116
- display: grid;
117
- place-items: center;
118
- }
119
- body.scanMode main > header {
120
- display: none;
121
- }
122
- body.scanMode #app {
123
- width: min(900px, 100%);
124
- min-height: min(62vh, 620px);
125
- display: grid;
126
- place-items: center;
127
- }
128
- .scanLoader {
129
- width: min(720px, 100%);
130
- border-top: 1px solid #c9cbc6;
131
- border-right: 1px solid #c9cbc6;
132
- border-bottom: 1px solid #c9cbc6;
133
- border-left: 5px solid var(--echo-ink-primary);
134
- border-radius: 2px;
135
- background: var(--echo-paper-white);
136
- padding: clamp(30px, 6vw, 58px);
137
- text-align: left;
138
- }
139
- .scanEyebrow {
140
- margin: 0 0 22px;
141
- color: var(--echo-ink-faint);
142
- font-family: var(--echo-font-mono);
143
- font-size: 14px;
144
- font-weight: 700;
145
- letter-spacing: 0.18em;
146
- text-transform: uppercase;
147
- }
148
- .scanLoader h2 {
149
- margin: 0;
150
- color: var(--echo-ink-text);
151
- font-size: clamp(42px, 5vw, 72px);
152
- line-height: 1;
153
- }
154
- .scanProgress {
155
- width: min(520px, 100%);
156
- height: 6px;
157
- margin: 38px 0 0;
158
- overflow: hidden;
159
- border-radius: 0;
160
- background: #e2e3e9;
161
- }
162
- .scanProgressFill {
163
- display: block;
164
- height: 100%;
165
- min-width: 10px;
166
- border-radius: 0;
167
- background: var(--echo-ink-primary);
168
- /* Matched to the 700ms report poll so the fill glides between updates instead of
169
- stepping and then sitting still for the rest of the interval. */
170
- transition: width 700ms linear;
171
- }
172
- .scanCaption {
173
- margin: 20px 0 0;
174
- color: var(--echo-ink-faint);
175
- font-family: var(--echo-font-mono);
176
- font-size: 16px;
177
- font-weight: 600;
178
- line-height: 1.45;
179
- }
180
- body.cityMode {
181
- background: var(--echo-paper-canvas);
182
- }
183
- body.cityMode main {
184
- width: 100%;
185
- padding: 0;
186
- margin: 0;
187
- }
188
- body.cityMode main > header {
189
- display: none;
190
- }
191
108
  body.extractMode main > header {
192
109
  display: none;
193
110
  }
@@ -210,12 +127,6 @@ export const SETUP_PAGE_STYLES_FOUNDATION = String.raw ` :root {
210
127
  body.readyMode {
211
128
  background: #f5f3ed;
212
129
  }
213
- body.cityMode #app {
214
- display: block;
215
- min-height: 100vh;
216
- color: var(--echo-ink-text);
217
- text-align: left;
218
- }
219
130
  body.extractMode #app {
220
131
  width: 100%;
221
132
  display: grid;
@@ -1,15 +1,13 @@
1
1
  // Final MVP onboarding layer. It deliberately mirrors the public Echo pricing,
2
- // login, and report surfaces instead of introducing another local-only design system.
2
+ // login, plan, and import surfaces instead of introducing another local-only design system.
3
3
  export const SETUP_PAGE_STYLES_MVP = String.raw `
4
4
  body.readyMode,
5
- body.scanMode,
6
5
  body.extractMode {
7
6
  background:
8
7
  radial-gradient(circle, rgba(26,58,143,0.12) 0 1.2px, transparent 1.5px) 0 0 / 22px 22px,
9
8
  var(--echo-paper-canvas);
10
9
  }
11
- body.readyMode main,
12
- body.scanMode main,
10
+ body.readyMode main main,
13
11
  body.extractMode main {
14
12
  width: 100%;
15
13
  min-height: 100vh;
@@ -19,19 +17,17 @@ export const SETUP_PAGE_STYLES_MVP = String.raw `
19
17
  }
20
18
  main > header { display: none; }
21
19
  #app.state { color: transparent; }
22
- body.readyMode #app,
23
- body.scanMode #app,
20
+ body.readyMode #app #app,
24
21
  body.extractMode #app {
25
22
  width: 100%;
26
23
  min-height: calc(100vh - clamp(56px, 10vw, 128px));
27
24
  }
28
- body.readyMode #app:has(> .setupJourney),
29
- body.scanMode #app:has(> .setupJourney),
25
+ body.readyMode #app:has(> .setupJourney) #app:has(> .setupJourney),
30
26
  body.extractMode #app:has(> .setupJourney) {
31
27
  padding-top: 0 !important;
32
28
  }
33
29
 
34
- /* One calm map across local scan, account/Stripe, import, and first value. */
30
+ /* One calm map across access, account/Stripe, import, and first value. */
35
31
  .setupJourney {
36
32
  width: min(760px, 100%);
37
33
  flex: 0 0 auto;
@@ -40,7 +36,7 @@ export const SETUP_PAGE_STYLES_MVP = String.raw `
40
36
  }
41
37
  .setupJourney ol {
42
38
  display: grid;
43
- grid-template-columns: repeat(5, minmax(0, 1fr));
39
+ grid-template-columns: repeat(4, minmax(0, 1fr));
44
40
  gap: 0;
45
41
  margin: 0;
46
42
  padding: 0;
@@ -99,11 +95,6 @@ export const SETUP_PAGE_STYLES_MVP = String.raw `
99
95
  box-shadow: 0 0 0 4px rgba(26,58,143,0.09);
100
96
  color: white;
101
97
  }
102
- body.cityMode .setupJourney {
103
- margin-top: 12px;
104
- margin-bottom: 8px;
105
- padding-inline: 18px;
106
- }
107
98
  #setupJourneySlot {
108
99
  width: 100%;
109
100
  flex: 0 0 auto;
@@ -151,21 +142,6 @@ export const SETUP_PAGE_STYLES_MVP = String.raw `
151
142
  text-align: center;
152
143
  }
153
144
  .mvpConsentTrustLine strong { color: var(--echo-ink-text); }
154
- .mvpReportPreview { overflow: hidden; border: 1px solid rgba(26,58,143,0.18); border-radius: 18px; background: rgba(255,255,255,0.84); box-shadow: 0 16px 36px -26px rgba(26,58,143,0.42); }
155
- .mvpReportPreview header { display: flex; align-items: center; gap: 13px; border-bottom: 1px solid rgba(26,58,143,0.10); background: color-mix(in srgb, var(--echo-paper-white) 66%, var(--echo-note-blue) 34%); padding: 13px 15px; }
156
- .mvpReportPreview header span,
157
- .mvpReportPreview header small,
158
- .mvpReportPreview header strong,
159
- .mvpPreviewRow span,
160
- .mvpPreviewRow small,
161
- .mvpPreviewRow strong { display: block; }
162
- .mvpReportPreview header small { color: var(--echo-ink-primary); font: 800 9px/1 var(--echo-font-mono); letter-spacing: 0.1em; text-transform: uppercase; }
163
- .mvpReportPreview header strong { margin-top: 5px; color: var(--echo-ink-text); font-size: 15px; }
164
- .mvpPreviewRow { display: grid; grid-template-columns: 22px minmax(0, 1fr); gap: 10px; align-items: start; padding: 11px 15px; }
165
- .mvpPreviewRow + .mvpPreviewRow { border-top: 1px solid rgba(26,58,143,0.08); }
166
- .mvpPreviewRow .setupGlyph { width: 19px; height: 19px; color: #2c8f68; }
167
- .mvpPreviewRow strong { color: var(--echo-ink-text); font-size: 13px; }
168
- .mvpPreviewRow small { margin-top: 2px; color: var(--echo-ink-mute); font-size: 11px; line-height: 1.35; }
169
145
  .mvpTrustCard { display: grid; grid-template-columns: 38px minmax(0, 1fr) auto; gap: 12px; align-items: center; border: 1px solid rgba(44,143,104,0.28); border-radius: 15px; background: color-mix(in srgb, var(--echo-paper-white) 62%, var(--echo-note-mint) 38%); padding: 13px 15px; }
170
146
  .mvpTrustIcon { width: 38px; height: 38px; display: grid; place-items: center; border-radius: 50%; background: rgba(44,143,104,0.13); color: #257b59; }
171
147
  .mvpTrustIcon .setupGlyph { width: 21px; height: 21px; }
@@ -537,125 +513,6 @@ export const SETUP_PAGE_STYLES_MVP = String.raw `
537
513
  font-weight: 750;
538
514
  }
539
515
 
540
- /* Reading --------------------------------------------------------------- */
541
- .scanLoading {
542
- display: flex !important;
543
- flex-direction: column;
544
- align-items: center;
545
- justify-content: flex-start;
546
- }
547
- .scanLoading .mvpReading {
548
- margin-block: auto;
549
- }
550
- .mvpReading {
551
- display: flex;
552
- width: min(460px, 100%);
553
- flex-direction: column;
554
- align-items: center;
555
- color: var(--echo-ink-text);
556
- text-align: center;
557
- }
558
- .mvpReadingMascot {
559
- display: block;
560
- width: 150px;
561
- height: 150px;
562
- margin: 0 auto 18px;
563
- object-fit: contain;
564
- filter: none;
565
- }
566
- .mvpReading h2 {
567
- margin: 0;
568
- color: var(--echo-ink-primary);
569
- font: 650 38px/1.05 var(--echo-font-script), cursive;
570
- }
571
- .mvpReadingCount {
572
- min-height: 18px;
573
- margin: 13px 0 0;
574
- color: var(--echo-ink-mute);
575
- font: 650 13px/1.4 var(--echo-font-mono);
576
- }
577
- .mvpReadingCount[hidden] {
578
- display: block;
579
- visibility: hidden;
580
- }
581
- .mvpReadingEta {
582
- min-height: 17px;
583
- margin: 5px 0 0;
584
- color: var(--echo-ink-faint);
585
- font: 600 12px/1.4 var(--echo-font-mono);
586
- font-variant-numeric: tabular-nums;
587
- }
588
- .mvpReadingProgress {
589
- width: min(260px, 100%);
590
- height: 3px;
591
- margin: 8px 0 0;
592
- background: rgba(26,58,143,0.12);
593
- }
594
- .discoveryLoader {
595
- width: 100%;
596
- min-height: min(62vh, 620px);
597
- display: grid;
598
- place-items: center;
599
- }
600
- .discoverySpinner span {
601
- display: block;
602
- width: 26px;
603
- height: 26px;
604
- border: 3px solid rgba(26,58,143,0.16);
605
- border-top-color: var(--echo-ink-primary);
606
- border-radius: 50%;
607
- animation: localAuthSpin 700ms linear infinite;
608
- }
609
- .extractionStartStage {
610
- display: flex !important;
611
- flex-direction: column;
612
- align-items: center;
613
- justify-content: flex-start;
614
- }
615
- .extractionStartStage .discoverySpinner {
616
- display: grid;
617
- flex: 1 1 auto;
618
- place-items: center;
619
- }
620
- .reportMessageStage {
621
- display: flex !important;
622
- justify-content: center;
623
- color: var(--echo-ink-text);
624
- }
625
- .reportMessage {
626
- width: min(620px, 100%);
627
- padding-top: clamp(48px, 10vh, 112px);
628
- }
629
- .reportMessage h2 {
630
- margin: 0;
631
- font-size: clamp(42px, 5vw, 58px);
632
- font-weight: 650;
633
- line-height: 1;
634
- letter-spacing: -0.045em;
635
- }
636
- .reportMessage p {
637
- margin: 16px 0 0;
638
- color: var(--echo-ink-mute);
639
- font-size: 16px;
640
- line-height: 1.55;
641
- }
642
- .reportMessage code {
643
- color: var(--echo-ink-primary);
644
- font-family: var(--echo-font-mono);
645
- }
646
- .reportTechnical {
647
- width: fit-content;
648
- margin-top: 22px;
649
- color: var(--echo-ink-faint);
650
- font: 650 11px/1.5 var(--echo-font-mono);
651
- }
652
- .reportTechnical summary {
653
- cursor: pointer;
654
- text-decoration: underline;
655
- text-underline-offset: 3px;
656
- }
657
- .reportTechnical code { display: block; margin-top: 8px; }
658
-
659
516
  /* Pricing copied from the public Echo habitat -------------------------- */
660
517
  .planGateStage {
661
518
  display: flex !important;
@@ -1575,7 +1432,7 @@ export const SETUP_PAGE_STYLES_MVP = String.raw `
1575
1432
  box-shadow: 0 6px 0 color-mix(in srgb, var(--echo-ink-primary) 77%, black);
1576
1433
  }
1577
1434
 
1578
- /* Extraction: let the existing animated plate carry the page ----------- */
1435
+ /* Extraction ------------------------------------------------------------- */
1579
1436
  .exHero {
1580
1437
  width: min(1180px, 100%);
1581
1438
  min-height: calc(100vh - clamp(56px, 10vw, 128px));
@@ -1585,7 +1442,8 @@ export const SETUP_PAGE_STYLES_MVP = String.raw `
1585
1442
  box-shadow: none;
1586
1443
  }
1587
1444
  .exHero.exHeroActive {
1588
- grid-template-columns: minmax(320px, 0.72fr) minmax(460px, 1.28fr);
1445
+ width: min(720px, 100%);
1446
+ grid-template-columns: minmax(0, 1fr);
1589
1447
  align-items: center;
1590
1448
  }
1591
1449
  .exHeroActive #exText { padding: clamp(20px, 4vw, 44px); }
@@ -1610,7 +1468,6 @@ export const SETUP_PAGE_STYLES_MVP = String.raw `
1610
1468
  -webkit-text-fill-color: var(--echo-ink-primary);
1611
1469
  font-weight: 650;
1612
1470
  }
1613
- .exHeroActive .exPlate { min-height: min(72vh, 720px); }
1614
1471
  .exHeroActive .exMeter {
1615
1472
  margin-top: 28px;
1616
1473
  border: 0;
@@ -1735,11 +1592,9 @@ export const SETUP_PAGE_STYLES_MVP = String.raw `
1735
1592
  .checkoutCommitment,
1736
1593
  .checkoutActions { grid-template-columns: 1fr; }
1737
1594
  .exHero.exHeroActive { grid-template-columns: 1fr; }
1738
- .exHeroActive .exPlate { min-height: 440px; }
1739
- }
1595
+ }
1740
1596
  @media (max-width: 560px) {
1741
1597
  body.readyMode main,
1742
- body.scanMode main,
1743
1598
  body.extractMode main { padding: 22px 14px 38px; }
1744
1599
  .setupJourney { margin-bottom: 24px; }
1745
1600
  .mvpConsentFooter { align-items: stretch; flex-direction: column; }