| <!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 => ({'&':'&','<':'<','>':'>','"':'"',"'":'''}[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> |
|
|