/* 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;
}
.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; }

/* 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-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;
}
.prefix-matrix .m-table .m-annot-lpp-bar {
    display: inline-flex;
    width: 56px;
    height: 8px;
    border-radius: 2px;
    overflow: hidden;
    background: #e2e8f0;
    vertical-align: middle;
    cursor: help;
}
.prefix-matrix .m-table .m-annot-lpp-seg {
    display: block;
    height: 100%;
}
/* LPP segment colours echo the global LPP report's per-prefix
   mini-bar: green = RE preferred, amber = AS-path determined,
   red = commodity preferred. */
.prefix-matrix .m-table .m-annot-lpp-seg.m-annot-lpp-re { background: #16a34a; }
.prefix-matrix .m-table .m-annot-lpp-seg.m-annot-lpp-path { background: #f59e0b; }
.prefix-matrix .m-table .m-annot-lpp-seg.m-annot-lpp-comm { background: #dc2626; }

/* 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;
}
.pfx-detail-chipset {
    display: flex;
    flex-wrap: wrap;
    gap: 4px;
}
.pfx-detail-chip {
    padding: 2px 8px;
    border-radius: 3px;
    font-family: monospace;
    font-size: 0.88em;
    font-weight: 600;
}
.pfx-detail-chip-prepend {
    background: rgba(180, 83, 9, 0.14);
    color: #b45309;
}
/* Inline AS-path strip rendered when a prefix has prepending. Hops
   read left-to-right from root (AS11537) on the left to the origin
   AS on the right; runs of identical consecutive hops collapse into
   a single highlighted "AS#### ×N" chip so the prepended segments
   stand out against the unprepended ones. */
.pfx-detail-path {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 4px 6px;
    font-family: monospace;
    font-size: 0.92em;
    line-height: 1.5;
    padding: 0.4em 0.6em;
    background: rgba(15, 23, 42, 0.04);
    border-radius: 4px;
    word-break: break-all;
}
.pfx-detail-path-label {
    color: var(--muted, #64748b);
    font-family: inherit;
    font-size: 0.85em;
    font-weight: 600;
    letter-spacing: 0.5px;
    text-transform: uppercase;
    margin-right: 0.2em;
}
.pfx-detail-path-hop {
    color: #334155;
}
.pfx-detail-path-prep {
    background: rgba(180, 83, 9, 0.16);
    color: #b45309;
    padding: 1px 6px;
    border-radius: 3px;
    font-weight: 700;
}
.pfx-detail-path-sep {
    color: #94a3b8;
}
.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%;
}
.pfx-detail-lpp-seg.pfx-detail-lpp-re { background: #16a34a; }
.pfx-detail-lpp-seg.pfx-detail-lpp-path { background: #f59e0b; }
.pfx-detail-lpp-seg.pfx-detail-lpp-comm { background: #dc2626; }
.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: 10px;
    height: 10px;
    border-radius: 2px;
}
.pfx-detail-lpp-dot.pfx-detail-lpp-re { background: #16a34a; }
.pfx-detail-lpp-dot.pfx-detail-lpp-path { background: #f59e0b; }
.pfx-detail-lpp-dot.pfx-detail-lpp-comm { background: #dc2626; }
.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;
}

