    /* scrollbar-gutter reserves the scrollbar's width whether or not it's
       drawn, so a sudden scroll-needed (e.g. when the value pane grows
       under hover preview) doesn't shift everything left by ~15px. */
    html { scrollbar-gutter: stable; }
    body { max-width: 60rem; margin: 0 auto; padding: 2rem 1.25rem; }

    /* Brand lockup: mark + wordmark, single clickable unit linking home.
       Page title ("explorer") sits beside it as a separate, lighter
       element so the relationship reads as [home] · [you-are-here].
       Matches the flashcards/journal/location treatment so the
       "streamo · <app>" line reads identically across apps — h1 as
       the flex container with baseline alignment, so the two text
       items share a single baseline regardless of size difference. */
    h1 { display: flex; align-items: baseline; margin-bottom: 0.4rem; font-weight: 600; }
    .brand-lockup {
      font-size: 1.4rem;
      letter-spacing: -0.02em;
      color: var(--ink);
      text-decoration: none;
    }
    .brand-lockup img { width: 1.6rem; height: 1.6rem; vertical-align: baseline; margin-right: 0.35rem; }
    .brand-lockup:hover { opacity: 0.8; }
    .page-title {
      font-weight: 400;
      font-size: 0.9rem;
      color: var(--ink-dim);
      letter-spacing: 0.04em;
      margin-left: 0.5rem;
    }
    .page-title::before { content: '· '; opacity: 0.5; }
    .crumbs  { font-size: 0.85rem; color: var(--ink-dim); }
    /* Back link from at-view → registry. Structurally mirrors
       .registry-stats (display: flex, same margin, same font-size,
       same baseline) so the "Repo Registry" text lands at exactly
       the same vertical position across the two views — only the
       arrow shifts the text right. */
    .back {
      cursor: pointer;
      font-size: 0.95rem;
      margin: 0.75rem 0 1.25rem;
      display: flex;
      gap: 0.45rem;
      align-items: baseline;
      width: fit-content;
      color: var(--ink);
      text-decoration: none;
    }
    .back strong { font-weight: 600; }
    .back:hover  { opacity: 0.7; }

    h2 { font-size: 1.05rem; font-weight: 600; margin: 1.25rem 0 0.5rem; }
    h2 .dim { font-weight: 400; font-size: 0.9rem; }

    /* Section subtext — explorer-side description of what each
       section is + which app's data it surfaces. Sits under the
       h2 (with its inline counter) at a smaller dim weight. */
    .subtext {
      font-size: 0.78rem;
      color: var(--ink-dim);
      margin: -0.5rem 0 0.6rem;
      font-weight: 400;
    }

    .row {
      display: grid;
      grid-template-columns: 1fr 5rem 11rem 14rem;
      gap: 0.75rem;
      align-items: center;
      padding: 0.55rem 0.75rem;
      border: 1.5px solid transparent;
      border-radius: var(--radius);
      cursor: pointer;
    }
    .row:hover { border-color: var(--ink); background: rgba(254, 240, 138, 0.4); }
    .row + .row { border-top-color: var(--rule); }
    .row:hover + .row { border-top-color: transparent; }

    /* Repo-card rows (home, members, journalists) wear a gray box so
       each repo reads as its own self-contained card — matching the
       home card's legibility while keeping its green box reserved as
       the "important / canonical" signal. The hover-clears-neighbor-top
       trick above is undone for repo-card pairs since each one is
       independent (no shared divider). */
    .row.repo-card { border: 1.5px solid var(--rule); margin-bottom: 0.4rem; }
    .row.repo-card + .row.repo-card { border-top-color: var(--rule); }
    .row.repo-card:hover + .row.repo-card { border-top-color: var(--rule); }

    /* Empty-state placeholders match the boxed-card geometry so
       sections that have no content still occupy a visible slot,
       but read pre-attentively as "this is a placeholder, not
       content" via italic dim text and centered alignment. */
    .empty {
      border: 1.5px solid var(--rule);
      border-radius: var(--radius);
      padding: 0.55rem 0.75rem;
      min-height: 2.5rem;
      display: flex;
      align-items: center;
      justify-content: center;
      color: var(--ink-dim);
      font-style: italic;
      font-size: 0.85rem;
      margin-bottom: 0.4rem;
    }

    /* signed-commit + unsigned-commit + commit + signature rows share the same
       column template so the page doesn't visually jitter as you scan a mixed
       list. cols: kind | message | date | addr. */
    .row.signed-commit, .row.unsigned-commit,
    .row.commit, .row.signature { grid-template-columns: 6rem 1fr 14rem 6rem; }

    .row .mono { font-size: 0.85rem; }
    .row .bytes { font-size: 0.78rem; font-family: monospace; text-align: right; }
    .row .bytes.dim { color: var(--ink-dim); font-style: italic; }
    .row .when { font-size: 0.78rem; color: var(--ink-dim); }
    .row .msg  { font-size: 0.85rem; }
    .row .row-name { font-size: 0.9rem; font-weight: 500; margin-right: 0.4rem; }

    /* Registry header — page identity + live counts. Sits above the
       per-section h2s (home, members, journalists). */
    .registry-stats {
      font-size: 0.95rem;
      margin: 0.75rem 0 1.25rem;
      display: flex;
      gap: 0.45rem;
      align-items: baseline;
    }
    .registry-stats strong { font-weight: 600; }

    /* Home card — the relay's public face, set apart from the members
       cascade below by a green-tinted border + faint wash. Same .row
       grid so layout stays uniform; just the framing changes. */
    .row.home-card {
      border: 1.5px solid #16a34a;
      background: rgba(22, 163, 74, 0.05);
      padding: 0.85rem;
    }
    .row.home-card .row-name { font-size: 1rem; }

    /* Paste-a-key form: text input + button, sized to feel like a real
       door (not a cramped admin field). Hint paragraph above the form
       explains why this exists; quiet enough to skip if you already
       know what to do. */
    .hint {
      font-size: 0.85rem;
      color: var(--ink-dim);
      margin: 0.25rem 0 0.6rem;
      line-height: 1.5;
    }
    .subscribe-form {
      display: flex;
      gap: 0.5rem;
      margin: 0.25rem 0 0.75rem;
    }
    .subscribe-form input {
      flex: 1;
      padding: 0.5rem 0.75rem;
      border: 1.5px solid var(--rule);
      border-radius: var(--radius);
      font-family: monospace;
      font-size: 0.85rem;
      background: #fefdf8;
      color: var(--ink);
    }
    .subscribe-form input:focus {
      outline: none;
      border-color: var(--ink);
    }
    .subscribe-form button {
      padding: 0.5rem 1rem;
      border: 1.5px solid var(--ink);
      border-radius: var(--radius);
      background: var(--ink);
      color: #fefdf8;
      font-size: 0.85rem;
      cursor: pointer;
    }
    .subscribe-form button:hover { opacity: 0.85; }
    .row .kind {
      font-size: 0.7rem;
      text-transform: uppercase;
      letter-spacing: 0.08em;
      color: var(--ink-dim);
      border: 1px solid var(--rule);
      border-radius: 999px;
      padding: 0.05rem 0.5rem;
      text-align: center;
      align-self: center;
    }
    .row.commit .kind                 { color: var(--accent); border-color: var(--accent); }
    .row.signature .kind              { color: var(--warn);   border-color: var(--warn); }
    .row.signed-commit .kind          { color: #16a34a;       border-color: #16a34a; }
    .row.signed-commit.unsigned .kind { color: var(--ink-dim); border-color: var(--ink-dim); }

    /* HEAD card — the most-recent signed commit, prominent and self-orienting. */
    .row.signed-commit.head-card {
      border: 1.5px solid #16a34a;
      background: rgba(22, 163, 74, 0.05);
      padding: 0.85rem;
    }
    .row.signed-commit.head-card .msg { font-size: 1rem; font-weight: 500; }

    /* Detached card — same layout as the head-card but neutral styling.
       Shown as the selector summary when the current address isn't a sig
       (you've drilled into raw memory). The dropdown body is still the
       way back — pick a real commit and you re-attach. */
    .row.signed-commit.detached-card {
      border: 1.5px dashed var(--rule);
      background: transparent;
      padding: 0.85rem;
      cursor: pointer;
    }
    .row.signed-commit.detached-card .kind {
      color: var(--ink-dim);
      border-color: var(--ink-dim);
    }
    .row.signed-commit.detached-card .msg { font-size: 0.95rem; }

    /* Commit selector: a real dropdown widget. Summary = currently-selected
       commit (HEAD by default), styled as the green head-card. Body =
       full list of signed commits, with the selected one marked. */
    details.commit-selector { margin: 0.5rem 0 1rem; }
    details.commit-selector > summary {
      cursor: pointer;
      list-style: none;
      padding: 0;
    }
    details.commit-selector > summary::-webkit-details-marker { display: none; }
    details.commit-selector > summary::marker { display: none; }
    details.commit-selector > summary::after {
      content: '▾';
      float: right;
      margin: 0.5rem 0.85rem;
      color: #16a34a;
      font-size: 0.85rem;
    }
    details.commit-selector[open] > summary::after { content: '▴'; }
    details.commit-selector .dropdown-body {
      margin-top: 0.25rem;
      border: 1px solid var(--rule);
      border-radius: var(--radius);
      padding: 0.25rem;
    }
    details.commit-selector .dropdown-body .row { padding: 0.45rem 0.6rem; }
    details.commit-selector .dropdown-body .row.selected {
      background: rgba(22, 163, 74, 0.07);
    }
    details.commit-selector .dropdown-body .row.selected .kind::after {
      content: ' ●';
      color: #16a34a;
    }

    /* The commit wheel — a Price-is-Right big-wheel commit picker that
       replaces the dropdown. Always-on: flick or scroll to spin, one
       commit always resting under the centre band. All geometry (the
       wheel's height, the band's top, each row's top) is inline-styled
       from commit-wheel.js's constants — this rule set is colour and
       type only, so the two can't drift. */
    .commit-wheel {
      position: relative;
      overflow: hidden;
      margin: 0.5rem 0 1rem;
      border: 1px solid var(--rule);
      border-radius: var(--radius);
      background: var(--bg, #fefdf8);
      cursor: grab;
      touch-action: none;
      user-select: none;
    }
    .commit-wheel.spinning { cursor: grabbing; }
    .wheel-track {
      position: absolute;
      left: 0; right: 0; top: 0;
      will-change: transform;
    }
    .wheel-row {
      position: absolute;
      left: 0; right: 0;
      display: flex;
      align-items: center;
      gap: 0.6rem;
      padding: 0 0.85rem;
      box-sizing: border-box;
      font-size: 0.85rem;
    }
    .wheel-tag {
      flex: none;
      width: 4.5rem;
      font-family: monospace;
      font-size: 0.78rem;
      color: var(--ink-dim);
    }
    .wheel-row.selected .wheel-tag { color: #16a34a; }
    .wheel-msg {
      flex: 1;
      overflow: hidden;
      white-space: nowrap;
      text-overflow: ellipsis;
    }
    .wheel-when {
      flex: none;
      font-size: 0.76rem;
      color: var(--ink-dim);
    }
    /* The centre band — the resting slot. Hairlines top and bottom plus a
       faint green wash echo the signed-commit head-card. */
    .wheel-band {
      position: absolute;
      left: 0; right: 0;
      pointer-events: none;
      background: rgba(22, 163, 74, 0.07);
      border-top: 1px solid rgba(22, 163, 74, 0.5);
      border-bottom: 1px solid rgba(22, 163, 74, 0.5);
    }
    /* Top/bottom fades — cheap depth, rows slipping out of view. */
    .wheel-fade {
      position: absolute;
      left: 0; right: 0;
      height: 2.2rem;
      pointer-events: none;
    }
    .wheel-fade-top    { top: 0;    background: linear-gradient(to bottom, var(--bg, #fefdf8), transparent); }
    .wheel-fade-bottom { bottom: 0; background: linear-gradient(to top,    var(--bg, #fefdf8), transparent); }

    /* Tucked-away secondary "storage chunks" list at the bottom of the
       repo view — a click away when you want to see the bytes underneath. */
    details.other-storage {
      margin: 1.25rem 0 0.5rem;
      border-top: 1px solid var(--rule);
      padding-top: 0.5rem;
    }
    details.other-storage > summary {
      cursor: pointer;
      font-size: 0.85rem;
      color: var(--ink-dim);
      padding: 0.35rem 0.25rem;
    }
    details.other-storage[open] > summary { color: var(--ink); }

    /* "What this is" banner — top of every value tab. Default neutral
       border for storage codecs; green .verified for commits or sigs
       backed by a valid signature; dim .unsigned for commits awaiting
       a signature. */
    .kind-banner {
      display: flex; align-items: center; gap: 0.5rem;
      padding: 0.65rem 0.85rem; margin: 0.5rem 0 1rem;
      border: 1.5px solid var(--rule); border-radius: var(--radius);
    }
    .kind-banner.verified {
      border-color: #16a34a;
      background: rgba(22, 163, 74, 0.06);
    }
    .kind-banner.unsigned { border-style: dashed; }
    .kind-banner .kind-label {
      font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.08em;
      font-weight: 600; color: var(--ink-dim);
    }
    .kind-banner.verified .kind-label { color: #16a34a; }
    .commit-card {
      padding: 0.6rem 0.85rem; margin: 0.4rem 0;
      border: 1px solid var(--rule); border-radius: var(--radius);
    }
    .commit-card .commit-msg { font-size: 0.95rem; margin-bottom: 0.25rem; }
    .commit-card .commit-meta { font-size: 0.8rem; }

    .verify-badge { font-weight: 700; padding-left: 0.35em; font-size: 0.95em; }
    .verify-badge.valid   { color: #16a34a; }
    .verify-badge.invalid { color: #dc2626; }
    .verify-badge.pending { color: var(--ink-dim); font-weight: 400; }
    .verify-badge.error   { color: #ca8a04; }

    .empty { color: var(--ink-dim); padding: 0.5rem 0.75rem; font-size: 0.9rem; }

    /* key/value table for the at-view */
    .kv { width: 100%; border-collapse: collapse; font-size: 0.85rem; margin: 0.75rem 0; }
    .kv td { padding: 0.4rem 0.6rem; vertical-align: top; }
    .kv tr + tr td { border-top: 1px dashed var(--rule); }
    .kv td:first-child {
      color: var(--ink-dim);
      width: 8rem;
      font-size: 0.78rem;
      text-transform: uppercase;
      letter-spacing: 0.06em;
    }

    /* clickable variant — whole row is the click target */
    .kv.clickable tr { cursor: pointer; }
    .kv.clickable tr:hover td { background: rgba(254, 240, 138, 0.4); }
    .kv.clickable td:last-child { color: var(--accent); text-align: right; }

    .addr-link {
      font-family: monospace;
      font-size: 0.85rem;
      color: var(--accent);
      cursor: pointer;
      text-decoration: underline dotted;
    }
    .addr-link:hover { background: var(--flash); text-decoration-style: solid; }

    .paths { list-style: none; padding: 0; }
    .paths li { padding: 0.2rem 0.5rem; font-size: 0.85rem; }
    .paths li + li { border-top: 1px dashed var(--rule); }

    h3 { font-size: 0.9rem; font-weight: 600; margin: 1.25rem 0 0.5rem; }
    h3 .dim { font-weight: 400; font-size: 0.85rem; }

    .explainer {
      font-size: 0.85rem;
      line-height: 1.55;
      color: var(--ink-dim);
      border-left: 2px solid var(--rule);
      padding: 0.4rem 0 0.4rem 0.85rem;
      margin: 0.6rem 0 0.9rem;
    }
    .explainer strong { color: var(--ink); }

    .conn { font-size: 0.75rem; color: var(--ink-dim); margin-bottom: 1.5rem; }
    .conn.ok  { color: #16a34a; }
    .conn.err { color: #dc2626; }

    /* `.keyfull` was the old at-view header line; `.subtext` now does
       that job. `.keyfull` declarations removed; the .subtext rule
       elsewhere in this stylesheet covers font-size + dim color +
       margins. The repo-link inside still gets monospace via its
       own .repo-link rule. */
    .repo-link {
      font-family: monospace;
      color: var(--accent);
      cursor: pointer;
      text-decoration: underline dotted;
    }
    .repo-link:hover { background: var(--flash); text-decoration-style: solid; }

    /* Sticky at-view header: selector + strip + tabs travel with you as
       you scroll long value trees or storage detail. Background-cover so
       content scrolling underneath doesn't bleed through. */
    .atview-header {
      position: sticky;
      top: 0;
      z-index: 10;
      background: var(--bg, #fefdf8);
      padding-top: 0.25rem;
      border-bottom: 1px solid var(--rule);
      margin-bottom: 0.75rem;
    }

    /* Byte stream — zoomed strip in a horizontally-scrollable container,
       click-drag-to-pan inside for "look around" navigation. */
    .byte-strip-container {
      width: 100%;
      overflow-x: auto;
      background: #faf9f4;
      border: 1.5px solid var(--rule);
      border-radius: var(--radius);
      margin: 0.4rem 0 1rem;
      cursor: grab;
    }
    .byte-strip-container.dragging { cursor: grabbing; user-select: none; }
    .byte-strip-container.dragging .chunk { cursor: grabbing; }
    .byte-strip { display: block; }

    /* Sig-coverage overlay: when hovering a sig anywhere on the page,
       this rect is positioned over its [signedFrom, signedTo] byte range
       on the strip. Subtle dashed band — doesn't fight the chunk colors. */
    .byte-strip .sig-coverage {
      fill: rgba(239, 68, 68, 0.12);
      stroke: rgba(239, 68, 68, 0.6);
      stroke-width: 1.5;
      stroke-dasharray: 4 3;
      opacity: 0;
      transition: opacity 0.08s;
    }
    .byte-strip .sig-coverage.active { opacity: 1; }

    /* Persistent context line for the at-view's current chunk: codec,
       address, length, percentage. Quiet by default (it's permanent,
       not the focus), lights up when something else on the page is
       hovered to show that chunk instead. Reverts via data-default
       on mouseout. */
    .chunk-inspector {
      font-family: monospace;
      font-size: 0.8rem;
      color: var(--ink-dim);
      padding: 0.25rem 0.5rem;
      margin: 0.25rem 0 0.75rem;
      border-radius: var(--radius);
      transition: color 0.08s, background 0.08s;
    }
    .chunk-inspector.active {
      color: var(--ink);
      background: var(--flash);
    }

    /* Per-codec reuse breakdown — quiet table under the byte strip
       showing each codec type's chunk count, total bytes, and dedup
       leverage. "—" in the leverage column marks chunks that aren't
       reachable from any commit's data tree (commit + signature
       chunks by structure — they're graph roots, not reuse candidates). */
    .reuse-by-type {
      width: auto;
      border-collapse: collapse;
      font-size: 0.78rem;
      margin: 0.25rem 0 0.75rem;
    }
    .reuse-by-type th,
    .reuse-by-type td {
      padding: 0.15rem 0.6rem 0.15rem 0;
      text-align: left;
      font-weight: normal;
      font-variant-numeric: tabular-nums;
    }
    .reuse-by-type th {
      color: var(--ink-dim);
      font-size: 0.7rem;
      text-transform: uppercase;
      letter-spacing: 0.05em;
    }
    .reuse-by-type tbody tr + tr td { border-top: 1px solid var(--rule); }
    .reuse-by-type td.mono { font-family: monospace; }

    /* The reuse table tucks into a <details> by default — it's a
       secondary breakdown that competes for real estate on narrower
       screens. Same pattern as other-storage. */
    details.reuse-breakdown { margin: 0.25rem 0 0.75rem; }
    details.reuse-breakdown > summary {
      cursor: pointer;
      font-size: 0.78rem;
      color: var(--ink-dim);
      padding: 0.25rem 0;
    }
    details.reuse-breakdown[open] > summary { color: var(--ink); }
    details.reuse-breakdown > .reuse-by-type { margin: 0.35rem 0 0.25rem; }

    /* Per-value economics footer — appears under every value-tab page,
       showing this value's subtree size, use count, and dedup story.
       Secondary information: dim by default, numbers in ink, leverage
       in slightly bolder ink. Dashed top rule to separate from the
       main value display without competing with content. */
    .value-economics {
      font-size: 0.78rem;
      color: var(--ink-dim);
      line-height: 1.6;
      margin: 1.5rem 0 0.5rem;
      padding-top: 0.65rem;
      border-top: 1px dashed var(--rule);
      font-family: monospace;
    }
    .value-economics .num {
      color: var(--ink);
      font-weight: 500;
    }
    .value-economics .num.leverage {
      font-weight: 600;
    }

    .byte-map {
      display: block;
    }
    .byte-map .chunk {
      cursor: pointer;
      stroke: rgba(0, 0, 0, 0.15);
      stroke-width: 0.4;
      transition: stroke-width 0.08s, fill-opacity 0.08s;
    }
    .byte-map .chunk:hover { stroke: var(--ink); stroke-width: 1.5; }
    .byte-map .chunk.current { stroke: var(--ink); stroke-width: 2; }
    .byte-map .chunk.hovered { fill-opacity: 0.55; }

    /* Streamo-typed value pills — every value gets a type-specific visual
       identity instead of flattening through JSON.stringify. Colors echo
       the byte-strip codec palette below so the visual language carries
       across the page. */
    .tv {
      display: inline-flex;
      align-items: center;
      gap: 0.25rem;
      padding: 0.05rem 0.4rem;
      border-radius: var(--radius);
      font-size: 0.85rem;
      max-width: 100%;
      vertical-align: baseline;
    }
    .tv-string { color: #047857; background: rgba(16, 185, 129, 0.10); font-family: monospace; }
    .tv-string .tv-quote { color: #10b981; opacity: 0.7; font-weight: 600; }
    .tv-num    { color: #475569; background: rgba(100, 116, 139, 0.10); font-family: monospace; }
    .tv-date   { color: #475569; background: rgba(100, 116, 139, 0.10); }
    .tv-date .tv-glyph { font-size: 0.75rem; }
    .tv-date time { font-variant-numeric: tabular-nums; }
    .tv-bool.tv-true   { color: #15803d; background: rgba(22, 163, 74, 0.10); font-family: monospace; }
    .tv-bool.tv-false  { color: #b91c1c; background: rgba(220, 38, 38, 0.10); font-family: monospace; }
    .tv-null, .tv-undefined { color: var(--ink-dim); background: transparent; font-style: italic; font-family: monospace; }
    .tv-bytes  { color: #4d7c0f; background: rgba(132, 204, 22, 0.10); font-family: monospace; }
    .tv-array, .tv-object { color: #1e40af; background: rgba(59, 130, 246, 0.10); }
    .tv-duple  { color: #6b21a8; background: rgba(168, 85, 247, 0.10); }

    /* Three-row byte chart: hex / char / decimal. One column per byte,
       widths driven by the widest cell so dec values up to 255 align
       cleanly. Olive on a very-faint olive wash keeps the cat-bytes
       identity; rows have distinct visual weight so you can pick the
       layer you're reading (chars for meaning, hex for canonical,
       decimal for math). Offset column shows on rawChunkSection. */
    .bytes-chart {
      display: inline-block;
      vertical-align: middle;
      background: rgba(132, 204, 22, 0.06);
      border: 1px solid rgba(132, 204, 22, 0.18);
      padding: 0.25rem 0.4rem;
      border-radius: var(--radius);
      font-family: monospace;
      line-height: 1.15;
    }
    .bytes-group {
      border-collapse: collapse;
      border-spacing: 0;
    }
    .bytes-group + .bytes-group { margin-top: 0.25rem; }
    .bytes-group td, .bytes-group th {
      padding: 0 0.25rem;
      text-align: center;
      font-variant-numeric: tabular-nums;
      font-weight: normal;
    }
    .bytes-group .hex td  { font-size: 0.78rem; color: #4d7c0f; }
    .bytes-group .char td.printable { font-size: 0.85rem; color: var(--ink); font-weight: 500; }
    .bytes-group .char td.nonprint  { font-size: 0.85rem; color: var(--ink-dim); }
    .bytes-group .dec td  { font-size: 0.65rem; color: var(--ink-dim); }
    .bytes-group th {
      color: var(--ink-dim);
      font-size: 0.7rem;
      padding-right: 0.6rem;
      text-align: right;
      font-weight: normal;
    }
    .bytes-group.with-offset .hex th { color: #4d7c0f; }
    .bytes-chart-more {
      font-size: 0.75rem;
      margin-top: 0.3rem;
      font-style: italic;
    }

    /* Recursive typed-value tree — used for rehydrated views. Composites
       render in two modes: tv-tree-inline (Chrome-console-style, one
       line, used when every child is a primitive and the line fits) and
       tv-tree (multi-line, used otherwise). Both expose the same
       click-to-collapse opening bracket. */
    .tv-tree {
      font-size: 0.85rem;
      line-height: 1.4;
      font-family: monospace;
      margin: 0.3rem 0;
    }
    .tv-tree-row {
      padding-left: 1.25rem;
    }
    .tv-tree .tv-bracket,
    .tv-tree-inline .tv-bracket {
      color: var(--ink-dim);
      font-weight: 600;
    }
    .tv-tree .tv-bracket.clickable,
    .tv-tree-inline .tv-bracket.clickable {
      cursor: pointer;
    }
    .tv-tree .tv-bracket.clickable:hover,
    .tv-tree-inline .tv-bracket.clickable:hover {
      background: var(--flash);
      color: var(--ink);
    }
    .tv-tree .tv-key,
    .tv-tree-inline .tv-key {
      color: var(--ink-dim);
      margin-right: 0.25rem;
    }
    .tv-tree-inline {
      font-size: 0.85rem;
      font-family: monospace;
    }
    .tv-tree-inline .tv-sep { color: var(--ink-dim); }
    .tv-drill {
      cursor: pointer;
      text-decoration: underline dotted var(--ink-dim);
    }
    .tv-drill:hover { background: var(--flash); text-decoration-style: solid; }

    /* Storage tab's chunk-graph tree — same visual register as
       tv-tree, but each row is a chunk (chip + @addr + value preview)
       and indentation walks `directReferences`, surfacing the duples
       that the value tree hides. */
    .storage-tree {
      font-size: 0.85rem;
      margin: 0.4rem 0;
    }
    .storage-row {
      display: flex;
      align-items: center;
      gap: 0.4rem;
      padding: 0.15rem 0;
      flex-wrap: wrap;
    }
    .storage-row .codec-chip { flex-shrink: 0; }
    .storage-toggle {
      cursor: pointer;
      width: 1rem;
      text-align: center;
      color: var(--ink-dim);
      font-weight: 600;
      text-decoration: none;
      user-select: none;
      flex-shrink: 0;
    }
    .storage-toggle:hover { color: var(--ink); }
    .storage-toggle.empty { cursor: default; opacity: 0.35; }
    .storage-toggle.empty:hover { color: var(--ink-dim); }
    .storage-preview { color: var(--ink-dim); }
    .storage-childcount {
      font-size: 0.75rem;
      color: var(--ink-dim);
      margin-left: auto;
    }
    .storage-children {
      padding-left: 1.25rem;
      border-left: 1px solid var(--rule);
      margin-left: 0.55rem;
    }

    /* codec-tag — colored codec name in the refs/referrers tables. Same
       palette as the byte-strip and the typed-value pills, so a chunk's
       codec reads visually consistent everywhere it appears. */
    .codec-tag { font-weight: 500; }
    .codec-tag.codec-commit    { color: #c2410c; }
    .codec-tag.codec-sig       { color: #b91c1c; }
    .codec-tag.codec-composite { color: #1e40af; }
    .codec-tag.codec-duple     { color: #6b21a8; }
    .codec-tag.codec-string    { color: #047857; }
    .codec-tag.codec-bytes     { color: #4d7c0f; }
    .codec-tag.codec-num       { color: #475569; }
    .codec-tag.codec-var       { color: #b45309; }
    .codec-tag.codec-other     { color: var(--ink-dim); }

    /* codec category palette — used by the SVG fills, the inspector chip,
       and any chip-styled element that wants to read as a chunk type. */
    .cat-commit    { fill: #f59e0b; background: #f59e0b; }
    .cat-sig       { fill: #ef4444; background: #ef4444; }
    .cat-composite { fill: #3b82f6; background: #3b82f6; }
    .cat-duple     { fill: #a855f7; background: #a855f7; }
    .cat-string    { fill: #10b981; background: #10b981; }
    .cat-bytes     { fill: #84cc16; background: #84cc16; }
    .cat-num       { fill: #64748b; background: #64748b; }
    .cat-var       { fill: #fbbf24; background: #fbbf24; }
    .cat-other     { fill: #cbd5e1; background: #cbd5e1; }

    /* codec chip — inline tag colored by codec category. Foreground
       colors are picked by relative luminance (Y = 0.2126R + 0.7152G +
       0.0722B): high-luminance backgrounds (lime, amber, yellow, light
       gray) use black text; mid-low luminance (red, blue, purple, slate)
       use white. Saturated mid-tones like emerald lean to white by
       convention even though the WCAG-correct call is marginal. */
    .codec-chip {
      display: inline-block;
      padding: 0.15rem 0.45rem;
      border-radius: var(--radius);
      font-size: 0.7rem;
      font-weight: 500;
      letter-spacing: 0.02em;
      line-height: 1;
      vertical-align: middle;
      color: #fff;
    }
    .codec-chip.cat-commit,
    .codec-chip.cat-bytes,
    .codec-chip.cat-var,
    .codec-chip.cat-other { color: #000; }

    /* Tab strip — hand-drawn underline aesthetic to match proto.css */
    .tabs {
      display: flex;
      gap: 1.25rem;
      border-bottom: 1.5px solid var(--rule);
      margin: 1.25rem 0 1rem;
    }
    .tab {
      padding: 0.45rem 0.1rem;
      margin-bottom: -1.5px;
      border-bottom: 2px solid transparent;
      cursor: pointer;
      font-size: 0.85rem;
      color: var(--ink-dim);
      letter-spacing: 0.04em;
      text-transform: lowercase;
    }
    .tab:hover { color: var(--ink); }
    .tab.active {
      color: var(--ink);
      border-bottom-color: var(--ink);
      font-weight: 600;
    }

    pre.value {
      font-family: monospace;
      font-size: 0.8rem;
      background: var(--rule);
      border-radius: var(--radius);
      padding: 1rem;
      overflow-x: auto;
      white-space: pre-wrap;
      word-break: break-word;
    }
