@geotechcli/core 0.4.39 → 0.4.41
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.
- package/dist/ingest/geotech-document.d.ts.map +1 -1
- package/dist/ingest/geotech-document.js +4 -1
- package/dist/ingest/geotech-document.js.map +1 -1
- package/dist/ingest/job-worker.d.ts.map +1 -1
- package/dist/ingest/job-worker.js +5 -1
- package/dist/ingest/job-worker.js.map +1 -1
- package/dist/meta/metadata.json +1 -1
- package/dist/report/html.d.ts.map +1 -1
- package/dist/report/html.js +653 -104
- package/dist/report/html.js.map +1 -1
- package/dist/report/ingest-dossier.js +7 -7
- package/dist/report/ingest-dossier.js.map +1 -1
- package/package.json +1 -1
package/dist/report/html.js
CHANGED
|
@@ -31,15 +31,15 @@ function toneClass(tone) {
|
|
|
31
31
|
function toneColor(tone) {
|
|
32
32
|
switch (tone) {
|
|
33
33
|
case 'good':
|
|
34
|
-
return '#
|
|
34
|
+
return '#0f3d2e';
|
|
35
35
|
case 'warning':
|
|
36
|
-
return '#
|
|
36
|
+
return '#5f3b0b';
|
|
37
37
|
case 'danger':
|
|
38
|
-
return '#
|
|
38
|
+
return '#5f1717';
|
|
39
39
|
case 'accent':
|
|
40
|
-
return '#
|
|
40
|
+
return '#0b3b56';
|
|
41
41
|
default:
|
|
42
|
-
return '#
|
|
42
|
+
return '#1f2937';
|
|
43
43
|
}
|
|
44
44
|
}
|
|
45
45
|
function parsePercent(value) {
|
|
@@ -68,6 +68,15 @@ function sourceModeLabel(sourceHint) {
|
|
|
68
68
|
return 'Extraction evidence retained';
|
|
69
69
|
}
|
|
70
70
|
}
|
|
71
|
+
function reviewFilterValue(item) {
|
|
72
|
+
if (/not extracted|missing/i.test(item.value) || /required/i.test(item.review)) {
|
|
73
|
+
return 'missing';
|
|
74
|
+
}
|
|
75
|
+
if (/recommended|verify|manual|visual|review/i.test(item.review) && !/ready/i.test(item.review)) {
|
|
76
|
+
return 'needs_review';
|
|
77
|
+
}
|
|
78
|
+
return 'verified';
|
|
79
|
+
}
|
|
71
80
|
function compactSvgText(value, maxLength = 28) {
|
|
72
81
|
const normalized = (value ?? '').replace(/\s+/g, ' ').trim();
|
|
73
82
|
if (normalized.length <= maxLength) {
|
|
@@ -90,6 +99,21 @@ function renderMetric(metric) {
|
|
|
90
99
|
</article>
|
|
91
100
|
`;
|
|
92
101
|
}
|
|
102
|
+
function renderStatusBadges(dossier) {
|
|
103
|
+
if (dossier.badges.length === 0) {
|
|
104
|
+
return '';
|
|
105
|
+
}
|
|
106
|
+
return `
|
|
107
|
+
<div class="status-badge-row" aria-label="Document status">
|
|
108
|
+
${dossier.badges.map((badge) => `
|
|
109
|
+
<span class="status-badge ${toneClass(badge.tone)}">
|
|
110
|
+
<span>${escapeHtml(badge.label)}</span>
|
|
111
|
+
<strong>${escapeHtml(badge.value)}</strong>
|
|
112
|
+
</span>
|
|
113
|
+
`).join('')}
|
|
114
|
+
</div>
|
|
115
|
+
`;
|
|
116
|
+
}
|
|
93
117
|
function renderTable(table, className = 'data-section') {
|
|
94
118
|
const tableId = escapeHtml(idFromTitle(table.title));
|
|
95
119
|
const body = table.rows.length === 0
|
|
@@ -119,8 +143,18 @@ function renderTable(table, className = 'data-section') {
|
|
|
119
143
|
function renderTrustTable(dossier) {
|
|
120
144
|
const rows = dossier.trustItems.length === 0
|
|
121
145
|
? '<tr><td colspan="6">No trust-layer rows were retained.</td></tr>'
|
|
122
|
-
: dossier.trustItems.map((item) =>
|
|
123
|
-
|
|
146
|
+
: dossier.trustItems.map((item) => {
|
|
147
|
+
const reviewFilter = reviewFilterValue(item);
|
|
148
|
+
const searchText = [
|
|
149
|
+
item.item,
|
|
150
|
+
item.value,
|
|
151
|
+
item.sourcePage,
|
|
152
|
+
item.confidence,
|
|
153
|
+
item.review,
|
|
154
|
+
item.evidence,
|
|
155
|
+
].join(' ').toLowerCase();
|
|
156
|
+
return `
|
|
157
|
+
<tr data-trust-row data-review="${escapeHtml(reviewFilter)}" data-search="${escapeHtml(searchText)}">
|
|
124
158
|
<td><strong>${escapeHtml(item.item)}</strong></td>
|
|
125
159
|
<td>${escapeHtml(item.value)}</td>
|
|
126
160
|
<td>${escapeHtml(item.sourcePage)}</td>
|
|
@@ -128,13 +162,26 @@ function renderTrustTable(dossier) {
|
|
|
128
162
|
<td>${escapeHtml(item.review)}</td>
|
|
129
163
|
<td>${escapeHtml(item.evidence)}</td>
|
|
130
164
|
</tr>
|
|
131
|
-
|
|
165
|
+
`;
|
|
166
|
+
}).join('');
|
|
132
167
|
return `
|
|
133
168
|
<section class="data-section" id="parameters">
|
|
134
169
|
<div class="section-heading">
|
|
135
170
|
<h2>Engineering Parameters</h2>
|
|
136
171
|
<p>Evidence-first review table. Every retained or missing item is shown with source, confidence, review posture, and an evidence snippet.</p>
|
|
137
172
|
</div>
|
|
173
|
+
<div class="trust-controls" aria-label="Parameter table controls">
|
|
174
|
+
<label>
|
|
175
|
+
<span>Search parameters</span>
|
|
176
|
+
<input id="trust-search" type="search" placeholder="Filter by parameter, value, source, or evidence" />
|
|
177
|
+
</label>
|
|
178
|
+
<div class="filter-row" aria-label="Review status filters">
|
|
179
|
+
<button type="button" class="filter-button active" data-trust-filter="all">All</button>
|
|
180
|
+
<button type="button" class="filter-button" data-trust-filter="needs_review">Needs review</button>
|
|
181
|
+
<button type="button" class="filter-button" data-trust-filter="missing">Missing</button>
|
|
182
|
+
<button type="button" class="filter-button" data-trust-filter="verified">Verified</button>
|
|
183
|
+
</div>
|
|
184
|
+
</div>
|
|
138
185
|
<div class="table-shell trust-table">
|
|
139
186
|
<table>
|
|
140
187
|
<thead>
|
|
@@ -153,6 +200,97 @@ function renderTrustTable(dossier) {
|
|
|
153
200
|
</section>
|
|
154
201
|
`;
|
|
155
202
|
}
|
|
203
|
+
function renderGroundModelCrossSection(profile) {
|
|
204
|
+
if (!profile || profile.columns.length < 2 || profile.maxDepth <= 0) {
|
|
205
|
+
return `
|
|
206
|
+
<section class="data-section" id="ground-cross-section">
|
|
207
|
+
<div class="section-heading">
|
|
208
|
+
<h2>Ground Model Cross-Section</h2>
|
|
209
|
+
<p>A schematic cross-section needs at least two boreholes with retained depth evidence.</p>
|
|
210
|
+
</div>
|
|
211
|
+
<div class="empty-state">Ground-model bands were not drawn because borehole depth and layer evidence were incomplete.</div>
|
|
212
|
+
</section>
|
|
213
|
+
`;
|
|
214
|
+
}
|
|
215
|
+
const width = 940;
|
|
216
|
+
const height = 280;
|
|
217
|
+
const plotTop = 52;
|
|
218
|
+
const plotHeight = 158;
|
|
219
|
+
const left = 84;
|
|
220
|
+
const right = width - 44;
|
|
221
|
+
const usableWidth = right - left;
|
|
222
|
+
const referenceLayers = profile.columns
|
|
223
|
+
.flatMap((column) => column.layers)
|
|
224
|
+
.sort((leftLayer, rightLayer) => leftLayer.depthFrom - rightLayer.depthFrom)
|
|
225
|
+
.slice(0, 8);
|
|
226
|
+
const layers = referenceLayers.length > 0
|
|
227
|
+
? referenceLayers
|
|
228
|
+
: [{
|
|
229
|
+
depthFrom: 0,
|
|
230
|
+
depthTo: profile.maxDepth,
|
|
231
|
+
label: 'Ground profile',
|
|
232
|
+
description: 'Layer boundaries were not available in structured form.',
|
|
233
|
+
tone: 'neutral',
|
|
234
|
+
uncertain: true,
|
|
235
|
+
}];
|
|
236
|
+
const yForDepth = (depth) => plotTop + (Math.max(0, Math.min(profile.maxDepth, depth)) / profile.maxDepth) * plotHeight;
|
|
237
|
+
const ticks = Array.from({ length: 6 }, (_value, index) => Number((profile.maxDepth * index / 5).toFixed(2)));
|
|
238
|
+
const columnX = (index) => profile.columns.length === 1
|
|
239
|
+
? left + usableWidth / 2
|
|
240
|
+
: left + (usableWidth * index / (profile.columns.length - 1));
|
|
241
|
+
return `
|
|
242
|
+
<section class="data-section" id="ground-cross-section">
|
|
243
|
+
<div class="section-heading">
|
|
244
|
+
<h2>Ground Model Cross-Section</h2>
|
|
245
|
+
<p>Conceptual cross-section connecting retained borehole evidence. Bands are schematic and should be verified against source logs before design use.</p>
|
|
246
|
+
</div>
|
|
247
|
+
<div class="profile-shell cross-section-shell">
|
|
248
|
+
<svg class="ground-cross-section" viewBox="0 0 ${width} ${height}" role="img" aria-label="AI-assisted ground model cross-section">
|
|
249
|
+
<rect x="0" y="0" width="${width}" height="${height}" rx="18" fill="#0f172a" />
|
|
250
|
+
${ticks.map((tick) => {
|
|
251
|
+
const y = yForDepth(tick);
|
|
252
|
+
return `
|
|
253
|
+
<line x1="${left - 34}" y1="${y.toFixed(2)}" x2="${right + 12}" y2="${y.toFixed(2)}" stroke="#334155" stroke-width="1" />
|
|
254
|
+
<text x="14" y="${(y + 4).toFixed(2)}" class="profile-axis">${escapeHtml(tick.toFixed(tick % 1 === 0 ? 0 : 1))} ${escapeHtml(profile.depthUnit)}</text>
|
|
255
|
+
`;
|
|
256
|
+
}).join('')}
|
|
257
|
+
${layers.map((layer) => {
|
|
258
|
+
const y1 = yForDepth(layer.depthFrom);
|
|
259
|
+
const y2 = Math.max(y1 + 18, yForDepth(layer.depthTo));
|
|
260
|
+
return `
|
|
261
|
+
<g>
|
|
262
|
+
<title>${escapeHtml(`${layer.label}: ${layer.description}`)}</title>
|
|
263
|
+
<path d="M ${left} ${y1.toFixed(2)} L ${right} ${y1.toFixed(2)} L ${right} ${y2.toFixed(2)} L ${left} ${y2.toFixed(2)} Z"
|
|
264
|
+
fill="${toneColor(layer.tone)}" stroke="#64748b" stroke-width="1.2" ${layer.uncertain ? 'stroke-dasharray="8 6"' : ''} opacity="0.92" />
|
|
265
|
+
<text x="${left + 18}" y="${(y1 + Math.min(34, (y2 - y1) / 2 + 5)).toFixed(2)}" class="profile-label">${escapeHtml(compactSvgText(layer.label, 32))}</text>
|
|
266
|
+
<text x="${left + 18}" y="${(y1 + Math.min(52, (y2 - y1) / 2 + 23)).toFixed(2)}" class="profile-small">${escapeHtml(compactSvgText(layer.description, 64))}</text>
|
|
267
|
+
</g>
|
|
268
|
+
`;
|
|
269
|
+
}).join('')}
|
|
270
|
+
${profile.columns.map((column, index) => {
|
|
271
|
+
const x = columnX(index);
|
|
272
|
+
const depth = column.totalDepth ?? profile.maxDepth;
|
|
273
|
+
const bottomY = yForDepth(depth);
|
|
274
|
+
const waterY = column.waterTableDepth != null ? yForDepth(column.waterTableDepth) : null;
|
|
275
|
+
return `
|
|
276
|
+
<g>
|
|
277
|
+
<line x1="${x.toFixed(2)}" y1="${plotTop - 10}" x2="${x.toFixed(2)}" y2="${bottomY.toFixed(2)}" stroke="#06b6d4" stroke-width="3" />
|
|
278
|
+
<circle cx="${x.toFixed(2)}" cy="${plotTop - 12}" r="6" fill="#06b6d4" />
|
|
279
|
+
<text x="${(x - 24).toFixed(2)}" y="28" class="profile-title">${escapeHtml(column.boreholeId)}</text>
|
|
280
|
+
<text x="${(x - 36).toFixed(2)}" y="${height - 32}" class="profile-small">TD ${escapeHtml(depth.toFixed(2))} ${escapeHtml(profile.depthUnit)}</text>
|
|
281
|
+
${waterY != null ? `
|
|
282
|
+
<line x1="${(x - 34).toFixed(2)}" y1="${waterY.toFixed(2)}" x2="${(x + 34).toFixed(2)}" y2="${waterY.toFixed(2)}" stroke="#0891b2" stroke-width="2" />
|
|
283
|
+
<text x="${(x - 40).toFixed(2)}" y="${(waterY - 7).toFixed(2)}" class="profile-water">GW</text>
|
|
284
|
+
` : ''}
|
|
285
|
+
</g>
|
|
286
|
+
`;
|
|
287
|
+
}).join('')}
|
|
288
|
+
</svg>
|
|
289
|
+
</div>
|
|
290
|
+
<p class="verification-note">AI-assisted ground model. Use source logs and engineering judgment before adopting layer continuity, groundwater, or design parameters.</p>
|
|
291
|
+
</section>
|
|
292
|
+
`;
|
|
293
|
+
}
|
|
156
294
|
function renderBoreholeProfile(profile) {
|
|
157
295
|
if (!profile || profile.columns.length === 0 || profile.maxDepth <= 0) {
|
|
158
296
|
return `
|
|
@@ -166,9 +304,9 @@ function renderBoreholeProfile(profile) {
|
|
|
166
304
|
`;
|
|
167
305
|
}
|
|
168
306
|
const plotTop = 44;
|
|
169
|
-
const plotHeight =
|
|
170
|
-
const columnWidth =
|
|
171
|
-
const gap =
|
|
307
|
+
const plotHeight = 330;
|
|
308
|
+
const columnWidth = 110;
|
|
309
|
+
const gap = 32;
|
|
172
310
|
const axisWidth = 78;
|
|
173
311
|
const width = axisWidth + profile.columns.length * columnWidth + Math.max(0, profile.columns.length - 1) * gap + 42;
|
|
174
312
|
const height = plotTop + plotHeight + 74;
|
|
@@ -196,11 +334,11 @@ function renderBoreholeProfile(profile) {
|
|
|
196
334
|
</div>
|
|
197
335
|
<div class="profile-shell">
|
|
198
336
|
<svg class="borehole-profile" viewBox="0 0 ${width} ${height}" role="img" aria-label="Borehole stratigraphy profile">
|
|
199
|
-
<rect x="0" y="0" width="${width}" height="${height}" rx="18" fill="#
|
|
337
|
+
<rect x="0" y="0" width="${width}" height="${height}" rx="18" fill="#0f172a" />
|
|
200
338
|
${ticks.map((tick) => {
|
|
201
339
|
const y = plotTop + (tick / profile.maxDepth) * plotHeight;
|
|
202
340
|
return `
|
|
203
|
-
<line x1="48" y1="${y.toFixed(2)}" x2="${width - 24}" y2="${y.toFixed(2)}" stroke="#
|
|
341
|
+
<line x1="48" y1="${y.toFixed(2)}" x2="${width - 24}" y2="${y.toFixed(2)}" stroke="#334155" stroke-width="1" />
|
|
204
342
|
<text x="8" y="${(y + 4).toFixed(2)}" class="profile-axis">${escapeHtml(tick.toFixed(tick % 1 === 0 ? 0 : 1))} ${escapeHtml(profile.depthUnit)}</text>
|
|
205
343
|
`;
|
|
206
344
|
}).join('')}
|
|
@@ -213,7 +351,7 @@ function renderBoreholeProfile(profile) {
|
|
|
213
351
|
<text x="${x}" y="24" class="profile-title">${escapeHtml(column.boreholeId)}</text>
|
|
214
352
|
${column.layers.map((layer) => layerRect(layer, columnIndex)).join('')}
|
|
215
353
|
${waterY != null ? `
|
|
216
|
-
<line x1="${x - 8}" y1="${waterY.toFixed(2)}" x2="${x + columnWidth + 8}" y2="${waterY.toFixed(2)}" stroke="#
|
|
354
|
+
<line x1="${x - 8}" y1="${waterY.toFixed(2)}" x2="${x + columnWidth + 8}" y2="${waterY.toFixed(2)}" stroke="#06b6d4" stroke-width="2" />
|
|
217
355
|
<text x="${x + 8}" y="${(waterY - 6).toFixed(2)}" class="profile-water">Groundwater</text>
|
|
218
356
|
` : ''}
|
|
219
357
|
<text x="${x}" y="${height - 24}" class="profile-small">TD ${escapeHtml(column.totalDepth != null ? `${column.totalDepth.toFixed(2)} m` : 'unavailable')}</text>
|
|
@@ -278,6 +416,11 @@ function renderSourceEvidence(dossier) {
|
|
|
278
416
|
].filter(Boolean).join(' | '))}</p>
|
|
279
417
|
${card.highlights.length > 0 ? `<ul>${card.highlights.map((item) => `<li>${escapeHtml(item)}</li>`).join('')}</ul>` : '<p>No strong structured highlight was retained for this page.</p>'}
|
|
280
418
|
${card.warnings.length > 0 ? `<small>Human verification recommended: ${escapeHtml(String(card.warnings.length))} retained warning(s).</small>` : ''}
|
|
419
|
+
<div class="evidence-actions" aria-label="${escapeHtml(`${card.pageLabel} review actions`)}">
|
|
420
|
+
<a href="#processing-audit">Open source page</a>
|
|
421
|
+
<button type="button" data-review-action="verified">Mark verified</button>
|
|
422
|
+
<button type="button" data-review-action="flagged">Flag issue</button>
|
|
423
|
+
</div>
|
|
281
424
|
</article>
|
|
282
425
|
`).join('')}
|
|
283
426
|
</div>
|
|
@@ -336,6 +479,28 @@ function renderProcessingAudit(dossier, auditTables) {
|
|
|
336
479
|
</details>
|
|
337
480
|
`;
|
|
338
481
|
}
|
|
482
|
+
function renderReviewBar(dossier) {
|
|
483
|
+
const missingCount = dossier.trustItems.filter((item) => reviewFilterValue(item) === 'missing').length;
|
|
484
|
+
const needsReviewCount = dossier.trustItems.filter((item) => reviewFilterValue(item) === 'needs_review').length;
|
|
485
|
+
const reviewBadgeValue = dossier.badges.find((badge) => /review/i.test(badge.label))?.value ?? 'Yes';
|
|
486
|
+
const reviewRequired = /^no$/i.test(reviewBadgeValue) ? 'Review ready' : 'Review required';
|
|
487
|
+
return `
|
|
488
|
+
<div class="review-bar" role="region" aria-label="Human review workflow">
|
|
489
|
+
<div>
|
|
490
|
+
<strong>${escapeHtml(reviewRequired)}</strong>
|
|
491
|
+
<span>${escapeHtml(`${dossier.pageCards.length} page(s), ${dossier.trustItems.length} review item(s), ${needsReviewCount} need review, ${missingCount} missing`)}</span>
|
|
492
|
+
</div>
|
|
493
|
+
<div class="review-actions">
|
|
494
|
+
<a href="#parameters" class="primary-action">Approve Extraction</a>
|
|
495
|
+
<a href="#risks">Flag for Review</a>
|
|
496
|
+
<button type="button" onclick="window.print()">Export Report</button>
|
|
497
|
+
<a href="#ground-cross-section">Generate Ground Model</a>
|
|
498
|
+
<a href="#source-evidence">Ask Geotech Agent</a>
|
|
499
|
+
<a href="#processing-audit">Open Audit Trail</a>
|
|
500
|
+
</div>
|
|
501
|
+
</div>
|
|
502
|
+
`;
|
|
503
|
+
}
|
|
339
504
|
function renderActionBar() {
|
|
340
505
|
return `
|
|
341
506
|
<div class="action-bar" aria-label="Review actions">
|
|
@@ -364,32 +529,37 @@ export function renderIngestDossierAsHtml(dossier) {
|
|
|
364
529
|
<head>
|
|
365
530
|
<meta charset="utf-8" />
|
|
366
531
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
367
|
-
<title>Geotechnical Intelligence
|
|
532
|
+
<title>Geotechnical Intelligence Report - ${escapeHtml(dossier.title)}</title>
|
|
368
533
|
<style>
|
|
369
534
|
:root {
|
|
370
|
-
--bg: #
|
|
371
|
-
--surface: #
|
|
372
|
-
--surface-soft: #
|
|
373
|
-
--
|
|
374
|
-
--
|
|
375
|
-
--
|
|
376
|
-
--primary
|
|
377
|
-
--
|
|
378
|
-
--success
|
|
379
|
-
--
|
|
380
|
-
--warning
|
|
381
|
-
--
|
|
382
|
-
--danger
|
|
383
|
-
--
|
|
384
|
-
--neutral
|
|
385
|
-
--
|
|
386
|
-
--
|
|
387
|
-
--
|
|
388
|
-
--
|
|
535
|
+
--bg: #060d1b;
|
|
536
|
+
--surface: #0f172a;
|
|
537
|
+
--surface-soft: #111827;
|
|
538
|
+
--surface-raised: #0b1120;
|
|
539
|
+
--text: #f8fafc;
|
|
540
|
+
--muted: #94a3b8;
|
|
541
|
+
--primary: #06b6d4;
|
|
542
|
+
--primary-soft: rgba(6, 182, 212, 0.12);
|
|
543
|
+
--success: #10b981;
|
|
544
|
+
--success-soft: rgba(16, 185, 129, 0.13);
|
|
545
|
+
--warning: #f59e0b;
|
|
546
|
+
--warning-soft: rgba(245, 158, 11, 0.13);
|
|
547
|
+
--danger: #ef4444;
|
|
548
|
+
--danger-soft: rgba(239, 68, 68, 0.13);
|
|
549
|
+
--neutral: #cbd5e1;
|
|
550
|
+
--neutral-soft: rgba(100, 116, 139, 0.15);
|
|
551
|
+
--border: #1e293b;
|
|
552
|
+
--border-strong: #334155;
|
|
553
|
+
--shadow: 0 20px 60px rgba(0, 0, 0, 0.28);
|
|
554
|
+
--radius: 14px;
|
|
555
|
+
--radius-sm: 10px;
|
|
389
556
|
}
|
|
390
557
|
|
|
391
558
|
* { box-sizing: border-box; }
|
|
392
|
-
html {
|
|
559
|
+
html {
|
|
560
|
+
scroll-behavior: smooth;
|
|
561
|
+
overflow-x: hidden;
|
|
562
|
+
}
|
|
393
563
|
html, body { margin: 0; min-height: 100%; }
|
|
394
564
|
|
|
395
565
|
body {
|
|
@@ -398,80 +568,105 @@ export function renderIngestDossierAsHtml(dossier) {
|
|
|
398
568
|
background: var(--bg);
|
|
399
569
|
font-variant-numeric: tabular-nums;
|
|
400
570
|
overflow-x: hidden;
|
|
571
|
+
padding-bottom: 98px;
|
|
401
572
|
}
|
|
402
573
|
|
|
403
574
|
.layout {
|
|
404
575
|
display: grid;
|
|
405
|
-
grid-template-columns:
|
|
406
|
-
gap:
|
|
576
|
+
grid-template-columns: minmax(0, 1fr);
|
|
577
|
+
gap: 24px;
|
|
407
578
|
width: 100%;
|
|
408
|
-
max-width:
|
|
579
|
+
max-width: 1280px;
|
|
409
580
|
margin: 0 auto;
|
|
410
|
-
padding:
|
|
581
|
+
padding: 16px 24px 120px;
|
|
411
582
|
min-width: 0;
|
|
412
583
|
}
|
|
413
584
|
|
|
414
585
|
.sidebar {
|
|
415
586
|
position: sticky;
|
|
416
|
-
top:
|
|
417
|
-
|
|
418
|
-
|
|
587
|
+
top: 0;
|
|
588
|
+
z-index: 30;
|
|
589
|
+
display: flex;
|
|
590
|
+
align-items: center;
|
|
591
|
+
justify-content: space-between;
|
|
592
|
+
gap: 18px;
|
|
593
|
+
align-self: stretch;
|
|
594
|
+
min-height: 58px;
|
|
419
595
|
min-width: 0;
|
|
420
|
-
padding:
|
|
596
|
+
padding: 12px 16px;
|
|
421
597
|
border: 1px solid var(--border);
|
|
422
|
-
border-radius:
|
|
423
|
-
background:
|
|
598
|
+
border-radius: 14px;
|
|
599
|
+
background: rgba(11, 17, 32, 0.94);
|
|
424
600
|
color: #f8fafc;
|
|
425
|
-
box-shadow:
|
|
601
|
+
box-shadow: 0 14px 44px rgba(0, 0, 0, 0.26);
|
|
602
|
+
backdrop-filter: blur(14px);
|
|
426
603
|
}
|
|
427
604
|
|
|
428
605
|
.brand {
|
|
429
|
-
display:
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
606
|
+
display: flex;
|
|
607
|
+
align-items: center;
|
|
608
|
+
gap: 10px;
|
|
609
|
+
padding: 0;
|
|
610
|
+
border-bottom: 0;
|
|
611
|
+
margin: 0;
|
|
612
|
+
min-width: 190px;
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
.brand::before {
|
|
616
|
+
content: "";
|
|
617
|
+
width: 28px;
|
|
618
|
+
height: 28px;
|
|
619
|
+
border-radius: 8px;
|
|
620
|
+
background: var(--primary);
|
|
621
|
+
box-shadow: 0 0 24px rgba(6, 182, 212, 0.28);
|
|
622
|
+
flex: 0 0 auto;
|
|
434
623
|
}
|
|
435
624
|
|
|
436
|
-
.brand strong { font-size:
|
|
437
|
-
.brand span {
|
|
625
|
+
.brand strong { font-size: 0.96rem; white-space: nowrap; }
|
|
626
|
+
.brand span { display: none; }
|
|
438
627
|
|
|
439
628
|
.nav-list {
|
|
440
|
-
display:
|
|
629
|
+
display: flex;
|
|
630
|
+
align-items: center;
|
|
631
|
+
justify-content: flex-end;
|
|
441
632
|
gap: 6px;
|
|
633
|
+
flex-wrap: wrap;
|
|
442
634
|
}
|
|
443
635
|
|
|
444
636
|
.nav-list a {
|
|
445
|
-
color: #
|
|
637
|
+
color: #cbd5e1;
|
|
446
638
|
text-decoration: none;
|
|
447
|
-
padding: 10px
|
|
448
|
-
border-radius:
|
|
639
|
+
padding: 8px 10px;
|
|
640
|
+
border-radius: 8px;
|
|
449
641
|
font-weight: 650;
|
|
450
|
-
font-size: 0.
|
|
642
|
+
font-size: 0.8rem;
|
|
451
643
|
overflow-wrap: anywhere;
|
|
452
644
|
}
|
|
453
645
|
|
|
454
646
|
.nav-list a:hover, .nav-list a:focus {
|
|
455
|
-
background:
|
|
647
|
+
background: rgba(100, 116, 139, 0.16);
|
|
456
648
|
outline: none;
|
|
457
649
|
}
|
|
458
650
|
|
|
459
651
|
.content {
|
|
460
652
|
display: grid;
|
|
461
|
-
gap:
|
|
653
|
+
gap: 28px;
|
|
462
654
|
min-width: 0;
|
|
463
655
|
max-width: 100%;
|
|
464
656
|
}
|
|
465
657
|
|
|
466
658
|
.hero {
|
|
467
|
-
padding:
|
|
659
|
+
padding: 30px;
|
|
468
660
|
border: 1px solid var(--border);
|
|
469
661
|
border-radius: 18px;
|
|
470
|
-
background:
|
|
662
|
+
background:
|
|
663
|
+
radial-gradient(circle at top right, rgba(6, 182, 212, 0.09), transparent 34%),
|
|
664
|
+
linear-gradient(135deg, #0b1120 0%, #0f172a 100%);
|
|
471
665
|
box-shadow: var(--shadow);
|
|
472
666
|
display: grid;
|
|
473
|
-
gap:
|
|
667
|
+
gap: 22px;
|
|
474
668
|
min-width: 0;
|
|
669
|
+
overflow: hidden;
|
|
475
670
|
}
|
|
476
671
|
|
|
477
672
|
.eyebrow {
|
|
@@ -480,7 +675,7 @@ export function renderIngestDossierAsHtml(dossier) {
|
|
|
480
675
|
padding: 8px 12px;
|
|
481
676
|
border-radius: 999px;
|
|
482
677
|
background: var(--primary-soft);
|
|
483
|
-
color:
|
|
678
|
+
color: #67e8f9;
|
|
484
679
|
font-weight: 800;
|
|
485
680
|
font-size: 0.78rem;
|
|
486
681
|
text-transform: uppercase;
|
|
@@ -488,7 +683,7 @@ export function renderIngestDossierAsHtml(dossier) {
|
|
|
488
683
|
|
|
489
684
|
.hero-main {
|
|
490
685
|
display: grid;
|
|
491
|
-
grid-template-columns: minmax(0, 1fr)
|
|
686
|
+
grid-template-columns: minmax(0, 1fr) 300px;
|
|
492
687
|
gap: 24px;
|
|
493
688
|
align-items: start;
|
|
494
689
|
min-width: 0;
|
|
@@ -510,7 +705,7 @@ export function renderIngestDossierAsHtml(dossier) {
|
|
|
510
705
|
|
|
511
706
|
.hero-subtitle {
|
|
512
707
|
margin-top: 12px;
|
|
513
|
-
color:
|
|
708
|
+
color: #67e8f9;
|
|
514
709
|
font-weight: 800;
|
|
515
710
|
font-size: 1.12rem;
|
|
516
711
|
}
|
|
@@ -527,16 +722,51 @@ export function renderIngestDossierAsHtml(dossier) {
|
|
|
527
722
|
display: grid;
|
|
528
723
|
gap: 10px;
|
|
529
724
|
padding: 18px;
|
|
530
|
-
border: 1px solid var(--border);
|
|
725
|
+
border: 1px solid var(--border-strong);
|
|
531
726
|
border-radius: var(--radius-sm);
|
|
532
|
-
background: rgba(
|
|
727
|
+
background: rgba(15, 23, 42, 0.74);
|
|
533
728
|
color: var(--muted);
|
|
534
729
|
font-size: 0.92rem;
|
|
535
730
|
min-width: 0;
|
|
536
731
|
overflow-wrap: anywhere;
|
|
537
732
|
}
|
|
538
733
|
|
|
539
|
-
.hero-meta strong {
|
|
734
|
+
.hero-meta strong {
|
|
735
|
+
color: var(--text);
|
|
736
|
+
overflow-wrap: anywhere;
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
.status-badge-row {
|
|
740
|
+
display: flex;
|
|
741
|
+
flex-wrap: wrap;
|
|
742
|
+
gap: 9px;
|
|
743
|
+
margin-top: 16px;
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
.status-badge {
|
|
747
|
+
display: inline-grid;
|
|
748
|
+
gap: 4px;
|
|
749
|
+
min-width: 126px;
|
|
750
|
+
padding: 10px 12px;
|
|
751
|
+
border: 1px solid currentColor;
|
|
752
|
+
border-radius: 999px;
|
|
753
|
+
line-height: 1.2;
|
|
754
|
+
background: rgba(15, 23, 42, 0.74);
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
.status-badge span {
|
|
758
|
+
color: currentColor;
|
|
759
|
+
opacity: 0.72;
|
|
760
|
+
font-size: 0.68rem;
|
|
761
|
+
text-transform: uppercase;
|
|
762
|
+
font-weight: 900;
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
.status-badge strong {
|
|
766
|
+
color: currentColor;
|
|
767
|
+
font-size: 0.86rem;
|
|
768
|
+
overflow-wrap: anywhere;
|
|
769
|
+
}
|
|
540
770
|
|
|
541
771
|
.executive-grid, .metric-grid, .card-grid, .evidence-grid, .audit-page-grid, .footer-grid {
|
|
542
772
|
display: grid;
|
|
@@ -568,6 +798,7 @@ export function renderIngestDossierAsHtml(dossier) {
|
|
|
568
798
|
}
|
|
569
799
|
|
|
570
800
|
.fact-card strong {
|
|
801
|
+
color: var(--text);
|
|
571
802
|
font-size: 1rem;
|
|
572
803
|
line-height: 1.45;
|
|
573
804
|
overflow-wrap: anywhere;
|
|
@@ -608,11 +839,11 @@ export function renderIngestDossierAsHtml(dossier) {
|
|
|
608
839
|
opacity: 0.75;
|
|
609
840
|
}
|
|
610
841
|
|
|
611
|
-
.tone-accent { background: var(--primary-soft); color:
|
|
612
|
-
.tone-good { background: var(--success-soft); color:
|
|
613
|
-
.tone-warning { background: var(--warning-soft); color:
|
|
614
|
-
.tone-danger { background: var(--danger-soft); color:
|
|
615
|
-
.tone-neutral { background: var(--neutral-soft); color:
|
|
842
|
+
.tone-accent { background: var(--primary-soft); color: #22d3ee; border-color: rgba(6, 182, 212, 0.28); }
|
|
843
|
+
.tone-good { background: var(--success-soft); color: #34d399; border-color: rgba(16, 185, 129, 0.28); }
|
|
844
|
+
.tone-warning { background: var(--warning-soft); color: #fcd34d; border-color: rgba(245, 158, 11, 0.3); }
|
|
845
|
+
.tone-danger { background: var(--danger-soft); color: #f87171; border-color: rgba(239, 68, 68, 0.32); }
|
|
846
|
+
.tone-neutral { background: var(--neutral-soft); color: #cbd5e1; border-color: rgba(100, 116, 139, 0.28); }
|
|
616
847
|
|
|
617
848
|
.action-bar {
|
|
618
849
|
display: flex;
|
|
@@ -621,15 +852,15 @@ export function renderIngestDossierAsHtml(dossier) {
|
|
|
621
852
|
}
|
|
622
853
|
|
|
623
854
|
.action-bar button, .action-bar a {
|
|
624
|
-
border: 1px solid var(--border);
|
|
625
|
-
border-radius:
|
|
626
|
-
background: var(--surface);
|
|
627
|
-
color:
|
|
628
|
-
padding:
|
|
855
|
+
border: 1px solid var(--border-strong);
|
|
856
|
+
border-radius: 8px;
|
|
857
|
+
background: var(--surface-soft);
|
|
858
|
+
color: #cbd5e1;
|
|
859
|
+
padding: 9px 12px;
|
|
629
860
|
font: inherit;
|
|
630
861
|
font-weight: 800;
|
|
631
862
|
text-decoration: none;
|
|
632
|
-
box-shadow:
|
|
863
|
+
box-shadow: none;
|
|
633
864
|
cursor: pointer;
|
|
634
865
|
overflow-wrap: anywhere;
|
|
635
866
|
}
|
|
@@ -637,7 +868,68 @@ export function renderIngestDossierAsHtml(dossier) {
|
|
|
637
868
|
.action-bar .primary-action {
|
|
638
869
|
background: var(--primary);
|
|
639
870
|
border-color: var(--primary);
|
|
640
|
-
color: #
|
|
871
|
+
color: #06111f;
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
.review-bar {
|
|
875
|
+
position: fixed;
|
|
876
|
+
left: 50%;
|
|
877
|
+
bottom: 0;
|
|
878
|
+
z-index: 45;
|
|
879
|
+
display: flex;
|
|
880
|
+
align-items: center;
|
|
881
|
+
justify-content: space-between;
|
|
882
|
+
gap: 18px;
|
|
883
|
+
width: min(1180px, calc(100vw - 36px));
|
|
884
|
+
margin: 10px auto 0;
|
|
885
|
+
padding: 13px 14px;
|
|
886
|
+
border: 1px solid #263244;
|
|
887
|
+
border-radius: 18px 18px 0 0;
|
|
888
|
+
background: rgba(11, 18, 32, 0.94);
|
|
889
|
+
color: #f8fafc;
|
|
890
|
+
box-shadow: 0 -18px 38px rgba(11, 18, 32, 0.2);
|
|
891
|
+
backdrop-filter: blur(14px);
|
|
892
|
+
transform: translate(-50%, 120%);
|
|
893
|
+
transition: transform 180ms ease;
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
.review-bar.visible {
|
|
897
|
+
transform: translate(-50%, 0);
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
.review-bar > div:first-child {
|
|
901
|
+
display: grid;
|
|
902
|
+
gap: 3px;
|
|
903
|
+
min-width: 220px;
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
.review-bar strong { font-size: 0.95rem; }
|
|
907
|
+
.review-bar span { color: #94a3b8; font-size: 0.84rem; line-height: 1.4; }
|
|
908
|
+
|
|
909
|
+
.review-actions {
|
|
910
|
+
display: flex;
|
|
911
|
+
flex-wrap: wrap;
|
|
912
|
+
justify-content: flex-end;
|
|
913
|
+
gap: 8px;
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
.review-actions a, .review-actions button {
|
|
917
|
+
border: 1px solid #334155;
|
|
918
|
+
border-radius: 10px;
|
|
919
|
+
background: #111827;
|
|
920
|
+
color: #dbeafe;
|
|
921
|
+
padding: 8px 10px;
|
|
922
|
+
font: inherit;
|
|
923
|
+
font-size: 0.78rem;
|
|
924
|
+
font-weight: 850;
|
|
925
|
+
text-decoration: none;
|
|
926
|
+
cursor: pointer;
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
.review-actions .primary-action {
|
|
930
|
+
background: #38bdf8;
|
|
931
|
+
border-color: #38bdf8;
|
|
932
|
+
color: #0b1220;
|
|
641
933
|
}
|
|
642
934
|
|
|
643
935
|
.data-section {
|
|
@@ -655,6 +947,7 @@ export function renderIngestDossierAsHtml(dossier) {
|
|
|
655
947
|
.section-heading h2 {
|
|
656
948
|
font-size: 1.35rem;
|
|
657
949
|
letter-spacing: 0;
|
|
950
|
+
color: var(--text);
|
|
658
951
|
}
|
|
659
952
|
|
|
660
953
|
.section-heading p {
|
|
@@ -690,26 +983,49 @@ export function renderIngestDossierAsHtml(dossier) {
|
|
|
690
983
|
}
|
|
691
984
|
|
|
692
985
|
.profile-shell, .table-shell {
|
|
693
|
-
overflow
|
|
986
|
+
overflow: auto;
|
|
694
987
|
border: 1px solid var(--border);
|
|
695
988
|
border-radius: var(--radius);
|
|
696
989
|
background: var(--surface);
|
|
697
990
|
min-width: 0;
|
|
698
991
|
max-width: 100%;
|
|
992
|
+
max-height: 560px;
|
|
699
993
|
}
|
|
700
994
|
|
|
701
995
|
.borehole-profile {
|
|
996
|
+
display: block;
|
|
997
|
+
min-width: 680px;
|
|
998
|
+
width: 100%;
|
|
999
|
+
height: auto;
|
|
1000
|
+
}
|
|
1001
|
+
|
|
1002
|
+
.cross-section-shell {
|
|
1003
|
+
background:
|
|
1004
|
+
linear-gradient(180deg, rgba(6, 182, 212, 0.06), rgba(16, 185, 129, 0.04)),
|
|
1005
|
+
var(--surface);
|
|
1006
|
+
max-height: 360px;
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
.ground-cross-section {
|
|
702
1010
|
display: block;
|
|
703
1011
|
min-width: 760px;
|
|
704
1012
|
width: 100%;
|
|
705
1013
|
height: auto;
|
|
706
1014
|
}
|
|
707
1015
|
|
|
708
|
-
.
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
1016
|
+
.verification-note {
|
|
1017
|
+
color: var(--muted);
|
|
1018
|
+
font-weight: 700;
|
|
1019
|
+
line-height: 1.55;
|
|
1020
|
+
padding-left: 14px;
|
|
1021
|
+
border-left: 3px solid var(--warning);
|
|
1022
|
+
}
|
|
1023
|
+
|
|
1024
|
+
.profile-title { font: 800 15px Inter, Segoe UI, sans-serif; fill: #f8fafc; }
|
|
1025
|
+
.profile-label { font: 800 13px Inter, Segoe UI, sans-serif; fill: #f8fafc; }
|
|
1026
|
+
.profile-small { font: 11px Inter, Segoe UI, sans-serif; fill: #cbd5e1; }
|
|
1027
|
+
.profile-axis { font: 11px Inter, Segoe UI, sans-serif; fill: #94a3b8; }
|
|
1028
|
+
.profile-water { font: 800 11px Inter, Segoe UI, sans-serif; fill: #67e8f9; }
|
|
713
1029
|
|
|
714
1030
|
.profile-notes {
|
|
715
1031
|
color: var(--muted);
|
|
@@ -724,19 +1040,20 @@ export function renderIngestDossierAsHtml(dossier) {
|
|
|
724
1040
|
|
|
725
1041
|
th, td {
|
|
726
1042
|
padding: 13px 14px;
|
|
727
|
-
border-bottom: 1px solid rgba(
|
|
1043
|
+
border-bottom: 1px solid rgba(100, 116, 139, 0.22);
|
|
728
1044
|
text-align: left;
|
|
729
1045
|
vertical-align: top;
|
|
730
1046
|
font-size: 0.92rem;
|
|
731
1047
|
line-height: 1.48;
|
|
732
1048
|
overflow-wrap: anywhere;
|
|
1049
|
+
color: #cbd5e1;
|
|
733
1050
|
}
|
|
734
1051
|
|
|
735
1052
|
th {
|
|
736
1053
|
position: sticky;
|
|
737
1054
|
top: 0;
|
|
738
1055
|
z-index: 1;
|
|
739
|
-
background: var(--surface-
|
|
1056
|
+
background: var(--surface-raised);
|
|
740
1057
|
color: var(--muted);
|
|
741
1058
|
font-size: 0.76rem;
|
|
742
1059
|
text-transform: uppercase;
|
|
@@ -757,6 +1074,71 @@ export function renderIngestDossierAsHtml(dossier) {
|
|
|
757
1074
|
line-height: 1;
|
|
758
1075
|
}
|
|
759
1076
|
|
|
1077
|
+
.trust-controls {
|
|
1078
|
+
display: flex;
|
|
1079
|
+
align-items: end;
|
|
1080
|
+
justify-content: space-between;
|
|
1081
|
+
gap: 12px;
|
|
1082
|
+
flex-wrap: wrap;
|
|
1083
|
+
padding: 14px;
|
|
1084
|
+
border: 1px solid var(--border);
|
|
1085
|
+
border-radius: var(--radius);
|
|
1086
|
+
background: var(--surface);
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1089
|
+
.trust-controls label {
|
|
1090
|
+
display: grid;
|
|
1091
|
+
gap: 7px;
|
|
1092
|
+
flex: 1 1 320px;
|
|
1093
|
+
color: var(--muted);
|
|
1094
|
+
font-size: 0.75rem;
|
|
1095
|
+
text-transform: uppercase;
|
|
1096
|
+
font-weight: 900;
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1099
|
+
.trust-controls input {
|
|
1100
|
+
width: 100%;
|
|
1101
|
+
border: 1px solid var(--border-strong);
|
|
1102
|
+
border-radius: 12px;
|
|
1103
|
+
background: #0b1120;
|
|
1104
|
+
color: var(--text);
|
|
1105
|
+
padding: 11px 12px;
|
|
1106
|
+
font: inherit;
|
|
1107
|
+
font-size: 0.92rem;
|
|
1108
|
+
outline: none;
|
|
1109
|
+
text-transform: none;
|
|
1110
|
+
font-weight: 600;
|
|
1111
|
+
}
|
|
1112
|
+
|
|
1113
|
+
.trust-controls input:focus {
|
|
1114
|
+
border-color: var(--primary);
|
|
1115
|
+
box-shadow: 0 0 0 4px rgba(6, 182, 212, 0.12);
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1118
|
+
.filter-row {
|
|
1119
|
+
display: flex;
|
|
1120
|
+
gap: 8px;
|
|
1121
|
+
flex-wrap: wrap;
|
|
1122
|
+
}
|
|
1123
|
+
|
|
1124
|
+
.filter-button {
|
|
1125
|
+
border: 1px solid var(--border-strong);
|
|
1126
|
+
border-radius: 999px;
|
|
1127
|
+
background: var(--surface-soft);
|
|
1128
|
+
color: var(--muted);
|
|
1129
|
+
padding: 9px 12px;
|
|
1130
|
+
font: inherit;
|
|
1131
|
+
font-size: 0.8rem;
|
|
1132
|
+
font-weight: 850;
|
|
1133
|
+
cursor: pointer;
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1136
|
+
.filter-button.active {
|
|
1137
|
+
background: var(--primary);
|
|
1138
|
+
color: #06111f;
|
|
1139
|
+
border-color: var(--primary);
|
|
1140
|
+
}
|
|
1141
|
+
|
|
760
1142
|
.evidence-grid {
|
|
761
1143
|
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
|
|
762
1144
|
}
|
|
@@ -785,6 +1167,38 @@ export function renderIngestDossierAsHtml(dossier) {
|
|
|
785
1167
|
line-height: 1.45;
|
|
786
1168
|
}
|
|
787
1169
|
|
|
1170
|
+
.evidence-actions {
|
|
1171
|
+
display: flex;
|
|
1172
|
+
flex-wrap: wrap;
|
|
1173
|
+
gap: 8px;
|
|
1174
|
+
padding-top: 4px;
|
|
1175
|
+
}
|
|
1176
|
+
|
|
1177
|
+
.evidence-actions a, .evidence-actions button {
|
|
1178
|
+
border: 1px solid var(--border-strong);
|
|
1179
|
+
border-radius: 10px;
|
|
1180
|
+
background: #0b1120;
|
|
1181
|
+
color: #67e8f9;
|
|
1182
|
+
padding: 8px 9px;
|
|
1183
|
+
font: inherit;
|
|
1184
|
+
font-size: 0.78rem;
|
|
1185
|
+
font-weight: 850;
|
|
1186
|
+
text-decoration: none;
|
|
1187
|
+
cursor: pointer;
|
|
1188
|
+
}
|
|
1189
|
+
|
|
1190
|
+
.evidence-actions button[data-state="verified"] {
|
|
1191
|
+
color: var(--success);
|
|
1192
|
+
border-color: rgba(22, 135, 93, 0.28);
|
|
1193
|
+
background: var(--success-soft);
|
|
1194
|
+
}
|
|
1195
|
+
|
|
1196
|
+
.evidence-actions button[data-state="flagged"] {
|
|
1197
|
+
color: var(--warning);
|
|
1198
|
+
border-color: rgba(183, 121, 31, 0.28);
|
|
1199
|
+
background: var(--warning-soft);
|
|
1200
|
+
}
|
|
1201
|
+
|
|
788
1202
|
.empty-state {
|
|
789
1203
|
padding: 18px;
|
|
790
1204
|
border: 1px dashed var(--border);
|
|
@@ -817,10 +1231,10 @@ export function renderIngestDossierAsHtml(dossier) {
|
|
|
817
1231
|
.audit-drawer small { color: var(--muted); }
|
|
818
1232
|
|
|
819
1233
|
.disclosure-hint {
|
|
820
|
-
border: 1px solid var(--border);
|
|
1234
|
+
border: 1px solid var(--border-strong);
|
|
821
1235
|
border-radius: 999px;
|
|
822
1236
|
padding: 7px 11px;
|
|
823
|
-
color:
|
|
1237
|
+
color: #67e8f9;
|
|
824
1238
|
background: var(--primary-soft);
|
|
825
1239
|
font-weight: 800;
|
|
826
1240
|
white-space: nowrap;
|
|
@@ -879,9 +1293,9 @@ export function renderIngestDossierAsHtml(dossier) {
|
|
|
879
1293
|
}
|
|
880
1294
|
|
|
881
1295
|
.stage-chip {
|
|
882
|
-
color:
|
|
1296
|
+
color: #67e8f9;
|
|
883
1297
|
background: var(--primary-soft);
|
|
884
|
-
border-color:
|
|
1298
|
+
border-color: rgba(6, 182, 212, 0.32);
|
|
885
1299
|
}
|
|
886
1300
|
|
|
887
1301
|
.footer-grid {
|
|
@@ -898,37 +1312,109 @@ export function renderIngestDossierAsHtml(dossier) {
|
|
|
898
1312
|
line-height: 1.58;
|
|
899
1313
|
}
|
|
900
1314
|
|
|
1315
|
+
tr[hidden] { display: none; }
|
|
1316
|
+
|
|
1317
|
+
.toast-region {
|
|
1318
|
+
position: fixed;
|
|
1319
|
+
top: 18px;
|
|
1320
|
+
right: 18px;
|
|
1321
|
+
z-index: 60;
|
|
1322
|
+
display: grid;
|
|
1323
|
+
gap: 8px;
|
|
1324
|
+
pointer-events: none;
|
|
1325
|
+
}
|
|
1326
|
+
|
|
1327
|
+
.toast {
|
|
1328
|
+
max-width: 320px;
|
|
1329
|
+
padding: 11px 14px;
|
|
1330
|
+
border: 1px solid #334155;
|
|
1331
|
+
border-radius: 12px;
|
|
1332
|
+
background: #0b1220;
|
|
1333
|
+
color: #f8fafc;
|
|
1334
|
+
box-shadow: 0 18px 38px rgba(11, 18, 32, 0.22);
|
|
1335
|
+
font-size: 0.86rem;
|
|
1336
|
+
font-weight: 700;
|
|
1337
|
+
}
|
|
1338
|
+
|
|
901
1339
|
@media (max-width: 980px) {
|
|
902
|
-
.layout {
|
|
903
|
-
.sidebar { position: static; min-height: auto; }
|
|
904
|
-
.nav-list {
|
|
1340
|
+
.layout { padding: 14px 16px 96px; max-width: 100%; }
|
|
1341
|
+
.sidebar { position: static; min-height: auto; align-items: flex-start; flex-direction: column; }
|
|
1342
|
+
.nav-list { justify-content: flex-start; }
|
|
905
1343
|
.hero-main { grid-template-columns: 1fr; }
|
|
906
1344
|
}
|
|
907
1345
|
|
|
908
1346
|
@media (max-width: 620px) {
|
|
909
|
-
|
|
1347
|
+
body { padding-bottom: 0; }
|
|
1348
|
+
.layout {
|
|
1349
|
+
display: grid;
|
|
1350
|
+
width: min(100vw, 390px);
|
|
1351
|
+
max-width: 390px;
|
|
1352
|
+
margin: 0;
|
|
1353
|
+
padding: 10px;
|
|
1354
|
+
gap: 18px;
|
|
1355
|
+
overflow: hidden;
|
|
1356
|
+
}
|
|
910
1357
|
.sidebar, .content, .hero, .data-section, .audit-drawer, .footer-grid {
|
|
911
1358
|
width: 100%;
|
|
912
1359
|
max-width: 100%;
|
|
913
1360
|
}
|
|
1361
|
+
.sidebar { padding: 14px; min-height: auto; border-radius: 14px; }
|
|
1362
|
+
.brand { gap: 8px; padding-bottom: 0; margin-bottom: 0; }
|
|
1363
|
+
.brand span { display: none; }
|
|
1364
|
+
.nav-list {
|
|
1365
|
+
width: 100%;
|
|
1366
|
+
display: grid;
|
|
1367
|
+
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
1368
|
+
gap: 5px;
|
|
1369
|
+
}
|
|
1370
|
+
.nav-list a {
|
|
1371
|
+
min-width: 0;
|
|
1372
|
+
padding: 7px 8px;
|
|
1373
|
+
font-size: 0.74rem;
|
|
1374
|
+
line-height: 1.25;
|
|
1375
|
+
}
|
|
914
1376
|
.content { margin-top: 18px; }
|
|
915
|
-
.hero { padding:
|
|
1377
|
+
.hero { padding: 20px; width: 100%; max-width: 100%; }
|
|
1378
|
+
.hero-main { width: 100%; max-width: 100%; grid-template-columns: minmax(0, 1fr); }
|
|
1379
|
+
.hero-main > div, .hero-meta, .status-badge-row {
|
|
1380
|
+
width: 100%;
|
|
1381
|
+
max-width: min(320px, 100%);
|
|
1382
|
+
}
|
|
916
1383
|
.executive-grid, .metric-grid, .card-grid, .evidence-grid, .footer-grid { grid-template-columns: 1fr; }
|
|
917
|
-
.
|
|
918
|
-
|
|
1384
|
+
.review-bar { position: static; transform: none; width: 100%; margin-top: 18px; border-radius: 18px; align-items: stretch; }
|
|
1385
|
+
.review-bar.visible { transform: none; }
|
|
1386
|
+
.review-actions { justify-content: flex-start; }
|
|
1387
|
+
h1 { font-size: 1.72rem; max-width: min(320px, 100%); line-height: 1.08; }
|
|
1388
|
+
.hero-subtitle { max-width: min(320px, 100%); font-size: 0.96rem; line-height: 1.35; overflow-wrap: anywhere; }
|
|
1389
|
+
.hero-summary { max-width: min(320px, 100%); font-size: 0.95rem; line-height: 1.55; }
|
|
1390
|
+
.status-badge-row { display: grid; grid-template-columns: minmax(0, 1fr); }
|
|
1391
|
+
.status-badge {
|
|
1392
|
+
width: 100%;
|
|
1393
|
+
max-width: 100%;
|
|
1394
|
+
min-width: 0;
|
|
1395
|
+
padding: 9px 10px;
|
|
1396
|
+
border-radius: 18px;
|
|
1397
|
+
}
|
|
1398
|
+
.status-badge span, .status-badge strong {
|
|
1399
|
+
min-width: 0;
|
|
1400
|
+
overflow-wrap: anywhere;
|
|
1401
|
+
word-break: break-word;
|
|
1402
|
+
}
|
|
1403
|
+
table { min-width: 760px; }
|
|
919
1404
|
}
|
|
920
1405
|
</style>
|
|
921
1406
|
</head>
|
|
922
1407
|
<body>
|
|
923
1408
|
<div class="layout">
|
|
924
|
-
<aside class="sidebar" aria-label="
|
|
1409
|
+
<aside class="sidebar" aria-label="Report navigation">
|
|
925
1410
|
<div class="brand">
|
|
926
|
-
<strong>
|
|
1411
|
+
<strong>GeotechCLI Intelligence</strong>
|
|
927
1412
|
<span>AI-assisted extraction, verification, and engineering interpretation from geotechnical reports.</span>
|
|
928
1413
|
</div>
|
|
929
1414
|
<nav class="nav-list">
|
|
930
1415
|
<a href="#overview">Overview</a>
|
|
931
1416
|
<a href="#ground-model">Ground Model</a>
|
|
1417
|
+
<a href="#ground-cross-section">Cross-Section</a>
|
|
932
1418
|
<a href="#boreholes">Boreholes</a>
|
|
933
1419
|
<a href="#parameters">Engineering Parameters</a>
|
|
934
1420
|
<a href="#risks">Risks and Limitations</a>
|
|
@@ -941,10 +1427,11 @@ export function renderIngestDossierAsHtml(dossier) {
|
|
|
941
1427
|
<header class="hero" id="overview">
|
|
942
1428
|
<div class="hero-main">
|
|
943
1429
|
<div>
|
|
944
|
-
<span class="eyebrow">
|
|
945
|
-
<h1
|
|
1430
|
+
<span class="eyebrow">Review Report</span>
|
|
1431
|
+
<h1>Geotechnical Intelligence Report</h1>
|
|
946
1432
|
<p class="hero-subtitle">${escapeHtml(subtitle)}</p>
|
|
947
1433
|
<p class="hero-summary">${escapeHtml(dossier.summary)}</p>
|
|
1434
|
+
${renderStatusBadges(dossier)}
|
|
948
1435
|
</div>
|
|
949
1436
|
<div class="hero-meta">
|
|
950
1437
|
<strong>${escapeHtml(dossier.sourceLabel)}</strong>
|
|
@@ -984,6 +1471,7 @@ export function renderIngestDossierAsHtml(dossier) {
|
|
|
984
1471
|
</div>
|
|
985
1472
|
</section>
|
|
986
1473
|
|
|
1474
|
+
${renderGroundModelCrossSection(dossier.boreholeProfile)}
|
|
987
1475
|
${renderBoreholeProfile(dossier.boreholeProfile)}
|
|
988
1476
|
${renderTrustTable(dossier)}
|
|
989
1477
|
${mainTables.map((table) => renderTable(table)).join('')}
|
|
@@ -1017,6 +1505,67 @@ export function renderIngestDossierAsHtml(dossier) {
|
|
|
1017
1505
|
</section>
|
|
1018
1506
|
</main>
|
|
1019
1507
|
</div>
|
|
1508
|
+
${renderReviewBar(dossier)}
|
|
1509
|
+
<div class="toast-region" aria-live="polite" aria-atomic="true"></div>
|
|
1510
|
+
<script>
|
|
1511
|
+
(() => {
|
|
1512
|
+
const search = document.getElementById('trust-search');
|
|
1513
|
+
const rows = Array.from(document.querySelectorAll('[data-trust-row]'));
|
|
1514
|
+
const buttons = Array.from(document.querySelectorAll('[data-trust-filter]'));
|
|
1515
|
+
const toastRegion = document.querySelector('.toast-region');
|
|
1516
|
+
const reviewBar = document.querySelector('.review-bar');
|
|
1517
|
+
let activeFilter = 'all';
|
|
1518
|
+
|
|
1519
|
+
const showToast = (message) => {
|
|
1520
|
+
if (!toastRegion) return;
|
|
1521
|
+
const toast = document.createElement('div');
|
|
1522
|
+
toast.className = 'toast';
|
|
1523
|
+
toast.textContent = message;
|
|
1524
|
+
toastRegion.appendChild(toast);
|
|
1525
|
+
window.setTimeout(() => toast.remove(), 2600);
|
|
1526
|
+
};
|
|
1527
|
+
|
|
1528
|
+
const applyTrustFilter = () => {
|
|
1529
|
+
const query = String(search?.value ?? '').trim().toLowerCase();
|
|
1530
|
+
rows.forEach((row) => {
|
|
1531
|
+
const text = row.getAttribute('data-search') ?? '';
|
|
1532
|
+
const review = row.getAttribute('data-review') ?? '';
|
|
1533
|
+
const queryMatch = !query || text.includes(query);
|
|
1534
|
+
const filterMatch = activeFilter === 'all' || review === activeFilter;
|
|
1535
|
+
row.hidden = !(queryMatch && filterMatch);
|
|
1536
|
+
});
|
|
1537
|
+
};
|
|
1538
|
+
|
|
1539
|
+
const syncReviewBar = () => {
|
|
1540
|
+
if (!reviewBar) return;
|
|
1541
|
+
reviewBar.classList.toggle('visible', window.innerWidth > 620 && window.scrollY > 640);
|
|
1542
|
+
};
|
|
1543
|
+
|
|
1544
|
+
syncReviewBar();
|
|
1545
|
+
window.addEventListener('scroll', syncReviewBar, { passive: true });
|
|
1546
|
+
window.addEventListener('resize', syncReviewBar);
|
|
1547
|
+
|
|
1548
|
+
search?.addEventListener('input', applyTrustFilter);
|
|
1549
|
+
buttons.forEach((button) => {
|
|
1550
|
+
button.addEventListener('click', () => {
|
|
1551
|
+
activeFilter = button.getAttribute('data-trust-filter') ?? 'all';
|
|
1552
|
+
buttons.forEach((candidate) => candidate.classList.toggle('active', candidate === button));
|
|
1553
|
+
applyTrustFilter();
|
|
1554
|
+
});
|
|
1555
|
+
});
|
|
1556
|
+
|
|
1557
|
+
document.querySelectorAll('[data-review-action]').forEach((control) => {
|
|
1558
|
+
control.addEventListener('click', () => {
|
|
1559
|
+
const state = control.getAttribute('data-review-action') ?? '';
|
|
1560
|
+
control.setAttribute('data-state', state);
|
|
1561
|
+
control.textContent = state === 'verified' ? 'Verified' : 'Issue flagged';
|
|
1562
|
+
showToast(state === 'verified'
|
|
1563
|
+
? 'Evidence item marked verified in this local report view.'
|
|
1564
|
+
: 'Evidence item flagged for engineering review in this local report view.');
|
|
1565
|
+
});
|
|
1566
|
+
});
|
|
1567
|
+
})();
|
|
1568
|
+
</script>
|
|
1020
1569
|
</body>
|
|
1021
1570
|
</html>`;
|
|
1022
1571
|
}
|