<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="color-scheme" content="dark" />
<meta
http-equiv="Content-Security-Policy"
content="default-src 'none'; script-src 'self'; style-src 'self'; img-src 'none'; connect-src 'none'; font-src 'none'; object-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'"
/>
<title>Quatopsy investigation console</title>
<link rel="stylesheet" href="viewer.css" />
</head>
<body>
<a class="skip" href="#main">Skip to main content</a>
<header class="masthead">
<div class="brand-lockup">
<svg
class="brand-mark"
viewBox="0 0 32 32"
role="img"
aria-label="Quatopsy"
data-brand-version="quatopsy.brand/2"
>
<defs>
<linearGradient
id="brand-ribbon-a"
gradientUnits="userSpaceOnUse"
x1="5"
y1="6"
x2="20"
y2="26"
>
<stop stop-color="#8b5cf6" />
<stop offset="0.48" stop-color="#b445eb" />
<stop offset="1" stop-color="#d946ef" />
</linearGradient>
<linearGradient
id="brand-ribbon-b"
gradientUnits="userSpaceOnUse"
x1="12"
y1="26"
x2="27"
y2="8"
>
<stop stop-color="#58104f" />
<stop offset="0.52" stop-color="#b21eaa" />
<stop offset="1" stop-color="#ec48c6" />
</linearGradient>
<linearGradient
id="brand-ribbon-c"
gradientUnits="userSpaceOnUse"
x1="6"
y1="22"
x2="26"
y2="15"
>
<stop stop-color="#f5c96a" />
<stop offset="0.45" stop-color="#fff1d6" />
<stop offset="1" stop-color="#fff8e8" />
</linearGradient>
</defs>
<path
d="M4.7 14.0 C3.1 9.0 6.0 5.0 11.0 4.8 C16.0 4.6 20.9 7.8 23.8 12.2 C19.8 9.4 15.3 7.1 11.3 7.3 C7.7 7.5 6.2 10.2 7.0 13.8 C8.0 18.1 12.5 22.8 18.6 25.9 C12.2 24.3 6.4 19.9 4.7 14.0 Z"
fill="url(#brand-ribbon-a)"
/>
<path
d="M20.2 5.8 C25.0 7.1 28.0 11.3 27.7 16.2 C27.3 21.4 22.4 24.5 16.3 25.8 C20.5 23.0 24.0 20.2 25.1 16.6 C26.2 13.0 24.3 8.9 20.2 5.8 Z M17.8 25.2 C21.1 26.2 23.8 25.4 26.3 23.1 C23.8 26.8 20.7 28.0 17.7 27.2 C14.7 26.4 12.3 25.7 10.4 24.3 C13.0 24.7 15.5 24.3 17.8 25.2 Z"
fill="url(#brand-ribbon-b)"
/>
<path
d="M6.0 23.1 C9.7 18.1 15.7 14.0 21.7 14.1 C23.5 14.1 24.8 14.7 25.6 15.6 C21.1 15.4 16.8 16.6 13.0 18.5 C9.8 20.1 7.3 22.1 6.0 23.1 Z"
fill="url(#brand-ribbon-c)"
/>
</svg>
<div>
<p class="eyebrow">See where rotations go wrong.</p>
<h1>QUATOPSY <span>/ investigation console</span></h1>
</div>
</div>
<p class="claim">Local advisory analysis. Not flight approval.</p>
</header>
<main id="main">
<div class="casebar">
<div id="result-banner" class="status-strip" role="status"></div>
<span id="metric-samples"></span
><span id="case-result" class="sr-only"></span
><span id="metric-findings" class="sr-only"></span>
</div>
<p id="case-explanation" class="case-explanation"></p>
<div class="console">
<aside class="panel evidence-panel" aria-labelledby="evidence-heading">
<header class="panel-head">
<div>
<p class="eyebrow">Canonical findings</p>
<h2 id="evidence-heading">Evidence queue</h2>
</div>
</header>
<label class="field" for="finding-search"
>Filter findings<input
id="finding-search"
type="search"
placeholder="Rule, explanation or row"
/></label>
<ol id="findings" class="finding-list"></ol>
<nav class="pager" aria-label="Finding pages">
<button id="findings-prev" type="button">Previous</button
><output id="findings-page"></output
><button id="findings-next" type="button">Next</button>
</nav>
</aside>
<section
class="workspace"
aria-label="Linked trajectory investigation views"
>
<article class="panel panel-physical">
<header class="panel-head">
<div>
<p class="eyebrow">Physical orientation</p>
<h2>Body-axis trail</h2>
</div>
<span class="data-chip">Derived</span>
</header>
<div class="plot-controls">
<label for="trail-axis"
>Trail<select id="trail-axis">
<option value="x">Body +X</option>
<option value="y">Body +Y</option>
<option value="z">Body +Z</option>
</select></label
><button
id="rotate-left"
type="button"
aria-label="Rotate camera left"
>
↶</button
><button
id="rotate-right"
type="button"
aria-label="Rotate camera right"
>
↷</button
><button id="camera-reset" type="button">Reset camera</button>
</div>
<div class="viewport-shell">
<canvas
id="physical"
width="720"
height="440"
role="img"
aria-labelledby="physical-caption"
></canvas
><span id="frame-label" class="viewport-label"></span>
</div>
<p id="physical-caption" class="layer-note">
Derived body axes and selected orientation. A single axis trail
does not represent all orientation changes.
</p>
</article>
<article class="panel panel-stereo">
<header class="panel-head">
<div>
<p class="eyebrow">Representation</p>
<h2>Projected S<sup>3</sup> lift</h2>
</div>
<span class="data-chip artefact-chip">Projection</span>
</header>
<div class="plot-controls">
<label for="stereo-layer"
>Layer<select id="stereo-layer">
<option value="lifted">Derived lift</option>
<option value="raw">Raw unit representation</option>
</select></label
><button id="stereo-fit" type="button">Fit</button
><button id="stereo-reset" type="button">Reset</button>
</div>
<div class="viewport-shell">
<canvas
id="stereo"
width="720"
height="440"
role="img"
aria-labelledby="stereo-caption"
></canvas>
</div>
<p id="stereo-caption" class="layer-note">
Non-physical view. <span id="projection-warning"></span>
<span id="pole-info"></span>
</p>
</article>
<article class="panel panel-timeline">
<header class="panel-head">
<h2>Interval evidence</h2>
<label for="timeline-axis"
>Horizontal axis<select id="timeline-axis">
<option value="time">Elapsed time</option>
<option value="index">Retained sample index</option>
</select></label
><label for="timeline-value"
>Measure<select id="timeline-value">
<option value="angle_rad">Quotient angle (rad)</option>
<option value="rate_rad_s">Angular rate (rad/s)</option>
</select></label
>
</header>
<canvas
id="timeline"
width="1440"
height="240"
role="img"
aria-label="Interval evidence timeline; use arrow keys to select samples"
tabindex="0"
></canvas>
<div class="transport" aria-label="Trajectory playback controls">
<button id="step-back" type="button" aria-label="Previous sample">
←</button
><button
id="play"
type="button"
aria-label="Play trajectory"
aria-pressed="false"
>
Play</button
><button id="step-forward" type="button" aria-label="Next sample">
→</button
><label for="sample-slider"
>Sample <output id="sample-position">0 / 0</output></label
><input
id="sample-slider"
type="range"
min="0"
max="0"
value="0"
/><label for="playback-mode"
>Playback<select id="playback-mode">
<option value="step">Sample steps</option>
<option value="time">Recorded time (1×)</option>
</select></label
><output id="metric-time"></output>
</div>
</article>
<article class="panel panel-components">
<header class="panel-head">
<h2>Quaternion components</h2>
<span class="layer-note"
>Raw solid · Lift dashed · Candidate dotted</span
>
</header>
<canvas
id="components"
width="1440"
height="240"
role="img"
aria-label="Raw, lifted and candidate quaternion component traces"
></canvas>
<p class="layer-note">
Measured raw samples remain separate from the derived lift and
unapplied candidate. Components: w (cream), x (coral), y (green),
z (blue).
</p>
</article>
</section>
<aside class="detail-stack" aria-label="Selected evidence details">
<section class="panel">
<p class="eyebrow">Selected finding</p>
<h2 id="finding-title">Select a finding</h2>
<p id="finding-summary"></p>
<p id="finding-meta" class="layer-note"></p>
<p id="finding-link" class="notice"></p>
<dl id="finding-evidence" class="evidence-values"></dl>
<details>
<summary>Exact source context</summary>
<div id="source-context" class="table-scroll"></div>
</details>
</section>
<section class="panel">
<header class="panel-head">
<h2>Selected sample</h2>
<span id="metric-row" class="data-chip"></span>
</header>
<p id="selection" class="sr-only" aria-live="polite"></p>
<dl class="sample-data">
<div>
<dt>Source row</dt>
<dd id="detail-row">--</dd>
</div>
<div>
<dt>Timestamp (ns)</dt>
<dd id="detail-time">--</dd>
</div>
</dl>
<div class="table-scroll">
<table class="quaternion-table">
<caption>
Representation comparison
</caption>
<thead>
<tr>
<th scope="col">Layer</th>
<th scope="col">w</th>
<th scope="col">x</th>
<th scope="col">y</th>
<th scope="col">z</th>
</tr>
</thead>
<tbody>
<tr id="detail-raw">
<th scope="row">Raw</th>
</tr>
<tr id="detail-lift">
<th scope="row">Lift</th>
</tr>
<tr id="detail-proposed">
<th scope="row">Candidate</th>
</tr>
</tbody>
</table>
</div>
</section>
<section class="panel">
<p class="eyebrow">Candidate output</p>
<h2>Repair candidates</h2>
<label class="field" for="repair-select"
>Compare candidate<select id="repair-select">
<option value="">No overlay</option>
</select></label
>
<div id="repairs"></div>
<p class="layer-note">
Proposed data is a candidate overlay. Apply explicitly with
<code>quatopsy repair</code> to a new file.
</p>
</section>
</aside>
</div>
<details class="provenance">
<summary>Layers and provenance</summary>
<p>
Raw: measured components. Derived: orientation, lift and interval
values. Proposed: unapplied candidate. Projection: non-physical
geometry. Missing values break paths; downsampled connections are
approximations.
</p>
<p id="downsample-note"></p>
<p id="analysis-id"></p>
</details>
</main>
<footer>
LOCAL · STATIC · OFFLINE
<span
>Canonical report owns the verdict; the viewer did not recompute
rules.</span
>
</footer>
<script type="application/json" id="quatopsy-report">
%%QUATOPSY_REPORT%%
</script>
<script type="application/json" id="quatopsy-view">
%%QUATOPSY_VIEW%%
</script>
<script src="viewer.js"></script>
</body>
</html>