/* Route popup — overlay shown when an AS bar in the Sankey is
   clicked. Floats on top of the focused-AS panel; dismissed via
   close button, backdrop click, or Escape. The card is centred and
   bounded so a long route list scrolls inside it instead of pushing
   the rest of the page around. */
.route-modal-overlay {
    display: none;
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.45);
    z-index: 200;
    align-items: center;
    justify-content: center;
    padding: 24px;
}
.route-modal-overlay.is-open { display: flex; }
.route-modal-card {
    background: var(--panel);
    border: 1px solid var(--line);
    border-radius: 8px;
    box-shadow: 0 20px 60px rgba(0, 0, 0, 0.25);
    max-width: 1100px;
    width: 100%;
    max-height: 90vh;
    display: flex;
    flex-direction: column;
    overflow: hidden;
}
.route-modal-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.6rem;
    padding: 14px 18px;
    background: var(--accent-soft);
    border-bottom: 1px solid var(--line);
}
.route-modal-title {
    color: var(--accent);
    font-size: 16px;
    font-weight: 600;
}
.route-modal-close {
    background: transparent;
    border: 0;
    color: var(--muted);
    font-size: 24px;
    line-height: 1;
    cursor: pointer;
    padding: 0 8px;
    border-radius: 4px;
    transition: background 0.12s, color 0.12s;
}
.route-modal-close:hover {
    color: var(--fg);
    background: rgba(0, 0, 0, 0.06);
}
.route-modal-body {
    overflow: auto;
    padding: 12px 18px 18px;
}
.route-modal-section-h {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 0.5rem;
    margin: 14px 0 4px;
    font-size: 13px;
    font-weight: 600;
    color: var(--accent);
}
.route-modal-section-h:first-child { margin-top: 0; }
.route-modal-section-h .meta {
    color: var(--muted);
    font-weight: 400;
    font-size: 0.92em;
}
.route-modal-empty {
    color: var(--muted);
    font-style: italic;
    padding: 8px 0;
}
.route-modal-scroll {
    border: 1px solid var(--line);
    border-radius: 6px;
    background: var(--panel);
    overflow: auto;
}
/* Suppress page-level scroll while the popup is open. */
body.route-modal-open { overflow: hidden; }
.route-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 12px;
    font-family: ui-monospace, "SF Mono", Menlo, monospace;
}
.route-table thead th {
    position: sticky;
    top: 0;
    background: var(--accent-soft);
    color: var(--accent);
    font-weight: 600;
    text-align: left;
    padding: 6px 10px;
    border-bottom: 1px solid var(--line);
    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 0.04em;
}
.route-table tbody td {
    padding: 4px 10px;
    border-bottom: 1px solid var(--line);
    vertical-align: top;
}
.route-table tbody tr:last-child td { border-bottom: none; }
.route-table tbody tr.rejected td { background: var(--rejected-soft); }
.route-table tbody tr.rejected .rt-status {
    color: var(--rejected);
    font-weight: 600;
}
.route-table tbody tr.accepted .rt-status { color: var(--accent); }
.route-table .rt-prefix { white-space: nowrap; font-weight: 500; }
.route-table .rt-holder {
    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
    color: var(--fg);
    max-width: 22em;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.route-table .rt-holder-none {
    color: var(--muted);
    font-style: italic;
}
.route-table .rt-status { text-transform: capitalize; }
.route-table .rt-peer-ip {
    font-family: ui-monospace, SFMono-Regular, Consolas, monospace;
    font-size: 11.5px;
    color: var(--fg);
    white-space: nowrap;
}
.route-table .rt-origin { white-space: nowrap; color: var(--fg); }
.route-table .rt-path {
    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
    font-size: 11.5px;
    line-height: 1.5;
}
.route-table .rt-as { color: var(--muted); }
.route-table .rt-as-hl {
    color: var(--origin);
    font-weight: 700;
}
/* AS-path prepending: highlight hops the announcing AS prepended
   itself, annotated with × the consecutive-run length. The orange
   matches the "AS-path determined" colour used elsewhere for
   prepending-related signal. */
.route-table .rt-as-prep {
    color: #fab387;
    background: rgba(250, 179, 135, 0.13);
    padding: 1px 5px;
    border-radius: 3px;
    font-weight: 600;
}
/* Best-path indicator on a prefix row — re-info reported this
   exact (path, prefix) tuple as Internet2's chosen best-path. The
   blue calls out the "we actually use this" status without competing
   with the route accept/reject colour scheme. */
.route-table .rt-best-badge {
    display: inline-block;
    background: #89b4fa;
    color: #1e1e2e;
    font-size: 0.72em;
    font-weight: 700;
    padding: 1px 5px;
    border-radius: 3px;
    margin-left: 6px;
    letter-spacing: 0.3px;
}
/* BGP community badges — palette comes from data.community_labels
   (mirrors the Global LPP report's colour assignments). */
.route-table .rt-comm-badge {
    display: inline-block;
    color: #1e1e2e;
    font-size: 0.72em;
    font-weight: 700;
    padding: 1px 6px;
    border-radius: 3px;
    margin-left: 6px;
    cursor: help;
}
/* Visually mute non-best-path rows when the panel is showing every
   observed route — the best-path rows pop without needing a positive
   highlight. (The tag itself stays full opacity.) */
.route-table tr.is-not-best td.rt-prefix,
.route-table tr.is-not-best td.rt-holder,
.route-table tr.is-not-best td.rt-status,
.route-table tr.is-not-best td.rt-peer-ip,
.route-table tr.is-not-best td.rt-origin,
.route-table tr.is-not-best td.rt-path {
    opacity: 0.6;
}
.route-table .rt-arrow { color: var(--line); margin: 0 2px; }

/* Sankey section header carries the toggles and download buttons on
   the right. Switch the header to a flex row so the title
   and the actions sit on the same baseline regardless of header
   width. */
.panel-section-h.sankey-section-h {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
    flex-wrap: wrap;
    /* The Sankey section header is no longer a label-only band — it
       carries 10+ controls whose labels need to read as sentence
       case. Override the .panel-section-h all-caps so controls
       inside don't inherit `text-transform: uppercase`. */
    text-transform: none;
    letter-spacing: 0;
}
.sankey-title-group {
    display: inline-flex;
    align-items: center;
    gap: 0.6rem;
    min-width: 0;
    flex-wrap: wrap;
}
.sankey-section-actions {
    display: flex;
    align-items: center;
    gap: 1.15rem;
    flex-shrink: 1;
    flex-wrap: wrap;
    justify-content: flex-end;
    row-gap: 0.55rem;
}
/* Each group is a self-contained chunk of related controls. Intra-
   group spacing is tighter than the gap between groups on the parent
   .sankey-section-actions row, which gives a visual rhythm —
   "scope" / "filter" / "view" / "export" / "help" — without
   resorting to dividers that fight ugly with flex-wrap. */
.sankey-actions-group {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    flex-wrap: wrap;
    row-gap: 0.35rem;
}
.sankey-actions-group:empty {
    display: none;
}
.sankey-upstream-toggle {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    font-size: 11px;
    font-weight: 500;
    text-transform: none;
    letter-spacing: 0;
    color: var(--muted);
    cursor: pointer;
    user-select: none;
    padding: 2px 6px;
    border-radius: 4px;
    transition: color 0.12s, background 0.12s;
}
.sankey-upstream-toggle:hover {
    color: var(--accent);
    background: var(--accent-soft);
}
.sankey-upstream-toggle input {
    margin: 0;
    accent-color: var(--accent);
    cursor: pointer;
}
.sankey-extra-as-control {
    gap: 0.45rem;
    align-items: center;
    cursor: default;
    position: relative;
}
.sankey-extra-as-trigger {
    align-items: center;
    background: var(--bg);
    border: 1px solid var(--border, #c8d0dc);
    border-radius: 4px;
    color: var(--accent);
    cursor: pointer;
    display: inline-flex;
    font: inherit;
    font-size: 11px;
    font-weight: 600;
    gap: 0.4rem;
    justify-content: space-between;
    min-height: 24px;
    min-width: 7rem;
    padding: 2px 6px 2px 8px;
    text-align: left;
    transition: background 0.12s, border-color 0.12s, color 0.12s;
}
.sankey-extra-as-trigger-label {
    flex: 1 1 auto;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.sankey-extra-as-trigger-caret {
    color: var(--muted);
    font-size: 0.9em;
    line-height: 1;
    pointer-events: none;
    transition: color 0.12s, transform 0.12s;
}
.sankey-extra-as-trigger.is-empty {
    color: var(--muted);
    font-weight: 500;
    font-style: italic;
}
.sankey-extra-as-trigger.is-selected {
    background: var(--accent-soft, rgba(59, 130, 246, 0.08));
    border-color: var(--accent);
}
.sankey-extra-as-trigger:hover {
    background: var(--accent);
    border-color: var(--accent);
    color: #ffffff;
    font-style: normal;
}
.sankey-extra-as-trigger:hover .sankey-extra-as-trigger-caret {
    color: #ffffff;
}
.sankey-extra-as-trigger[aria-expanded="true"] .sankey-extra-as-trigger-caret {
    transform: rotate(180deg);
}
.sankey-extra-as-trigger:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 1px;
}
.sankey-extra-as-menu {
    background: var(--panel);
    border: 1px solid var(--border);
    border-radius: 6px;
    box-shadow: 0 12px 30px rgba(15, 23, 42, 0.18);
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    padding: 0.55rem;
    position: absolute;
    right: 0;
    top: calc(100% + 6px);
    width: min(22rem, calc(100vw - 2rem));
    z-index: 20;
}
.sankey-extra-as-menu[hidden] {
    display: none;
}
/* Primary action button inside the first-panel-tip modal. Matches
   the participate-cta affordance so users recognise it as the
   recommended dismiss action. The modal also accepts close via the
   X, backdrop click, and Escape. */
.first-panel-tip-ok {
    background: var(--accent);
    border: 1px solid var(--accent);
    border-radius: 6px;
    color: #ffffff;
    cursor: pointer;
    font: inherit;
    font-size: 13px;
    font-weight: 600;
    margin-top: 18px;
    padding: 8px 18px;
    transition: opacity 0.12s;
}
.first-panel-tip-ok:hover,
.first-panel-tip-ok:focus-visible {
    opacity: 0.9;
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

/* Scope control group — wraps the Reduce Scope toggle and the
   Customize… popup trigger in a single bordered container with a
   leading "Scope" label, so the two controls read as related
   overrides of the default panel scope rather than as independent
   filters. ``position: relative`` anchors the absolutely-positioned
   Customize popup to the group rather than to the panel. */
.sankey-scope-control {
    align-items: center;
    background: var(--panel, #ffffff);
    border: 1px solid var(--line, #e3e6eb);
    border-radius: 6px;
    display: inline-flex;
    gap: 4px;
    padding: 2px 6px 2px 8px;
    position: relative;
}
.sankey-scope-control-label {
    color: var(--muted, #5b6472);
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 0.06em;
    margin-right: 2px;
    text-transform: uppercase;
}
/* The toggle inside the scope group inherits its checkbox styling
   from .sankey-upstream-toggle; drop the default outer border the
   group already supplies so the toggle reads as a child of the
   wrapper, not as a peer control with its own outline. */
.sankey-scope-control-toggle {
    border: none;
    padding-left: 0;
    padding-right: 0;
}
/* Explanatory blurb shown at the top of the Customize popup so a
   first-time user understands what the default scope is before they
   start picking ASes. Quiet typography (small, muted) so it reads as
   contextual help, not as the primary content of the popup. */
.sankey-extra-as-description {
    color: var(--muted, #5b6472);
    font-size: 11px;
    line-height: 1.4;
    margin: 0;
    padding: 0.15rem 0.25rem 0.4rem;
    border-bottom: 1px solid var(--line, #e3e6eb);
}
.sankey-extra-as-list {
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: 4px;
    color: var(--text);
    display: flex;
    flex-direction: column;
    font-size: 11px;
    gap: 1px;
    max-height: 16rem;
    min-width: 0;
    max-width: none;
    overflow: auto;
    padding: 4px 5px;
}
.sankey-extra-as-group {
    display: flex;
    flex-direction: column;
    gap: 1px;
}
.sankey-extra-as-group-title {
    color: var(--muted);
    font-size: 10px;
    font-weight: 700;
    padding: 4px 3px 2px;
    text-transform: uppercase;
}
.sankey-extra-as-choice {
    align-items: flex-start;
    border-radius: 3px;
    color: var(--text);
    cursor: pointer;
    display: flex;
    gap: 0.35rem;
    line-height: 1.25;
    padding: 2px 3px;
}
.sankey-extra-as-choice:hover {
    background: var(--accent-soft);
}
.sankey-extra-as-choice input {
    flex: 0 0 auto;
    margin-top: 1px;
}
.sankey-extra-as-choice-all {
    border-bottom: 1px solid var(--border);
    margin-bottom: 2px;
    padding-bottom: 4px;
}
.sankey-extra-as-actions {
    display: inline-flex;
    flex-wrap: wrap;
    gap: 0.3rem;
    max-width: none;
}
.sankey-extra-as-footer {
    border-top: 1px solid var(--border);
    display: flex;
    gap: 0.35rem;
    justify-content: flex-end;
    padding-top: 0.5rem;
}
.sankey-extra-as-action {
    border: 1px solid var(--border);
    border-radius: 4px;
    background: var(--panel);
    color: var(--muted);
    cursor: pointer;
    font: inherit;
    font-size: 11px;
    min-height: 24px;
    padding: 2px 7px;
}
.sankey-extra-as-apply {
    background: var(--accent);
    border-color: var(--accent);
    color: #fff;
}
.sankey-extra-as-action:hover {
    color: var(--accent);
    border-color: var(--accent);
}
.sankey-extra-as-action:disabled {
    cursor: default;
    opacity: 0.45;
}
.sankey-extra-as-action:disabled:hover {
    color: var(--muted);
    border-color: var(--border);
}
.sankey-extra-as-action:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 1px;
}
.sankey-extra-as-apply:hover {
    background: #0d4f8c;
    border-color: #0d4f8c;
    color: #fff;
}
/* Disabled toggle (e.g. "Best paths only" while re-info hasn't
   landed yet) — muted text, no hover affordance, default cursor on
   the underlying input. The tooltip explains why. */
.sankey-upstream-toggle.is-disabled {
    opacity: 0.55;
    cursor: not-allowed;
}
.sankey-upstream-toggle.is-disabled:hover {
    color: var(--muted);
    background: transparent;
}
.sankey-upstream-toggle.is-disabled input {
    cursor: not-allowed;
}
.sankey-export-btn {
    background: var(--bg);
    border: 1px solid var(--border, #c8d0dc);
    color: var(--accent);
    font-size: 11px;
    font-weight: 600;
    text-transform: none;
    letter-spacing: 0;
    padding: 3px 8px;
    border-radius: 4px;
    cursor: pointer;
    transition: background 0.12s, border-color 0.12s;
}
.sankey-export-btn:hover {
    background: var(--accent);
    color: #ffffff;
    border-color: var(--accent);
}
.sankey-export-btn:disabled {
    opacity: 0.6;
    cursor: wait;
}
.sankey-export-btn:disabled:hover {
    background: var(--bg);
    color: var(--accent);
    border-color: var(--border, #c8d0dc);
}
.sankey-export-btn:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}
.sankey-export-menu-wrap {
    position: relative;
    display: inline-flex;
}
.sankey-export-trigger {
    align-items: center;
    display: inline-flex;
    gap: 0.4rem;
}
.sankey-export-trigger[aria-expanded="true"] .sankey-extra-as-trigger-caret {
    transform: rotate(180deg);
}
.sankey-export-menu {
    position: absolute;
    top: calc(100% + 4px);
    right: 0;
    z-index: 25;
    background: var(--panel, #ffffff);
    border: 1px solid var(--border, #c8d0dc);
    border-radius: 6px;
    box-shadow: 0 12px 30px rgba(15, 23, 42, 0.18);
    display: flex;
    flex-direction: column;
    min-width: 11rem;
    padding: 0.25rem;
}
.sankey-export-menu[hidden] { display: none; }
.sankey-export-menu-item {
    background: transparent;
    border: none;
    border-radius: 4px;
    color: var(--text, #1e1e2e);
    cursor: pointer;
    font: inherit;
    font-size: 12px;
    padding: 0.45rem 0.65rem;
    text-align: left;
}
.sankey-export-menu-item:hover,
.sankey-export-menu-item:focus-visible {
    background: var(--accent-soft, rgba(59, 130, 246, 0.1));
    color: var(--accent);
    outline: none;
}

/* Inline spinner used beside disabled-state checkboxes (e.g. "Best
   paths only" while re-info is still warming up). 12px circle with
   a thin track and an animated arc — matches the muted text color
   so it doesn't compete with the label. */
.sankey-inline-spinner {
    display: inline-block;
    width: 12px;
    height: 12px;
    margin-left: 4px;
    border-radius: 50%;
    border: 2px solid var(--border, #c8d0dc);
    border-top-color: var(--accent);
    animation: sankey-spinner-rotate 0.8s linear infinite;
    flex: 0 0 auto;
}
@keyframes sankey-spinner-rotate {
    to { transform: rotate(360deg); }
}
.sankey-zoom-btn {
    background: var(--bg);
    border: 1px solid var(--border, #c8d0dc);
    color: var(--accent);
    font-size: 11px;
    font-weight: 600;
    padding: 3px 8px;
    border-radius: 4px;
    cursor: pointer;
    transition: background 0.12s, border-color 0.12s;
}
.sankey-zoom-btn:hover {
    background: var(--accent);
    color: #ffffff;
    border-color: var(--accent);
}
.sankey-zoom-btn:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}
.sankey-layout-select {
    align-items: center;
    color: var(--muted);
    display: inline-flex;
    font-size: 11px;
    gap: 5px;
    user-select: none;
}
.sankey-layout-select span {
    color: var(--fg);
    font-weight: 600;
}
.sankey-layout-select select {
    background: var(--bg);
    border: 1px solid var(--border, #c8d0dc);
    border-radius: 4px;
    color: var(--accent);
    cursor: pointer;
    font: inherit;
    font-weight: 600;
    min-height: 24px;
    padding: 2px 22px 2px 7px;
}
.sankey-layout-select select:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}
.sankey-highlight-rejects {
    align-items: center;
    color: var(--muted);
    display: inline-flex;
    font-size: 11px;
    gap: 5px;
    user-select: none;
    cursor: pointer;
}
.sankey-highlight-rejects span {
    color: var(--fg);
    font-weight: 600;
}
.sankey-highlight-rejects input[type="checkbox"] {
    cursor: pointer;
    margin: 0;
}
.sankey-highlight-rejects input[type="checkbox"]:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}
/* Primary placement: when "Highlight rejects" lives on the left side
   of the section header (next to the title text + Show-prefixes
   button) it reads as a primary, frequently-used display control.
   Give it a pill background so it's visually distinguishable from
   the section title text, with a stronger fill when the toggle is
   active so users can see at a glance whether reject-highlighting
   is on without having to look at the checkbox state. */
.sankey-highlight-rejects-primary {
    background: var(--bg);
    border: 1px solid var(--border, #c8d0dc);
    border-radius: 999px;
    padding: 3px 10px 3px 8px;
    font-size: 12px;
    transition: background 0.12s, border-color 0.12s, color 0.12s;
}
.sankey-highlight-rejects-primary:hover {
    border-color: var(--accent);
    color: var(--accent);
}
.sankey-highlight-rejects-primary:has(input:checked) {
    background: rgba(220, 38, 38, 0.09);
    border-color: rgba(220, 38, 38, 0.55);
}
.sankey-highlight-rejects-primary:has(input:checked) span {
    color: #b91c1c;
}
.sankey-prefix-filter {
    align-items: center;
    color: var(--muted);
    display: inline-flex;
    font-size: 11px;
    gap: 5px;
    min-width: 0;
    user-select: none;
}
.sankey-prefix-filter span {
    color: var(--fg);
    font-weight: 600;
    white-space: nowrap;
}
.sankey-prefix-filter input {
    background: var(--bg);
    border: 1px solid var(--border, #c8d0dc);
    border-radius: 4px;
    color: var(--accent);
    font: inherit;
    font-weight: 600;
    min-height: 24px;
    min-width: 12rem;
    padding: 2px 7px;
    /* Wider by default and stretches with the panel so network
       engineers can paste long regex strings without the field
       cropping. */
    width: min(18rem, 42vw);
    flex: 1 1 auto;
}
.sankey-prefix-filter {
    flex: 1 1 18rem;
    min-width: 12rem;
}
.sankey-prefix-filter input:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}
.sankey-prefix-filter.is-invalid input {
    border-color: #dc2626;
    color: #991b1b;
}
.sankey-prefix-filter.is-invalid input:focus-visible {
    outline-color: #dc2626;
}

/* Hop-limit slider in the Sankey section header. Inline label +
   range input that caps how many AS-hops from AS11537 the diagram
   shows. Slider pinned right reads "All". */
.sankey-hop-slider {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: 11px;
    color: var(--muted);
    user-select: none;
    cursor: pointer;
}
.sankey-hop-slider span {
    font-weight: 600;
    color: var(--fg);
    min-width: 5.5em;
    text-align: right;
    white-space: nowrap;
}
.sankey-hop-slider input[type="range"] {
    width: 90px;
    accent-color: var(--accent);
    cursor: pointer;
    /* Slider sliders typically sit flush against neighbors.
       Add a few px of right padding so the thumb has breathing
       room when this control is the rightmost item in a wrap row. */
    margin-right: 4px;
}

/* "How to read this" help modal — prose-only explanation of how
   flows / ribbons / colors are organised. Uses the project's light
   theme tokens (--fg, --muted, --accent) so the text reads cleanly
   against the white modal background. */
.sankey-help-modal { width: min(94vw, 720px); max-height: 86vh; }
.sankey-help-body {
    padding: 1em 1.4em 1.4em;
    overflow: auto;
    color: var(--fg);
}
.sankey-help-body h4 {
    margin: 1.1em 0 0.4em;
    font-size: 0.98em;
    font-weight: 700;
    color: var(--accent);
}
.sankey-help-body h4:first-child { margin-top: 0; }
.sankey-help-body p,
.sankey-help-body ul {
    margin: 0 0 0.7em;
    color: var(--fg);
    font-size: 0.94em;
    line-height: 1.55;
}
.sankey-help-body ul { padding-left: 1.3em; }
.sankey-help-body li { margin-bottom: 0.4em; }
.sankey-help-body code {
    background: var(--bg);
    border: 1px solid var(--line);
    padding: 0 5px;
    border-radius: 3px;
    font-size: 0.88em;
}
.sankey-help-body strong { color: #1c1f26; font-weight: 700; }

/* Per-flow selection state. Earlier iterations tried "dim hard"
   (2-5% opacity on non-selected) but a thin selected flow couldn't
   visually compete with thick dimmed neighbors at shared nodes.
   Switched to fully hiding everything that isn't the selected flow:
   non-selected ribbons get visibility:hidden (so they don't draw
   but the SVG layout stays put), and off-path AS nodes hide too,
   leaving the path's AS nodes and its single chain of ribbons
   visible. On-path nodes get an outline so the path's stops are
   easy to read. */
/* Selection-disabled banner above the SVG. Shown when the diagram
   has too many distinct paths to highlight interactively (the
   threshold lives in renderSankeyDAG). */
.sankey-selection-disabled-notice {
    background: #fef9c3;
    border-left: 4px solid #ca8a04;
    color: #713f12;
    padding: 8px 12px;
    border-radius: 4px;
    margin: 0 0 8px 0;
    font-size: 13px;
    line-height: 1.45;
}
.sankey-selection-disabled-notice strong { color: #713f12; }
.sankey-selection-disabled-notice em {
    font-style: normal;
    font-weight: 600;
}
/* In selection-disabled mode, remove ribbons and AS bars from pointer
   hit-testing entirely. That prevents the browser and JS from doing
   expensive "what route set was clicked?" work across thousands of
   ribbons when selection is intentionally unavailable. */
svg.sankey.selection-disabled .sankey-segment,
svg.sankey.selection-disabled .sankey-node {
    cursor: default;
    pointer-events: none;
}
.sankey-segment {
    cursor: pointer;
    /* No transition: with thousands of ribbons in a large topology,
       a fill-opacity / opacity transition fires a transitionend per
       ribbon on every selection change, which queued enough events
       to make the page unresponsive. The dimmed state is
       visibility:hidden anyway, so there's nothing meaningful to
       animate. */
}
/* Non-selected ribbons live in linksGBase (selected ones move to
   linksGTop). When a selection is active, applySelection sets
   visibility:hidden on linksGBase directly — one DOM mutation
   hides every non-selected ribbon via inheritance, with no
   per-element style recalc or paint invalidation. The earlier
   approach used `svg.has-selection .sankey-segment:not(.is-selected)`
   which read like a single rule but still forced the browser to
   evaluate the selector against every .sankey-segment, putting an
   O(N) cost back in the style engine. */
.sankey-segment.is-selected {
    fill-opacity: 0.86 !important;
    stroke: #cdd6f4;
    stroke-width: 1.5;
}

svg.sankey.has-selection .sankey-node:not(.is-on-path),
svg.sankey.has-selection .sankey-node-labels:not(.is-on-path) {
    visibility: hidden;
}
svg.sankey .sankey-node.is-on-path rect,
svg.sankey .sankey-node.is-on-path .sankey-node-bar {
    stroke: #cdd6f4;
    stroke-width: 1.5;
}

/* Component C: during an AS-context highlight, fade the fill of every
   on-path transit bar EXCEPT the AS the highlight is anchored on, so
   route ribbons read as flowing behind the bars rather than slamming
   into a hard junction at each one. Only the fill is faded — the
   outline (a separate stroke property) and the label stay crisp, so
   the transit ASes remain identifiable. The anchored AS keeps full
   opacity via :not(.is-context-as). */
svg.sankey.has-as-context .sankey-node.is-on-path:not(.is-context-as) rect {
    fill-opacity: 0.4;
}

/* Hover spine (Component A): when a ribbon is hovered, every AS it
   truly traverses gets its whole bar lit — solid fill + a bold blue
   outline — so the route's real path is unmistakable. !important
   mirrors the .is-selected ribbon rule above and guarantees this beats
   the higher-specificity Component-C fade so a hovered transit bar
   snaps back to full opacity. */
svg.sankey .sankey-node.is-ribbon-hover rect {
    fill-opacity: 1 !important;
    stroke: #1d4ed8 !important;
    stroke-width: 2.5 !important;
}
/* Bold the labels of the hovered ribbon's ASes to match the bar
   emphasis (the label group carries .sankey-node-labels, not
   .sankey-node, so it needs its own selector). */
svg.sankey .sankey-node-labels.is-ribbon-hover text {
    font-weight: 700;
}

/* About modal — keyed off the same light palette as the rest of
   the page (white panel, blue accent, muted grey text) so it
   matches the live page rather than the dark dialog used in the
   ASPA-graph service. Triggered by the "About this report" link in
   the header. */
.about-modal {
    position: fixed;
    inset: 0;
    z-index: 1000;
    background: rgba(15, 23, 42, 0.45);
    display: flex;
    align-items: flex-start;
    justify-content: center;
    padding: 4vh 16px;
    overflow-y: auto;
}
.about-modal[hidden] { display: none; }
.about-modal-content {
    position: relative;
    background: var(--panel);
    color: var(--fg);
    border: 1px solid var(--line);
    border-radius: 8px;
    box-shadow: 0 12px 40px rgba(0, 0, 0, 0.18);
    max-width: 760px;
    width: 100%;
    padding: 32px 36px 28px;
    line-height: 1.55;
}
.about-modal-content h2 {
    margin: 0 0 12px;
    font-size: 22px;
    color: var(--accent);
}
.about-modal-content h3 {
    margin: 22px 0 6px;
    font-size: 15px;
    color: var(--fg);
    border-bottom: 1px solid var(--line);
    padding-bottom: 4px;
}
.about-modal-content p { margin: 8px 0; }
.about-modal-content code {
    background: var(--bg);
    border: 1px solid var(--line);
    border-radius: 3px;
    padding: 0 4px;
    font-size: 0.92em;
}
.about-modal-content a { color: var(--accent); text-decoration: none; }
.about-modal-content a:hover { text-decoration: underline; }
.about-modal-content ul { margin: 6px 0 12px 18px; padding: 0; }
.about-modal-content ul ul { margin-top: 4px; margin-bottom: 4px; }
.about-modal-content li { margin: 4px 0; }
.about-modal-close {
    position: absolute;
    top: 10px;
    right: 14px;
    border: none;
    background: none;
    font-size: 26px;
    line-height: 1;
    color: var(--muted);
    cursor: pointer;
    padding: 4px 8px;
    border-radius: 4px;
}
.about-modal-close:hover { color: var(--fg); background: var(--bg); }
.about-logos {
    display: flex;
    align-items: center;
    gap: 28px;
    margin: 6px 0 18px;
    padding: 14px 18px;
    /* Dark backdrop for the Internet2 wordmark (which is the
       white-on-transparent variant from internet2.edu). The CAIDA
       logo gets its own white tile via .logo-caida below so its
       grey-on-white image still has contrast. */
    background: var(--accent);
    border-radius: 6px;
}
.about-logos a { display: inline-flex; align-items: center; }
.about-logos img { height: 36px; width: auto; display: block; }
.about-logos .logo-i2 { height: 30px; }
.about-logos .logo-caida {
    height: 48px;
    background: #ffffff;
    padding: 4px 8px;
    border-radius: 4px;
}
.about-lead { font-size: 14px; }
.about-nsf-notice {
    margin-top: 22px;
    padding: 12px 14px;
    background: var(--bg);
    border-left: 3px solid var(--accent);
    border-radius: 4px;
    font-size: 12.5px;
    color: var(--muted);
    line-height: 1.5;
}

/* Data Sources modal — reuses .about-modal{,-content} for the
   overlay/box and adds a compact, monospace-ish freshness table. */
.data-sources-table {
    width: 100%;
    border-collapse: collapse;
    margin-top: 12px;
    font-size: 13px;
}
.data-sources-table th,
.data-sources-table td {
    text-align: left;
    padding: 8px 10px;
    border-bottom: 1px solid var(--border);
    vertical-align: top;
}
.data-sources-table th {
    color: var(--muted);
    font-weight: 600;
    font-size: 11.5px;
    text-transform: uppercase;
    letter-spacing: 0.04em;
}
.data-sources-table th.num,
.data-sources-table td.num {
    text-align: right;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}
.data-sources-table .ds-rel {
    color: var(--muted);
    font-size: 11.5px;
}

.sankey-flow-readout {
    display: flex;
    align-items: center;
    gap: 0.6em;
    padding: 0.4em 0.8em;
    margin: 0.4em 0;
    background: #1e1e2e;
    border: 1px solid #45475a;
    border-radius: 4px;
    font-size: 0.9em;
    color: #cdd6f4;
    flex-wrap: wrap;
}
.flow-readout-label {
    color: #a6adc8;
    text-transform: uppercase;
    font-size: 0.78em;
    letter-spacing: 0.5px;
}
.flow-readout-path {
    font-family: monospace;
    color: #cdd6f4;
}
.flow-readout-as {
    color: #89b4fa;
    cursor: pointer;
    text-decoration: none;
}
.flow-readout-as:hover { text-decoration: underline; }
.flow-readout-arrow { color: #585b70; margin: 0 0.15em; }
.flow-readout-status {
    padding: 1px 8px;
    border-radius: 10px;
    font-size: 0.78em;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    font-weight: 600;
}
.flow-readout-status.accepted { background: #a6e3a133; color: #a6e3a1; }
.flow-readout-status.rejected { background: #f38ba833; color: #f38ba8; }
.flow-readout-prefixes { color: #a6adc8; }
/* Rejected-route coverage summary on the readout strip — at-a-glance
   "X of Y prefixes also reach AS11537 via accepted routes" so an
   operator can tell apart redundant rejections from net loss
   without opening the modal. */
.flow-readout-coverage { color: #a6adc8; font-size: 0.88em; }
.flow-readout-uncovered { color: #f38ba8; font-weight: 600; }
/* Clickable variant in the readout strip — pops the per-AS-path
   prefix table. Uses the readout strip's own dark-theme palette
   (light-blue accent on transparent, matching .flow-readout-as)
   so the button reads against the dark #1e1e2e strip background. */
.flow-readout-prefixes-btn {
    background: transparent;
    border: 1px solid #45475a;
    color: #89b4fa;
    padding: 1px 8px;
    border-radius: 10px;
    cursor: pointer;
    font: inherit;
    font-size: 0.92em;
    text-decoration: underline;
    text-decoration-color: rgba(137, 180, 250, 0.4);
    text-underline-offset: 2px;
}
.flow-readout-prefixes-btn:hover {
    background: #313244;
    color: #cdd6f4;
    border-color: #585b70;
    text-decoration-color: #cdd6f4;
}
.flow-readout-prefixes-btn:focus-visible {
    outline: 2px solid #89b4fa;
    outline-offset: 2px;
}

/* Per-AS-path prefix-listing modal — opened from the readout
   strip's clickable prefix count. Header shows the AS-path and
   accepted/rejected status; body is a two-column table of
   (prefix, holder). */
.flow-pfx-modal {
    width: min(92vw, 720px);
    height: min(720px, 86vh);
}
.flow-pfx-modal .route-modal-body {
    display: flex;
    flex-direction: column;
    min-height: 0;
    overflow: hidden;
}
.flow-pfx-modal .route-modal-scroll {
    flex: 1;
    min-height: 0;
}
.flow-pfx-title {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.5em;
    font-size: 0.94em;
}
.flow-pfx-title-label {
    color: var(--muted, #5b6472);
    font-size: 0.82em;
    text-transform: uppercase;
    letter-spacing: 0.5px;
}
.flow-pfx-title-path {
    font-family: monospace;
    color: var(--fg, #1c1f26);
}
.flow-pfx-title-count {
    color: var(--muted, #5b6472);
    font-size: 0.88em;
}
.flow-pfx-filterbar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px;
    margin: 0 0 8px;
}
.flow-pfx-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 0.92em;
}
.flow-pfx-table th {
    text-align: left;
    padding: 0.4em 0.7em;
    background: var(--bg, #fafbfc);
    border-bottom: 1px solid var(--line, #e3e6eb);
    color: var(--muted, #5b6472);
    font-weight: 600;
    font-size: 0.82em;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    position: sticky;
    top: 0;
}
.flow-pfx-table td {
    padding: 0.35em 0.7em;
    border-bottom: 1px solid var(--zebra, #eef1f5);
    vertical-align: top;
    color: var(--fg, #1c1f26);
}
/* Section header row inserted between flows in the aggregated
   prefix list. Shows the AS-path as it changes, plus the
   flow's kind chip and prefix count. */
.flow-pfx-section-row td {
    padding: 0.55em 0.7em 0.35em;
    background: var(--bg, #fafbfc);
    border-top: 1px solid var(--line, #e3e6eb);
    border-bottom: 1px solid var(--line, #e3e6eb);
    font-size: 0.92em;
    color: var(--muted, #5b6472);
    position: sticky;
    top: 2.1em;
    z-index: 1;
}
.flow-pfx-section-kind {
    display: inline-block;
    padding: 0.1em 0.55em;
    border-radius: 10px;
    font-size: 0.78em;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.4px;
    margin-right: 8px;
}
.flow-pfx-section-kind.accepted {
    background: rgba(26, 79, 139, 0.12);
    color: var(--accent, #1a4f8b);
}
.flow-pfx-section-kind.rejected {
    background: rgba(192, 57, 43, 0.12);
    color: var(--rejected, #c0392b);
}
.flow-pfx-section-path {
    font-family: ui-monospace, "SF Mono", Menlo, monospace;
    font-size: 0.95em;
    color: var(--fg, #1c1f26);
    margin-right: 10px;
}
.flow-pfx-section-count {
    font-size: 0.85em;
    color: var(--muted, #5b6472);
}
/* "Received via" column for the aggregated prefix list.
   Per-prefix list of peering-session badges (peer IP + family +
   router location). Compact so multi-session prefixes still
   read on one or two rows; family colour matches the matrix
   view (blue for v4, teal for v6). */
.flow-pfx-cell-peers {
    font-family: ui-monospace, "SF Mono", Menlo, monospace;
    font-size: 0.85em;
    color: var(--fg, #1c1f26);
}
.flow-pfx-peer-badge {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 1px 6px;
    margin: 1px 4px 1px 0;
    border-radius: 4px;
    background: var(--bg, #fafbfc);
    border: 1px solid var(--line, #e3e6eb);
}
.flow-pfx-peer-fam {
    display: inline-block;
    font-size: 0.78em;
    font-weight: 700;
    text-transform: uppercase;
    padding: 0 4px;
    border-radius: 3px;
    color: #fff;
}
.flow-pfx-peer-v4 .flow-pfx-peer-fam { background: #1a4f8b; }
.flow-pfx-peer-v6 .flow-pfx-peer-fam { background: #0d8a8a; }
.flow-pfx-peer-ip {
    color: var(--fg, #1c1f26);
}
.flow-pfx-peer-loc {
    color: var(--muted, #5b6472);
    font-size: 0.92em;
}
.flow-pfx-peer-loc::before {
    content: "@ ";
    color: var(--muted, #5b6472);
}
.flow-pfx-cell-pfx {
    font-family: monospace;
    white-space: nowrap;
}
.flow-pfx-cell-holder {
    color: var(--muted, #5b6472);
    word-break: break-word;
}
/* "Covered by" column for rejected-route prefix tables. Distinguishes
   covered (=  prefix is also accepted via another path on the
   diagram) from uncovered (= net loss, only this rejected path
   carries the prefix). */
.flow-pfx-cell-cover {
    color: var(--fg, #1c1f26);
    font-size: 0.92em;
}
.flow-pfx-cell-cover code {
    background: var(--bg, #fafbfc);
    border: 1px solid var(--line, #e3e6eb);
    padding: 0 4px;
    border-radius: 3px;
    font-size: 0.92em;
}
.flow-pfx-cell-uncovered {
    color: var(--rejected, #c0392b);
    font-weight: 600;
}
.flow-pfx-cover-as {
    color: var(--accent, #1a4f8b);
    font-family: monospace;
    font-weight: 600;
}
.flow-pfx-title-coverage {
    color: var(--muted, #5b6472);
    font-size: 0.88em;
    padding: 1px 8px;
    border-radius: 10px;
    background: var(--bg, #fafbfc);
    border: 1px solid var(--line, #e3e6eb);
}
.flow-readout-clear {
    margin-left: auto;
    background: transparent;
    border: 1px solid #45475a;
    color: #a6adc8;
    width: 22px;
    height: 22px;
    border-radius: 11px;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 14px;
    line-height: 1;
}
.flow-readout-clear:hover {
    background: #313244;
    color: #cdd6f4;
}

/* "Focus on selection" / "Back to full graph" pills in the readout
   strip. The first of them carries margin-left:auto so the
   focus/back/✕ cluster floats to the right edge together (the ✕'s
   own auto-margin then has no free space left to act on). */
.flow-readout-focus,
.flow-readout-back {
    margin-left: auto;
    background: transparent;
    border: 1px solid #45475a;
    color: #cdd6f4;
    height: 22px;
    padding: 2px 10px;
    border-radius: 11px;
    cursor: pointer;
    font: inherit;
    font-size: 12px;
    line-height: 1;
    white-space: nowrap;
}
.flow-readout-focus + .flow-readout-back,
.flow-readout-focus ~ .flow-readout-clear,
.flow-readout-back ~ .flow-readout-clear {
    margin-left: 6px;
}
.flow-readout-focus {
    border-color: #89b4fa;
    color: #89b4fa;
}
.flow-readout-focus:hover {
    background: #89b4fa;
    color: #1e1e2e;
}
.flow-readout-back:hover {
    background: #313244;
    color: #cdd6f4;
}

/* AS-bar click popover — two-button menu offering "highlight every
   flow through this AS" or "list flows through this AS". Position is
   set inline at the click location; sizing and chrome live here.
   Floats above the SVG via fixed positioning so it isn't clipped by
   the panel's overflow. */
.sankey-as-popover {
    position: fixed;
    z-index: 1000;
    background: #1e1e2e;
    border: 1px solid #45475a;
    border-radius: 6px;
    box-shadow: 0 6px 18px rgba(0, 0, 0, 0.45);
    padding: 0.3em 0;
    min-width: 220px;
    color: #cdd6f4;
    font-size: 0.92em;
}
.sankey-as-popover-header {
    padding: 0.45em 0.9em 0.3em;
    color: #a6adc8;
    font-size: 0.82em;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    font-weight: 600;
}
.sankey-as-popover-holder {
    padding: 0 0.9em 0.35em;
    color: #cdd6f4;
    font-size: 0.92em;
    line-height: 1.25;
}
.sankey-as-popover-links {
    padding: 0 0.9em 0.45em;
    border-bottom: 1px solid #313244;
    font-size: 0.85em;
}
.sankey-as-popover-links a {
    color: #89b4fa;
    text-decoration: none;
}
.sankey-as-popover-links a:hover {
    text-decoration: underline;
}
/* Ribbon picker variant: lay out the header text and the close X on
   a flex row so the dismiss button anchors to the right edge. The
   AS-action popover sheds the header border so the holder/links
   meta rows can flow as one block, but the ribbon picker has no
   such meta rows and needs the separator to remain. */
.sankey-ribbon-picker .sankey-as-popover-header {
    display: flex;
    align-items: center;
    gap: 0.5em;
    padding-bottom: 0.45em;
    border-bottom: 1px solid #313244;
}
.sankey-ribbon-picker .ribbon-pick-header-text {
    flex: 1 1 auto;
    min-width: 0;
}
.sankey-ribbon-picker .ribbon-pick-close {
    background: transparent;
    border: none;
    color: #a6adc8;
    cursor: pointer;
    flex: 0 0 auto;
    font-size: 1.6em;
    line-height: 1;
    padding: 0 4px;
    text-transform: none;
    letter-spacing: 0;
}
.sankey-ribbon-picker .ribbon-pick-close:hover {
    color: #cdd6f4;
}
.sankey-ribbon-picker .ribbon-pick-close:focus-visible {
    outline: 2px solid #89b4fa;
    outline-offset: 2px;
}
.sankey-as-popover-btn {
    display: block;
    width: 100%;
    text-align: left;
    background: transparent;
    border: none;
    color: #cdd6f4;
    padding: 0.5em 0.9em;
    font: inherit;
    cursor: pointer;
}
.sankey-as-popover-btn:hover {
    background: #313244;
    color: #f5c2e7;
}
.sankey-as-popover-icon {
    color: #6c7086;
    margin-right: 0.4em;
}

/* Ribbon picker popover. Same dark-theme chrome as the AS-action
   popover, but rows include a colour swatch and a one-line
   summary (kind / full path / prefix count), plus regex filtering
   and expandable prefixes, so the user can identify the right
   ribbon when several stack or fan across the click point. */
.sankey-ribbon-picker {
    min-width: 380px;
    max-width: min(760px, calc(100vw - 16px));
}
.sankey-ribbon-picker .ribbon-pick-filter {
    padding: 0.45em 0.75em 0.5em;
    border-bottom: 1px solid #313244;
}
.sankey-ribbon-picker .ribbon-pick-filter-label {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    align-items: center;
    gap: 0.55em;
    color: #a6adc8;
    font-size: 0.82em;
}
.sankey-ribbon-picker .ribbon-pick-filter input {
    width: 100%;
    min-width: 0;
    border: 1px solid #45475a;
    border-radius: 4px;
    background: #11111b;
    color: #cdd6f4;
    padding: 0.32em 0.45em;
    font: inherit;
}
.sankey-ribbon-picker .ribbon-pick-filter input:focus {
    outline: 2px solid #89b4fa;
    outline-offset: 1px;
}
.sankey-ribbon-picker .ribbon-pick-filter input.is-invalid {
    border-color: #f38ba8;
    outline-color: #f38ba8;
}
.sankey-ribbon-picker .ribbon-pick-filter-status {
    margin-top: 0.35em;
    color: #6c7086;
    font-size: 0.78em;
}
.sankey-ribbon-picker .ribbon-pick-list {
    max-height: min(62vh, 560px);
    overflow: auto;
}
.sankey-ribbon-picker .ribbon-pick-row {
    border-bottom: 1px solid rgba(69, 71, 90, 0.55);
}
.sankey-ribbon-picker .ribbon-pick-row[hidden],
.sankey-ribbon-picker .ribbon-pick-row.is-filtered-out,
.sankey-ribbon-picker .ribbon-pick-empty[hidden] {
    display: none !important;
}
.sankey-ribbon-picker .ribbon-pick-row:last-child {
    border-bottom: none;
}
.sankey-ribbon-picker .ribbon-pick-btn {
    display: flex;
    align-items: center;
    gap: 0.5em;
    font-size: 0.88em;
    padding: 0.4em 0.9em;
    white-space: nowrap;
}
.sankey-ribbon-picker .ribbon-pick-swatch {
    display: inline-block;
    width: 14px;
    height: 14px;
    border-radius: 2px;
    flex: 0 0 auto;
    border: 1px solid #45475a;
}
.sankey-ribbon-picker .ribbon-pick-kind {
    text-transform: uppercase;
    font-size: 0.75em;
    font-weight: 700;
    letter-spacing: 0.06em;
    padding: 0 4px;
    border-radius: 3px;
}
.sankey-ribbon-picker .ribbon-pick-acc { background: #2a3a2c; color: #a6e3a1; }
.sankey-ribbon-picker .ribbon-pick-rej { background: #3a2a2a; color: #f38ba8; }
.sankey-ribbon-picker .ribbon-pick-path {
    color: #cdd6f4;
    font-family: ui-monospace, "SF Mono", Menlo, monospace;
    font-size: 0.95em;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
}
.sankey-ribbon-picker .ribbon-pick-count {
    margin-left: auto;
    color: #a6adc8;
    font-size: 0.8em;
    flex: 0 0 auto;
}
.sankey-ribbon-picker .ribbon-pick-prefixes {
    padding: 0 0.9em 0.45em 2.7em;
    color: #a6adc8;
    font-size: 0.8em;
}
.sankey-ribbon-picker .ribbon-pick-prefixes summary {
    cursor: pointer;
    width: fit-content;
}
.sankey-ribbon-picker .ribbon-pick-prefixes summary:hover {
    color: #cdd6f4;
}
.sankey-ribbon-picker .ribbon-pick-prefix-list {
    margin: 0.4em 0 0;
    max-height: 12rem;
    overflow: auto;
    color: #cdd6f4;
    background: #11111b;
    border: 1px solid #313244;
    border-radius: 4px;
    padding: 0.55em 0.7em;
    font: 0.95em/1.45 ui-monospace, "SF Mono", Menlo, monospace;
}
.sankey-ribbon-picker .ribbon-pick-prefix-row {
    display: flex;
    gap: 1.2em;
    align-items: baseline;
}
.sankey-ribbon-picker .ribbon-pick-prefix-pfx {
    flex: 0 0 auto;
    white-space: nowrap;
}
.sankey-ribbon-picker .ribbon-pick-prefix-holder {
    flex: 1 1 auto;
    color: #a6adc8;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.sankey-ribbon-picker .ribbon-pick-empty {
    padding: 0.75em 0.9em;
    color: #a6adc8;
    font-size: 0.85em;
}
/* Hover-halo applied to every ribbon segment of the flow whose
   picker row the cursor is on. A bright stroke makes the path
   pop without otherwise altering the Sankey, so the user can
   confirm what they're about to pick before clicking. */
.sankey-segment.ribbon-pick-halo {
    stroke: #ffd166;
    stroke-width: 2;
    stroke-opacity: 0.95;
    paint-order: stroke fill;
}

/* Flow table modal — replaces the per-prefix route table. One row
   per flow; click the row to close the modal and select that flow
   in the Sankey behind it. */
.flow-modal { width: min(96vw, 1100px); max-height: 86vh; }
.flow-modal .flow-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 0.92em;
}
.flow-modal .flow-table th {
    text-align: left;
    padding: 0.4em 0.6em;
    border-bottom: 1px solid #45475a;
    color: #a6adc8;
    font-weight: 600;
    font-size: 0.85em;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    position: sticky;
    top: 0;
    background: #1e1e2e;
}
.flow-modal .flow-table td {
    padding: 0.4em 0.6em;
    border-bottom: 1px solid #313244;
    vertical-align: top;
}
.flow-modal .flow-row { cursor: pointer; }
.flow-modal .flow-row:hover { background: #313244; }
.flow-swatch {
    display: inline-block;
    width: 14px;
    height: 14px;
    border-radius: 3px;
    vertical-align: middle;
}
.flow-status {
    padding: 1px 8px;
    border-radius: 10px;
    font-size: 0.75em;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    font-weight: 600;
}
.flow-status.accepted { background: #a6e3a133; color: #a6e3a1; }
.flow-status.rejected { background: #f38ba833; color: #f38ba8; }
.flow-table-as {
    color: #89b4fa;
    cursor: pointer;
    text-decoration: none;
    font-family: monospace;
}
.flow-table-as:hover { text-decoration: underline; }
.flow-table-arrow { color: #585b70; margin: 0 0.2em; }
.flow-pfx-line {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.3em;
    max-width: 60ch;
}
.flow-pfx-line.flow-pfx-expanded {
    max-width: none;
    max-height: 16em;
    overflow: auto;
    border: 1px solid #45475a;
    border-radius: 3px;
    padding: 0.4em;
}
.flow-pfx-count {
    font-weight: 600;
    color: #cdd6f4;
    margin-right: 0.4em;
}
.flow-prefix-pill {
    background: #313244;
    color: #cdd6f4;
    padding: 1px 6px;
    border-radius: 3px;
    font-family: monospace;
    font-size: 0.85em;
}
.flow-prefix-expand {
    background: transparent;
    border: 1px solid #45475a;
    color: #89b4fa;
    padding: 1px 8px;
    border-radius: 10px;
    cursor: pointer;
    font-size: 0.78em;
}
.flow-prefix-expand:hover {
    background: #313244;
}
.flow-show-btn {
    background: transparent;
    border: 1px solid #45475a;
    color: #89b4fa;
    padding: 2px 10px;
    border-radius: 3px;
    cursor: pointer;
    font-size: 0.85em;
    white-space: nowrap;
}
.flow-show-btn:hover {
    background: #313244;
    color: #cdd6f4;
}
