HirModel's picture
Upload 2 files
4e143a3 verified
Raw
History Blame Contribute Delete
31.8 kB
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>C2PA / Adobe Bridge Layer — Trace Capsule Prototype v0.1</title>
<meta name="description" content="Independent public-review prototype demonstrating a lightweight Trace Capsule bridge layer for provenance-bearing content: credential summary, HIR/OAM pressure check, loop state, and source-return." />
<meta property="og:title" content="C2PA / Adobe Bridge Layer — Trace Capsule Prototype v0.1" />
<meta property="og:description" content="A public-review demo showing how provenance-bearing content can preserve bounded interpretation, attribution, uncertainty, loop state, and source-return after visibility." />
<meta name="theme-color" content="#0f65d6" />
<style>
:root {
--bg: #f5f9ff;
--panel: #ffffff;
--ink: #14233b;
--muted: #5a6880;
--line: #d9e6f7;
--blue: #0f65d6;
--blue-2: #0b83e6;
--cyan: #13bfd0;
--green: #17875d;
--amber: #9b6400;
--orange: #c7511f;
--red: #b42318;
--violet: #6544c5;
--shadow: 0 22px 70px rgba(16, 67, 136, .16);
--radius: 22px;
--mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace;
--sans: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
}
* { box-sizing: border-box; }
body {
margin: 0;
font-family: var(--sans);
color: var(--ink);
background:
radial-gradient(circle at 18% 0%, rgba(19,191,208,.28), transparent 32rem),
radial-gradient(circle at 80% 0%, rgba(15,101,214,.20), transparent 28rem),
linear-gradient(180deg, #eef7ff 0%, var(--bg) 46%, #ffffff 100%);
line-height: 1.5;
}
a { color: var(--blue); }
.skip-link {
position: absolute;
left: -999px;
top: 1rem;
background: var(--ink);
color: #fff;
padding: .75rem 1rem;
border-radius: .75rem;
z-index: 1000;
}
.skip-link:focus { left: 1rem; }
.wrap { max-width: 1180px; margin: 0 auto; padding: 28px; }
header.hero {
position: relative;
overflow: hidden;
border: 1px solid rgba(15,101,214,.14);
border-radius: 30px;
background: rgba(255,255,255,.78);
box-shadow: var(--shadow);
padding: clamp(24px, 5vw, 58px);
margin: 22px 0 26px;
}
.hero::before {
content: "";
position: absolute;
inset: -1px;
background: radial-gradient(circle at 78% 20%, rgba(19,191,208,.18), transparent 22rem);
pointer-events: none;
}
.hero > * { position: relative; z-index: 1; }
.kicker {
display: inline-flex;
align-items: center;
gap: .55rem;
font-size: .82rem;
font-weight: 800;
letter-spacing: .08em;
text-transform: uppercase;
color: #0a4ea8;
background: #eaf4ff;
border: 1px solid #cfe5ff;
border-radius: 999px;
padding: .42rem .75rem;
}
.pin {
width: 24px; height: 24px; border-radius: 50%;
display: inline-grid; place-items: center;
background: #111827; color: #fff;
font-size: .68rem; font-weight: 900;
letter-spacing: -.03em;
}
h1 { font-size: clamp(2.25rem, 6vw, 5.4rem); line-height: .94; margin: 20px 0 14px; letter-spacing: -.06em; }
h2 { font-size: clamp(1.35rem, 2.6vw, 2.25rem); letter-spacing: -.035em; margin: 0 0 1rem; }
h3 { font-size: 1.05rem; margin: 0 0 .65rem; letter-spacing: -.02em; }
.subtitle { max-width: 860px; color: var(--muted); font-size: clamp(1.05rem, 2vw, 1.35rem); margin: 0 0 28px; }
.boundary {
display: grid; gap: 10px;
max-width: 960px;
border: 1px solid #d9e6f7;
background: #f8fbff;
border-radius: 18px;
padding: 16px 18px;
color: #41516c;
}
.boundary strong { color: var(--ink); }
.flow {
display: grid;
grid-template-columns: repeat(7, minmax(0, 1fr));
gap: 10px;
align-items: stretch;
margin: 24px 0 8px;
}
.flow-step {
min-height: 82px;
border: 1px solid var(--line);
border-radius: 18px;
background: #fff;
padding: 12px;
box-shadow: 0 8px 28px rgba(41, 89, 153, .08);
}
.flow-step span { display:block; font-size:.72rem; color:var(--muted); text-transform:uppercase; letter-spacing:.08em; font-weight:800; }
.flow-step b { display:block; margin-top:6px; font-size:.98rem; }
.grid { display: grid; gap: 20px; }
.grid.two { grid-template-columns: minmax(0, .95fr) minmax(0, 1.05fr); }
.grid.three { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.card {
background: var(--panel);
border: 1px solid var(--line);
border-radius: var(--radius);
padding: 20px;
box-shadow: 0 12px 40px rgba(27, 69, 128, .08);
}
.section { margin: 20px 0; }
.small { font-size: .9rem; color: var(--muted); }
.micro { font-size: .78rem; color: var(--muted); }
.pill-row { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 12px; }
.pill {
display: inline-flex; align-items: center; gap: .35rem;
border: 1px solid #d7e5f7; background: #f7fbff; color: #31506e;
border-radius: 999px; padding: .38rem .65rem; font-size: .78rem; font-weight: 750;
}
.pill.blue { background: #eaf4ff; border-color:#cfe5ff; color:#0a4ea8; }
.pill.green { background: #eaf8f3; border-color:#c7efdf; color:#126a49; }
.pill.amber { background: #fff6e4; border-color:#f5d68c; color:#7a4b00; }
.pill.red { background: #fff0ef; border-color:#ffd1ce; color:#9c1d15; }
.credential-media {
border-radius: 18px;
min-height: 210px;
background:
linear-gradient(135deg, rgba(11,131,230,.18), rgba(19,191,208,.16)),
radial-gradient(circle at 30% 25%, rgba(255,255,255,.95), transparent 14rem),
linear-gradient(160deg, #0e3866, #0b83e6 46%, #93e2ee 100%);
position: relative;
overflow: hidden;
border: 1px solid #cfe5ff;
}
.credential-media::before, .credential-media::after {
content: ""; position: absolute; border-radius: 999px; background: rgba(255,255,255,.34);
}
.credential-media::before { width: 300px; height: 300px; right: -90px; top: -110px; }
.credential-media::after { width: 460px; height: 160px; left: -160px; bottom: -60px; transform: rotate(-8deg); }
.media-label {
position: absolute; left: 14px; top: 14px; z-index: 1;
display: inline-flex; gap: .45rem; align-items: center;
color: #10243c; background: rgba(255,255,255,.86);
border: 1px solid rgba(255,255,255,.75);
border-radius: 999px; padding: .45rem .7rem; font-size: .78rem; font-weight: 800;
}
dl.meta { display: grid; gap: 10px; margin: 16px 0 0; }
.row {
display: grid; grid-template-columns: 150px minmax(0,1fr); gap: 12px;
padding: 10px 0; border-top: 1px solid #eef3fb;
}
.row dt { color: var(--muted); font-weight: 750; }
.row dd { margin: 0; font-weight: 700; word-break: break-word; }
.control-group { display: grid; gap: 12px; margin-top: 12px; }
label.check, .radio-like {
display: flex; align-items: flex-start; gap: 10px;
border: 1px solid #e1ebf8;
background: #f9fcff;
border-radius: 14px;
padding: 10px 12px;
cursor: pointer;
}
input[type="checkbox"] { width: 18px; height: 18px; margin-top: 2px; accent-color: var(--blue); flex: 0 0 auto; }
select, input[type="text"], textarea {
width: 100%; border: 1px solid #d6e4f6; border-radius: 14px; padding: 11px 12px;
font: inherit; color: var(--ink); background: #fff;
}
textarea { min-height: 96px; resize: vertical; }
.state-panel {
border-radius: 24px; padding: 22px;
background: linear-gradient(135deg, #fff, #f7fbff);
border: 1px solid #d7e6f9;
position: sticky; top: 16px;
}
.state-badge {
display: inline-flex; align-items: center; gap: .55rem;
border-radius: 999px; padding: .6rem .85rem;
font-weight: 900; letter-spacing: .04em; font-size: .84rem;
text-transform: uppercase;
border: 1px solid transparent;
}
.state-badge.HELD { background:#eaf4ff; color:#0a4ea8; border-color:#cfe5ff; }
.state-badge.STRAINED { background:#fff6e4; color:#7a4b00; border-color:#f5d68c; }
.state-badge.REPAIRING { background:#f0f3ff; color:#5136a3; border-color:#d9d2ff; }
.state-badge.FALSE_CLOSURE_RISK { background:#fff0ef; color:#9c1d15; border-color:#ffd1ce; }
.state-badge.CLOSED_FOR_CURRENT_SCOPE { background:#eaf8f3; color:#126a49; border-color:#c7efdf; }
.meter {
height: 14px; background:#e8f0fb; border-radius:999px; overflow:hidden; margin: 16px 0 10px; border: 1px solid #d7e5f7;
}
.meter > div { height: 100%; width: 0%; background: linear-gradient(90deg, var(--blue), var(--cyan)); transition: width .25s ease; }
.flag-list { display: grid; gap: 8px; margin: 14px 0; padding: 0; list-style: none; }
.flag-list li {
display: flex; gap: 10px; align-items: flex-start;
background: #f8fbff; border: 1px solid #e3eefb; border-radius: 14px; padding: 10px;
}
.flag-dot { width: 10px; height: 10px; border-radius: 50%; margin-top: 7px; background: var(--blue); flex: 0 0 auto; }
.flag-list li.warn .flag-dot { background: var(--amber); }
.flag-list li.risk .flag-dot { background: var(--red); }
.flag-list li.good .flag-dot { background: var(--green); }
.actions { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 16px; }
button {
border: 0; border-radius: 14px; padding: 11px 14px;
background: var(--blue); color:#fff; font-weight: 850; cursor:pointer;
box-shadow: 0 10px 24px rgba(15,101,214,.22);
}
button.secondary { background:#eef5ff; color:#0a4ea8; box-shadow:none; border:1px solid #cfe5ff; }
button.ghost { background:transparent; color:#31506e; box-shadow:none; border:1px solid #d7e5f7; }
button:focus-visible, input:focus-visible, select:focus-visible, textarea:focus-visible { outline: 3px solid rgba(15,101,214,.34); outline-offset: 2px; }
.capsule-json {
max-height: 310px; overflow:auto;
background: #0f172a; color: #d8e5ff;
border-radius: 18px; padding: 16px; font-family: var(--mono); font-size: .78rem;
white-space: pre-wrap; border: 1px solid #22304f;
}
.layers { display:grid; grid-template-columns: repeat(4, minmax(0,1fr)); gap: 12px; }
.layer-card { border-radius: 18px; padding: 16px; border: 1px solid #dbe9fa; background: #fff; }
.layer-card b { display:block; margin-bottom: 6px; }
footer { margin: 28px 0 10px; color: var(--muted); text-align:center; font-size:.9rem; }
@media (max-width: 930px) {
.grid.two, .grid.three, .layers, .flow { grid-template-columns: 1fr; }
.state-panel { position: static; }
.row { grid-template-columns: 1fr; gap: 2px; }
.wrap { padding: 16px; }
}
</style>
</head>
<body>
<a class="skip-link" href="#main">Skip to prototype</a>
<div class="wrap">
<header class="hero" role="banner">
<div class="kicker"><span class="pin" aria-hidden="true">TC</span> Public Review Prototype · v0.1</div>
<h1>The C2PA / Adobe Bridge Layer</h1>
<p class="subtitle">Trace Capsules, HIR/OAM pressure logic, and a lightweight integrity kernel for provenance-bearing content after it enters interpretation.</p>
<div class="boundary" aria-label="Public review boundary">
<strong>Boundary:</strong>
<span>This is an independent public-review demo. It is not an Adobe product claim, not a C2PA implementation, not a Content Credentials replacement, and not an assertion of affiliation, endorsement, adoption, approval, or inside knowledge.</span>
</div>
<div class="flow" aria-label="Bridge flow">
<div class="flow-step"><span>01</span><b>Create</b></div>
<div class="flow-step"><span>02</span><b>Credential</b></div>
<div class="flow-step"><span>03</span><b>Display</b></div>
<div class="flow-step"><span>04</span><b>Capsule</b></div>
<div class="flow-step"><span>05</span><b>Check</b></div>
<div class="flow-step"><span>06</span><b>State</b></div>
<div class="flow-step"><span>07</span><b>Return</b></div>
</div>
</header>
<main id="main">
<section class="section grid two" aria-label="Prototype controls and output">
<div class="grid">
<article class="card" aria-labelledby="credential-title">
<h2 id="credential-title">Credential Summary</h2>
<p class="small">Demo content card showing the public-facing side of a provenance-bearing trace. This does not inspect real C2PA data.</p>
<div class="credential-media" role="img" aria-label="Abstract sample media card representing provenance-bearing creative content">
<span class="media-label"><span class="pin" aria-hidden="true">Cr</span> Sample credential signal</span>
</div>
<dl class="meta">
<div class="row"><dt>Content</dt><dd>Sample editorial image / article header</dd></div>
<div class="row"><dt>Credential state</dt><dd id="metaCredential">Visible · demo only</dd></div>
<div class="row"><dt>Origin claim</dt><dd>Created by Collin D. Weber · public review draft</dd></div>
<div class="row"><dt>Use context</dt><dd id="metaUse">Public article / X post / prototype review</dd></div>
</dl>
</article>
<article class="card" aria-labelledby="pressure-title">
<h2 id="pressure-title">Interactive Pressure Check</h2>
<p class="small">Toggle trace conditions to see the Trace Capsule state and HIR/OAM flags update.</p>
<div class="control-group">
<label class="check"><input id="credentialVisible" type="checkbox" checked><span><strong>Credential visible</strong><br><span class="micro">The viewer can access a provenance signal or summary.</span></span></label>
<label class="check"><input id="sourcePointer" type="checkbox" checked><span><strong>Source pointer preserved</strong><br><span class="micro">The trace still routes toward origin or source context.</span></span></label>
<label class="check"><input id="attributionPresent" type="checkbox" checked><span><strong>Attribution visible</strong><br><span class="micro">Creator / contributor path remains present where available.</span></span></label>
<label class="check"><input id="uncertaintyVisible" type="checkbox" checked><span><strong>Uncertainty visible</strong><br><span class="micro">The output preserves what the trace does and does not support.</span></span></label>
<label class="check"><input id="transformLabeled" type="checkbox" checked><span><strong>Transformation labeled</strong><br><span class="micro">Edits, summaries, remixes, or derivative context are disclosed.</span></span></label>
<label class="check"><input id="sourceReturn" type="checkbox" checked><span><strong>Source-return path active</strong><br><span class="micro">The loop can return toward attribution, credential state, rights context, or repair.</span></span></label>
<label class="check"><input id="repairActive" type="checkbox"><span><strong>Repair active</strong><br><span class="micro">A correction, clarification, attribution, or boundary repair is underway.</span></span></label>
</div>
<label for="claimScope" class="small" style="display:block;margin-top:16px;font-weight:800;">Claim scope</label>
<select id="claimScope">
<option value="bounded">Bounded to what the trace supports</option>
<option value="wide">Possibly broader than the trace supports</option>
<option value="overclaim">Overclaims the trace as proof beyond scope</option>
</select>
<label for="summaryText" class="small" style="display:block;margin-top:16px;font-weight:800;">Current interpretation / summary boundary</label>
<textarea id="summaryText">This demo trace can support provenance-aware review of source, attribution, uncertainty, and boundary. It should not be treated as proof beyond its current scope.</textarea>
</article>
</div>
<aside class="state-panel" aria-labelledby="state-title">
<h2 id="state-title">Trace Capsule Output</h2>
<div id="stateBadge" class="state-badge HELD" aria-live="polite">HELD</div>
<div class="meter" aria-label="Trace stability meter"><div id="meterFill"></div></div>
<p id="stateExplanation" class="small" aria-live="polite"></p>
<div class="pill-row" id="capsulePills" aria-label="Capsule quick fields"></div>
<h3>HIR/OAM Flags</h3>
<ul id="flagList" class="flag-list" aria-live="polite"></ul>
<div class="actions">
<button type="button" id="downloadJson">Download review packet</button>
<button type="button" id="copyJson" class="secondary">Copy JSON</button>
<button type="button" id="resetDemo" class="ghost">Reset demo</button>
</div>
</aside>
</section>
<section class="section grid two" aria-label="Trace capsule details">
<article class="card">
<h2>Trace Capsule Panel</h2>
<p class="small">A Trace Capsule preserves the relationship between visible provenance and bounded interpretation. It does not replace the credential.</p>
<dl class="meta" id="capsuleMeta"></dl>
</article>
<article class="card">
<h2>Review Packet Preview</h2>
<pre id="jsonPreview" class="capsule-json" tabindex="0" aria-label="Trace Capsule JSON review packet preview"></pre>
</article>
</section>
<section class="section card" aria-labelledby="layers-title">
<h2 id="layers-title">Public Layer Mapping</h2>
<p class="small">This demo uses only observable public relationships. It does not assume private routing or internal implementation details.</p>
<div class="layers">
<div class="layer-card"><b>Content Credentials</b><span class="small">Make provenance visible to users.</span></div>
<div class="layer-card"><b>C2PA</b><span class="small">Provides the provenance standard layer.</span></div>
<div class="layer-card"><b>Adobe-facing workflows</b><span class="small">Bring provenance into real creator, editing, and product environments.</span></div>
<div class="layer-card"><b>Trace Capsules + HIR/OAM</b><span class="small">Preserve interpretation boundary, loop state, and source-return after visibility.</span></div>
</div>
</section>
<section class="section grid three" aria-label="Use cases">
<article class="card"><h3>Creator Attribution</h3><p class="small">Keeps creator/source-return visible when content is clipped, remixed, reposted, summarized, or AI-ingested.</p></article>
<article class="card"><h3>AI Summaries</h3><p class="small">Makes derivative AI output carry source, boundary, uncertainty, attribution, and claim scope.</p></article>
<article class="card"><h3>Public Evidence</h3><p class="small">Separates trace authenticity from claim validity so genuine content is not overclaimed.</p></article>
</section>
<section class="section card" aria-labelledby="nonclaims-title">
<h2 id="nonclaims-title">What This Demo Does Not Claim</h2>
<div class="pill-row">
<span class="pill red">No Adobe endorsement claim</span>
<span class="pill red">No C2PA endorsement claim</span>
<span class="pill red">No Content Credentials replacement</span>
<span class="pill red">No cryptographic provenance implementation</span>
<span class="pill red">No truth-proof claim</span>
<span class="pill red">No private-system knowledge</span>
<span class="pill red">No full-stack rewrite requirement</span>
</div>
</section>
</main>
<footer>
Created and Developed by Collin D. Weber · Public review demo · Single-file HTML / inline CSS / inline JS · Demo data only
</footer>
</div>
<script>
const els = {
credentialVisible: document.getElementById('credentialVisible'),
sourcePointer: document.getElementById('sourcePointer'),
attributionPresent: document.getElementById('attributionPresent'),
uncertaintyVisible: document.getElementById('uncertaintyVisible'),
transformLabeled: document.getElementById('transformLabeled'),
sourceReturn: document.getElementById('sourceReturn'),
repairActive: document.getElementById('repairActive'),
claimScope: document.getElementById('claimScope'),
summaryText: document.getElementById('summaryText'),
stateBadge: document.getElementById('stateBadge'),
stateExplanation: document.getElementById('stateExplanation'),
meterFill: document.getElementById('meterFill'),
flagList: document.getElementById('flagList'),
capsulePills: document.getElementById('capsulePills'),
capsuleMeta: document.getElementById('capsuleMeta'),
jsonPreview: document.getElementById('jsonPreview'),
downloadJson: document.getElementById('downloadJson'),
copyJson: document.getElementById('copyJson'),
resetDemo: document.getElementById('resetDemo'),
metaCredential: document.getElementById('metaCredential')
};
const stateText = {
HELD: 'The trace is coherent enough to handle carefully. Source, credential, boundary, attribution, and uncertainty are visible.',
STRAINED: 'Something needs clarification. The credential may be present, but context, attribution, uncertainty, or claim scope is under pressure.',
REPAIRING: 'A repair path is active. Source-return, attribution, uncertainty, or context repair is underway.',
FALSE_CLOSURE_RISK: 'The loop is at risk of being treated as closed before the trace supports closure. Narrow, repair, or stop the claim.',
CLOSED_FOR_CURRENT_SCOPE: 'The current loop is handled honestly enough for its specific scope. Source, boundary, uncertainty, and return are intact.'
};
function collect() {
return {
credential_visible: els.credentialVisible.checked,
source_pointer_preserved: els.sourcePointer.checked,
attribution_visible: els.attributionPresent.checked,
uncertainty_visible: els.uncertaintyVisible.checked,
transformation_labeled: els.transformLabeled.checked,
source_return_active: els.sourceReturn.checked,
repair_active: els.repairActive.checked,
claim_scope: els.claimScope.value,
interpretation_summary: els.summaryText.value.trim()
};
}
function evaluate(input) {
let score = 100;
const flags = [];
const add = (type, title, detail, impact) => { flags.push({ type, title, detail, impact }); score -= impact; };
if (!input.credential_visible) add('risk', 'Credential non-display', 'Provenance may exist but is not visible to the user.', 22);
else flags.push({ type: 'good', title: 'Credential visible', detail: 'The provenance signal can be accessed by the user.', impact: 0 });
if (!input.source_pointer_preserved) add('risk', 'Source pointer loss', 'The trace no longer routes cleanly toward origin or source context.', 24);
else flags.push({ type: 'good', title: 'Source pointer preserved', detail: 'The trace can still return toward source context.', impact: 0 });
if (!input.attribution_visible) add('warn', 'Attribution weakening', 'Creator or contributor visibility is weakened.', 16);
if (!input.uncertainty_visible) add('warn', 'Uncertainty erased', 'The output hides what the trace does and does not support.', 18);
if (!input.transformation_labeled) add('warn', 'Transformation not labeled', 'A summary, edit, remix, or derivative context is not clearly disclosed.', 13);
if (!input.source_return_active) add('warn', 'Source-return inactive', 'The loop does not preserve a clear route toward attribution, credential state, or repair.', 18);
if (input.claim_scope === 'wide') add('warn', 'Claim scope widening', 'The current claim may be broader than the trace supports.', 18);
if (input.claim_scope === 'overclaim') add('risk', 'Claim inflation / proof laundering', 'The trace is being treated as proof beyond its supported scope.', 31);
let state = 'HELD';
if (input.repair_active) state = 'REPAIRING';
if (score >= 92 && input.claim_scope === 'bounded' && input.source_return_active && input.attribution_visible) state = 'CLOSED_FOR_CURRENT_SCOPE';
if (score < 92 && score >= 58 && !input.repair_active) state = 'STRAINED';
if ((input.claim_scope === 'overclaim' && !input.uncertainty_visible) || (!input.source_pointer_preserved && input.claim_scope !== 'bounded') || score < 50) state = 'FALSE_CLOSURE_RISK';
if (input.repair_active && state !== 'FALSE_CLOSURE_RISK') state = 'REPAIRING';
score = Math.max(0, Math.min(100, score));
if (!flags.length) flags.push({ type:'good', title:'No pressure flags', detail:'No degradation flags detected under current demo settings.', impact:0 });
return { score, state, flags };
}
function capsule(input, result) {
return {
prototype: 'trace_capsule_bridge_v0_1_public_review_demo',
boundary: 'Independent public-review demo. Not a C2PA implementation, Adobe product claim, endorsement claim, or cryptographic provenance replacement.',
flow: ['Create','Credential','Display','Capsule','Check','State','Return'],
credential_summary: {
state: input.credential_visible ? 'visible_demo_signal' : 'not_visible_in_current_context',
source_pointer: input.source_pointer_preserved ? 'preserved' : 'missing_or_weakened',
origin_claim: 'Created by Collin D. Weber · public review draft',
current_use_context: 'Public article / X post / prototype review'
},
trace_capsule: {
source_pointer: input.source_pointer_preserved,
attribution_state: input.attribution_visible ? 'visible' : 'weakened',
uncertainty_state: input.uncertainty_visible ? 'visible' : 'erased_or_hidden',
transformation_label: input.transformation_labeled ? 'visible' : 'missing',
claim_scope: input.claim_scope,
interpretation_boundary: input.interpretation_summary,
source_return_path: input.source_return_active ? 'active' : 'inactive',
repair_path: input.repair_active ? 'active' : 'not_active'
},
hir_oam_pressure_check: {
trace_stability_score: result.score,
loop_state: result.state,
flags: result.flags
},
generated_at: new Date().toISOString()
};
}
function render() {
const input = collect();
const result = evaluate(input);
const packet = capsule(input, result);
els.stateBadge.className = 'state-badge ' + result.state;
els.stateBadge.textContent = result.state.replaceAll('_', ' ');
els.stateExplanation.textContent = stateText[result.state];
els.meterFill.style.width = result.score + '%';
els.metaCredential.textContent = input.credential_visible ? 'Visible · demo only' : 'Not visible in current context · demo only';
els.flagList.innerHTML = '';
result.flags.forEach(flag => {
const li = document.createElement('li');
li.className = flag.type;
li.innerHTML = '<span class="flag-dot" aria-hidden="true"></span><span><strong>' + escapeHtml(flag.title) + '</strong><br><span class="small">' + escapeHtml(flag.detail) + '</span></span>';
els.flagList.appendChild(li);
});
const pills = [
['Credential', input.credential_visible ? 'visible' : 'hidden', input.credential_visible ? 'green' : 'red'],
['Attribution', input.attribution_visible ? 'visible' : 'weakened', input.attribution_visible ? 'green' : 'amber'],
['Uncertainty', input.uncertainty_visible ? 'visible' : 'hidden', input.uncertainty_visible ? 'green' : 'amber'],
['Return', input.source_return_active ? 'active' : 'inactive', input.source_return_active ? 'green' : 'amber'],
['Claim', input.claim_scope, input.claim_scope === 'bounded' ? 'green' : input.claim_scope === 'wide' ? 'amber' : 'red']
];
els.capsulePills.innerHTML = pills.map(([k,v,c]) => '<span class="pill '+ c +'">'+ escapeHtml(k) + ': ' + escapeHtml(v) + '</span>').join('');
const metaRows = [
['Source pointer', input.source_pointer_preserved ? 'Preserved' : 'Missing / weakened'],
['Credential state', input.credential_visible ? 'Visible demo signal' : 'Not visible in current context'],
['Attribution state', input.attribution_visible ? 'Visible' : 'Weakened'],
['Uncertainty state', input.uncertainty_visible ? 'Visible' : 'Erased or hidden'],
['Claim boundary', input.claim_scope],
['Loop state', result.state.replaceAll('_', ' ')],
['Source-return', input.source_return_active ? 'Active' : 'Inactive'],
['Repair path', input.repair_active ? 'Active' : 'Not active']
];
els.capsuleMeta.innerHTML = metaRows.map(([dt,dd]) => '<div class="row"><dt>'+ escapeHtml(dt) +'</dt><dd>'+ escapeHtml(dd) +'</dd></div>').join('');
els.jsonPreview.textContent = JSON.stringify(packet, null, 2);
window.currentPacket = packet;
}
function escapeHtml(value) {
return String(value).replace(/[&<>"']/g, ch => ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#039;'}[ch]));
}
function resetDemo() {
els.credentialVisible.checked = true;
els.sourcePointer.checked = true;
els.attributionPresent.checked = true;
els.uncertaintyVisible.checked = true;
els.transformLabeled.checked = true;
els.sourceReturn.checked = true;
els.repairActive.checked = false;
els.claimScope.value = 'bounded';
els.summaryText.value = 'This demo trace can support provenance-aware review of source, attribution, uncertainty, and boundary. It should not be treated as proof beyond its current scope.';
render();
}
document.querySelectorAll('input, select, textarea').forEach(el => el.addEventListener('input', render));
document.querySelectorAll('input, select, textarea').forEach(el => el.addEventListener('change', render));
els.downloadJson.addEventListener('click', () => {
const blob = new Blob([JSON.stringify(window.currentPacket || {}, null, 2)], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'trace_capsule_review_packet_demo.json';
document.body.appendChild(a);
a.click();
a.remove();
URL.revokeObjectURL(url);
});
els.copyJson.addEventListener('click', async () => {
const text = JSON.stringify(window.currentPacket || {}, null, 2);
try {
await navigator.clipboard.writeText(text);
els.copyJson.textContent = 'Copied';
setTimeout(() => els.copyJson.textContent = 'Copy JSON', 1100);
} catch (err) {
els.jsonPreview.focus();
}
});
els.resetDemo.addEventListener('click', resetDemo);
render();
</script>
</body>
</html>