/* Session × prefix matrix table — replaces the per-verdict bulleted
   lists for the focus AS's originated prefixes. Each row is one
   prefix; each session column shows whether that prefix was seen on
   that BGP peering session and which verdict applied (accepted or
   rejected). Rows are sorted by their session-pattern so prefixes
   with identical coverage cluster together; a thin divider marks the
   transition between pattern groups so a row that breaks the common
   shape stands out. */
.prefix-matrix .m-summary {
    font-size: 12px;
    color: var(--muted);
    margin-bottom: 4px;
}
/* The matrix uses a more saturated red than the global --rejected
   token. The default --rejected (#c0392b) reads as muted next to
   the bold, monospace prefix text, which made rejected rows hard
   to scan. --rejected-strong is the same red, deepened for higher
   contrast without changing the global rejected colour anywhere
   else (legend, count pills, h4s, etc.). */
.prefix-matrix {
    --rejected-strong: #b91c1c;
}
.prefix-matrix .m-summary-rej { color: var(--rejected-strong); }
.prefix-matrix .m-toolbar {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    margin-bottom: 6px;
}
.prefix-matrix .m-filterbar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px;
    margin: 4px 0 8px;
}
.prefix-matrix .m-filter-field,
.flow-pfx-filter-field {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    color: var(--muted);
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
}
.prefix-matrix .m-regex-filter,
.flow-pfx-regex-filter {
    width: clamp(120px, 18vw, 220px);
    border: 1px solid var(--line);
    border-radius: 4px;
    padding: 4px 7px;
    color: var(--fg);
    background: var(--panel);
    font-family: ui-monospace, "SF Mono", Menlo, monospace;
    font-size: 12px;
    letter-spacing: 0;
    text-transform: none;
}
.prefix-matrix .m-regex-filter:focus,
.flow-pfx-regex-filter:focus {
    outline: 2px solid var(--accent-soft);
    border-color: var(--accent);
}
.prefix-matrix .m-regex-filter.is-invalid,
.flow-pfx-regex-filter.is-invalid {
    border-color: var(--rejected-strong, #b91c1c);
    outline: 2px solid rgba(185, 28, 28, 0.12);
}
.prefix-matrix .m-filter-clear,
.flow-pfx-filter-clear {
    background: var(--panel);
    color: var(--accent);
    border: 1px solid var(--line);
    border-radius: 4px;
    padding: 4px 9px;
    font-size: 11px;
    font-weight: 600;
    cursor: pointer;
    line-height: 1.3;
}
.prefix-matrix .m-filter-clear:hover,
.flow-pfx-filter-clear:hover {
    background: var(--accent-soft);
    border-color: var(--accent);
}
.prefix-matrix .m-filter-status,
.flow-pfx-filter-status {
    color: var(--muted);
    font-size: 11px;
    line-height: 1.4;
}
.prefix-matrix .m-csv-btn,
.prefix-matrix .m-pdf-btn {
    background: var(--panel);
    color: var(--accent);
    border: 1px solid var(--line);
    border-radius: 4px;
    padding: 3px 10px;
    font-size: 11px;
    font-weight: 600;
    cursor: pointer;
    line-height: 1.4;
}
.prefix-matrix .m-csv-btn:hover,
.prefix-matrix .m-pdf-btn:hover {
    background: var(--accent-soft);
    border-color: var(--accent);
}
.prefix-matrix .m-pdf-btn:disabled {
    opacity: 0.6;
    cursor: progress;
}
.prefix-matrix .m-legend {
    font-size: 11px;
    color: var(--muted);
    margin-bottom: 8px;
}
.prefix-matrix .m-legend-cell {
    display: inline-block;
    width: 14px;
    text-align: center;
    font-weight: 600;
}
.prefix-matrix .m-scroll {
    /* Inside the modal layout this wrapper is the sole scroll
       context for the matrix in BOTH axes. The .prefix-matrix-modal
       flex column gives it ``flex: 1; min-height: 0`` so it fills
       the modal body's remaining space — that pins the horizontal
       scrollbar to the bottom of the visible modal area instead of
       the bottom of the (potentially much taller) table, which is
       what makes the right-edge content discoverable. */
    overflow: auto;
    flex: 1;
    min-height: 0;
    /* Force a visible scrollbar even on macOS overlay scrollbars,
       so the user can immediately tell when there's more content
       to the right. Custom-styled to match the report palette. */
    scrollbar-width: thin;
    scrollbar-color: var(--accent) var(--bg);
    /* Right-edge fade gradient as an additional visual cue. Built
       with the local/scroll background-attachment trick (Lea
       Verou): a "blanket" gradient (background-attachment: local)
       moves with the content and covers the static shadow when
       the scroll position is at the matching edge; the shadow
       gradients (default scroll attachment) stay fixed against
       the viewport, so they reveal whichever edge has more
       content beyond it. */
    background:
        linear-gradient(to right, var(--bg), transparent) left center / 14px 100% no-repeat local,
        linear-gradient(to left, var(--bg), transparent) right center / 14px 100% no-repeat local,
        linear-gradient(to right, rgba(0, 0, 0, 0.12), transparent) left center / 14px 100% no-repeat,
        linear-gradient(to left, rgba(0, 0, 0, 0.12), transparent) right center / 14px 100% no-repeat;
}
.prefix-matrix .m-scroll::-webkit-scrollbar {
    height: 10px;
    width: 10px;
}
.prefix-matrix .m-scroll::-webkit-scrollbar-track {
    background: var(--bg);
    border-radius: 5px;
}
.prefix-matrix .m-scroll::-webkit-scrollbar-thumb {
    background: var(--accent);
    border-radius: 5px;
    border: 2px solid var(--bg);
}
.prefix-matrix .m-scroll::-webkit-scrollbar-thumb:hover {
    background: #143a66;
}
.prefix-matrix .m-table {
    /* ``border-collapse: separate`` is required for the sticky
       thead to actually paint over scrolled body rows. With
       ``collapse`` the table's borders are owned at the table
       level and sticky cells lose their backgrounds when scrolled
       out of place in some browsers. ``border-spacing: 0`` keeps
       cells visually flush. */
    border-collapse: separate;
    border-spacing: 0;
    font-size: 12px;
    font-family: ui-monospace, "SF Mono", Menlo, monospace;
    /* ``fixed`` is essential for big prefix lists. With the default
       ``auto`` layout the browser measures EVERY cell across all
       rows to size the columns — an O(rows × cols) pass that locks
       the tab for seconds on a 5000+ prefix AS like GEANT. ``fixed``
       sizes columns from the header row's explicit widths (below) in
       a single linear pass, so render cost scales with on-screen
       rows, not the whole table. Column widths are set on the thead
       cells since fixed layout reads widths from the first row. */
    table-layout: fixed;
    width: max-content;
}
/* Explicit column widths for the fixed layout. Set on the header
   cells (the first row, which fixed layout uses to size columns).
   Generous enough that prefixes/holders don't clip; the session
   columns keep their existing 60px rule below. */
.prefix-matrix .m-table thead th.m-pfx { width: 240px; }
.prefix-matrix .m-table thead th.m-holder { width: 200px; }
.prefix-matrix .m-table thead th.m-paths { width: 64px; }
.prefix-matrix .m-table thead th.m-annot-h {
    width: 72px;
    /* Annotation headers ("AS Prepends", "% more-specific") are
       wider than their narrow data cells — let them wrap instead of
       overflowing the fixed column. */
    white-space: normal;
}
/* Belt-and-suspenders so an unusually long IPv6 prefix ellipsizes
   within its fixed column rather than spilling into the Holder
   column. The full value is always available via the row's detail
   popup. */
.prefix-matrix .m-table td.m-pfx {
    overflow: hidden;
    text-overflow: ellipsis;
}
.prefix-matrix .m-table th,
.prefix-matrix .m-table td {
    padding: 2px 8px;
    text-align: left;
    border-bottom: 1px solid transparent;
    white-space: nowrap;
}
.prefix-matrix .m-table thead th {
    color: var(--muted);
    font-weight: 600;
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    border-bottom: 1px solid var(--border, #45475a);
    padding-bottom: 4px;
    /* Lock the header row at the top of the panel-body scroll
       region so it stays visible while the user scrolls through
       a long prefix list. Sticky on individual <th>s (rather than
       <thead>) is the syntax that browsers actually honour for
       table headers. The opaque background hides body rows that
       scroll up underneath; the box-shadow stands in for the
       border-bottom (which sticky elements don't always paint
       cleanly over scrolled content). */
    position: sticky;
    top: 0;
    background: var(--panel);
    z-index: 2;
    box-shadow: 0 1px 0 var(--line);
}
.prefix-matrix .m-table th.m-sess,
.prefix-matrix .m-table td.m-cell {
    text-align: center;
}
/* Session columns use short ``intN`` labels (int0, int1, …) so
   each column stays narrow regardless of whether the underlying
   peer is v4 or v6. Click an intN cell to reveal the full peer
   IP address in a popover anchored to the column header. */
.prefix-matrix .m-table th.m-sess {
    width: 60px;
    min-width: 60px;
    padding: 4px 6px;
    vertical-align: bottom;
    cursor: pointer;
}
.prefix-matrix .m-table th.m-sess:hover {
    background: var(--accent-soft);
}
.prefix-matrix .m-table th.m-sess.is-open {
    background: var(--accent-soft);
    outline: 2px solid var(--accent);
    outline-offset: -2px;
}
.prefix-matrix .m-table th.m-sess .m-sess-int {
    font-family: ui-monospace, "SF Mono", Menlo, monospace;
    font-size: 12px;
    font-weight: 700;
    color: var(--fg);
    text-transform: none;
    letter-spacing: 0;
    text-align: center;
}
/* Address-family suffix attached to intN labels (and echoed in
   the popover). v4 sessions get the report's accent blue; v6
   sessions get a contrasting teal so the two families form a
   visual stripe across the column row. The pill shape distin-
   guishes it from the integer interface index. */
.m-sess-fam {
    display: inline-block;
    margin-left: 3px;
    padding: 0 4px;
    border-radius: 3px;
    font-size: 9px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    vertical-align: 1px;
    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
}
.m-sess-fam-v4 { background: var(--accent-soft); color: var(--accent); }
.m-sess-fam-v6 { background: #ccfbf1; color: #0d8a8a; }

/* Column-sort affordances. Sortable text headers (Prefix / Holder /
   Paths / Verdict) take the click themselves; session columns get a
   small dedicated button below the AS line, because the session
   header's own click is taken by the peer-IP popover. The indicator
   span is filled with ▲ / ▼ by the sort wiring when active. */
.prefix-matrix .m-table thead th.m-sortable {
    cursor: pointer;
    user-select: none;
}
.prefix-matrix .m-table thead th.m-sortable:hover {
    background: var(--accent-soft);
}
.prefix-matrix .m-table thead th.m-sortable.m-sort-active {
    color: var(--accent);
}
.m-sort-ind {
    display: inline-block;
    min-width: 1em;
    margin-left: 2px;
    font-size: 9px;
    color: var(--accent);
}
.m-sess-sort {
    display: block;
    margin: 2px auto 0;
    padding: 0 4px;
    border: 1px solid var(--line);
    border-radius: 3px;
    background: transparent;
    font-size: 9px;
    line-height: 14px;
    color: var(--muted);
    cursor: pointer;
}
.m-sess-sort:hover {
    background: var(--accent-soft);
    color: var(--accent);
}
.m-sess-sort.m-sort-active {
    border-color: var(--accent);
    color: var(--accent);
}
/* An empty indicator inside the session button still needs a
   visible glyph to click on; show a neutral ⇅ until active. */
.m-sess-sort .m-sort-ind { margin-left: 0; color: inherit; }
.m-sess-sort .m-sort-ind:empty::before { content: "⇅"; }

/* Floating popover that reveals the peer IP + ASN when a
   session column header is clicked. Lives at document.body root
   so it can escape the table's overflow:auto clipping. */
.m-sess-popover {
    position: fixed;
    z-index: 200;
    background: var(--panel);
    border: 1px solid var(--accent);
    border-radius: 6px;
    box-shadow: 0 6px 20px rgba(0, 0, 0, 0.18);
    padding: 8px 12px;
    font-size: 12px;
    color: var(--fg);
    min-width: 180px;
    pointer-events: auto;
}
.m-sess-popover-row {
    display: flex;
    align-items: baseline;
    gap: 10px;
    padding: 2px 0;
}
.m-sess-popover-key {
    color: var(--muted);
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    min-width: 70px;
}
.m-sess-popover-val {
    font-family: ui-monospace, "SF Mono", Menlo, monospace;
    font-weight: 600;
}
/* Friendly AS name inside the Peer AS row, rendered in the
   sans-serif body font so it reads as a proper organisation
   name (not a code-style identifier like the AS number itself). */
.m-sess-popover-name {
    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
    font-weight: 500;
    color: var(--muted);
    margin-left: 4px;
}
.prefix-matrix .m-table .m-holder {
    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
    color: var(--muted);
    max-width: 23em;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    cursor: pointer;
}
/* Click-to-expand: when a holder name is too long to fit in the
   23em column, the cell is rendered with ellipsis. Clicking it
   toggles is-expanded, which drops the truncation so the full
   holder string is visible (and copy-selectable). Pending /
   empty cells stay non-interactive. */
.prefix-matrix .m-table .m-holder.is-expanded {
    max-width: none;
    white-space: normal;
    overflow: visible;
    text-overflow: clip;
    word-break: break-word;
}
.prefix-matrix .m-table .m-holder-pending,
.prefix-matrix .m-table .m-holder-empty {
    cursor: default;
}
.prefix-matrix .m-table .m-holder-pending,
.prefix-matrix .m-table .m-holder-empty {
    color: var(--muted);
    opacity: 0.65;
}
/* Three-dot pulsing indicator shown in any holder cell whose name is
   still being resolved by the cached /api/prefix-holders fetch. The
   prefix matrix renders immediately, so this gives a subtle "working"
   cue while RIPE Stat / ARIN lookups complete in the background. */
.holder-fetching {
    display: inline-flex;
    gap: 3px;
    align-items: center;
    vertical-align: middle;
}
.holder-fetching > span {
    width: 4px;
    height: 4px;
    border-radius: 50%;
    background: currentColor;
    opacity: 0.25;
    animation: holderFetchingPulse 1.2s ease-in-out infinite;
}
.holder-fetching > span:nth-child(2) { animation-delay: 0.15s; }
.holder-fetching > span:nth-child(3) { animation-delay: 0.3s; }
@keyframes holderFetchingPulse {
    0%, 80%, 100% { opacity: 0.25; }
    40% { opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
    .holder-fetching > span {
        animation: none;
        opacity: 0.6;
    }
}
.prefix-matrix .m-table .m-acc { color: #16a34a; font-weight: 700; }
.prefix-matrix .m-table .m-rej { color: var(--rejected-strong); font-weight: 700; }
.prefix-matrix .m-table .m-none { color: #cbd5e1; }
.prefix-matrix .m-table .m-row.m-row-rej .m-pfx { color: var(--rejected-strong); font-weight: 600; }
.prefix-matrix .m-table .m-row.m-row-rej .m-holder { color: var(--rejected-strong); opacity: 0.85; }
/* Zebra-stripe for prefix rows. Faint enough to stay quiet against
   the rejected / mixed text colours, but visible enough that a
   row of session-cell glyphs reads as a single unit when the
   table is wide. Hover bumps it to a slightly stronger tint so
   the row the cursor is on stands out. */
.prefix-matrix .m-table .m-row.m-row-zebra td { background: var(--zebra); }
.prefix-matrix .m-table .m-row:hover td { background: var(--accent-soft); }
/* Mixed = accepted on some peering sessions and rejected on others.
   Using amber-900 (very dark amber) instead of a yellow tint —
   yellow on white sits below readable contrast for body text. The
   colour still differs enough from the rejected-row red that the
   two states are distinguishable. */
.prefix-matrix .m-table .m-row.m-row-mixed .m-pfx { color: #78350f; font-weight: 700; }
.prefix-matrix .m-table .m-row.m-row-mixed .m-holder { color: #78350f; opacity: 0.85; }
.prefix-matrix .m-table .m-row .m-holder-pending,
.prefix-matrix .m-table .m-row .m-holder-empty {
    color: var(--muted);
    opacity: 0.65;
}
/* Two-line session column header: IP on top, peer AS centered
   below. Both centered so a row of ASes reads cleanly across the
   table when multiple sessions belong to different peers. */
.prefix-matrix .m-table .m-sess-ip {
    text-align: center;
}
/* Three-line column header. ``.m-sess-int`` (top) is the intN
   label with the family pill; ``.m-sess-loc`` (middle) is the
   Internet2 router location code parsed from the service URI;
   ``.m-sess-asn`` (bottom) is the peer ASN. All three centred
   so a row of header columns reads cleanly across the table.
   Empty location / asn lines still take vertical space so columns
   without parsed values stay aligned with neighbours that have
   them. */
.prefix-matrix .m-table .m-sess-loc {
    text-align: center;
    color: var(--fg);
    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
    font-weight: 700;
    font-size: 10px;
    line-height: 1.2;
    min-height: 12px;
    margin-top: 2px;
    text-transform: uppercase;
    letter-spacing: 0.04em;
}
.prefix-matrix .m-table .m-sess-asn {
    text-align: center;
    color: var(--accent, #1a4f8b);
    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
    font-weight: 600;
    font-size: 10px;
    line-height: 1.2;
    min-height: 12px;
    margin-top: 1px;
    text-transform: none;
    letter-spacing: 0;
}
/* Thin divider between session-pattern groups. Rows with the same
   pattern have no top border; the first row of a NEW pattern group
   wears one. The effect is a quiet horizontal line that separates
   blocks of identical-coverage prefixes from blocks that arrived on
   a different set of sessions. */
.prefix-matrix .m-table .m-row.m-row-divider td {
    border-top: 1px dashed var(--border, #45475a);
}
.prefix-matrix .m-table .m-verdict {
    text-align: left;
    color: var(--muted);
}

/* Paths column. Click the cell to toggle a per-prefix expansion
   sub-row that lists every observed AS-path with its verdict. The
   ▾ chevron and a tinted background on hover hint that the cell
   is interactive. */
.prefix-matrix .m-table .m-paths {
    text-align: center;
    color: var(--muted);
    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
}
.prefix-matrix .m-table .m-paths-clickable {
    cursor: pointer;
    user-select: none;
}
.prefix-matrix .m-table .m-paths-clickable:hover {
    background: var(--accent-soft);
}
.prefix-matrix .m-table .m-paths-clickable.is-open {
    background: var(--accent-soft);
}
.prefix-matrix .m-table .m-paths-count {
    font-weight: 600;
    color: var(--fg);
}
.prefix-matrix .m-table .m-paths-badge {
    display: inline-block;
    margin-left: 4px;
    padding: 0 4px;
    font-size: 11px;
    font-weight: 700;
    border-radius: 3px;
}
.prefix-matrix .m-table .m-paths-mixed {
    background: #fef3c7;
    color: #78350f;
}
.prefix-matrix .m-table .m-paths-rej {
    background: var(--rejected-soft);
    color: var(--rejected-strong);
}
/* Expansion sub-row. Spans every column; renders one line per
   observed AS-path with its verdict + peering-session IPs. */
.prefix-matrix .m-table .m-row-expand td {
    background: var(--bg);
    padding: 6px 12px 8px;
    border-top: 1px dashed var(--line);
}
.prefix-matrix .m-paths-list {
    font-size: 11px;
    line-height: 1.6;
    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
}
/* Coverage banner shown above the per-path list when any path
   for the prefix was rejected. Tells the operator whether the
   rejected route is "redundant" (covered by an accepted route)
   or "net loss" (uncovered). Three states: exact-match, covered
   by less-specific, uncovered. Colour-coded green for the two
   covered cases, red for uncovered. */
.prefix-matrix .m-coverage {
    margin-bottom: 6px;
    padding: 6px 10px;
    border-radius: 4px;
    font-size: 11px;
    line-height: 1.45;
}
.prefix-matrix .m-coverage code {
    font-family: ui-monospace, "SF Mono", Menlo, monospace;
    font-size: 11px;
    background: rgba(0, 0, 0, 0.06);
    padding: 0 4px;
    border-radius: 3px;
}
.prefix-matrix .m-coverage .m-cov-icon {
    display: inline-block;
    width: 14px;
    text-align: center;
    font-weight: 700;
    margin-right: 4px;
}
.prefix-matrix .m-cov-exact,
.prefix-matrix .m-cov-loose {
    background: #ecfdf5;
    border: 1px solid #a7f3d0;
    color: #065f46;
}
.prefix-matrix .m-cov-exact .m-cov-icon,
.prefix-matrix .m-cov-loose .m-cov-icon { color: #16a34a; }
.prefix-matrix .m-cov-none {
    background: var(--rejected-soft);
    border: 1px solid #fecaca;
    color: var(--rejected-strong);
}
.prefix-matrix .m-cov-none .m-cov-icon { color: var(--rejected-strong); }
.prefix-matrix .m-pline {
    padding: 1px 0;
}
.prefix-matrix .m-pline .m-pverdict {
    display: inline-block;
    min-width: 5.5em;
    font-weight: 700;
    margin-right: 0.5em;
}
.prefix-matrix .m-pline .m-pverdict-acc { color: #16a34a; }
.prefix-matrix .m-pline .m-pverdict-rej { color: var(--rejected-strong); }
.prefix-matrix .m-pseg {
    color: var(--accent);
    font-family: ui-monospace, "SF Mono", Menlo, monospace;
    font-size: 11px;
}
.prefix-matrix .m-pseg.is-focus {
    color: var(--origin);
    font-weight: 700;
}
.prefix-matrix .m-pseg.is-t1 {
    background: rgba(109, 40, 217, 0.14);
    color: #6d28d9;
    padding: 0 4px;
    border-radius: 3px;
    font-weight: 700;
}
.prefix-matrix .m-pseg.is-private {
    background: rgba(190, 24, 93, 0.12);
    color: #be185d;
    padding: 0 4px;
    border-radius: 3px;
    font-weight: 700;
}
.prefix-matrix .m-pseg.is-bogon {
    color: #b91c1c;
    border-color: rgba(185, 28, 28, 0.6);
    border-style: dashed;
}
.prefix-matrix .m-pseg.is-reserved {
    color: #92400e;
}
.prefix-matrix .m-origin.is-bogon {
    color: #b91c1c;
    font-weight: 600;
}
.prefix-matrix .m-origin.is-reserved {
    color: #92400e;
}
.prefix-matrix .m-arrow {
    color: var(--muted);
    margin: 0 0.25em;
}
.prefix-matrix .m-pips {
    color: var(--muted);
    font-family: ui-monospace, "SF Mono", Menlo, monospace;
    font-size: 10px;
    margin-left: 0.5em;
}

/* Open-popup button replacing the inline prefix block. */
.panel-open-prefixes-btn {
    display: block;
    margin: 16px 0 8px;
    background: var(--panel);
    color: var(--accent);
    border: 1px solid var(--line);
    border-radius: 4px;
    padding: 8px 14px;
    font-size: 13px;
    font-weight: 600;
    cursor: pointer;
    text-align: left;
}
.panel-open-prefixes-btn:hover {
    background: var(--accent-soft);
    border-color: var(--accent);
}
.panel-open-prefixes-btn-inline {
    display: inline-flex;
    align-items: center;
    margin: 0;
    padding: 3px 8px;
    font-size: 11px;
    font-weight: 600;
    line-height: 1.2;
    min-height: 24px;
    white-space: nowrap;
    background: var(--bg);
    border: 1px solid var(--border, #c8d0dc);
    color: var(--accent);
    transition: background 0.12s, border-color 0.12s, color 0.12s;
}
.panel-open-prefixes-btn-inline:hover {
    background: var(--accent);
    border-color: var(--accent);
    color: #ffffff;
}

/* Modal-hosted matrix: wider card, full-height flex column so
   the matrix-scroll region can fill whatever vertical space is
   left after the summary / toolbar / legend, and the horizontal
   scrollbar stays pinned to the visible bottom of the modal —
   not buried below the table when the table is taller than the
   modal. The sticky thead anchors to .m-scroll (now the sole
   scroll context for both axes) so column headers stay locked
   while the user scrolls within the table. */
.route-modal-card.prefix-matrix-modal {
    width: min(1280px, 96vw);
    height: min(820px, 90vh);
    display: flex;
    flex-direction: column;
}
.route-modal-card.prefix-matrix-modal .route-modal-body {
    overflow: hidden;
    padding: 12px 18px 18px;
    display: flex;
    flex-direction: column;
    flex: 1;
    min-height: 0;
}
.route-modal-card.prefix-matrix-modal .prefix-matrix {
    display: flex;
    flex-direction: column;
    flex: 1;
    min-height: 0;
    margin: 0;
}

/* Three dedicated annotation columns rendered between the Paths cell
   and the per-session matrix: AS-path prepend, more-specifics in the
   DFZ (as % of address space), and the LPP probe mini-bar. Sized as
   narrow as the content allows so the per-session columns to the
   right keep their visible width. Cells are filled asynchronously
   after the /api/lpp-annotations fetch; empty cells stay empty (no
   placeholder) so the table looks calm when most prefixes have no
   data.

   The prefix in column 1 is now a clickable link that opens a detail
   popup; the per-row cells are intentionally one-glance summaries
   only, and the popup is where the user lands for the full list. */
.prefix-matrix .m-table .m-pfx {
    white-space: nowrap;
}
.prefix-matrix .m-table .m-pfx-link {
    color: inherit;
    text-decoration: none;
    border-bottom: 1px dotted currentColor;
    cursor: pointer;
}
.prefix-matrix .m-table .m-pfx-link:hover,
.prefix-matrix .m-table .m-pfx-link:focus-visible {
    color: var(--accent-strong, #1d4ed8);
    border-bottom-style: solid;
    outline: none;
}
.prefix-matrix .m-table th.m-annot-h {
    text-align: center;
    font-size: 0.82em;
    padding-left: 0.4em;
    padding-right: 0.4em;
    white-space: nowrap;
    width: 1%;
}
.prefix-matrix .m-table td.m-annot {
    text-align: center;
    padding-left: 0.4em;
    padding-right: 0.4em;
    white-space: nowrap;
    width: 1%;
    font-size: 0.85em;
    line-height: 1.2;
}
.prefix-matrix .m-table td.m-annot:empty::before {
    content: "";
}
/* While /api/lpp-annotations is in flight, every annotation cell
   carries the .m-annot-pending class so the user can tell "empty"
   apart from "still loading". The three-dot spinner uses the same
   keyframes as the holder-column fetcher. */
.prefix-matrix .m-table td.m-annot-pending {
    color: var(--muted, #94a3b8);
    opacity: 0.7;
}
.prefix-matrix .m-table .m-annot-fetching {
    /* Centre the dot row in the narrow column. */
    justify-content: center;
}
.prefix-matrix .m-table .m-annot-pill {
    display: inline-block;
    padding: 1px 6px;
    border-radius: 3px;
    font-family: monospace;
    font-size: 0.92em;
    font-weight: 600;
    line-height: 1.3;
    cursor: help;
    white-space: nowrap;
}
.prefix-matrix .m-table .m-annot-prepend-pill {
    background: rgba(180, 83, 9, 0.12);
    color: #b45309;
}
.prefix-matrix .m-table .m-annot-leaked-pill {
    background: rgba(220, 38, 38, 0.10);
    color: #b91c1c;
}
.prefix-matrix .m-table .m-annot-default-pill {
    background: rgba(71, 85, 105, 0.14);
    color: #334155;
}
.prefix-matrix .m-table .m-annot-default-all {
    background: rgba(71, 85, 105, 0.20);
    color: #1e293b;
    font-weight: 700;
}
.prefix-matrix .m-table .m-annot-roa-pill {
    min-width: 18px;
    text-align: center;
    font-weight: 700;
}
.prefix-matrix .m-table .m-annot-roa-valid {
    background: rgba(22, 163, 74, 0.16);
    color: #15803d;
}
.prefix-matrix .m-table .m-annot-roa-invalid {
    background: rgba(220, 38, 38, 0.16);
    color: #b91c1c;
}
.prefix-matrix .m-table .m-annot-roa-none {
    background: rgba(148, 163, 184, 0.18);
    color: #475569;
    font-weight: 600;
}
/* Base bar/segment look is shared by the inline column cells and the
   legend swatches below the table, so it's scoped to .prefix-matrix
   rather than .m-table. */
.prefix-matrix .m-annot-lpp-bar {
    display: inline-flex;
    height: 8px;
    border-radius: 2px;
    overflow: hidden;
    background: #e2e8f0;
    vertical-align: middle;
}
.prefix-matrix .m-table .m-annot-lpp-bar {
    width: 56px;
    cursor: help;
}
.prefix-matrix .m-annot-lpp-seg {
    display: block;
    height: 100%;
    /* 1px white divider on each segment's trailing edge keeps segment
       boundaries — and therefore the proportions — visible no matter
       the hue: a non-colour cue for colour-blind readers. */
    box-shadow: inset -1px 0 0 rgba(255, 255, 255, 0.9);
}
/* Colour-blind-safe LPP encoding. The old green/amber/red traffic
   light collapses under red-green colour-vision deficiency, so each
   category now carries THREE independent cues — a safe hue, a texture,
   and the fixed left→right segment order (R&E → AS-path → commodity):
     R&E preferred       → blue,  solid
     AS-path determined  → grey,  diagonal hatch
     commodity preferred → red,   dots
   blue / grey / red is a diverging set that stays distinct by hue even
   under deuteranopia/protanopia (amber would have collapsed toward red);
   grey also reads as the neutral "neither-preferred" middle. Texture is
   faint on the 8px inline bar but clear on the larger detail-popup bar
   and the legend swatches. */
.prefix-matrix .m-annot-lpp-seg.m-annot-lpp-re { background-color: #2c7fb8; }
.prefix-matrix .m-annot-lpp-seg.m-annot-lpp-path {
    background-color: #64748b;
    background-image: repeating-linear-gradient(
        45deg, rgba(0, 0, 0, 0.34) 0 1.5px, transparent 1.5px 4px);
}
.prefix-matrix .m-annot-lpp-seg.m-annot-lpp-comm {
    background-color: #d7301f;
    background-image: radial-gradient(
        rgba(255, 255, 255, 0.9) 0.8px, transparent 1.1px);
    background-size: 4px 4px;
}

/* Persistent key for the LPP column, rendered above the table so the
   inline bars can be decoded without relying on colour at all. */
.prefix-matrix .m-lpp-key {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 4px 14px;
    margin: 0 0 8px;
    font-size: 11px;
    color: var(--muted, #475569);
}
.prefix-matrix .m-lpp-key-label {
    font-weight: 600;
    color: var(--fg, #1e293b);
}
.prefix-matrix .m-lpp-key-item {
    display: inline-flex;
    align-items: center;
    gap: 6px;
}
.prefix-matrix .m-lpp-key .m-lpp-key-swatch {
    width: 26px;
    height: 9px;
}
/* A full-width single-segment swatch needs no trailing divider. */
.prefix-matrix .m-lpp-key-swatch .m-annot-lpp-seg { box-shadow: none; }
.prefix-matrix .m-lpp-key-note { font-style: italic; }

/* "How to read this" column guide — a disclosure above the table,
   collapsed by default so it never pushes the prefix list down. */
.prefix-matrix .m-colguide {
    margin: 0 0 10px;
    border: 0.5px solid var(--line, #e2e8f0);
    border-radius: 6px;
    background: var(--panel, #ffffff);
}
.prefix-matrix .m-colguide-summary {
    cursor: pointer;
    list-style: none;
    display: flex;
    align-items: center;
    gap: 7px;
    padding: 8px 12px;
    font-size: 12px;
    font-weight: 600;
    color: var(--accent, #185fa5);
    user-select: none;
}
.prefix-matrix .m-colguide-summary::-webkit-details-marker { display: none; }
.prefix-matrix .m-colguide-summary::after {
    content: "\25B8"; /* ▸ — rotates to ▾ when open */
    margin-left: auto;
    color: var(--muted, #64748b);
    transition: transform 0.12s ease-out;
}
.prefix-matrix .m-colguide[open] .m-colguide-summary::after {
    transform: rotate(90deg);
}
.prefix-matrix .m-colguide-summary:hover { text-decoration: underline; }
.prefix-matrix .m-colguide-summary:focus-visible {
    outline: 2px solid var(--accent, #185fa5);
    outline-offset: 2px;
    border-radius: 4px;
}
.prefix-matrix .m-colguide-icon { font-size: 14px; }
.prefix-matrix .m-colguide-body {
    display: grid;
    grid-template-columns: max-content 1fr;
    gap: 7px 14px;
    margin: 0;
    padding: 4px 14px 12px;
    border-top: 0.5px solid var(--line, #e2e8f0);
    font-size: 12px;
    line-height: 1.5;
}
.prefix-matrix .m-colguide-body dt {
    font-weight: 700;
    color: var(--fg, #1e293b);
    white-space: nowrap;
}
.prefix-matrix .m-colguide-body dd {
    margin: 0;
    color: var(--muted, #475569);
}
.prefix-matrix .m-colguide-body strong { color: var(--fg, #1e293b); }
.prefix-matrix .m-colguide-body code {
    font-family: ui-monospace, "SF Mono", Menlo, monospace;
    font-size: 11px;
    background: var(--bg, #f1f5f9);
    padding: 0 3px;
    border-radius: 3px;
}
/* Worked example bars under the LPP entry. Taller and wider than the
   8px inline column bar so the hue + texture read clearly. */
.prefix-matrix .m-colguide-egs {
    display: flex;
    flex-direction: column;
    gap: 6px;
    margin-top: 8px;
}
.prefix-matrix .m-colguide-eg {
    display: flex;
    align-items: center;
    gap: 10px;
}
.prefix-matrix .m-colguide-eg .m-colguide-eg-bar {
    flex: 0 0 auto;
    width: 120px;
    height: 14px;
}
.prefix-matrix .m-colguide-eg-cap {
    font-size: 11px;
    color: var(--muted, #475569);
}

/* Per-prefix detail popup, stacked above the matrix modal so the
   underlying prefix list stays visible while the user drills into
   one entry. */
.pfx-detail-overlay {
    z-index: 1200;
}
.pfx-detail-card {
    max-width: min(720px, 92vw);
}
.pfx-detail-title {
    display: flex;
    align-items: baseline;
    gap: 0.8em;
    flex-wrap: wrap;
}
.pfx-detail-prefix {
    font-family: monospace;
    font-size: 1.15em;
    font-weight: 700;
}
.pfx-detail-holder {
    color: var(--muted, #64748b);
    font-size: 0.9em;
    font-weight: 400;
}
.pfx-detail-body {
    display: flex;
    flex-direction: column;
    gap: 1.1em;
    padding: 0.4em 0;
}
.pfx-detail-section h4 {
    margin: 0 0 0.4em 0;
    font-size: 1em;
}
.pfx-detail-lead {
    margin: 0 0 0.6em 0;
    color: var(--muted, #475569);
    font-size: 0.92em;
}
/* AS-path strips rendered as "hop cards". Hops read left-to-right
   from root (AS11537) on the left to the origin AS on the right.
   Each hop is a small stacked card: AS number on top, AS name
   under it, CAIDA org holder as a faint third line — so the
   identity of every AS reads straight off the path. Runs of
   identical consecutive hops collapse into a single card with an
   amber ×N count; Tier-1 transit hops are tinted violet and
   tagged; the origin hop carries a green "origin" tag. */
.pfx-detail-path {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 4px 6px;
    font-size: 0.92em;
    line-height: 1.5;
    padding: 0.4em 0.6em;
    background: rgba(15, 23, 42, 0.04);
    border-radius: 4px;
}
.pfx-detail-path-label {
    color: var(--muted, #64748b);
    font-size: 0.85em;
    font-weight: 600;
    letter-spacing: 0.5px;
    text-transform: uppercase;
    margin-right: 0.2em;
}
.pfx-detail-path-sep {
    color: #94a3b8;
    font-weight: 700;
    flex: 0 0 auto;
}
/* All-paths list in the detail popup: one row per observed AS-path,
   verdict glyph on the left, the hop-card strip on the right. */
.pfx-detail-pathlist {
    list-style: none;
    padding: 0;
    margin: 0;
    display: flex;
    flex-direction: column;
    gap: 6px;
}
.pfx-detail-pathrow {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 0.92em;
    line-height: 1.5;
    padding: 0.35em 0.6em;
    background: rgba(15, 23, 42, 0.04);
    border-radius: 4px;
}
.pfx-detail-pathrow-verdict {
    flex: 0 0 auto;
    font-weight: 700;
    font-family: monospace;
}
.pfx-detail-pathrow-acc { color: #166534; }
.pfx-detail-pathrow-rej { color: #991b1b; }
.pfx-detail-pathrow-chips {
    display: inline-flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 4px 6px;
}
/* One hop card. */
.pfx-detail-hopcard {
    display: inline-flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 0;
    max-width: 13em;
    min-width: 0;
    padding: 3px 8px 4px;
    border: 1px solid rgba(15, 23, 42, 0.14);
    border-radius: 6px;
    background: #fff;
    line-height: 1.35;
}
.pfx-detail-hop-asn {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    font-family: ui-monospace, "SF Mono", Menlo, monospace;
    font-size: 11.5px;
    font-weight: 700;
    color: #334155;
    white-space: nowrap;
}
.pfx-detail-hop-xn {
    color: #b45309;
}
.pfx-detail-hop-name {
    max-width: 100%;
    font-size: 11px;
    color: #0f172a;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.pfx-detail-hop-holder {
    max-width: 100%;
    font-size: 10.5px;
    color: var(--muted, #64748b);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.pfx-detail-hop-tag {
    font-size: 8.5px;
    font-weight: 700;
    letter-spacing: 0.4px;
    text-transform: uppercase;
    padding: 0 5px;
    border-radius: 999px;
}
.pfx-detail-hop-t1 {
    color: #6d28d9;
    background: rgba(109, 40, 217, 0.14);
}
.pfx-detail-hop-private {
    color: #be185d;
    background: rgba(190, 24, 93, 0.13);
}
.pfx-detail-hop-origin {
    color: #166534;
    background: rgba(22, 101, 52, 0.12);
}
.pfx-detail-hopcard.is-t1 {
    border-color: rgba(109, 40, 217, 0.45);
    background: rgba(109, 40, 217, 0.06);
}
.pfx-detail-hopcard.is-t1 .pfx-detail-hop-asn {
    color: #6d28d9;
}
/* Private-use ASN hops — rose, with a dashed border to read as
   "shouldn't be here" rather than merely notable. */
.pfx-detail-hopcard.is-private {
    border-color: rgba(190, 24, 93, 0.5);
    border-style: dashed;
    background: rgba(190, 24, 93, 0.05);
}
.pfx-detail-hopcard.is-private .pfx-detail-hop-asn {
    color: #be185d;
}
/* Bogon = assignable but unassigned ASN; reserved = special-purpose range. */
.pfx-detail-hop-bogon {
    color: #b91c1c;
    background: rgba(185, 28, 28, 0.14);
}
.pfx-detail-hop-reserved {
    color: #92400e;
    background: rgba(146, 64, 14, 0.13);
}
.pfx-detail-hopcard.is-bogon {
    border-color: rgba(185, 28, 28, 0.6);
    border-style: dashed;
    background: rgba(185, 28, 28, 0.06);
}
.pfx-detail-hopcard.is-bogon .pfx-detail-hop-asn {
    color: #b91c1c;
}
.pfx-detail-hopcard.is-reserved {
    border-color: rgba(146, 64, 14, 0.5);
    border-style: dashed;
}
/* The amber prepend treatment is the operational signal, so it
   wins over the Tier-1 tint when a hop is both. */
.pfx-detail-hopcard.is-prep {
    border-color: rgba(180, 83, 9, 0.45);
    background: rgba(180, 83, 9, 0.07);
}
/* Action row at the bottom of the popup — close + highlight the
   ribbons that carry this prefix in the Sankey behind the modal. */
.pfx-detail-actions {
    display: flex;
    justify-content: flex-end;
}
.pfx-detail-show-ribbons {
    padding: 5px 12px;
    border: 1px solid var(--accent, #2563eb);
    border-radius: 5px;
    background: transparent;
    color: var(--accent, #2563eb);
    font-size: 12.5px;
    font-weight: 600;
    cursor: pointer;
}
.pfx-detail-show-ribbons:hover {
    background: var(--accent-soft, rgba(37, 99, 235, 0.12));
}
.pfx-detail-leaked-list {
    list-style: none;
    padding: 0;
    margin: 0;
    max-height: 260px;
    overflow-y: auto;
    border: 1px solid rgba(220, 38, 38, 0.18);
    border-radius: 4px;
    background: rgba(220, 38, 38, 0.04);
}
.pfx-detail-leaked-list li {
    padding: 4px 10px;
    border-bottom: 1px solid rgba(220, 38, 38, 0.10);
    font-size: 0.92em;
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: 1em;
}
.pfx-detail-leaked-list li:last-child {
    border-bottom: none;
}
.pfx-detail-leaked-origin {
    color: var(--muted, #64748b);
    font-size: 0.85em;
    white-space: nowrap;
}
.pfx-detail-lpp-bar {
    display: flex;
    width: 100%;
    height: 18px;
    border-radius: 3px;
    overflow: hidden;
    background: #e2e8f0;
    margin-bottom: 0.6em;
}
.pfx-detail-lpp-seg {
    display: block;
    height: 100%;
    box-shadow: inset -1px 0 0 rgba(255, 255, 255, 0.9);
}
/* Same colour-blind-safe hue + texture set as the inline column bar
   (see the .m-annot-lpp-seg block above). At 18px tall the textures
   read clearly here. */
.pfx-detail-lpp-seg.pfx-detail-lpp-re { background-color: #2c7fb8; }
.pfx-detail-lpp-seg.pfx-detail-lpp-path {
    background-color: #64748b;
    background-image: repeating-linear-gradient(
        45deg, rgba(0, 0, 0, 0.34) 0 2px, transparent 2px 5px);
}
.pfx-detail-lpp-seg.pfx-detail-lpp-comm {
    background-color: #d7301f;
    background-image: radial-gradient(
        rgba(255, 255, 255, 0.9) 1px, transparent 1.4px);
    background-size: 5px 5px;
}
.pfx-detail-lpp-legend {
    display: grid;
    grid-template-columns: max-content max-content;
    gap: 0.2em 1em;
    margin: 0;
    font-size: 0.9em;
}
.pfx-detail-lpp-legend dt {
    display: flex;
    align-items: center;
    gap: 0.4em;
    margin: 0;
}
.pfx-detail-lpp-legend dd {
    margin: 0;
    color: var(--muted, #475569);
}
.pfx-detail-lpp-dot {
    display: inline-block;
    width: 11px;
    height: 11px;
    border-radius: 2px;
}
/* Legend dots carry the same hue + texture as the bar segments so the
   key teaches the texture mapping, not just the colour. */
.pfx-detail-lpp-dot.pfx-detail-lpp-re { background-color: #2c7fb8; }
.pfx-detail-lpp-dot.pfx-detail-lpp-path {
    background-color: #64748b;
    background-image: repeating-linear-gradient(
        45deg, rgba(0, 0, 0, 0.34) 0 2px, transparent 2px 5px);
}
.pfx-detail-lpp-dot.pfx-detail-lpp-comm {
    background-color: #d7301f;
    background-image: radial-gradient(
        rgba(255, 255, 255, 0.9) 1px, transparent 1.4px);
    background-size: 5px 5px;
}
.pfx-detail-empty p {
    color: var(--muted, #64748b);
    font-style: italic;
}
/* Routing-status section — leads the popup and highlights whether
   Internet2 accepted, rejected, or mixed verdicts on this prefix
   across its peering sessions. The session breakdown shows which
   peerings saw which outcome — important when diagnosing a rejected
   route, since the answer to "why doesn't this prefix reach me"
   often depends on which neighbour announced it. */
.pfx-detail-verdict {
    display: inline-block;
    padding: 2px 10px;
    border-radius: 3px;
    font-size: 0.78em;
    font-weight: 700;
    letter-spacing: 0.4px;
    margin-left: 0.5em;
    vertical-align: middle;
}
.pfx-detail-verdict-accepted {
    background: rgba(22, 163, 74, 0.18);
    color: #15803d;
}
.pfx-detail-verdict-rejected {
    background: rgba(220, 38, 38, 0.20);
    color: #b91c1c;
}
.pfx-detail-verdict-mixed {
    background: rgba(245, 158, 11, 0.22);
    color: #92400e;
}
.pfx-detail-path-summary {
    color: var(--muted, #64748b);
    font-size: 0.85em;
    font-weight: 400;
    margin-left: 0.4em;
}
.pfx-detail-sessions {
    list-style: none;
    padding: 0;
    margin: 0.4em 0 0 0;
    border: 1px solid rgba(15, 23, 42, 0.10);
    border-radius: 4px;
    background: rgba(15, 23, 42, 0.02);
}
.pfx-detail-sess-row {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.4em 0.9em;
    padding: 5px 10px;
    border-bottom: 1px solid rgba(15, 23, 42, 0.06);
    font-size: 0.92em;
}
.pfx-detail-sess-row:last-child {
    border-bottom: none;
}
.pfx-detail-sess-verdict {
    flex: 0 0 auto;
    min-width: 5.5em;
    padding: 1px 6px;
    border-radius: 3px;
    font-size: 0.85em;
    font-weight: 700;
    text-align: center;
}
.pfx-detail-sess-acc {
    background: rgba(22, 163, 74, 0.18);
    color: #15803d;
}
.pfx-detail-sess-rej {
    background: rgba(220, 38, 38, 0.18);
    color: #b91c1c;
}
.pfx-detail-sess-mix {
    background: rgba(245, 158, 11, 0.22);
    color: #92400e;
}
.pfx-detail-sess-ip {
    font-family: monospace;
    font-weight: 600;
}
.pfx-detail-sess-fam-v4 { color: #1d4ed8; }
.pfx-detail-sess-fam-v6 { color: #0e7490; }
.pfx-detail-sess-loc {
    color: var(--muted, #64748b);
    font-size: 0.85em;
}
.pfx-detail-sess-peer {
    margin-left: auto;
    color: var(--muted, #475569);
    font-size: 0.9em;
}
.pfx-detail-empty-line {
    margin: 0.4em 0 0 0;
}
.pfx-detail-default-dot {
    display: inline-block;
    width: 10px;
    height: 10px;
    border-radius: 2px;
    margin-right: 0.4em;
    vertical-align: middle;
}
.pfx-detail-default-dot.pfx-detail-default-d { background: #475569; }
.pfx-detail-default-dot.pfx-detail-default-f { background: #94a3b8; }
.pfx-detail-roa-status {
    display: inline-block;
    padding: 1px 8px;
    border-radius: 3px;
    font-size: 0.78em;
    font-weight: 700;
    letter-spacing: 0.3px;
    margin-left: 0.5em;
    vertical-align: middle;
}
.pfx-detail-roa-status-valid {
    background: rgba(22, 163, 74, 0.18);
    color: #15803d;
}
.pfx-detail-roa-status-invalid {
    background: rgba(220, 38, 38, 0.18);
    color: #b91c1c;
}
.pfx-detail-roa-status-none {
    background: rgba(148, 163, 184, 0.20);
    color: #475569;
}
.pfx-detail-roa-list {
    list-style: none;
    padding: 0;
    margin: 0.4em 0 0 0;
    border: 1px solid rgba(15, 23, 42, 0.10);
    border-radius: 4px;
    background: rgba(15, 23, 42, 0.02);
}
.pfx-detail-roa-list li {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.4em 1em;
    padding: 5px 10px;
    border-bottom: 1px solid rgba(15, 23, 42, 0.06);
    font-size: 0.92em;
}
.pfx-detail-roa-list li:last-child {
    border-bottom: none;
}
.pfx-detail-roa-list code {
    font-family: monospace;
    font-weight: 600;
}
.pfx-detail-roa-maxlen,
.pfx-detail-roa-asn {
    color: var(--muted, #64748b);
    font-family: monospace;
    font-size: 0.85em;
}
.pfx-detail-roa-match {
    margin-left: auto;
    font-size: 0.85em;
    padding: 1px 6px;
    border-radius: 3px;
}
.pfx-detail-roa-match-ok {
    background: rgba(22, 163, 74, 0.16);
    color: #15803d;
}
.pfx-detail-roa-match-bad {
    background: rgba(220, 38, 38, 0.12);
    color: #b91c1c;
}

