/**
 * Bottleneck Insights Explorer Styles
 */

/* ─── Design tokens ───────────────────────────────────────────────
 * Single source of truth for the module's accent, severity signals and
 * shared surfaces. The brand accent is indigo (#4338ca); selection state
 * uses it everywhere so "which card is selected" never competes with the
 * severity left-strip. Severity colours are the desaturated Tailwind set
 * already used by the filter chips, so the strip and the chip agree. */
:root {
    --ct-accent: #4338ca;
    --ct-accent-strong: #3730a3;
    --ct-accent-soft: #eef2ff;
    --ct-accent-border: #c7d2fe;
    --ct-accent-wash: linear-gradient(180deg, #ffffff 0%, #f5f3ff 100%);
    --ct-accent-tint: rgba(67, 56, 202, 0.16);
    --ct-accent-shadow: 0 10px 24px -8px rgba(67, 56, 202, 0.24);

    --ct-sev-high: #ef4444;
    --ct-sev-medium: #f59e0b;
    --ct-sev-low: #3b82f6;

    /* Strong easing curves — the built-in CSS easings feel weak. */
    /* Leanmote's action blue (custom.css --lm-primary-light). The indigo above
     * is the accent of the DATA — cells, chips, heat — while a filled BUTTON in
     * this product is this blue. */
    --ct-btn-blue: #419fff;
    --ct-btn-blue-ink: #1668cc;
    --ct-btn-blue-soft: #eaf4ff;
    --ct-btn-blue-border: #b9dbff;

    --ct-ease-out: cubic-bezier(0.23, 1, 0.32, 1);
    --ct-ease-in-out: cubic-bezier(0.77, 0, 0.175, 1);
}

/* Data-heavy values (KPIs, counts, drag cost) read cleaner with
 * fixed-width figures — digits stop jiggling as numbers update. */
.ct-feed-metric__value,
.ct-header-count,
.ct-type-filter__count,
.ct-origin-filter__count,
.ct-insight-card__drag-cost,
/* Every use of the metric chip carries a figure — the composite's
   per-metric captions, and the single-metric anomaly's delta. */
.ct-metric-badge {
    font-variant-numeric: tabular-nums;
    font-feature-settings: "tnum" 1;
}

/* RD-1916: view-switch loading overlay.
 *
 * Pinned to the viewport (position: fixed) but sized by JS via
 * left/top/width/height inline styles set in showViewSwitchOverlay()
 * so the overlay covers ONLY the main content area (#dashboard-container)
 * — the sidebar stays interactive and the spinner card stays centered
 * within the visible content area. Fallback `inset: 0` covers the full
 * viewport if for some reason JS hasn't sized the overlay yet.
 * Pointer-events live on the overlay so stale rows underneath can't be
 * clicked. The hidden attribute drives display:none — JS toggles it
 * via .hidden=true/false. */
.ct-view-switch-overlay {
    position: fixed;
    inset: 0;
    z-index: 1080;
    background: rgba(248, 250, 252, 0.65);
    -webkit-backdrop-filter: blur(2px);
    backdrop-filter: blur(2px);
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: wait;
    animation: ct-view-switch-overlay-in 120ms ease-out;
}

.ct-view-switch-overlay[hidden] {
    display: none;
}

.ct-view-switch-overlay__card {
    background: #ffffff;
    border: 1px solid #e2e8f0;
    border-radius: 12px;
    padding: 14px 22px;
    display: flex;
    align-items: center;
    gap: 12px;
    box-shadow: 0 10px 30px rgba(15, 23, 42, 0.10);
    color: #334155;
    font-weight: 600;
    font-size: 0.95rem;
}

.ct-view-switch-overlay__spinner {
    width: 22px;
    height: 22px;
    border: 2.5px solid #cbd5e1;
    border-top-color: #419FFF;
    border-radius: 50%;
    animation: ct-view-switch-overlay-spin 0.75s linear infinite;
    flex-shrink: 0;
}

@keyframes ct-view-switch-overlay-in {
    from { opacity: 0; }
    to { opacity: 1; }
}

@keyframes ct-view-switch-overlay-spin {
    to { transform: rotate(360deg); }
}

/* RD-2203: first-load "Thinking…" blocker. Server-rendered inside
 * #kt_app_content_container (which gets .ct-bi-blocking → position: relative)
 * ONLY when the per-role filter still has to run, so it never flashes on
 * cached visits. Absolute + inset:0 self-positions over the BI content area
 * (no JS sizing, no full-viewport flash) and blocks it; JS removes it the
 * moment the feed renders. */
.ct-bi-blocking {
    position: relative;
}

.ct-bi-thinking-block {
    position: absolute;
    inset: 0;
    z-index: 20;
    background: rgba(248, 250, 252, 0.65);
    -webkit-backdrop-filter: blur(2px);
    backdrop-filter: blur(2px);
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: wait;
    animation: ct-view-switch-overlay-in 120ms ease-out;
}

@media (prefers-reduced-motion: reduce) {
    .ct-view-switch-overlay,
    .ct-bi-thinking-block {
        animation: none;
    }
    .ct-view-switch-overlay__spinner {
        animation-duration: 1.4s;
    }
}

/* ─── Main Layout — 3-panel explorer row ──────────────────────────
 *
 * The 3 cards (Insights Detected, Evidence, Resolution Plan) must
 * collectively cap at the viewport so the "In Progress" and "Resolution
 * Archive" sections below are reachable via page scroll — not pushed
 * off by a tall left column.
 *
 * The Active Feed used to be a 3-col split-pane where each panel scrolled
 * independently inside a viewport-capped row. The new design (full-width
 * list → full-width detail) doesn't need that — let the explorer flow at
 * its natural height and use natural page scroll. The Cumulative Drag
 * widget stays at its natural size when stacked. */
#bottleneck-insights-explorer,
#bottleneck-insights-explorer-ip {
    margin-bottom: 3rem;
}

#bottleneck-insights-explorer .ct-cumulative-drag-card {
    flex: 0 0 auto;
}

/* Cards inside the explorer carry Metronic's `.h-xl-100` which resolves
 * to `height: 100%`. With the row no longer setting a height, that 100%
 * resolves against an auto-height parent and can collapse the card to
 * zero in some layouts. Force natural height — content sizes the card. */
#bottleneck-insights-explorer .card.card-flush.h-xl-100 {
    height: auto !important;
}

/* Backwards compatibility: the existing `.ct-insights-panel` / `.ct-scrollable-body`
   classes no longer drive scroll — the rules above do. Keep them as
   visual no-ops so the HTML can still carry them without breaking. */
.ct-scrollable-body,
.ct-insights-panel {
    min-height: 0;
}

/* ─── Header ─── */
/* ─── RD-2543: Actions Hub breadcrumb ──────────────────────────────
 * The hub's two sections (Insights, Alerts) used to share the tab row
 * with the three Insights states, so four items read as one flat level
 * of four. The path states the nesting instead: the current section is
 * the only interactive segment, its siblings live in the dropdown it
 * opens, and the tab row underneath belongs to Insights alone.
 *
 * It renders in the APP HEADER, continuing the page title rather than
 * repeating it — "<Org> - Actions Hub" was already the root, so a
 * separate breadcrumb row below would have printed the hub's name
 * twice. Consequences: the switcher is title-sized (the nav carries the
 * title's own fs-2 fw-bold and everything here sizes in `em` off it),
 * it inherits the title's colour so the line reads as one continuous
 * string, and it stays reachable while the page scrolls because the
 * header is fixed. Markup in
 * app/Views/layouts/app/components/actions-hub-switcher.php. */
.ct-hub-nav {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    min-width: 0;
    margin-left: 8px;
}

/* The Problem map is the hub's own landing view, so it shows no tail at all
   (RD-2608) — see actions-hub-switcher.php. */
.ct-hub-nav.is-hidden {
    display: none;
}

/* "<Org> - Actions Hub" is the breadcrumb ROOT: it goes back to the map
   (RD-2608). Kept as understated as a breadcrumb root should be — no
   permanent link colour, just a hover that says it is clickable. */
.lm-hub-home {
    cursor: pointer;
    transition: color 140ms var(--ct-ease-out);
}

@media (hover: hover) and (pointer: fine) {
    .lm-hub-home:hover {
        color: var(--ct-accent);
    }
}

.lm-hub-home:focus-visible {
    outline: 2px solid var(--ct-accent-border);
    outline-offset: 3px;
    border-radius: 6px;
}

/* The slash is the only thing saying "this is a path, and the next word is a
   place you can change" — at 0.8em/#cbd4e1 it disappeared next to an fs-2
   title. Bigger, thinner and darker: readable without competing with either
   side of it. */
.ct-hub-nav__sep {
    color: #a1acc4;
    font-size: 1.05em;
    font-weight: 300;
    line-height: 1;
}

.ct-hub-nav__switcher {
    display: inline-flex;
    min-width: 0;
}

/* Everything is inherited or in `em` on purpose: the section has to be
 * typographically indistinguishable from the title it continues, and
 * fs-2 is fluid (calc(1.275rem + 0.3vw)), so any hardcoded px would
 * drift from the title between breakpoints. The pill and the caret are
 * the only affordance that this segment is a control. */
.ct-hub-nav__current {
    display: inline-flex;
    align-items: center;
    gap: 0.34em;
    padding: 1px 7px 2px;
    border: 0;
    border-radius: 8px;
    background: transparent;
    font-size: inherit;
    font-weight: inherit;
    letter-spacing: inherit;
    line-height: inherit;
    color: inherit;
    cursor: pointer;
    transition: background-color 140ms var(--ct-ease-out), transform 140ms var(--ct-ease-out);
}

@media (hover: hover) and (pointer: fine) {
    .ct-hub-nav__current:hover {
        background: #eef1f7;
    }
}

.ct-hub-nav__current.show {
    background: #eef1f7;
}

.ct-hub-nav__current:focus-visible {
    outline: 2px solid var(--ct-accent-border);
    outline-offset: 2px;
}

.ct-hub-nav__current:active {
    transform: scale(0.98);
}

/* Every Font Awesome glyph in this block carries an `i` prefix on purpose:
 * style.bundle.css ships `i[class*=" fa-"] { color: var(--bs-text-muted) }`
 * at 0-1-1, which beats a bare class rule no matter which sheet loads last
 * — and it also blocks colour from cascading down into the glyph, so the
 * colour has to sit on the <i> itself, not on its wrapper. (Same trick as
 * the `i.ct-scope-ink--N` tints further down.) */
i.ct-hub-nav__caret {
    font-size: 0.42em;
    color: #9aa4b8;
    transition: transform 160ms var(--ct-ease-out), color 140ms var(--ct-ease-out);
}

.ct-hub-nav__current.show i.ct-hub-nav__caret {
    transform: rotate(180deg);
    color: #5c6882;
}

/* Two-layer menu: Popper owns the outer element's `transform` for
 * positioning, so the entry animation has to run on an inner card or the
 * two fight and the menu lands in the wrong place. The outer div is a
 * bare positioning shell; the card carries every visible surface. */
.ct-hub-nav__menu {
    /* Popper shifts the menu back inside the viewport, but on a phone a fixed
       320px next to a ~140px crumb offset leaves it flush against the edge —
       the clamp keeps a gutter on both sides instead. */
    width: min(320px, calc(100vw - 32px));
    padding: 0;
    margin-top: 6px !important;
    border: 0;
    background: transparent;
    box-shadow: none;
}

/* The font reset matters: the nav above carries the title's fluid fs-2 and
 * fw-bold, and the menu is a panel — not part of the title — so it has to
 * step back out of that scale before its own type rules apply. */
.ct-hub-nav__menu-card {
    padding: 7px;
    background: #ffffff;
    border: 1px solid #dfe6f1;
    border-radius: 14px;
    box-shadow: 0 18px 34px rgba(15, 23, 42, 0.12);
    font-size: 13.5px;
    font-weight: 500;
    letter-spacing: normal;
    line-height: 1.4;
    color: #1d2942;
    transform-origin: top left; /* scales out of the trigger, not out of its own centre */
}

.ct-hub-nav__menu.show .ct-hub-nav__menu-card {
    animation: ct-hub-nav-menu-in 150ms var(--ct-ease-out);
}

@keyframes ct-hub-nav-menu-in {
    from {
        opacity: 0;
        transform: scale(0.97) translateY(-3px);
    }
    to {
        opacity: 1;
        transform: none;
    }
}

.ct-hub-nav__menu-title {
    padding: 5px 9px 7px;
    font-size: 10.5px;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: #98a2b8;
}

.ct-hub-nav__option {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    width: 100%;
    padding: 9px 10px;
    border: 0;
    border-radius: 10px;
    background: transparent;
    text-align: left;
    cursor: pointer;
    transition: background-color 130ms var(--ct-ease-out);
}

.ct-hub-nav__option + .ct-hub-nav__option {
    margin-top: 2px;
}

@media (hover: hover) and (pointer: fine) {
    .ct-hub-nav__option:hover {
        background: #f4f6fb;
    }
}

.ct-hub-nav__option:focus-visible {
    outline: 2px solid var(--ct-accent-border);
    outline-offset: -1px;
}

.ct-hub-nav__option.is-selected {
    background: var(--ct-accent-soft);
}

.ct-hub-nav__option-icon {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 26px;
    height: 26px;
    margin-top: 1px;
    border-radius: 8px;
    background: #f1f4fa;
    font-size: 11px;
    transition: background-color 130ms var(--ct-ease-out);
}

.ct-hub-nav__option-icon i {
    color: #6b7690;
    transition: color 130ms var(--ct-ease-out);
}

.ct-hub-nav__option.is-selected .ct-hub-nav__option-icon {
    background: #ffffff;
}

.ct-hub-nav__option.is-selected .ct-hub-nav__option-icon i {
    color: var(--ct-accent);
}

.ct-hub-nav__option-text {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
}

.ct-hub-nav__option-label {
    font-size: 13.5px;
    font-weight: 600;
    letter-spacing: -0.008em;
    color: #1d2942;
}

.ct-hub-nav__option.is-selected .ct-hub-nav__option-label {
    color: var(--ct-accent-strong);
}

.ct-hub-nav__option-desc {
    font-size: 11.5px;
    font-weight: 500;
    line-height: 1.45;
    color: #7d879c;
    text-wrap: pretty;
}

i.ct-hub-nav__option-check {
    flex: 0 0 auto;
    margin-top: 7px;
    margin-left: auto;
    font-size: 10px;
    color: var(--ct-accent);
    opacity: 0;
}

.ct-hub-nav__option.is-selected i.ct-hub-nav__option-check {
    opacity: 1;
}

/* The feed-tab row is pure Bootstrap plumbing since RD-2759: the Insights
 * section has exactly one state (the Inbox), so there is nothing to pick.
 * The triggers stay in the DOM (the tab plugin needs them to keep exactly
 * one pane active, and the ?tab= deep links resolve against them) but the
 * row itself never paints. */
#ct-feed-tabs {
    display: none;
}

/* Alerts owns neither the scope bar nor the state tabs, so the whole sticky
 * header goes rather than just its contents: an empty .lm-sticky-tabs still
 * satisfies sticky-tabs.js's pin test and would paint its .lm-stuck shadow
 * as a stray hairline under the app header on every scroll. */
[data-hub-section="alerts"] .lm-sticky-tabs {
    display: none;
}

/* …which leaves the alerts panel flush against the top of the content area,
 * so it takes over the breathing room the tab row used to provide. */
[data-hub-section="alerts"] .tab-content {
    padding-top: 10px;
}

/* Motion here is pure affordance, so reduced-motion keeps the fade (it
 * explains where the menu came from) and drops the movement. */
@media (prefers-reduced-motion: reduce) {
    .ct-hub-nav__menu.show .ct-hub-nav__menu-card {
        animation: ct-hub-nav-menu-fade 120ms ease;
    }

    .ct-hub-nav__current:active {
        transform: none;
    }

    .ct-hub-nav__current.show .ct-hub-nav__caret {
        transition: none;
    }
}

@keyframes ct-hub-nav-menu-fade {
    from {
        opacity: 0;
    }
    to {
        opacity: 1;
    }
}

/* ─── RD-2327: view scope bar ──────────────────────────────────────
 * The selected Performance Delivery views render as removable chips in
 * a flat row pinned above the feed tabs — next to the content they
 * filter — replacing the old header-toolbar dropdown users didn't
 * discover. No card chrome: the chips carry the visual weight. The
 * "+ Add view" button (which anchors the .bi-source-toolbar__menu
 * picker below) sits before the chips so it keeps a stable position
 * as views get added. Cross-view state: visible on every feed tab
 * except Alerts (JS toggles .is-hidden). */
.ct-scope-bar {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 8px 10px;
    margin: 2px 0 14px;
}

.ct-scope-bar.is-hidden {
    display: none;
}

/* Quiet pill, not naked text: neutral surface tint, no border and no
 * hover so it can't be mistaken for a control (the indigo belongs to
 * the chips). Same 28px height as the Add button and the chips, so the
 * whole row reads as one family of rounded elements. */
.ct-scope-bar__label {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    min-height: 28px;
    margin-right: 2px;
    padding: 3px 13px 3px 11px;
    background: #f1f4fa;
    border-radius: 999px;
    font-size: 10.5px;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: #5c6882;
    white-space: nowrap;
    cursor: help; /* it carries the "what are these views?" tooltip */
}

.ct-scope-bar__label .ki-element-11 {
    font-size: 12px;
    color: var(--ct-accent); /* the PD mark is the label's single color accent */
}

.ct-scope-bar__chips {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 8px;
    min-width: 0;
}

.ct-scope-chip {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    padding: 4px 8px 4px 11px;
    background: var(--ct-accent-soft);
    border: 1px solid var(--ct-accent-border);
    border-radius: 999px;
    font-size: 13px;
    font-weight: 600;
    color: var(--ct-accent-strong);
}

.ct-scope-chip__dot {
    flex: 0 0 auto;
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: #8090aa; /* fallback when no ct-scope-dot--N class applies */
}

/* Stable per-view palette: index = the view's position in the picker
 * list (mod 8). The --dot variants paint the chip dots (background);
 * the --ink variants tint each card's ki-element-11 meta icon (the
 * Performance Delivery mark) via `color`. Same index rule everywhere
 * (PHP chips, JS re-renders, card meta) so the color link never
 * drifts. */
.ct-scope-dot--0 { background: #419fff; }
.ct-scope-dot--1 { background: #8b5cf6; }
.ct-scope-dot--2 { background: #14b8a6; }
.ct-scope-dot--3 { background: #f59e0b; }
.ct-scope-dot--4 { background: #ec4899; }
.ct-scope-dot--5 { background: #0ea5e9; }
.ct-scope-dot--6 { background: #84cc16; }
.ct-scope-dot--7 { background: #f97316; }

/* The `i` prefix lifts specificity to 0-1-1 so the per-view tint beats
 * .ct-insight-card__meta-icon's fixed indigo (0-1-0, defined later in
 * this file). */
i.ct-scope-ink--0 { color: #419fff; }
i.ct-scope-ink--1 { color: #8b5cf6; }
i.ct-scope-ink--2 { color: #14b8a6; }
i.ct-scope-ink--3 { color: #f59e0b; }
i.ct-scope-ink--4 { color: #ec4899; }
i.ct-scope-ink--5 { color: #0ea5e9; }
i.ct-scope-ink--6 { color: #84cc16; }
i.ct-scope-ink--7 { color: #f97316; }

.ct-scope-chip__count {
    padding: 0 6px;
    background: #ffffff;
    border: 1px solid var(--ct-accent-border);
    border-radius: 999px;
    font-size: 10.5px;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    color: var(--ct-accent);
}

.ct-scope-chip__shared {
    color: #7c87b8;
    font-size: 10px;
}

.ct-scope-chip__remove {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 18px;
    height: 18px;
    padding: 0;
    border: 0;
    border-radius: 50%;
    background: transparent;
    color: #8f9dd8;
    font-size: 10px;
    cursor: pointer;
    transition: background-color 0.13s var(--ct-ease-out), color 0.13s var(--ct-ease-out), transform 0.12s var(--ct-ease-out);
}

.ct-scope-chip__remove:hover {
    background: #dde4ff;
    color: var(--ct-accent-strong);
}

.ct-scope-chip__remove:active {
    transform: scale(0.94);
}

/* Plain element (no Bootstrap .btn class — the theme's .btn strips the
 * border and left this reading as bare text). Dashed pill visible at
 * rest so it affords "add" before any hover. */
.ct-scope-bar__add {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    min-height: 28px;
    padding: 3px 13px;
    border: 1.5px dashed #b6c3d8;
    border-radius: 999px;
    background: #ffffff;
    font-size: 12.5px;
    font-weight: 600;
    color: #5c6882;
    cursor: pointer;
    transition: border-color 0.15s var(--ct-ease-out), color 0.15s var(--ct-ease-out), background-color 0.15s var(--ct-ease-out), transform 0.12s var(--ct-ease-out);
}

.ct-scope-bar__add .fa-plus {
    font-size: 10px;
}

.ct-scope-bar__add:hover,
.ct-scope-bar__add:focus,
.ct-scope-bar__add.show {
    border-color: var(--ct-accent-border);
    color: var(--ct-accent);
    background: #f8f9ff;
}

.ct-scope-bar__add:focus-visible {
    outline: 2px solid var(--ct-accent-border);
    outline-offset: 2px;
}

.ct-scope-bar__add:active {
    transform: scale(0.97);
}

.bi-source-toolbar__menu {
    width: 312px;
    padding: 0;
    margin-top: 8px !important;
    border: 1px solid #d8e2f0;
    border-radius: 16px;
    box-shadow: 0 18px 34px rgba(15, 23, 42, 0.12);
    overflow: hidden;
}

.bi-source-toolbar__menu-header {
    padding: 16px 18px 14px;
    border-bottom: 1px solid #edf1f7;
    background: #ffffff;
}

.bi-source-toolbar__menu-title {
    font-size: 14px;
    font-weight: 700;
    color: #1f2a44;
}

.bi-source-toolbar__menu-copy {
    margin-top: 6px;
    font-size: 12px;
    line-height: 1.55;
    color: #7d8aa3;
}

.bi-source-toolbar__menu-list {
    max-height: 280px;
    overflow-y: auto;
    padding: 12px;
    background: #ffffff;
}

.bi-source-toolbar__option {
    display: flex;
    align-items: center;
    gap: 12px;
    min-height: 48px;
    margin-bottom: 6px;
    padding: 12px 14px;
    border-radius: 12px;
    border: 1px solid transparent;
    cursor: pointer;
    transition: background-color 0.15s ease, border-color 0.15s ease, box-shadow 0.15s ease;
}

.bi-source-toolbar__option:last-child {
    margin-bottom: 0;
}

.bi-source-toolbar__option:hover {
    background: #f8faff;
}

/* The JS toggles .is-selected on every render (general.js
   syncSourceDashboardOptionState / the scope-bar rebuild), so the row's own
   selected surface has to exist here — the descendant rules below only recolour
   its label and shared indicator. */
.bi-source-toolbar__option.is-selected {
    background: #f3f7ff;
    border-color: #d5e2ff;
    box-shadow: 0 0 0 1px rgba(213, 226, 255, 0.35);
}

.bi-source-toolbar__checkbox {
    margin: 0;
    flex: 0 0 auto;
    width: 20px;
    height: 20px;
    border-color: #c7d1e0;
    box-shadow: none;
    accent-color: #2f80ed;
}

.bi-source-toolbar__option-content {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    min-width: 0;
}

.bi-source-toolbar__option-label {
    font-size: 13px;
    font-weight: 700;
    color: #1f2a44;
}

.bi-source-toolbar__option.is-selected .bi-source-toolbar__option-label {
    color: #236ce5;
}

.bi-source-toolbar__shared-indicator {
    flex: 0 0 auto;
    color: #8a95a9;
}

.bi-source-toolbar__option.is-selected .bi-source-toolbar__shared-indicator {
    color: #7c879e;
}

@media (max-width: 991.98px) {
    .bi-source-toolbar__menu {
        width: min(320px, 100vw - 32px);
    }
}

.ct-header-label {
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: #64748b;
}

.ct-header-count {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 24px;
    height: 24px;
    padding: 0 6px;
    font-size: 12px;
    font-weight: 600;
    color: #fff;
    background-color: #419FFF;
    border-radius: 12px;
}

/* ─── Insight Cards ─── */
.ct-insight-card {
    position: relative;
    padding: 16px 18px;
    /* RD-2448: a bit more separation between cards. */
    margin-bottom: 12px;
    background: #fff;
    border: 1px solid #e2e8f0;
    border-left: 4px solid #e2e8f0;
    border-radius: 10px;
    cursor: pointer;
    box-shadow: 0 1px 2px rgba(15, 23, 42, 0.03);
    transition: border-color 0.18s ease, box-shadow 0.18s ease, background-color 0.18s ease, transform 0.18s ease;
}

/* No translate on hover: the list scroll container clips with overflow-y,
 * so lifting the first row would shave off its top border. A shadow + border
 * shift reads as elevation without the clip — and it's the right call for a
 * row hovered dozens of times per session. */
@media (hover: hover) and (pointer: fine) {
    .ct-insight-card:hover {
        border-color: #cbd5e1;
        box-shadow: 0 8px 20px -8px rgba(15, 23, 42, 0.12);
    }
}

.ct-insight-card.is-active {
    background-color: #f8fbff;
    border-color: #419FFF;
    border-left-width: 4px;
    box-shadow: 0 8px 22px rgba(65, 159, 255, 0.14);
}

.ct-insight-card[data-severity="high"] {
    border-left-color: #ff6b6b;
    background: #fff;
}

.ct-insight-card[data-severity="medium"] {
    border-left-color: #f5c542;
    background: #fff;
}

.ct-insight-card[data-severity="low"] {
    border-left-color: #419FFF;
    background: #fff;
}

.ct-insight-card.is-active[data-severity="high"] {
    border-color: #ff6b6b;
    background: linear-gradient(180deg, #fff 0%, #fff8f8 100%);
    box-shadow: 0 8px 22px rgba(255, 107, 107, 0.13);
}

.ct-insight-card.is-active[data-severity="medium"] {
    border-color: #f5c542;
    background: linear-gradient(180deg, #fff 0%, #fffcf1 100%);
    box-shadow: 0 8px 22px rgba(245, 197, 66, 0.14);
}

.ct-insight-card.is-active[data-severity="low"] {
    border-color: #419FFF;
    background: linear-gradient(180deg, #fff 0%, #f4f9ff 100%);
    box-shadow: 0 8px 22px rgba(65, 159, 255, 0.14);
}

/* RD-1885: read-only treatment for insights produced from a dashboard the
   current viewer does not own. Cards stay legible but the chrome desaturates
   so the user can scan ownership at a glance. */
.ct-insight-card--shared {
    background: #f8fafc;
}

.ct-insight-card--shared .ct-insight-card__title {
    color: #475569;
}

.ct-insight-card--shared.is-active {
    box-shadow: 0 4px 14px rgba(100, 116, 139, 0.16);
}

/* RD-2537: sits inline after the card title, so it needs to align to the text
   it trails rather than to a line of its own. `middle` centers it against the
   14px/700 title; the -1px lifts it off the optical baseline, which reads
   centered where the pure math reads low. `white-space: nowrap` keeps the pill
   from breaking apart when the title wraps right before it. */
.ct-insight-card__shared-badge {
    display: inline-flex;
    align-items: center;
    vertical-align: middle;
    position: relative;
    top: -1px;
    gap: 4px;
    margin-left: 6px;
    padding: 2px 8px;
    font-size: 10px;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    white-space: nowrap;
    color: #475569;
    background: #e2e8f0;
    border-radius: 10px;
    cursor: help;
}

.ct-insight-card__shared-badge i {
    font-size: 11px;
    color: #64748b;
}

.ct-insight-card__feedback--disabled {
    opacity: 0.45;
    cursor: not-allowed;
}

.ct-insight-card__feedback--disabled .ct-feedback-btn {
    cursor: not-allowed;
    pointer-events: none;
}

/* RD-1918: one-shot entry animation for the card that was just created
   by an agent approval. Slides in from above with a violet glow ring
   that fades after ~2s — long enough that the user clearly identifies
   the new row, short enough to not be distracting on subsequent
   filter changes. Plays on top of the persistent violet wash from
   `--from-agent` below. */
.ct-insight-card--just-created {
    animation: ct-insight-card-slide-in 0.6s cubic-bezier(0.25, 1, 0.4, 1) both,
               ct-insight-card-glow-pulse 2.2s ease-out both;
}

@keyframes ct-insight-card-slide-in {
    0%   { transform: translateY(-24px); opacity: 0; }
    100% { transform: translateY(0);     opacity: 1; }
}

@keyframes ct-insight-card-glow-pulse {
    0% {
        box-shadow: 0 0 0 0 rgba(124, 58, 237, 0.55),
                    0 10px 28px rgba(124, 58, 237, 0.30);
    }
    35% {
        box-shadow: 0 0 0 10px rgba(124, 58, 237, 0),
                    0 12px 32px rgba(124, 58, 237, 0.22);
    }
    100% {
        box-shadow: 0 1px 2px rgba(15, 23, 42, 0.03);
    }
}

@media (prefers-reduced-motion: reduce) {
    .ct-insight-card--just-created { animation: none; }
}

/* RD-1918: Research-Agent-created insights. Subtle violet wash on the
   card background + violet outline ties the row to the Research Agent
   chip + launcher, without competing with the severity left strip
   (which still paints high/medium/low through `[data-severity]`). The
   tint is intentionally soft — the analyst should notice "this is from
   the agent" at a glance, not feel the card is shouting.

   Specificity note: the severity rules use `.ct-insight-card[data-severity="…"]`
   which is (0,2,0). A bare `.ct-insight-card--from-agent` is (0,1,0) and
   loses the cascade on `background`. Doubling the class
   (`.ct-insight-card.ct-insight-card--from-agent`) bumps us to (0,2,0)
   so source order wins — these rules sit AFTER the severity block and
   override only the background/border, leaving the severity-driven
   left strip color untouched. Same trick applied to hover + active. */
.ct-insight-card.ct-insight-card--from-agent {
    background: #faf5ff;
    border-color: #e9d5ff;
}

.ct-insight-card.ct-insight-card--from-agent:hover {
    border-color: #d8b4fe;
    box-shadow: 0 8px 18px rgba(124, 58, 237, 0.10);
}

/* Active selection keeps the same violet identity but deepens the wash
   and outline so the chosen card still reads "selected" against the
   neutral cards above/below. */
.ct-insight-card.ct-insight-card--from-agent.is-active {
    background: linear-gradient(180deg, #faf5ff 0%, #f3e8ff 100%);
    border-color: #c4b5fd;
    box-shadow: 0 8px 22px rgba(124, 58, 237, 0.16);
}

/* ─── RD-2904 · the top-right actions rail ───────────────────────────
 *
 * Where the feedback cluster (👍 👎 ✕) lives now, next to the agent badge
 * that already had this corner to itself.
 *
 * The cluster used to be the bottom half of a rail pinned to the card's right
 * edge, vertically centred, with the cost badge above it — and clients were
 * not finding either one. The vertical middle of a wide row is the one place
 * nothing else on the card points at: the eye goes down the left column
 * (title → view → metrics → narrative) and never crosses back. So the two
 * split up by kind: the cost is a status and joined the status row at the
 * bottom, actions came up here, to the corner where actions belong.
 *
 * Absolute rather than a flex child of the card: the card has a stack of
 * conditional children (composite metrics, delta chip, linked chip) and any
 * track-based layout over it turns brittle per-card. Pinning the rail keeps
 * the left column flowing normally, which is the same reason the old footer
 * was pinned.
 *
 * The inset is the SAME on both sides — a corner element is read against the
 * corner, and 10/17 looked like a mistake even though the 17 was deliberate
 * (it aligned the rail with the content gutter the text flows in). Alignment
 * to a gutter loses to symmetry at a corner: the two borders meet a few pixels
 * away, and the eye compares the two gaps, not the rail to a text edge.
 *
 * 10px, and it is capped rather than chosen freely: the rail is 28px tall, so
 * measured it runs 11→39px and the meta row starts at exactly 39px. Anything
 * looser puts the buttons INTO the meta's band, so this is the roomiest the
 * corner gets. It also lands the rail on the title's first line box (14→33px,
 * optical centre 23.5px against the rail's 25px).
 *
 * The room the rail needs is reserved as padding on the title, the only
 * content that reaches this height; see the list-mode block further down. */
.ct-insight-card__actions {
    position: absolute;
    top: 10px;
    right: 10px;
    display: flex;
    align-items: center;
    /* Wide enough that the star badge reads as a mark and the buttons as a
       group, rather than the four of them as one undifferentiated strip. */
    gap: 8px;
    z-index: 2;
}

/* Cards render the rail before `_db_id` is known, i.e. before there is any
   feedback to put in it, and an insight that no agent created has no badge
   either. An empty absolute box collapses to 0×0 and hurts nothing today, so
   this is the same guard the status row carries: it keeps the slot out of the
   layout on purpose rather than by accident, and it is what stops a future
   padding or min-height from turning it into a live target in the corner. */
.ct-insight-card__actions:empty {
    display: none;
}

/* RD-1918: Bootstrap-Icons "stars" badge that labels agent-generated
   insights. Bootstrap tooltip wired up by initBottleneckTypeTooltips
   reveals "Generated by Research Agent" on hover. Color is intentionally
   inherited (no override) so the icon reads as a neutral mark.
   RD-2904: it used to pin itself to this corner. It shares the corner with
   the feedback cluster now, so the rail owns the geometry and the badge is
   just its first child — two absolute boxes at the same offsets would have
   sat on top of each other. */
.ct-insight-card__agent-badge {
    line-height: 1;
    cursor: help;
    user-select: none;
    transition: transform 0.15s ease;
}

@media (hover: hover) and (pointer: fine) {
    .ct-insight-card__agent-badge:hover {
        transform: scale(1.15);
    }
}

/* RD-1885: badge that appears next to the view name in the insight detail
   header — mirrors the shared chip on the card so ownership stays visible
   when the user drills into a single insight. */
.ct-detail-header__shared-badge {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    margin-left: 8px;
    padding: 2px 8px;
    font-size: 10px;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: #475569;
    background: #e2e8f0;
    border-radius: 10px;
    cursor: help;
}

.ct-detail-header__shared-badge i {
    font-size: 11px;
    color: #64748b;
}

.ct-insight-card__title {
    font-size: 14px;
    font-weight: 700;
    color: #1e293b;
    /* RD-2448: a touch more rhythm between title → meta → description. */
    margin-bottom: 6px;
    line-height: 1.35;
}

.ct-insight-card__meta {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 12px;
    color: #94a3b8;
    margin-bottom: 9px;
}

.ct-insight-card__meta-icon {
    font-size: 14px;
    /* RD-2198: "View" glyph (ki-element-11) tinted in the indigo view accent,
     * matching the source-view chip in the detail header. */
    color: #6366f1;
}

/* ─── Composite Card ─── */
.ct-insight-card--composite {
    border-left-width: 4px;
}

.ct-insight-card__metrics-list {
    display: flex;
    flex-wrap: wrap;
    gap: 4px;
    margin-bottom: 6px;
}

.ct-metric-badge {
    font-size: 10px;
    font-weight: 500;
    padding: 1px 6px;
    border-radius: 3px;
    background: #f1f5f9;
    color: #475569;
    white-space: nowrap;
}

.ct-metric-badge[data-severity="high"] {
    background: #fef2f2;
    color: #dc2626;
}

.ct-metric-badge[data-severity="medium"] {
    background: #fffbeb;
    color: #d97706;
}

/* RD-2582 — the composite's "3 metrics out of range" chip. It summarises the
 * badges beside it rather than reporting a finding of its own, so it stays
 * quieter than they are: no fill, one hairline border, and the muted end of the
 * existing text ramp. Tabular figures keep the count from jiggling when a
 * re-analysis changes it. No new tokens, no new colours. */
.ct-metric-badge--count {
    background: transparent;
    border: 1px solid #e2e8f0;
    color: #64748b;
    font-variant-numeric: tabular-nums;
    font-feature-settings: "tnum" 1;
}

/* ─── Composite Metrics Table (evidence panel) ─── */
.ct-composite-metrics {
    margin: 12px 0;
    border: 1px solid #e2e8f0;
    border-radius: 6px;
    overflow: hidden;
}

.ct-composite-metrics__header {
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: #64748b;
    padding: 8px 12px;
    background: #f8fafc;
    border-bottom: 1px solid #e2e8f0;
}

.ct-composite-metrics__table {
    width: 100%;
    font-size: 12px;
    border-collapse: collapse;
}

.ct-composite-metrics__table th {
    padding: 6px 12px;
    text-align: left;
    font-weight: 600;
    color: #64748b;
    border-bottom: 1px solid #e2e8f0;
    background: #f8fafc;
}

.ct-composite-metrics__table td {
    padding: 6px 12px;
    border-bottom: 1px solid #f1f5f9;
}

.ct-insight-card__description {
    font-size: 13px;
    /* RD-2448: slightly softer ink + taller lines so dense feeds breathe.
     * No bottom margin: the tags row below owns the 12px gap, so cards
     * without tags end at exactly the top padding's distance (16px). */
    color: #46536e;
    line-height: 1.55;
    margin-bottom: 0;
}

/* RD-2904 removed the card footer — RD-2198's right-edge rail, which held the
 * cost badge stacked over the feedback cluster in the card's vertical middle.
 * The cost is in the status row now (.ct-insight-card__tags) and the cluster
 * in the top-right actions rail (.ct-insight-card__actions). The class name
 * is deliberately not written here: InsightCardSlotsTest greps this file for
 * it, so the guard stays a plain string check. */

.ct-insight-card.is-active .ct-insight-card__title {
    color: #0f172a;
}

/* ─── Drag Cost Badge ─── */
/* RD-2724: this renders as a <button> on every surface that carries a cost
 * payload, because it opens a dialog now. The rules below therefore include
 * the button reset (font, border, text-align) — without them the badge
 * inherits the UA's 13.33px system face and centres its own label. The
 * `--saved` archive variant is still a plain <div> and shares this block, so
 * every affordance that implies a click is scoped to `[data-ct-cost]`. */
.ct-insight-card__drag-cost {
    display: inline-flex;
    align-items: center;
    gap: 3px;
    font-family: inherit;
    font-size: 11px;
    font-weight: 600;
    line-height: inherit;
    text-align: left;
    color: #dc2626;
    background: rgba(220, 38, 38, 0.08);
    border: 0;
    padding: 2px 7px;
    border-radius: 4px;
    white-space: nowrap;
    cursor: default;
    position: relative;
    -webkit-appearance: none;
    appearance: none;
    transition: background-color 150ms var(--ct-ease-out),
                transform 150ms var(--ct-ease-out);
}

.ct-insight-card__drag-cost i {
    font-size: 10px;
    color: #dc2626;
}

.ct-insight-card__drag-cost[data-ct-cost] {
    cursor: pointer;
}

/* Gated behind a real pointer: on touch, `:hover` sticks after a tap and the
 * badge would sit permanently lit next to badges that are not. */
@media (hover: hover) and (pointer: fine) {
    .ct-insight-card__drag-cost[data-ct-cost]:hover {
        background: rgba(220, 38, 38, 0.15);
    }

    .ct-insight-card__drag-cost--at-risk[data-ct-cost]:hover {
        background: rgba(245, 158, 11, 0.20);
    }
}

/* The press has to be felt: this badge now costs a panel, and a control that
 * does not answer the finger reads as one that did not hear it. */
.ct-insight-card__drag-cost[data-ct-cost]:active {
    transform: scale(0.97);
}

.ct-insight-card__drag-cost[data-ct-cost]:focus-visible {
    outline: 2px solid var(--ct-accent-border);
    outline-offset: 2px;
}

/* The info glyph is the only part of the badge that says "there is more here"
 * before you touch it, so it warms up with the rest on hover. */
@media (hover: hover) and (pointer: fine) {
    .ct-insight-card__drag-cost[data-ct-cost]:hover .ct-insight-card__drag-info {
        color: currentColor;
    }
}

/* At-risk variant — used for Early Warning carrying cost. Amber palette so
   the manager reads it as a softer signal than the red realised-drag badge
   on anomalies. */
.ct-insight-card__drag-cost--at-risk {
    color: #b45309;
    background: rgba(245, 158, 11, 0.12);
    border: 1px solid rgba(245, 158, 11, 0.35);
}

.ct-insight-card__drag-cost--at-risk i {
    color: #b45309;
}

/* Sized-up variant for the detail-header slot. The active feed cards
 * stick with the compact 11px badge; the detail view is roomier so
 * the cost reads at a glance next to the title. */
.ct-insight-card__drag-cost--lg {
    font-size: 14px;
    padding: 5px 12px;
    gap: 5px;
    border-radius: 999px;
}

.ct-insight-card__drag-cost--lg i {
    font-size: 12px;
}

.ct-insight-card__drag-cost--lg .ct-insight-card__drag-info {
    font-size: 12px;
}

.ct-insight-card__linked-context {
    display: inline-flex;
    align-items: center;
    gap: 5px;
}

/* Linked-insight chip. The card keeps a compact jump target plus a small
   info icon for the meaning of the relationship. */
.ct-insight-card__linked-chip {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 5px;
    min-height: 24px;
    padding: 3px 8px;
    font-size: 11px;
    font-weight: 600;
    line-height: 1;
    color: #475569;
    background: #f8fafc;
    border: 1px solid #e2e8f0;
    border-radius: 4px;
    cursor: pointer;
    transition: background 0.15s ease, color 0.15s ease, border-color 0.15s ease, box-shadow 0.15s ease;
}

.ct-insight-card__linked-chip:hover {
    color: #0f172a;
    background: #ffffff;
    border-color: #cbd5e1;
    box-shadow: 0 1px 2px rgba(15, 23, 42, 0.08);
}

.ct-insight-card__linked-chip i {
    font-size: 10px;
    color: inherit;
    line-height: 1;
}

.ct-insight-card__linked-chip > span:not(.ct-insight-card__linked-info) {
    line-height: 1;
}

.ct-insight-card__linked-info {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 13px;
    height: 13px;
    position: relative;
    color: #94a3b8;
    cursor: help;
    margin-left: 1px;
}

.ct-insight-card__linked-info:hover {
    color: #475569;
}

.ct-insight-card__linked-info::after {
    content: attr(data-tooltip);
    position: absolute;
    right: 0;
    bottom: calc(100% + 8px);
    width: max-content;
    max-width: 260px;
    padding: 7px 9px;
    color: #334155;
    background: #ffffff;
    border: 1px solid #cbd5e1;
    border-radius: 6px;
    box-shadow: 0 8px 18px rgba(15, 23, 42, 0.12);
    font-size: 11px;
    font-weight: 500;
    line-height: 1.35;
    white-space: normal;
    opacity: 0;
    visibility: hidden;
    transform: translateY(2px);
    transition: opacity 0.08s ease, transform 0.08s ease;
    pointer-events: none;
    z-index: 20;
}

.ct-insight-card__linked-info::before {
    content: '';
    position: absolute;
    right: 4px;
    bottom: calc(100% + 3px);
    width: 8px;
    height: 8px;
    background: #ffffff;
    border-right: 1px solid #cbd5e1;
    border-bottom: 1px solid #cbd5e1;
    transform: rotate(45deg);
    opacity: 0;
    visibility: hidden;
    transition: opacity 0.08s ease;
    pointer-events: none;
    z-index: 21;
}

.ct-insight-card__linked-info:hover::after,
.ct-insight-card__linked-info:hover::before,
.ct-insight-card__linked-info:focus::after,
.ct-insight-card__linked-info:focus::before {
    opacity: 1;
    visibility: visible;
}

.ct-insight-card__linked-info:hover::after,
.ct-insight-card__linked-info:focus::after {
    transform: translateY(0);
}

/* ─── Drag Cost Info Icon ─── */
.ct-insight-card__drag-info {
    font-size: 10px;
    color: #94a3b8;
    margin-left: 2px;
    cursor: help;
}

/* ─── RD-2724 · the hover peek ───────────────────────────────────────
 *
 * What is left of the old drag-cost tooltip. That one carried the whole
 * derivation — four headings, ~30 rows, a struck pre-cap row, three italic
 * footnotes — and lost all of it the moment the pointer moved. Reading it and
 * checking a figure against the dashboard at the same time was impossible, and
 * on a 13" screen its tail ran off the viewport.
 *
 * So it split in two. This is the half you read while scanning: the rate, one
 * horizon, and a line saying where the rest went. Three lines, 125ms, no
 * scroll. The derivation lives in the drawer at the bottom of this file.
 *
 * The node exists only while the peek is on screen — the controller builds it
 * on hover and drops it on the way out. That is not an optimisation: this box
 * used to ship inside every badge with `display: none`, and when RD-2724 made
 * it `visibility: hidden` so the fade had something to animate, every badge
 * started carrying ~110px of real geometry sticking past the content edge.
 * Harmless against the viewport, but inside any ancestor that establishes a
 * containing block for fixed descendants (a transform, a filter, `contain`)
 * it counts as scrollable overflow — which is a horizontal scrollbar across
 * the whole feed. Mounted into <body> on demand, the containing block is
 * always the viewport and nothing upstream can capture it.
 *
 * No display or visibility juggling left, therefore: the element is only ever
 * present when it is meant to be seen, so opacity and transform carry the
 * whole entrance. */
.ct-drag-tooltip {
    position: fixed;
    z-index: 10000;
    min-width: 186px;
    max-width: 264px;
    padding: 10px 12px 9px;
    background: #ffffff;
    color: #071437;
    border: 1px solid #e9edf3;
    border-radius: 10px;
    box-shadow: 0 10px 26px rgba(7, 20, 55, 0.13),
                0 1px 2px  rgba(7, 20, 55, 0.05);
    font-size: 11px;
    font-weight: 500;
    line-height: 1.45;
    text-align: left;
    white-space: normal;
    pointer-events: none;
    opacity: 0;
    transform: translateY(-3px) scale(0.98);
    transition: opacity 125ms var(--ct-ease-out),
                transform 125ms var(--ct-ease-out);
}

.ct-drag-tooltip.is-visible {
    opacity: 1;
    transform: none;
}

/* Every node here is a <span>: the peek is nested inside the badge, and a
 * <button>'s content model is phrasing content. CSS does the blocking out. */
.ct-drag-tooltip__figure {
    display: flex;
    align-items: baseline;
    gap: 2px;
}

.ct-drag-tooltip__amount {
    font-size: 19px;
    font-weight: 700;
    letter-spacing: -0.025em;
    line-height: 1;
    color: #dc2626;
    font-variant-numeric: tabular-nums;
}

.ct-drag-tooltip__unit {
    font-size: 11px;
    font-weight: 600;
    color: #dc2626;
    opacity: 0.78;
}

/* The at-risk tone has to ride on the peek itself, not on an ancestor: the
 * node is moved into <body> before it is shown, so by then the amber badge it
 * came from is no longer anywhere above it. */
.ct-drag-tooltip--at-risk .ct-drag-tooltip__amount,
.ct-drag-tooltip--at-risk .ct-drag-tooltip__unit {
    color: #b45309;
}

/* The line that makes the badge a door. Ink rather than accent: the peek is
 * two lines now, and a coloured one of them competes with the figure — which
 * is the thing being glanced at. The hairline above carries the separation
 * the old middle row used to provide. */
.ct-drag-tooltip__cta {
    display: block;
    margin-top: 8px;
    padding-top: 8px;
    border-top: 1px solid #f1f4f9;
    font-size: 10px;
    font-weight: 650;
    letter-spacing: 0.01em;
    color: #071437;
}

/* ─── Cumulative Drag Card (top-right panel) ─── */
/* Hidden in the active feed flow: the same number now lives on the
 * DRAG COST card in the detail header. Kept in the DOM (JS still
 * writes to `#ct-cumulative-drag-value`) but visually suppressed. */
#ct-cumulative-drag-card {
    display: none !important;
}

.ct-cumulative-drag-card {
    background: linear-gradient(180deg, #ffffff 0%, #fff8f8 100%);
    border: 1px solid rgba(220, 38, 38, 0.16);
    border-radius: 8px;
    padding: 14px 16px;
    margin-bottom: 14px;
    box-shadow: 0 8px 20px rgba(15, 23, 42, 0.04);
}

.ct-cumulative-drag-card__header {
    display: flex;
    align-items: center;
    justify-content: flex-start;
    gap: 10px;
    margin-bottom: 8px;
}

.ct-cumulative-drag-card__label {
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0;
    color: #475569;
}

.ct-cumulative-drag-card__value {
    font-size: 26px;
    font-weight: 800;
    color: #dc2626;
    line-height: 1;
}

.ct-cumulative-drag-card__note {
    margin-top: 6px;
    font-size: 11.5px;
    font-weight: 500;
    color: #94a3b8;
}

.ct-evidence-subtitle {
    font-size: 14px;
    color: #64748b;
    margin-bottom: 10px;
    max-width: calc(100% - 170px);
    line-height: 1.45;
}

.ct-last-analyzed {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    color: #64748b;
    font-size: 11.5px;
    font-weight: 600;
    white-space: nowrap;
    /* RD-2380: the detection gear used to sit between this timestamp and the
       Update button; without it, add breathing room so "Nd ago" isn't glued
       to the button (adds to the group's 8px flex gap → ~16px total). */
    margin-right: 8px;
}

.ct-last-analyzed i {
    color: #94a3b8;
}

.ct-last-analyzed strong {
    color: #334155;
    font-weight: 700;
}

.ct-last-analyzed__separator {
    color: #cbd5e1;
}

@media (max-width: 575.98px) {
    .ct-evidence-subtitle {
        max-width: 100%;
    }

    .ct-last-analyzed {
        justify-content: flex-start;
        flex-wrap: wrap;
    }
}

/* ─── Graph Container ─── */
.ct-graph-container {
    position: relative;
    width: 100%;
    height: 450px;
    background-color: #fafbfc;
    background-image: radial-gradient(circle, #d0d5dd 0.8px, transparent 0.8px);
    background-size: 20px 20px;
    border-radius: 10px;
    border: 1px solid rgba(148, 163, 184, 0.15);
    overflow: hidden;
}

.ct-graph-container #cy-insight,
.ct-graph-container #cy-archive-insight,
.ct-graph-container [id^="cy-"] {
    width: 100%;
    height: 100%;
}

.ct-graph-controls {
    position: absolute;
    top: 14px;
    left: 14px;
    z-index: 4;
    display: flex;
    flex-direction: column;
    gap: 4px;
    padding: 6px;
    border: 1px solid #dbe3ef;
    border-radius: 10px;
    background: rgba(255, 255, 255, 0.96);
    box-shadow: 0 8px 20px rgba(15, 23, 42, 0.12);
}

.ct-graph-controls__btn {
    width: 34px;
    height: 34px;
    border: 1px solid #dbe3ef;
    border-radius: 6px;
    background: #ffffff;
    color: #475569;
    font-size: 18px;
    line-height: 1;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    transition: border-color 0.15s ease, background-color 0.15s ease, color 0.15s ease;
}

.ct-graph-controls__btn:hover {
    border-color: #b9c6da;
    background: #f8fbff;
    color: #1e293b;
}

.ct-graph-controls__btn:active {
    background: #eef4fb;
}

/* ─── Graph Legend ─── */
.ct-graph-legend {
    display: flex;
    align-items: center;
    gap: 18px;
    padding: 12px 0 0;
}

.ct-graph-legend__item {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: 12px;
    color: #64748b;
    font-weight: 500;
}

.ct-graph-legend__dot {
    width: 10px;
    height: 10px;
    border-radius: 50%;
    flex-shrink: 0;
}

.ct-graph-legend__dot--bottleneck {
    background-color: #FF3366;
}

.ct-graph-legend__dot--healthy {
    background-color: #C4C4C4;
}

/* Separator between status and node-type groups */
.ct-graph-legend__sep {
    width: 1px;
    height: 14px;
    background: #e2e8f0;
    flex-shrink: 0;
}

/* Rounded-rectangle shape for board/repo/pr node types */
.ct-graph-legend__shape {
    width: 16px;
    height: 10px;
    border-radius: 3px;
    border: 2px solid #cbd5e1;
    flex-shrink: 0;
    background: #fff;
}

.ct-graph-legend__shape--repo {
    border-color: #419FFF;
}

.ct-graph-legend__shape--board {
    border-color: #cbd5e1;
}

.ct-graph-legend__shape--pr {
    border-color: #cbd5e1;
}

/* Circle shape for person in legend */
.ct-graph-legend__circle {
    width: 12px;
    height: 12px;
    border-radius: 50%;
    border: 2px solid #94a3b8;
    flex-shrink: 0;
    background: #fff;
}

/* Team: indigo border + tinted fill to match graph */
.ct-graph-legend__circle--team {
    width: 15px;
    height: 15px;
    border-color: #6366f1;
    border-width: 3px;
    background: #eef2ff;
}

/* ─── KPI Row ─── */
.ct-kpi-row {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
    gap: 12px;
    margin-top: 18px;
}

/* Variant placed ABOVE the graph (primary metric cards for anomalies).
   Tighter top margin + stronger bottom margin to separate from the
   graph/evidence below. Hidden when empty so the whitespace doesn't
   appear on EW / PF selections where no metric cards are rendered. */
.ct-kpi-row--top {
    margin-top: 4px;
    margin-bottom: 16px;
}

.ct-kpi-row--top:empty {
    display: none;
}

.ct-kpi-card {
    position: relative;
    min-width: 0;
    min-height: 126px;
    padding: 16px;
    background: linear-gradient(180deg, #ffffff 0%, #fbfdff 100%);
    border: 1px solid #e2e8f0;
    border-radius: 10px;
    display: flex;
    flex-direction: column;
    box-shadow: 0 1px 2px rgba(15, 23, 42, 0.03);
    overflow: hidden;
}

.ct-kpi-card::before {
    content: '';
    position: absolute;
    left: 0;
    top: 0;
    bottom: 0;
    width: 3px;
    background: #cbd5e1;
}

.ct-kpi-card--up::before {
    background: #ef4444;
}

.ct-kpi-card--down::before {
    background: #16a34a;
}

.ct-kpi-card--neutral::before {
    background: #94a3b8;
}

.ct-kpi-card__details {
    margin-top: auto;
    align-self: flex-end;
    padding: 0;
    border: none;
    background: transparent;
    color: #419FFF;
    font-size: 12px;
    font-weight: 700;
    cursor: pointer;
}

.ct-kpi-card__details:hover {
    text-decoration: underline;
}

.ct-kpi-card__label {
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: #64748b;
    margin-bottom: 10px;
}

/* RD-2090: platform isologos shown next to a group title in the evidence (the
   metric KPI card, the "Top tasks/PRs" header and the early-warning "Affected
   tasks" header). The backend only attaches them when the org has a connected
   integration feeding that category; these rules keep the marks aligned. */
.ct-source-logos {
    vertical-align: middle;
}

.ct-source-logos img,
.ct-source-logo {
    vertical-align: middle;
    flex-shrink: 0;
}

/* Logos sit to the right of the metric title, so push them off the label. */
.ct-kpi-card__label .ct-source-logos {
    margin-left: 6px;
}

/* Group-header strips ("Top tasks/PRs", "Affected tasks") sit after the title. */
.ct-top-tasks__header .ct-source-logos {
    margin-left: 6px;
}

.ct-kpi-card__value-row {
    /* RD-2247: metric value + change chip on one line, vertically centered. */
    display: flex;
    align-items: center;
    gap: 8px;
    min-width: 0;
    margin-bottom: 10px;
}

.ct-kpi-card__value {
    font-size: 30px;
    font-weight: 800;
    color: #1e293b;
    line-height: 1;
    margin-bottom: 0;
    word-break: break-word;
    font-variant-numeric: tabular-nums;
    letter-spacing: -0.02em;
}

/* Compact reference lines for previous-period and baseline values. Muted
   tone so the primary value stays the focal point of the card. */
.ct-kpi-card__references {
    display: grid;
    gap: 4px;
    margin-bottom: 8px;
}

.ct-kpi-card__reference {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    min-height: 24px;
    padding: 5px 10px;
    border-radius: 6px;
    background: #f1f5f9;
    color: #64748b;
    font-size: 11px;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
}

.ct-kpi-card__reference strong {
    color: #475569;
    font-weight: 700;
    white-space: nowrap;
}

.ct-kpi-card__comparison {
    /* RD-2247: inline beside the metric value (was absolutely positioned
       top-right) so the number and its delta read as one unit. */
    display: inline-flex;
    align-items: center;
    gap: 3px;
    font-size: 13px;
    font-weight: 700;
    line-height: 1;
    padding: 4px 9px;
    border-radius: 999px;
    font-variant-numeric: tabular-nums;
}

.ct-kpi-card__comparison i {
    font-size: 12px;
}

.ct-kpi-card__comparison--up {
    color: #dc2626;
    background: #fef2f2;
}

.ct-kpi-card__comparison--down {
    color: #16a34a;
    background: #f0fdf4;
}

.ct-kpi-card__comparison--neutral {
    color: #64748b;
    background: #f1f5f9;
}

/* ─── Empty State ─── */
.ct-empty-state {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    padding: 60px 20px;
    text-align: center;
}

.ct-empty-state__icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 64px;
    height: 64px;
    margin-bottom: 16px;
    border-radius: 16px;
    background: #f1f5f9;
    color: #94a3b8;
    font-size: 28px;
}

.ct-empty-state__text {
    font-size: 13.5px;
    font-weight: 500;
    color: #64748b;
    max-width: 320px;
    line-height: 1.5;
}

.ct-table-empty-row td {
    padding: 0 !important;
}

.ct-table-empty {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    min-height: 132px;
    padding: 26px 20px;
    text-align: center;
    background: linear-gradient(180deg, #ffffff 0%, #fbfdff 100%);
}

.ct-table-empty__icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 38px;
    height: 38px;
    margin-bottom: 10px;
    border-radius: 8px;
    color: #7c8da8;
    background: #f1f5f9;
    border: 1px solid #e2e8f0;
}

.ct-table-empty__title {
    color: #334155;
    font-size: 13px;
    font-weight: 800;
}

.ct-table-empty__text {
    max-width: 430px;
    margin-top: 4px;
    color: #7b8aa3;
    font-size: 12px;
    line-height: 1.45;
}

/* ─── Loading spinner ─── */
.ct-spinner {
    width: 36px;
    height: 36px;
    border: 3px solid #e2e8f0;
    border-top-color: #419FFF;
    border-radius: 50%;
    animation: ct-spin 0.8s linear infinite;
}

@keyframes ct-spin {
    to { transform: rotate(360deg); }
}

/* ─── Stale filters banner (RD-1848) ─── */
.ct-stale-filters-banner {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 10px 12px;
    background: #fff8e1;
    border: 1px solid #f5c97b;
    border-radius: 8px;
    color: #7a5b00;
    font-size: 12px;
    line-height: 1.4;
}

.ct-stale-filters-banner i {
    color: #b07e00;
    flex-shrink: 0;
}

.ct-stale-filters-banner__text {
    flex: 1;
}

/* Inline variant — sits next to the INSIGHTS DETECTED count so the
 * "filters drifted" hint reads as a chip rather than a banner row.
 * Smaller padding, no border-radius difference, single-line text. */
.ct-stale-filters-banner--inline {
    padding: 4px 10px;
    border-radius: 999px;
    font-size: 11px;
    line-height: 1.2;
    gap: 6px;
    max-width: 100%;
}

.ct-stale-filters-banner--inline .ct-stale-filters-banner__text {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

@media (max-width: 1199.98px) {
    /* Allow wrapping on narrower screens so the chip never crowds the
     * count badge or the Analyze button into a single squashed row. */
    .ct-stale-filters-banner--inline {
        white-space: normal;
    }
    .ct-stale-filters-banner--inline .ct-stale-filters-banner__text {
        white-space: normal;
        overflow: visible;
        text-overflow: clip;
    }
}

/* ─── Feed Tabs ─── */
#ct-feed-tabs .badge {
    font-size: 10px;
    min-width: 20px;
    height: 20px;
    padding: 0 5px;
    line-height: 20px;
}

/* ─── Impact Filter Buttons ─── */
.ct-impact-filters {
    display: flex;
    align-items: flex-start;
    column-gap: 6px;
    row-gap: 7px;
    flex-wrap: wrap;
}

.ct-impact-filters__label {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    flex: 0 0 100%;
    font-size: 11px;
    font-weight: 600;
    color: #475569;
    white-space: nowrap;
}

.ct-impact-filters__label i {
    font-size: 12px;
    color: #94a3b8;
}

/* RD-2909: these chips are toggles now (several can be on at once), so they
 * get the press + focus treatment .ct-cluster-filter already had — a chip you
 * press repeatedly has to answer the press. Same tokens, no new vocabulary. */
.ct-impact-filter {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    font-size: 11px;
    font-weight: 500;
    color: #64748b;
    background: #f8fafc;
    border: 1px solid #e2e8f0;
    border-radius: 6px;
    padding: 4px 10px;
    cursor: pointer;
    transition: color 150ms var(--ct-ease-out), background-color 150ms var(--ct-ease-out),
                border-color 150ms var(--ct-ease-out), transform 150ms var(--ct-ease-out);
}

.ct-impact-filter:hover {
    background: #f1f5f9;
    border-color: #cbd5e1;
}

.ct-impact-filter:active {
    transform: scale(0.97);
}

.ct-impact-filter:focus-visible {
    outline: 2px solid var(--ct-accent-border);
    outline-offset: 1px;
}

.ct-impact-filter.is-active {
    background: #eef2ff;
    border-color: #818cf8;
    color: #4f46e5;
}

.ct-impact-filter__dot {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    flex-shrink: 0;
}

.ct-impact-filter--high .ct-impact-filter__dot { background: #ef4444; }
.ct-impact-filter--medium .ct-impact-filter__dot { background: #f59e0b; }
.ct-impact-filter--low .ct-impact-filter__dot { background: #3b82f6; }

.ct-impact-filter__count {
    font-size: 10px;
    background: #e2e8f0;
    color: #475569;
    border-radius: 4px;
    padding: 0 4px;
    min-width: 16px;
    text-align: center;
}

.ct-impact-filter.is-active .ct-impact-filter__count {
    background: #c7d2fe;
    color: #4338ca;
}

/* ─── Bottleneck Type Filter Chips ─── */
/* Mirrors .ct-impact-filter styling. Placed above the active feed and archive
 * to scope visible insights to a given bottleneck type. */
.ct-type-filters {
    display: flex;
    align-items: center;
    gap: 6px;
    flex-wrap: wrap;
    margin-bottom: 16px;
}

/* RD-2909: these chips are toggles now (several can be on at once), so they
 * get the press + focus treatment .ct-cluster-filter already had — a chip you
 * press repeatedly has to answer the press. Same tokens, no new vocabulary. */
.ct-type-filter {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: 12px;
    font-weight: 500;
    color: #475569;
    background: #f8fafc;
    border: 1px solid #e2e8f0;
    border-radius: 999px;
    padding: 5px 12px;
    cursor: pointer;
    transition: color 150ms var(--ct-ease-out), background-color 150ms var(--ct-ease-out),
                border-color 150ms var(--ct-ease-out), transform 150ms var(--ct-ease-out);
}

.ct-type-filter:hover {
    background: #f1f5f9;
    border-color: #cbd5e1;
}

.ct-type-filter:active {
    transform: scale(0.97);
}

.ct-type-filter:focus-visible {
    outline: 2px solid var(--ct-accent-border);
    outline-offset: 1px;
}

.ct-type-filter.is-active {
    background: #eef2ff;
    border-color: #818cf8;
    color: #4f46e5;
}

/* RD-2448 (option B): the type glyph is monochrome — it inherits the chip's
   text color (gray idle, indigo when active) so type NEVER borrows the
   red/amber that mean risk. */
.ct-type-filter__ic {
    display: inline-flex;
    flex-shrink: 0;
    width: 13px;
    height: 13px;
    color: currentColor;
    opacity: 0.75;
}
.ct-type-filter__ic svg { width: 13px; height: 13px; }

.ct-type-filter__count {
    font-size: 10px;
    background: #e2e8f0;
    color: #475569;
    border-radius: 4px;
    padding: 0 5px;
    min-width: 18px;
    text-align: center;
}

.ct-type-filter.is-active .ct-type-filter__count {
    background: #c7d2fe;
    color: #4338ca;
}

/* ─── Origin filter (RD-1918) ─── */
/* Independent toggle chip that ANDs with the type/severity filters. Lives
 * inline inside `.ct-type-filters` to inherit the same 6px gap as the
 * type chips, but uses a distinct class (`.ct-origin-filter`) so the
 * bottleneck-type click handler — which filters on `.ct-type-filter` —
 * ignores it. Violet palette matches the Research Agent launcher button
 * so users associate the filter with that source. */
.ct-origin-filter {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: 12px;
    font-weight: 500;
    color: #5b21b6;
    background: #faf5ff;
    border: 1px solid #e9d5ff;
    border-radius: 999px;
    padding: 5px 12px;
    cursor: pointer;
    /* RD-2909: same press + focus treatment as its neighbours in the row,
       which became toggles in that ticket. */
    transition: color 150ms var(--ct-ease-out), background-color 150ms var(--ct-ease-out),
                border-color 150ms var(--ct-ease-out), transform 150ms var(--ct-ease-out);
    /* Visual separator from the type chips on the left. The pseudo
       element sits inside the flex `gap` between this chip and the last
       type chip so the divider reads "this is a different kind of
       filter" without breaking the rounded pill shape. Extra 12px of
       margin widens the visual gap and gives the violet line room to
       breathe. */
    position: relative;
    margin-left: 12px;
}

.ct-origin-filter::before {
    content: '';
    position: absolute;
    left: -10px;
    top: -2px;
    bottom: -2px;
    width: 2px;
    border-radius: 2px;
    background: linear-gradient(
        180deg,
        rgba(124, 58, 237, 0) 0%,
        rgba(124, 58, 237, 0.85) 30%,
        rgba(124, 58, 237, 0.85) 70%,
        rgba(124, 58, 237, 0) 100%
    );
    pointer-events: none;
}

.ct-origin-filter:hover {
    background: #f3e8ff;
    border-color: #d8b4fe;
}

.ct-origin-filter:active {
    transform: scale(0.97);
}

.ct-origin-filter:focus-visible {
    outline: 2px solid var(--ct-accent-border);
    outline-offset: 1px;
}

.ct-origin-filter.is-active {
    color: #fff;
    background: linear-gradient(135deg, #4f46e5 0%, #7c3aed 100%);
    border-color: transparent;
    box-shadow: 0 2px 8px rgba(124, 58, 237, 0.28);
}

.ct-origin-filter__icon {
    color: #7c3aed;
    font-size: 12px;
}

.ct-origin-filter.is-active .ct-origin-filter__icon {
    color: #fff;
}

.ct-origin-filter__count {
    font-size: 10px;
    background: #ede9fe;
    color: #5b21b6;
    border-radius: 4px;
    padding: 0 5px;
    min-width: 18px;
    text-align: center;
}

.ct-origin-filter.is-active .ct-origin-filter__count {
    background: rgba(255, 255, 255, 0.22);
    color: #fff;
}

/* ─── Bottleneck Type Pill (per-card) ─── */
/* Injected into each insight card. Identifies which detector produced the
 * insight (anomaly / early_warning). Color is intentionally
 * subtle so it complements rather than competes with the severity badge. */
.ct-type-pill {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    font-size: 10px;
    font-weight: 600;
    letter-spacing: 0.02em;
    text-transform: uppercase;
    padding: 2px 8px;
    border-radius: 999px;
    border: 1px solid transparent;
    margin-bottom: 8px;
    /* Same vertical-align as the sibling .ct-approval-badge so the two pills
       line up on the same row instead of one floating higher. */
    vertical-align: top;
}

/* When a pill carries an explainer tooltip, hint at it via the help cursor
 * so users discover the hover affordance. Pills without a tooltip keep the
 * default cursor. */
.ct-type-pill[data-bs-toggle="tooltip"] {
    cursor: help;
}

/* ─── Bottleneck Type Tooltip (HTML payload) ─── */
/* Custom Bootstrap tooltip used by .ct-type-filter / .ct-type-pill.
 * The HTML payload has two visual blocks:
 * a definition (top) and an example (bottom) separated by a hairline so
 * the user can scan both at a glance instead of reading inline prose.
 * Light surface to align with the rest of the page. */
.tooltip.ct-type-tooltip-popover {
    --bs-tooltip-bg: #ffffff;
    --bs-tooltip-color: #0f172a;
    --bs-tooltip-max-width: 280px;
    --bs-tooltip-opacity: 1;
    --bs-tooltip-padding-x: 0;
    --bs-tooltip-padding-y: 0;
}

.tooltip.ct-type-tooltip-popover .tooltip-inner {
    text-align: left;
    padding: 0;
    border-radius: 8px;
    border: 1px solid #e2e8f0;
    box-shadow: 0 10px 24px -8px rgba(15, 23, 42, 0.18);
    overflow: hidden;
}

/* Match the arrow color to the light surface so it doesn't appear as a
 * stray dark notch when placement flips. */
.tooltip.ct-type-tooltip-popover .tooltip-arrow::before {
    border-top-color: #ffffff;
    border-bottom-color: #ffffff;
    border-left-color: #ffffff;
    border-right-color: #ffffff;
}

.ct-type-tooltip {
    display: flex;
    flex-direction: column;
}

.ct-type-tooltip__def {
    font-size: 12px;
    font-weight: 500;
    line-height: 1.45;
    color: #0f172a;
    padding: 10px 12px 8px;
}

.ct-type-tooltip__example {
    display: flex;
    flex-direction: column;
    gap: 3px;
    padding: 8px 12px 10px;
    border-top: 1px solid #e2e8f0;
    background: #f8fafc;
}

.ct-type-tooltip__example-label {
    font-size: 9px;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: #64748b;
}

.ct-type-tooltip__example-text {
    font-size: 11.5px;
    font-weight: 400;
    line-height: 1.4;
    color: #475569;
}

.ct-type-pill__dot {
    width: 6px;
    height: 6px;
    border-radius: 50%;
    flex-shrink: 0;
}

.ct-type-pill--anomaly {
    color: #b91c1c;
    background: #fee2e2;
    border-color: #fecaca;
}
.ct-type-pill--anomaly .ct-type-pill__dot { background: #ef4444; }

.ct-type-pill--early_warning {
    color: #b45309;
    background: #fef3c7;
    border-color: #fde68a;
}
.ct-type-pill--early_warning .ct-type-pill__dot { background: #f59e0b; }

/* ─── RD-2448: simplified feed cards ─── */

/* Mono type glyph (option B): the type is encoded by SHAPE — pulse / clock /
   person — never by color, so red/amber stay exclusive to risk. Base class
   sized by context modifiers below. */
.ct-type-ic {
    display: inline-flex;
    flex: none;
    color: #64748b;
}
.ct-type-ic svg { width: 100%; height: 100%; }
/* On the card title (cost sort / In Progress): carries the type tooltip. */
.ct-type-ic--card {
    width: 13px;
    height: 13px;
    margin-right: 7px;
    vertical-align: -1.5px;
    cursor: default;
}
/* In the group header. */
.ct-type-ic--ghead { width: 14px; height: 14px; }
/* In the detail-header chip (gap comes from the chip's flex gap). */
.ct-type-ic--chip { width: 13px; height: 13px; color: currentColor; opacity: 0.8; }

/* RD-2448: in list mode the feed sheds its card chrome — the toolbar reads as
   a slim bar (Filters left · Analyzed + Update right) and the insight cards
   sit directly on the page background. The card element stays in the DOM
   because it drives the viewport-bound scroll layout (see the list-mode
   media rules); only its visual shell goes. Detail mode hides this panel
   entirely, so nothing else is affected.
   RD-2588: Activos y Cerrados pierden el mismo chrome. Con el Inbox sobre el
   fondo de página y las otras dos dentro de un panel blanco, cambiar de
   pestaña se leía como cambiar de producto — el panel era el resto del layout
   de 3 columnas, no una decisión. El elemento card queda en el DOM (ancla los
   ids de las listas); solo se va la cáscara. */
#ct-tab-active-feed:not(.is-detail) #ct-left-panel .card.card-flush {
    background: transparent;
    border: 0;
    box-shadow: none;
}
#ct-tab-active-feed:not(.is-detail) #ct-left-panel .card.card-flush > .card-header {
    padding-left: 2px;
    padding-right: 2px;
    /* RD-2448: the toolbar moved up to the feed topbar; this header only
       hosts the (usually hidden) Analyze progress, so it collapses to zero —
       Metronic's default .card-header min-height (70px) was leaving a big
       empty band between the topbar and the list. The topbar's own
       margin-bottom now provides the gap. */
    min-height: 0;
    padding-top: 0;
    padding-bottom: 0;
}
/* RD-2588: el mismo inset de 2px en Activos y Cerrados — sin él, la card
 * conserva el padding default de Metronic (~28px) ENCIMA del medio-gutter que
 * ya pone la columna bootstrap que la envuelve, así que "ANOMALÍAS" quedaba
 * a ~40px del borde mientras las pestañas y el botón Filtros (fuera de esa
 * columna) quedan a ~0px — el desalineado que se veía entre el nav de
 * pestañas, la lista y el topbar. */
#ct-tab-active-feed:not(.is-detail) #ct-left-panel .card.card-flush > .card-body.ct-insights-panel {
    padding-left: 2px;
    padding-right: 2px;
}

/* Slim per-type group header — the type reads once as group context instead
   of repeating as a pill on every card. RD-2588 gave it to Activos and
   Cerrados; the Inbox has no grouping to title since RD-2608 (it is ordered
   by cost), so only those two lists render this. */
.ct-feed-ghead {
    display: flex;
    align-items: center;
    gap: 8px;
    margin: 26px 2px 10px;
    font-size: 11.5px;
    font-weight: 700;
    letter-spacing: 0.07em;
    text-transform: uppercase;
    color: #5b6b8c;
}
.ct-feed-ghead:first-child { margin-top: 4px; }
/* Icon+name wrapper — carries the what-is-this tooltip (RD-2448). */
.ct-feed-ghead__label { display: inline-flex; align-items: center; gap: 8px; cursor: default; }
.ct-feed-ghead__count { font-weight: 600; color: #94a3b8; letter-spacing: 0; text-transform: none; }
.ct-feed-ghead__rule { flex: 1; height: 1px; background: #e5eaf2; }

/* Bottom status row. A dedicated flex row with line-height slack killed, so
   the pill-to-border distance is deterministic and matches the card's top
   padding. Always rendered by the builder; :empty hides it when the card has
   no status tags.
   RD-2904: the cost badge is its first member now, with "Analysis ready" to
   its right — this is the spot the ticket moved the cost to, because it is
   where the eye already lands at the end of the narrative. */
.ct-insight-card__tags {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px;
    margin-top: 12px;
    line-height: 1;
}
.ct-insight-card__tags:empty { display: none; }
/* Inside the tags row every pill shares the exact same metrics, so the cost,
   "Ready to resolve" and "Analysis ready" read as one aligned set (the
   approval badge's base style carries feed-era margins and a smaller font). */
.ct-insight-card__tags .ct-report-chip {
    margin: 0;
    font-size: 11px;
    line-height: 1.4;
    padding: 3px 10px;
}
/* Same, for the cost badge that RD-2904 moved into this row. Its own metrics
   were tuned for the old rail, where it stood alone: `line-height: inherit`
   collapsed to the row's 1, no radius to speak of, and no border — three ways
   of being 8px shorter than the chip it now sits beside. The hairline is the
   badge's own red at low alpha (and the at-risk variant already carries its
   amber one), so the box matches without a new colour. */
.ct-insight-card__tags .ct-insight-card__drag-cost {
    padding: 3px 10px;
    border: 1px solid rgba(220, 38, 38, 0.18);
    border-radius: 999px;
    line-height: 1.4;
}

/* AISTRAT-131 (5.2): Trend badge — shown next to the type pill when the
 * anomaly was detected via linear-regression trend (not z-score). Visually
 * secondary so it does not compete with the severity / type cues. */
.ct-trend-badge {
    display: inline-block;
    margin-left: 6px;
    padding: 1px 6px;
    font-size: 9px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    color: #475569;
    background: #f1f5f9;
    border: 1px dashed #cbd5e1;
    border-radius: 4px;
    vertical-align: middle;
}

/* RD-1744: detection-basis badge — distinguishes target breaches from
   baseline (z-score) anomalies in the feed at a glance. */
.ct-basis-badge {
    display: inline-block;
    margin-left: 6px;
    padding: 1px 6px;
    font-size: 9px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    border-radius: 4px;
    vertical-align: middle;
}
.ct-basis-badge--target,
.ct-basis-badge--baseline_and_target {
    /* Same red as the Anomaly type pill. */
    color: #b91c1c;
    background: #fee2e2;
    border: 1px solid #fecaca;
}

/* RD-1744: target badge shown next to the EVIDENCE title in the detail view. */
.ct-evidence-target-badge {
    display: inline-flex;
    align-items: center;
    margin-left: 10px;
    padding: 2px 8px;
    font-size: 11px;
    font-weight: 600;
    /* Same red as the Anomaly type pill. */
    color: #b91c1c;
    background: #fee2e2;
    border: 1px solid #fecaca;
    border-radius: 6px;
    cursor: help;
    vertical-align: middle;
}

/* ─── Early Warning Evidence Panel (AISTRAT-131 Phase 3) ─── */
/* Shown in the center evidence panel when the selected insight is an early
 * warning. Renders the item list the detector emitted (PRs, stuck tasks, WIP
 * outliers) with a link out to the source, replacing the z-score / baseline
 * charts the anomaly renderer would normally draw. */
.ct-early-warning__projected {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 11px 14px;
    background: #fffbeb;
    border: 1px solid #fde9b8;
    border-left: 3px solid #f59e0b;
    border-radius: 8px;
    line-height: 1.5;
}

/* Label sits inside the amber callout, so it shares the amber palette
 * instead of a clashing violet/blue badge. One cohesive colour block. */
.ct-projected__label {
    flex-shrink: 0;
    display: inline-flex;
    align-items: center;
    padding: 3px 10px;
    border-radius: 999px;
    background: #fce4b8;
    color: #b45309;
    font-size: 10.5px;
    font-weight: 700;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    white-space: nowrap;
}

.ct-early-warning__items {
    border: 1px solid #e2e8f0;
    border-radius: 10px;
    padding: 14px;
    background: #fafbfc;
}

.ct-early-warning__items-header {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    margin-bottom: 10px;
}

.ct-early-warning__list {
    list-style: none;
    padding: 0;
    margin: 0;
    display: flex;
    flex-direction: column;
    gap: 8px;
    /* Cap the visible list at roughly the first 5 items, then scroll
     * inside. Short lists (1-5 items) render at their natural height
     * (no scrollbar), long ones (10/18/25 items) stop adding page
     * height instead of growing the evidence panel indefinitely. The
     * exact pixel cap is sized to ~5 rows (item title + meta row +
     * vertical padding + the 6px row gap). */
    max-height: 280px;
    overflow-y: auto;
}

.ct-early-warning__item {
    background: #ffffff;
    border: 1px solid #e2e8f0;
    border-radius: 8px;
    transition: border-color 0.15s var(--ct-ease-out), box-shadow 0.15s var(--ct-ease-out);
}

@media (hover: hover) and (pointer: fine) {
    .ct-early-warning__item:hover {
        border-color: #c7d2fe;
        box-shadow: 0 4px 12px -5px rgba(15, 23, 42, 0.14);
    }
}

.ct-early-warning__item-link,
.ct-early-warning__item-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 10px 12px;
    text-decoration: none;
    color: inherit;
}

.ct-early-warning__item-link:hover {
    text-decoration: none;
    color: inherit;
}

.ct-early-warning__item-main {
    min-width: 0;
}

.ct-early-warning__item-title {
    font-size: 12.5px;
    font-weight: 600;
    color: #0f172a;
    line-height: 1.35;
}

.ct-early-warning__item-sub {
    margin-top: 5px;
}

.ct-ew-owner {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: 11px;
    font-weight: 500;
    color: #64748b;
}

.ct-ew-owner__avatar {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 18px;
    height: 18px;
    border-radius: 50%;
    background: #eef2ff;
    color: #4f46e5;
    font-size: 9px;
    font-weight: 700;
    flex-shrink: 0;
}

.ct-early-warning__item-tags {
    display: flex;
    align-items: center;
    gap: 6px;
    flex-shrink: 0;
}

.ct-ew-chip {
    display: inline-flex;
    align-items: center;
    padding: 2px 9px;
    border-radius: 999px;
    font-size: 10.5px;
    font-weight: 600;
    background: #f1f5f9;
    color: #475569;
    white-space: nowrap;
    font-variant-numeric: tabular-nums;
}

.ct-ew-chip--status {
    background: #eef2ff;
    color: #4f46e5;
}

.ct-ew-chip--age.ct-ew-chip--warm {
    background: #fffbeb;
    color: #b45309;
}

.ct-ew-chip--age.ct-ew-chip--hot {
    background: #fef2f2;
    color: #dc2626;
}

/* ─── Feed filters popover (RD-2380) ─── */
/* Single funnel-triggered dropdown that consolidates the Type, Risk level and
 * Cost sort controls into one control. Clones the (removed) .ct-bottleneck-settings
 * look so it stays native to the module. */
.ct-feed-filters-dropdown {
    display: inline-flex;
}

.ct-feed-filters-toggle {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 6px 12px;
    font-size: 12px;
    font-weight: 600;
    color: #334155;
    background: #ffffff;
    border: 1px solid #e2e8f0;
    border-radius: 8px;
    cursor: pointer;
    transition: border-color 0.15s ease, background 0.15s ease, transform 0.12s cubic-bezier(0.23, 1, 0.32, 1);
}

.ct-feed-filters-toggle:hover {
    background: #f8fafc;
    border-color: #cbd5e1;
}

.ct-feed-filters-toggle:active {
    transform: scale(0.97);
}

.ct-feed-filters-toggle:focus-visible {
    outline: 2px solid rgba(65, 159, 255, 0.5);
    outline-offset: 2px;
}

.ct-feed-filters-toggle[aria-expanded="true"] {
    border-color: #419FFF;
    background: #f0f7ff;
}

.ct-feed-filters-toggle__icon {
    color: #64748b;
}

.ct-feed-filters-toggle__badge {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 18px;
    height: 18px;
    padding: 0 5px;
    font-size: 11px;
    font-weight: 700;
    color: #ffffff;
    background: #419FFF;
    border-radius: 9px;
    line-height: 1;
    font-variant-numeric: tabular-nums;
}

.ct-feed-filters-popover {
    min-width: 300px;
    padding: 12px;
    transform-origin: top left;
}

/* Gentle opacity-only entrance. Popper controls the menu position via an inline
 * transform, so animating transform here would fight it — opacity is safe and
 * enough for a filter dropdown opened occasionally. */
.ct-feed-filters-popover.show {
    animation: ctFeedFiltersIn 150ms cubic-bezier(0.23, 1, 0.32, 1);
}

@keyframes ctFeedFiltersIn {
    from { opacity: 0; }
    to { opacity: 1; }
}

/* Chips wrap inside the narrower popover instead of overflowing the row. */
.ct-feed-filters-popover .ct-type-filters,
.ct-feed-filters-popover .ct-impact-filters {
    flex-wrap: wrap;
    gap: 6px;
}

.ct-feed-filters-popover__section {
    padding: 6px 0;
}

.ct-feed-filters-popover__section + .ct-feed-filters-popover__section {
    border-top: 1px solid #f1f5f9;
}

.ct-feed-filters-popover__title {
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    color: #64748b;
    letter-spacing: 0.04em;
    margin-bottom: 8px;
}

.ct-feed-filters-popover__footer {
    display: flex;
    justify-content: flex-end;
    margin-top: 8px;
    padding-top: 10px;
    border-top: 1px solid #e2e8f0;
}

@media (prefers-reduced-motion: reduce) {
    .ct-feed-filters-popover.show {
        animation: none;
    }
    .ct-feed-filters-toggle {
        transition: border-color 0.15s ease, background 0.15s ease;
    }
    .ct-feed-filters-toggle:active {
        transform: none;
    }
}

/* ─── View Details Button ─── */
.ct-view-details-toggle {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    font-size: 12px;
    font-weight: 500;
    color: #6366f1;
    background: none;
    border: 1px solid #e2e8f0;
    border-radius: 6px;
    padding: 6px 16px;
    cursor: pointer;
    width: 100%;
    margin-top: 12px;
    transition: all 0.15s ease;
}

.ct-view-details-toggle:hover {
    background: #f8fafc;
    border-color: #818cf8;
}

.ct-view-details-toggle__arrow {
    font-size: 10px;
}

/* ─── Details Modal ─── */
.ct-details-modal-overlay {
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background: rgba(0, 0, 0, 0.4);
    z-index: 1050;
    display: flex;
    align-items: center;
    justify-content: center;
    animation: ct-fade-in 0.15s ease;
}

@keyframes ct-fade-in {
    from { opacity: 0; }
    to { opacity: 1; }
}

.ct-details-modal {
    background: #fff;
    border-radius: 12px;
    box-shadow: 0 20px 60px rgba(0, 0, 0, 0.2);
    width: 90%;
    max-width: 700px;
    max-height: 80vh;
    display: flex;
    flex-direction: column;
    animation: ct-slide-up 0.2s ease;
}

@keyframes ct-slide-up {
    from { transform: translateY(20px); opacity: 0; }
    to { transform: translateY(0); opacity: 1; }
}

.ct-details-modal__header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 16px 20px;
    border-bottom: 1px solid #e2e8f0;
    flex-shrink: 0;
}

.ct-details-modal__title {
    font-size: 15px;
    font-weight: 600;
    color: #1e293b;
}

.ct-details-modal__close {
    background: none;
    border: none;
    font-size: 22px;
    color: #94a3b8;
    cursor: pointer;
    padding: 0 4px;
    line-height: 1;
}

.ct-details-modal__close:hover {
    color: #475569;
}

.ct-details-modal__body {
    padding: 16px 20px;
    overflow-y: auto;
    flex: 1;
}

.ct-details-modal__body .ct-top-tasks,
.ct-details-modal__body .ct-person-metrics,
.ct-details-modal__body .ct-composite-metrics {
    margin-bottom: 16px;
}

.ct-details-modal__body .ct-top-tasks:last-child,
.ct-details-modal__body .ct-person-metrics:last-child,
.ct-details-modal__body .ct-composite-metrics:last-child {
    margin-bottom: 0;
}

/* ─── Feedback Buttons ─── */
.ct-insight-card__feedback {
    display: flex;
    align-items: center;
    gap: 4px;
    margin-left: auto;
}

.ct-feedback-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    border: 1px solid #e2e8f0;
    border-radius: 7px;
    background: #fff;
    color: #94a3b8;
    cursor: pointer;
    transition: border-color 0.15s ease, color 0.15s ease, background-color 0.15s ease, transform 0.1s ease;
    font-size: 14px;
    padding: 0;
}

.ct-feedback-btn:hover {
    border-color: #cbd5e1;
    color: #475569;
    background: #f8fafc;
}

/* Physical press feedback so the action feels tactile. */
.ct-feedback-btn:active {
    transform: scale(0.92);
}

.ct-feedback-btn:focus-visible {
    outline: 2px solid var(--ct-accent);
    outline-offset: 2px;
}

.ct-feedback-btn.is-active[data-feedback="thumbs_up"] {
    border-color: #16a34a;
    color: #16a34a;
    background: #f0fdf4;
}

.ct-feedback-btn.is-active[data-feedback="thumbs_down"] {
    border-color: #dc2626;
    color: #dc2626;
    background: #fef2f2;
}

.ct-feedback-btn--dismiss:hover {
    border-color: #94a3b8;
    color: #64748b;
}

@media (max-width: 991.98px) {

    .ct-graph-controls {
        top: 10px;
        left: 10px;
        padding: 4px;
        border-radius: 8px;
    }

    .ct-graph-controls__btn {
        width: 30px;
        height: 30px;
        font-size: 16px;
    }
}

.ct-archive-detail {
    position: relative;
}

.ct-pr-black-hole-group + .ct-pr-black-hole-group {
    margin-top: 18px;
}

.ct-pr-black-hole-group__header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    margin-bottom: 10px;
}

.ct-pr-black-hole-group__title {
    font-size: 15px;
    font-weight: 700;
    color: #0f172a;
}

.ct-pr-black-hole-group__count {
    font-size: 12px;
    font-weight: 700;
    color: #64748b;
}

.ct-pr-black-hole-group__list {
    display: flex;
    flex-direction: column;
    gap: 10px;
}

.ct-pr-black-hole-group__item {
    padding: 12px 14px;
    border: 1px solid #e5edf6;
    border-radius: 10px;
    background: #fff;
}

.ct-pr-black-hole-group__item-title {
    font-size: 13px;
    font-weight: 700;
    color: #0f172a;
}

.ct-pr-black-hole-group__item-meta {
    margin-top: 4px;
    font-size: 11px;
    color: #64748b;
}

/* ─── Anomaly Evidence Panel ─── */
.ct-anomaly-evidence {
    display: flex;
    flex-direction: column;
    gap: 24px;
    max-width: 480px;
    margin: 0 auto;
    padding: 32px 24px;
}

.ct-anomaly-evidence__header {
    display: flex;
    align-items: center;
    gap: 12px;
}

.ct-anomaly-evidence__metric {
    font-size: 18px;
    font-weight: 600;
    color: #1e293b;
}

.ct-anomaly-evidence__badge {
    display: inline-flex;
    align-items: center;
    padding: 3px 10px;
    border-radius: 6px;
    font-size: 12px;
    font-weight: 600;
    letter-spacing: 0.02em;
}

.ct-anomaly-evidence__badge--high {
    background: #fee2e2;
    color: #dc2626;
}

.ct-anomaly-evidence__badge--medium {
    background: #fff7ed;
    color: #ea580c;
}

.ct-anomaly-evidence__badge--low {
    background: #fefce8;
    color: #ca8a04;
}

.ct-anomaly-evidence__value {
    display: flex;
    align-items: baseline;
    gap: 12px;
}

.ct-anomaly-evidence__current {
    font-size: 42px;
    font-weight: 700;
    color: #1e293b;
    line-height: 1;
}

.ct-anomaly-evidence__change {
    font-size: 20px;
    font-weight: 600;
}

.ct-anomaly-evidence__change--bad {
    color: #dc2626;
}

.ct-anomaly-evidence__change--good {
    color: #16a34a;
}

.ct-anomaly-evidence__details {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 16px;
    padding: 20px;
    background: #f8fafc;
    border-radius: 12px;
    border: 1px solid #e2e8f0;
}

.ct-anomaly-evidence__detail {
    display: flex;
    flex-direction: column;
    gap: 4px;
}

.ct-anomaly-evidence__detail-label {
    font-size: 12px;
    font-weight: 500;
    color: #64748b;
    text-transform: uppercase;
    letter-spacing: 0.05em;
}

.ct-anomaly-evidence__detail-value {
    font-size: 16px;
    font-weight: 600;
    color: #334155;
}

.ct-anomaly-evidence__footer {
    padding-top: 8px;
    border-top: 1px solid #e2e8f0;
}

/* ─── Anomaly Driver Section ─── */
.ct-anomaly-evidence__driver {
    display: flex;
    flex-direction: column;
    gap: 12px;
    padding: 20px;
    background: linear-gradient(135deg, #eff6ff 0%, #f0fdf4 100%);
    border-radius: 12px;
    border: 1px solid #bfdbfe;
}

.ct-anomaly-evidence__driver-header {
    display: flex;
    align-items: center;
    gap: 8px;
    color: #1e40af;
}

.ct-anomaly-evidence__driver-title {
    font-size: 13px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.05em;
}

.ct-anomaly-evidence__driver-body {
    display: flex;
    align-items: baseline;
    gap: 10px;
}

.ct-anomaly-evidence__driver-name {
    font-size: 22px;
    font-weight: 700;
    color: #1e293b;
}

.ct-anomaly-evidence__driver-dimension {
    font-size: 13px;
    color: #64748b;
    font-weight: 500;
}

.ct-anomaly-evidence__driver-stats {
    display: grid;
    grid-template-columns: 1fr 1fr 1fr;
    gap: 12px;
    margin-top: 4px;
}

.ct-kpi-card--driver {
    border-left: 3px solid #3b82f6;
}

.ct-kpi-card__sub {
    font-size: 11px;
    color: #64748b;
    margin-top: 2px;
}

/* ─── Anomaly Evidence Panel (Phase 3: split layout above graph) ─── */
.ct-anomaly-evidence-panel {
    border-bottom: 1px solid #e2e8f0;
    margin-bottom: 12px;
    padding-bottom: 12px;
    max-height: 220px;
    overflow-y: auto;
    transition: max-height 0.2s ease, margin-bottom 0.2s ease, padding-bottom 0.2s ease, border-color 0.2s ease;
}

/* AISTRAT-131: early warnings hide the driver graph + KPI row below, so
 * their evidence panel can grow freely without pushing other content
 * off-screen. This modifier drops the 220px cap so the full item list
 * renders without inner scrolling. */
.ct-anomaly-evidence-panel--expanded {
    max-height: none;
    overflow-y: visible;
}

.ct-anomaly-evidence-panel .ct-anomaly-evidence {
    max-width: 100%;
    gap: 12px;
    padding: 12px 16px;
}

.ct-anomaly-evidence-panel .ct-anomaly-evidence__value {
    gap: 10px;
}

.ct-anomaly-evidence-panel .ct-anomaly-evidence__details {
    grid-template-columns: repeat(4, 1fr);
}

.ct-anomaly-evidence-panel .ct-anomaly-evidence__driver {
    padding: 10px 14px;
}

.ct-anomaly-evidence-panel .ct-anomaly-evidence__driver-stats {
    grid-template-columns: 1fr 1fr 1fr;
}

.ct-anomaly-evidence-panel .ct-anomaly-evidence__footer {
    margin-top: 0;
}

/* ─── LLM Narrative Block ─── */
.ct-anomaly-evidence__narrative {
    padding: 14px 16px;
    background: #f8fafc;
    border: 1px solid #e2e8f0;
    border-left: 3px solid #419FFF;
    border-radius: 8px;
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.75);
}

.ct-anomaly-evidence__narrative.is-open {
    /* RD-2247: always expanded, sized to content (no inner height cap) so the
       trailing "Chat about this" action inside the box stays visible. */
    opacity: 1;
}

/* RD-2178: bullet-point narrative. Replaces the dense paragraph with a tight,
   scannable list. Tuned for a 10-second read: compact rhythm, custom celeste
   markers matching the panel's accent border, no list indentation overshoot. */
.ct-anomaly-evidence__bullets {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 7px;
}

.ct-anomaly-evidence__bullets li {
    position: relative;
    padding-left: 17px;
    font-size: 12.5px;
    line-height: 1.45;
    color: #334155;
    font-variant-numeric: tabular-nums;
}

.ct-anomaly-evidence__bullets li::before {
    content: "";
    position: absolute;
    left: 2px;
    top: 0.62em;
    width: 5px;
    height: 5px;
    border-radius: 50%;
    background: #419FFF;
}

/* RD-2178: inline clickable PR / task reference inside a narrative bullet.
   Reads as a native link — celeste, weight bump, hairline underline on hover —
   without breaking the line rhythm of the surrounding text. */
.ct-narrative-link {
    color: #419FFF;
    font-weight: 600;
    text-decoration: none;
    border-radius: 3px;
    transition: color 0.15s var(--ct-ease-out), background-color 0.15s var(--ct-ease-out);
}

.ct-narrative-link:hover,
.ct-narrative-link:focus-visible {
    color: #2b6fd6;
    text-decoration: underline;
    text-underline-offset: 2px;
}

.ct-narrative-link:focus-visible {
    outline: 2px solid var(--ct-accent);
    outline-offset: 1px;
    text-decoration: none;
}

/* ─── Top Tasks List ─── */
/* RD-2406 — WIP composition in View Details. Deliberately built from the
   .ct-top-tasks vocabulary (same border, radius, header treatment) because it
   sits directly above it in the same modal and the two must read as one list,
   not as two components that happen to be adjacent. */
.ct-wip-comp {
    margin: 12px 0;
    border: 1px solid #e2e8f0;
    border-radius: 8px;
    overflow: hidden;
}

.ct-wip-comp__header {
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    color: #64748b;
    padding: 10px 16px;
    background: #f8fafc;
    border-bottom: 1px solid #e2e8f0;
}

.ct-wip-comp__split {
    padding: 12px 16px 10px;
    border-bottom: 1px solid #f1f5f9;
}

.ct-wip-comp__split-bar {
    height: 6px;
    border-radius: 3px;
    /* The track IS the waiting share — one element, two meanings, so the two
       segments can never disagree by a rounding pixel. */
    background: #cbd5e1;
    overflow: hidden;
}

.ct-wip-comp__split-fill.is-active {
    display: block;
    height: 100%;
    background: #419FFF;
}

.ct-wip-comp__split-legend {
    display: flex;
    gap: 16px;
    margin-top: 8px;
    font-size: 12px;
    color: #64748b;
}

.ct-wip-comp__legend strong {
    color: #0f172a;
    font-variant-numeric: tabular-nums;
}

.ct-wip-comp__legend.is-active::before,
.ct-wip-comp__legend.is-waiting::before {
    content: '';
    display: inline-block;
    width: 8px;
    height: 8px;
    border-radius: 2px;
    margin-right: 6px;
    vertical-align: baseline;
}

.ct-wip-comp__legend.is-active::before {
    background: #419FFF;
}

.ct-wip-comp__legend.is-waiting::before {
    background: #cbd5e1;
}

.ct-wip-comp__item {
    display: flex;
    align-items: baseline;
    gap: 10px;
    padding: 8px 16px;
    border-bottom: 1px solid #f1f5f9;
    font-size: 13px;
}

.ct-wip-comp__item:last-child {
    border-bottom: none;
}

.ct-wip-comp__status {
    flex: 1;
    min-width: 0;
    color: #0f172a;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.ct-wip-comp__status.is-waiting {
    color: #64748b;
}

.ct-wip-comp__count {
    font-weight: 600;
    color: #0f172a;
    font-variant-numeric: tabular-nums;
}

.ct-wip-comp__meta {
    color: #94a3b8;
    font-size: 12px;
    white-space: nowrap;
}

.ct-wip-comp__delta {
    font-variant-numeric: tabular-nums;
}

.ct-wip-comp__foot {
    padding: 8px 16px;
    font-size: 11px;
    color: #94a3b8;
    background: #f8fafc;
    border-top: 1px solid #f1f5f9;
}

/* Quieter than everything above on purpose: this is a hint for whoever owns the
   board configuration, not a finding about the team's work. */
.ct-wip-comp__note {
    padding: 8px 16px;
    font-size: 11px;
    line-height: 1.5;
    color: #64748b;
    background: #fffbeb;
    border-top: 1px solid #fef3c7;
}

.ct-top-tasks {
    margin: 12px 0;
    border: 1px solid #e2e8f0;
    border-radius: 8px;
    overflow: hidden;
}

.ct-top-tasks__header {
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    color: #64748b;
    padding: 10px 16px;
    background: #f8fafc;
    border-bottom: 1px solid #e2e8f0;
}

.ct-top-tasks__item {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 8px 16px;
    border-bottom: 1px solid #f1f5f9;
}

.ct-top-tasks__item:last-child {
    border-bottom: none;
}

.ct-top-tasks__name {
    flex: 1;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: 13px;
}

.ct-top-tasks__link {
    color: #3b82f6;
    text-decoration: none;
}

.ct-top-tasks__link:hover {
    text-decoration: underline;
}

.ct-top-tasks__meta {
    flex-shrink: 0;
    font-size: 12px;
    color: #64748b;
    margin-left: 12px;
}

.ct-top-tasks__value {
    font-weight: 600;
    color: #1e293b;
}

/* ─── Person Metrics Table ─── */
.ct-person-metrics {
    margin: 12px 0;
    border: 1px solid #e2e8f0;
    border-radius: 8px;
    overflow: hidden;
}

.ct-person-metrics__header {
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: #64748b;
    padding: 10px 16px 6px;
    background: #f8fafc;
}

.ct-person-metrics__table {
    width: 100%;
    border-collapse: collapse;
    font-size: 12px;
}

.ct-person-metrics__table th {
    text-align: left;
    padding: 6px 12px;
    font-weight: 600;
    color: #475569;
    background: #f8fafc;
    border-bottom: 1px solid #e2e8f0;
    font-size: 11px;
    white-space: nowrap;
}

.ct-person-metrics__table td {
    padding: 8px 12px;
    border-bottom: 1px solid #f1f5f9;
    color: #334155;
}

.ct-person-metrics__table tr:last-child td {
    border-bottom: none;
}

.ct-person-metrics__table tr:hover td {
    background: #f8fafc;
}

.ct-person-metrics__name {
    font-weight: 500;
    color: #1e293b;
}

.ct-trend {
    font-size: 10px;
    font-weight: 700;
}

.ct-trend--worse {
    color: #ef4444;
}

.ct-trend--better {
    color: #22c55e;
}

/* RD-1748 / RD-1749: Notifications tab — gray out the per-dashboard table
   while the master toggle is OFF. The disabled inputs are still in the DOM
   (so the user can see what their preferences would be once they opt in)
   but pointer-events are blocked to prevent accidental edits. The class
   lives on the per-channel wrapper so Slack and Email can be in different
   states simultaneously. */
.ct-notifications-channel.ct-notifications-disabled .ct-notifications-settings-table tbody {
    opacity: 0.45;
    pointer-events: none;
}

#ct-notifications-section .ct-notifications-master {
    align-items: center;
}

/* ─── Active Feed list/detail navigation ──────────────────────────
 *
 * Two display modes inside the Active Feed tab pane:
 *   default    — list mode: type filters, left panel (insights list),
 *                center+right panels side-by-side, in-progress section
 *                visible. Back button hidden.
 *   .is-detail — detail mode: type filters / left panel / in-progress
 *                hidden; center + right panels span full width and stack;
 *                back button visible.
 *
 * The class is toggled from JS (enterFeedDetail / exitFeedDetail). */
.ct-feed-back-btn {
    display: none;
    align-items: center;
    align-self: flex-start; /* don't stretch in the parent flex column */
    gap: 0.45rem;
    /* Negative left margin lets the icon sit flush with the column
     * edge while keeping enough click padding around the label. */
    padding: 0.4rem 0.75rem;
    margin-left: -0.375rem;
    margin-bottom: 1.25rem;
    border: 1px solid #ecedf3;
    background: #f9fafc;
    color: #4b5675;
    font-size: 0.925rem;
    font-weight: 600;
    cursor: pointer;
    border-radius: 0.5rem;
    transition: color 0.16s ease, background-color 0.16s ease, border-color 0.16s ease, box-shadow 0.16s ease, transform 0.12s ease-out;
}

.ct-feed-back-btn i {
    transition: transform 0.16s cubic-bezier(0.23, 1, 0.32, 1);
}

.ct-feed-back-btn:active {
    transform: scale(0.98);
}

/* Subtle back chip: light indigo tint + the arrow slides left on hover.
 * Same affordance as "Back to Settings" on the Integrations page. The
 * scrolled/sticky pill variants below override background + border. */
@media (hover: hover) and (pointer: fine) {
    .ct-feed-back-btn:hover {
        background: #eef2ff;
        border-color: #dfe4fb;
        color: #4338ca;
        box-shadow: 0 1px 2px rgba(16, 24, 40, 0.05);
    }

    .ct-feed-back-btn:hover i {
        color: #4338ca;
        transform: translateX(-3px);
    }
}

@media (prefers-reduced-motion: reduce) {
    .ct-feed-back-btn {
        transition: color 0.16s ease, background-color 0.16s ease, border-color 0.16s ease;
    }
    .ct-feed-back-btn:active {
        transform: none;
    }
    .ct-feed-back-btn:hover i {
        transform: none;
    }
}

@keyframes ct-detail-enter {
    from {
        opacity: 0;
        transform: translateY(6px);
    }
    to {
        opacity: 1;
        transform: translateY(0);
    }
}

/* RD-2759 — the Insights pane has ONE column now.
 *
 * It used to be a flex row: the insights column plus the Planning Agent's
 * side panel, with `.is-agent-collapsed` toggled from JS to drop the panel
 * and hand the full width back to the column, capped at 1280px and centred.
 * The panel, its gutter and the JS that set that class all left with the
 * Planning Agent, so the class is never applied any more — which silently
 * left the pane stuck in the OTHER branch, the one shaped for two columns.
 * That is what widened the cards and cost the list its own scrollbar.
 *
 * What is below is the collapsed branch, unconditional: the layout the feed
 * has always had in its normal state. (The conversational bot is unaffected —
 * it lives in the page-level `leanmote-bot-panel`, not in this pane.)
 *
 * The `.active` qualifier is required: an unqualified `#ct-tab-active-feed`
 * rule would beat Bootstrap's `.tab-content > .tab-pane { display: none }`
 * on specificity (1,0,0 vs 0,1,1) and force the pane to keep its layout
 * while inactive — invisible but still claiming space, which pushes the
 * sibling panes below the viewport. */
#ct-tab-active-feed.active {
    display: block;
    max-width: 1280px;
    margin-left: auto;
    margin-right: auto;
    position: relative;
}

.ct-feed-main-column {
    width: 100%;
    display: flex;
    flex-direction: column;
    flex: 1 1 0%;
    min-width: 0;
}

/* While the user is dragging the handle, suppress pointer-events on
 * iframes / text selection so the drag stays smooth. */
#ct-tab-active-feed.is-agent-resizing,
#ct-tab-active-feed.is-agent-resizing * {
    user-select: none;
    cursor: col-resize !important;
}

#ct-tab-active-feed:not(.is-detail) #ct-left-panel {
    flex: 0 0 100%;
    max-width: 100%;
    width: 100%;
}

/* List-mode single-scroll layout. The whole Active Feed tab pane is a
 * flex container capped at the viewport so the page itself never scrolls;
 * only the insights card body does. KPI metrics + type filters keep
 * their natural height, the explorer row flexes to fill the remaining
 * space, and the card inside fills that row.
 *
 * Two tiers of viewport bounds:
 *   - 992px+: legacy list-mode cap (agent collapsed). The single column
 *     fits in any lg+ viewport, so cap is safe.
 *   - 1170px+: cap applies even when the agent panel is open, because
 *     only above that width do both columns fit side-by-side without
 *     forcing the main column under its 850px floor.
 *
 * Direction: `row` when the agent panel is open (main column + agent
 * column side-by-side); the base rule above already sets that. When
 * collapsed we go back to a single-column layout (the base rule sets
 * `display: block`; here we promote it to flex-column so the explorer
 * keeps its viewport-bound flex sizing). */
@media (min-width: 992px) {
    /* Viewport-bound single column: the page never scrolls, the list does.
     *
     * Both offsets are measured and written onto this element by
     * `sizeActiveFeedViewport()` in js/bottleneck-insights/general.js — the
     * chrome above the pane (breadcrumb + scope bar, which wraps with the
     * number of views) and below it (pb-10 + the app footer) are not fixed
     * heights, and guessing them left the pane hanging below the fold with a
     * page scrollbar next to the list's. The literals below are the no-JS
     * fallback only; they are not the source of truth. */
    #ct-tab-active-feed.active:not(.is-detail) {
        display: flex;
        flex-direction: column;
        height: calc(100vh - var(--ct-feed-top-offset, 11rem) - var(--ct-feed-bottom-offset, 4rem));
    }

    /* Shared flex sizing for the main column in list mode at lg+. */
    #ct-tab-active-feed.active:not(.is-detail) .ct-feed-main-column {
        flex: 1 1 0%;
        min-height: 0;
        min-width: 0;
    }

    /* List-mode card sizing — applies regardless of agent state since
     * the explorer row + insights list are always the dominant content
     * in list mode and benefit from filling the viewport-bound parent. */
    #ct-tab-active-feed.active:not(.is-detail) #bottleneck-insights-explorer {
        flex: 1 1 auto;
        min-height: 0;
        margin-bottom: 0;
    }

    #ct-tab-active-feed.active:not(.is-detail) #ct-left-panel {
        height: 100%;
        display: flex;
    }

    /* Override the global `height: auto !important` so the card
     * stretches to fill its parent (which is now flex-bound). */
    #ct-tab-active-feed:not(.is-detail) #ct-left-panel .card.card-flush {
        flex: 1 1 auto;
        height: auto !important;
        min-height: 380px;
    }

    /* Header keeps its natural size (Filters + analyzed timestamp + Update);
     * only the body shrinks/scrolls. `overflow: visible` on the header makes
     * sure the timestamp can extend beyond the natural padding without
     * clipping. */
    #ct-tab-active-feed:not(.is-detail) #ct-left-panel .card.card-flush > .card-header {
        flex: 0 0 auto;
    }

    #ct-tab-active-feed:not(.is-detail) #ct-left-panel .card.card-flush > .card-header {
        overflow: visible;
    }
}

#ct-tab-active-feed:not(.is-detail) #ct-insights-list {
    flex: 1 1 auto;
    overflow-y: auto;
    min-height: 0;
    /* Custom scrollbar — slim and unobtrusive so it doesn't fight the
     * card chrome when the list overflows. */
    scrollbar-width: thin;
    scrollbar-color: #cbd5e1 transparent;
}

#ct-tab-active-feed:not(.is-detail) #ct-insights-list::-webkit-scrollbar { width: 6px; }
#ct-tab-active-feed:not(.is-detail) #ct-insights-list::-webkit-scrollbar-thumb {
    background-color: #cbd5e1;
    border-radius: 3px;
}

/* Full-width insight rows in list mode. Everything in the card stacks and
 * flows on the left (title / meta / metrics / description / status row); the
 * only pinned thing is the top-right actions rail, declared with the rest of
 * the card near the top of this file.
 *
 * RD-2904: the padding is symmetric again. The 11rem right gutter existed to
 * keep the flowing content clear of RD-2198's vertically-centred right rail,
 * which spanned the card's full height — every line of narrative paid for it,
 * and on a wide row the text stopped a third of the way from the edge for no
 * reason a reader could see. Now only the title reaches the rail's height, so
 * only the title reserves room for it. */
#ct-tab-active-feed:not(.is-detail) #ct-insights-list .ct-insight-card {
    position: relative;
    padding: 1rem 1.25rem;
}

/* The rail's footprint, measured: three 28px buttons with 4px between them is
 * 92px, and the star badge plus its 8px gap takes the widest case to 123px.
 * The title's padding is measured from its own border-box edge, which sits at
 * the card's 1.25rem (17px) padding while the rail sits at 10px, so clearing
 * the rail needs 123 + 10 - 17 = 116px. 128px leaves 12px on top of that, so a
 * title long enough to fill the gutter still stops short of the buttons
 * instead of tucking under them.
 *
 * Reserved UNCONDITIONALLY, on cards with no badge and no cluster too:
 * `_db_id` lands asynchronously, and a gutter that appeared with it would
 * re-wrap the title under the reader. */
#ct-tab-active-feed:not(.is-detail) #ct-insights-list .ct-insight-card__title {
    padding-right: 128px;
}

/* The card body has list-mode padding tweaks too — remove the narrow
 * `card-body pt-0` cramp it had as a 3-col side panel so the rows
 * breathe. */
#ct-tab-active-feed:not(.is-detail) #ct-insights-list {
    padding: 0.5rem 0.75rem 1rem;
}

/* ─── Feed metrics row (list mode only) ──────────────────────────
 *
 * Four aggregated cards above the list: Cumulative drag, Avg time to
 * action, Agent-resolved today, Pending approval. Mockup-aligned and
 * mostly placeholder values in v1 — the agent flow will populate the
 * "Agent-resolved" and "Pending approval" cards once it ships. */
.ct-feed-metrics {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(240px, 340px));
    justify-content: start;
    gap: 1.25rem;
    /* RD-2448: bottom margin moved to the topbar wrapper. */
    margin-bottom: 0;
}

/* RD-2448: feed topbar — metrics left, toolbar (Filters/Update + analyzed
   timestamp + stale chip) right-aligned at the metrics' height. The bottom
   margin + the first group header's 4px = 24px to the list. */
.ct-feed-topbar {
    display: flex;
    align-items: stretch;
    justify-content: space-between;
    gap: 1rem;
    flex-wrap: wrap;
    margin-bottom: 1.25rem;
}
.ct-feed-actions {
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    /* Bottom-aligned against the metric card so the buttons + timestamp sit
       close to the list they act on, not floating at the top of the band. */
    justify-content: flex-end;
    gap: 8px;
    padding-bottom: 4px;
}
.ct-feed-actions__row {
    display: flex;
    align-items: center;
    gap: 8px;
}
.ct-feed-actions__meta { min-height: 18px; }
.ct-feed-actions__meta:empty { display: none; }
/* RD-2588: Activos y Cerrados usan el mismo topbar que el Inbox. Cuando no
   hay métricas a la izquierda (Activos), el auto-margin manda el bloque de
   acciones a la derecha igual; con métricas presentes el space-between del
   topbar ya lo hacía, así que no cambia nada en las otras dos. */
.ct-feed-topbar > .ct-feed-actions { margin-left: auto; }
/* Dentro del topbar la grilla de métricas necesita ancho definido: como flex
   item se encoge a su contenido y repeat(auto-fit, …) colapsa a UNA columna
   — los dos KPI de Cerrados quedaban apilados en vertical. flex:1 le da el
   ancho de la banda (menos las acciones) y auto-fit vuelve a poner las
   tarjetas lado a lado, alineadas a la izquierda como el KPI del Inbox. */
.ct-feed-topbar > .ct-feed-metrics {
    flex: 1 1 auto;
    min-width: 0;
}

@media (max-width: 991.98px) {
    .ct-feed-metrics { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 575.98px) {
    .ct-feed-metrics { grid-template-columns: 1fr; }
}

.ct-feed-metric {
    display: flex;
    flex-direction: column;
    gap: 0.875rem;
    padding: 1.15rem 1.35rem 1.3rem;
    background: #fff;
    border: 1px solid #e2e8f0;
    border-radius: 0.875rem;
    box-shadow: 0 1px 2px rgba(15, 23, 42, 0.04);
    transition: box-shadow 0.2s var(--ct-ease-out), border-color 0.2s var(--ct-ease-out), transform 0.2s var(--ct-ease-out);
    /* One-time entrance on first paint. The KPI row is static markup that
     * JS only writes text into (not recreated), so this plays once and the
     * `forwards` fill holds the visible state across tab switches. */
    opacity: 0;
    transform: translateY(8px);
    animation: ct-kpi-in 0.5s var(--ct-ease-out) forwards;
}

.ct-feed-metric:nth-child(1) { animation-delay: 40ms; }
.ct-feed-metric:nth-child(2) { animation-delay: 90ms; }
.ct-feed-metric:nth-child(3) { animation-delay: 140ms; }
.ct-feed-metric:nth-child(4) { animation-delay: 190ms; }

@keyframes ct-kpi-in {
    to { opacity: 1; transform: translateY(0); }
}

/* Hover gated to real pointers so touch taps don't get a stuck hover. */
@media (hover: hover) and (pointer: fine) {
    .ct-feed-metric:hover {
        border-color: var(--ct-accent-border);
        box-shadow: 0 8px 20px -6px rgba(15, 23, 42, 0.1);
        transform: translateY(-3px);
    }
}

.ct-feed-metric__head {
    display: flex;
    align-items: center;
    gap: 0.5rem;
}

/* Tinted icon chip. Neutral by default; the colour variants below give each
 * KPI its own signal hue so the row reads as a family without four loud
 * accent bars competing for attention. */
.ct-feed-metric__icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 30px;
    height: 30px;
    border-radius: 9px;
    background: #f1f5f9;
    color: #64748b;
    flex-shrink: 0;
}

.ct-feed-metric__icon i {
    font-size: 1rem;
    line-height: 1;
}

.ct-feed-metric__label {
    font-size: 0.6875rem;
    color: #64748b;
    font-weight: 600;
    letter-spacing: 0.06em;
    text-transform: uppercase;
}

.ct-feed-metric__value {
    font-size: 1.9rem;
    font-weight: 700;
    color: #0f172a;
    line-height: 1;
    letter-spacing: -0.02em;
    font-variant-numeric: tabular-nums;
}

.ct-feed-metric__value--danger {
    color: #dc2626;
}

.ct-feed-metric__value--success {
    color: #16a34a;
}

.ct-feed-metric--danger .ct-feed-metric__icon { background: #fef2f2; color: #dc2626; }
.ct-feed-metric--success .ct-feed-metric__icon { background: #f0fdf4; color: #16a34a; }
.ct-feed-metric--accent .ct-feed-metric__icon { background: var(--ct-accent-soft); color: var(--ct-accent); }

/* RD-2090: the Pending approval metric doubles as a feed filter. */
.ct-feed-metric--clickable {
    cursor: pointer;
    user-select: none;
}

.ct-feed-metric--clickable:focus-visible {
    outline: 2px solid var(--ct-accent);
    outline-offset: 2px;
}

.ct-feed-metric--clickable:active {
    transform: scale(0.99);
}

/* Filter active: the card reads as "selected" so it's clear the feed below is
   scoped to pending-approval insights. */
.ct-feed-metric--clickable.is-active {
    border-color: var(--ct-accent);
    background: var(--ct-accent-soft);
    box-shadow: 0 8px 20px -6px var(--ct-accent-border);
}

.ct-feed-metric--clickable.is-active .ct-feed-metric__value,
.ct-feed-metric--clickable.is-active .ct-feed-metric__label {
    color: var(--ct-accent);
}

@media (prefers-reduced-motion: reduce) {
    .ct-feed-metric {
        opacity: 1;
        transform: none;
        animation: none;
    }
}

/* ─── Detail header (severity + title + metric cards) ──────────────
 *
 * Sits above the explorer row in detail mode only. Hidden in list mode
 * via the missing `.is-detail` class on the parent. */
.ct-detail-header {
    display: none;
    margin-bottom: 1.25rem;
}

.ct-detail-header__meta {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.625rem;
    margin-bottom: 0.75rem;
    font-size: 0.8125rem;
    font-weight: 600;
}

/* Impact severity chip — labelled "Impact: …", coloured by tier and
 * sized up so it reads next to the title without squinting. */
.ct-detail-header__severity {
    display: inline-flex;
    align-items: center;
    padding: 0.3rem 0.75rem;
    border-radius: 999px;
    text-transform: none;
    letter-spacing: 0;
    background: #fef2f2;
    color: #b91c1c;
    font-size: 0.8125rem;
    font-weight: 600;
}

.ct-detail-header__severity--medium {
    background: #fef3c7;
    color: #b45309;
}

.ct-detail-header__severity--low {
    background: #ecfeff;
    color: #0891b2;
}

/* Type chip — Anomaly / Early Warning. RD-2448 (option B):
 * neutral chip + mono type glyph; type is shape-coded so the red/amber
 * palette stays exclusive to the risk level. */
.ct-detail-header__type {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.3rem 0.75rem;
    border-radius: 999px;
    background: #f1f5f9;
    color: #475569;
    font-size: 0.8125rem;
    font-weight: 600;
}
/* The per-type colored variants are gone on purpose (one color = one
 * meaning); the neutral base + the glyph carry the type. The modifier
 * classes are still toggled by JS so a future re-skin stays one CSS change. */

/* RD-2448: Off Target chip in the detail header — soft red tint (it's an
 * alert about the target, so it shares the risk palette on purpose). */
.ct-detail-header__basis {
    display: inline-flex;
    align-items: center;
    padding: 0.3rem 0.75rem;
    border-radius: 999px;
    background: #fef2f2;
    color: #b91c1c;
    font-size: 0.8125rem;
    font-weight: 600;
    cursor: default;
}

/* Source view chip — same `ki-element-11` glyph as the sidebar's
 * Performance Delivery so the analyst recognises this as a view.
 * RD-2448: pill background so it reads like its sibling chips, plus a
 * self-explaining tooltip (wired in renderDetailHeader). */
.ct-detail-header__source {
    display: inline-flex;
    align-items: center;
    gap: 0.375rem;
    padding: 0.3rem 0.75rem;
    border-radius: 999px;
    background: #f1f5f9;
    color: #475569;
    font-weight: 600;
    font-size: 0.8125rem;
    cursor: default;
}

.ct-detail-header__source i {
    color: #4338ca;
}

/* RD-2329: "Created / started by" chip in the Active-detail header, next to
 * the view name — surfaces which collaborator generated and launched the
 * running plan on a shared view. Soft-indigo (the product's AI/collaboration
 * accent), same pill geometry as the sibling meta chips. Hidden when empty. */
.ct-detail-header__plan-by {
    display: inline-flex;
    align-items: center;
    gap: 0.375rem;
    padding: 0.3rem 0.75rem;
    border-radius: 999px;
    background: #eef2ff;
    color: #4338ca;
    font-size: 0.8125rem;
    font-weight: 600;
}

.ct-detail-header__plan-by:empty {
    display: none;
}

/* RD-2194: "Chat about this" action in the detail header meta row. Opens
 * the Leanmote bot prefilled with a deep-research prompt about the open
 * insight. Soft-indigo pill (the product's AI/bot accent) so it reads as
 * the actionable element next to the static chips; `margin-left:auto`
 * pushes it to the right edge of the row. Same pill geometry as the
 * severity/type chips above for visual cohesion. */
.ct-detail-header__chat {
    display: inline-flex;
    align-items: center;
    gap: 0.45rem;
    margin-left: auto;
    padding: 0.45rem 0.9rem;
    border: 1px solid #c7d2fe;
    border-radius: 999px;
    background: #eef2ff;
    color: #4338ca;
    font-size: 0.875rem;
    font-weight: 600;
    line-height: 1;
    cursor: pointer;
    /* Animate only transform/background/border/color (never `all`), with a
     * strong ease-out so the press/hover feedback feels instant. */
    transition: background-color 0.16s cubic-bezier(0.23, 1, 0.32, 1),
        border-color 0.16s cubic-bezier(0.23, 1, 0.32, 1),
        color 0.16s cubic-bezier(0.23, 1, 0.32, 1),
        transform 0.16s cubic-bezier(0.23, 1, 0.32, 1);
}

/* Slightly larger icon so the action reads as a real button, not a faint
 * chip. Stays subordinate to the page title. */
.ct-detail-header__chat .ki-outline {
    font-size: 1rem;
}

/* Hover gated to fine pointers so a tap on touch doesn't trigger a
 * sticky hover state. Deepens the fill + border into the indigo accent. */
@media (hover: hover) and (pointer: fine) {
    .ct-detail-header__chat:hover {
        background: #e0e7ff;
        border-color: #a5b4fc;
        color: #3730a3;
    }
}

/* Press feedback — subtle scale so the button confirms it heard the
 * click (Emil: pressable elements must feel responsive). */
.ct-detail-header__chat:active {
    transform: scale(0.97);
}

@media (prefers-reduced-motion: reduce) {
    .ct-detail-header__chat {
        transition: background-color 0.16s ease, border-color 0.16s ease,
            color 0.16s ease;
    }
    .ct-detail-header__chat:active {
        transform: none;
    }
}

/* RD-2247: "Chat about this" sits INSIDE the narrative box, as the last element
 * after the bullets. Resets the header row's margin-left:auto and adds top
 * spacing so it reads as a trailing action under the summary. */
.ct-evidence-chat-row {
    margin-top: 12px;
}

.ct-evidence-chat {
    margin-left: 0;
}

/* Title row — title on the left, DRAG COST badge on the right, both
 * baseline-aligned. Wraps the badge below on narrow viewports so a
 * long title doesn't crowd the badge. */
.ct-detail-header__title-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 0.75rem;
    margin-bottom: 0.5rem;
}

.ct-detail-header__title {
    margin: 0;
    flex: 1 1 auto;
    min-width: 0;
    font-size: 1.5rem;
    font-weight: 700;
    color: #0f172a;
    line-height: 1.2;
}

.ct-detail-header__drag-slot {
    flex: 0 0 auto;
}

.ct-detail-header__subtitle {
    margin: 0 0 1rem;
    color: #64748b;
    font-size: 0.9375rem;
}

/* ─── Agent side panels ──────────────────────────────────────────
 *
 * Two agents live as in-flow flex siblings of `.ct-feed-main-column`
 * (positioning rules earlier in the file):
 *   - Research → visible in list mode (no .is-detail on tab pane)
 *   - Planning → visible in detail mode (.is-detail on tab pane)
 *
 * The panel is hidden by default; a `.ct-agent-expand-btn` inside the
 * Insights tab (one in the chip row for list mode, one above the
 * EVIDENCE card for detail mode) brings it in. The minimize button on
 * the panel header sends it back. Users drag the left edge of the
 * panel to set their preferred width. */

.ct-agent-side-panel {
    display: flex;
    flex-direction: column;
    background: #fff;
    overflow: hidden;
    /* RD-1947: positioning context for the history popover. The popover
     * is anchored under the History button in the header and overlays
     * the feed without taking horizontal space. */
    position: relative;
}

/* Pick which agent flavor is "active" based on list/detail mode. The
 * other one is display:none. Collapse state is layered on top via the
 * `transform: translateX()` rule on the layout selector — it doesn't
 * touch `display`, so the mode swap stays instant while the slide
 * animation handles the visible transition. */
.ct-agent-side-panel--planning { display: none; }

/* Collapsed state wins over the mode-based show rule above — the
 * planning rule (`is-detail .ct-agent-side-panel--planning { display: flex }`)
 * matches with the same specificity as the earlier
 * the pane-scoped hide rule that RD-2759 removed with the panel,
 * so source-order would otherwise leave the planning panel visible
 * in detail mode while the user has the agent minimized. Re-asserting
 * `display: none` here keeps it hidden regardless of mode. */
#ct-tab-active-feed .ct-agent-side-panel--research,
#ct-tab-active-feed .ct-agent-side-panel--planning {
    display: none;
}

.ct-agent-side-panel__header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 0.875rem 1rem;
    border-bottom: 1px solid #e2e8f0;
    flex: 0 0 auto;
}

.ct-agent-side-panel__title {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    font-size: 0.9375rem;
    font-weight: 700;
    color: #0f172a;
}

.ct-agent-side-panel__title-icon { color: #4338ca; }

.ct-agent-side-panel__badge {
    font-size: 0.625rem;
    font-weight: 700;
    letter-spacing: 0.05em;
    padding: 0.125rem 0.375rem;
    border-radius: 0.25rem;
    background: #ecfdf5;
    color: #059669;
}

.ct-agent-side-panel__minimize {
    border: none;
    background: transparent;
    color: #64748b;
    width: 30px;
    height: 30px;
    border-radius: 0.375rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    transition: background 0.15s ease, color 0.15s ease;
}

.ct-agent-side-panel__minimize:hover {
    background: #f1f5f9;
    color: #0f172a;
}

.ct-agent-side-panel__hint {
    padding: 0.75rem 1rem;
    border-bottom: 1px solid #f1f5f9;
    font-size: 0.8125rem;
    color: #64748b;
    line-height: 1.4;
    flex: 0 0 auto;
}

.ct-agent-side-panel__feed {
    flex: 1 1 auto;
    overflow-y: auto;
    padding: 1rem;
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
    scrollbar-width: thin;
    scrollbar-color: #cbd5e1 transparent;
}

.ct-agent-side-panel__feed::-webkit-scrollbar { width: 5px; }
.ct-agent-side-panel__feed::-webkit-scrollbar-thumb {
    background-color: #cbd5e1;
    border-radius: 3px;
}

/* Composer footer — proper bottom chrome so the textarea doesn't feel
 * like a naked input floating against the viewport. Subtle gradient
 * background + soft top shadow create a clear boundary between the
 * scrollable feed above and the input area below. */
.ct-agent-side-panel__composer {
    display: flex;
    align-items: flex-end;
    gap: 0.5rem;
    padding: 0.875rem 1rem 1rem;
    border-top: 1px solid #e2e8f0;
    background: linear-gradient(180deg, #f8fafc 0%, #f1f5f9 100%);
    box-shadow: 0 -4px 12px -8px rgba(15, 23, 42, 0.08);
    flex: 0 0 auto;
}

.ct-agent-side-panel__input {
    flex: 1 1 auto;
    resize: none;
    max-height: 8rem;
    border: 1px solid #cbd5e1;
    border-radius: 0.625rem;
    padding: 0.625rem 0.875rem;
    font-family: inherit;
    font-size: 0.9375rem;
    line-height: 1.4;
    color: #0f172a;
    background: #fff;
    outline: none;
    box-shadow: 0 1px 2px rgba(15, 23, 42, 0.04);
    transition: border-color 0.15s ease, box-shadow 0.15s ease;
}

/* RD-1947: flat context line under the composer that tells the user
 * which dashboard view the agent is scoped to. No box — just icon +
 * label + value inline. agents.js fills it on init from
 * window.getSelectedSourceContexts and wires the click. */
.ct-agent-side-panel__context {
    display: none;
    align-items: center;
    gap: 0.5rem;
    /* Extra top padding gives the chip room to breathe under the
     * composer. */
    padding: 0.875rem 1rem 0.875rem;
    background: transparent;
    flex: 0 0 auto;
    line-height: 1.3;
}

.ct-agent-side-panel__context.is-visible { display: flex; }

.ct-agent-side-panel__context.is-clickable {
    cursor: pointer;
}

.ct-agent-side-panel__context.is-clickable:hover .ct-agent-side-panel__context-value {
    color: #4338ca;
}

.ct-agent-side-panel__context.is-clickable:focus-visible {
    outline: 2px solid #6366f1;
    outline-offset: 2px;
    border-radius: 4px;
}

/* The label + value share a single tooltip wrapper so the tooltip
 * centres over the combined text. The wrapper sizes to its content;
 * inner spans are inline so they flow on one line with a small gap. */
.ct-agent-side-panel__context-text {
    display: inline-flex;
    align-items: baseline;
    gap: 0.375rem;
    cursor: help;
    min-width: 0;
    flex: 0 1 auto;
}

.ct-agent-side-panel__context-label {
    color: #94a3b8;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    font-weight: 700;
    font-size: 0.8125rem;
    flex: 0 0 auto;
}

.ct-agent-side-panel__context-value {
    color: #0f172a;
    font-weight: 600;
    font-size: 0.875rem;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    min-width: 0;
    transition: color 0.15s ease;
}

.ct-agent-side-panel__context-icon {
    color: #4338ca;
    flex: 0 0 auto;
    font-size: 1.0625rem;
}

.ct-agent-side-panel__input:focus {
    border-color: #6366f1;
    box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.15);
}
.ct-agent-side-panel__input::placeholder { color: #94a3b8; }

.ct-agent-side-panel__send {
    flex: 0 0 auto;
    width: 36px;
    height: 36px;
    border: none;
    border-radius: 0.5rem;
    background: #4338ca;
    color: #fff;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    transition: background 0.15s ease, opacity 0.15s ease;
}

.ct-agent-side-panel__send:hover { background: #3730a3; }
.ct-agent-side-panel__send:disabled { opacity: 0.5; cursor: not-allowed; }

/* Agent launcher (`.ct-agent-expand-btn`). Solid violet CTA so users
 * notice the agent entrypoint immediately against the surrounding
 * chrome. Subtle gradient + pulsing glow ring keep it from looking
 * flat without distracting from neighboring controls. */
.ct-agent-expand-btn,
.ct-agent-expand-btn:focus {
    color: #fff;
    background: linear-gradient(135deg, #4f46e5 0%, #7c3aed 100%);
    border: 1px solid transparent;
    box-shadow: 0 4px 10px rgba(79, 70, 229, 0.32),
                0 0 0 0 rgba(124, 58, 237, 0.55);
    animation: ct-agent-btn-pulse 2.4s ease-in-out infinite;
    transition: background 0.18s ease, box-shadow 0.18s ease,
                transform 0.18s ease;
}

.ct-agent-expand-btn:hover {
    color: #fff;
    background: linear-gradient(135deg, #4338ca 0%, #6d28d9 100%);
    box-shadow: 0 6px 18px rgba(79, 70, 229, 0.42),
                0 0 0 4px rgba(124, 58, 237, 0.22);
    /* Pause the pulse on hover so the box-shadow override sticks. */
    animation: none;
    transform: translateY(-1px);
}

.ct-agent-expand-btn:active {
    transform: translateY(0);
}

.ct-agent-expand-btn .js-agent-expand-btn-icon {
    color: #fff;
}

@keyframes ct-agent-btn-pulse {
    0%, 100% {
        box-shadow: 0 4px 10px rgba(79, 70, 229, 0.32),
                    0 0 0 0 rgba(124, 58, 237, 0.55);
    }
    50% {
        box-shadow: 0 4px 10px rgba(79, 70, 229, 0.32),
                    0 0 0 6px rgba(124, 58, 237, 0);
    }
}

@media (prefers-reduced-motion: reduce) {
    .ct-agent-expand-btn { animation: none; }
}

/* ─── Header actions + history popover (RD-1947) ────────────────
 *
 * The panel header carries three buttons on the right: New (start a
 * fresh chat), History (toggle the past-sessions popover), Minimize.
 * The popover overlays the feed when open, so the chat column keeps
 * its full width and the panel doesn't feel cramped. */
.ct-agent-side-panel__actions {
    display: flex;
    align-items: center;
    gap: 0.25rem;
}

.ct-agent-side-panel__action {
    border: none;
    background: transparent;
    color: #64748b;
    width: 30px;
    height: 30px;
    border-radius: 0.375rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    transition: background 0.15s ease, color 0.15s ease;
}

.ct-agent-side-panel__action:hover {
    background: #f1f5f9;
    color: #0f172a;
}

.ct-agent-side-panel__action.is-active {
    background: #eef2ff;
    color: #4338ca;
}

.ct-agent-history-popover {
    /* Anchored to the panel's upper-right (under the History button).
     * `right: 0.5rem; top: 3.25rem` keeps it clear of the header chrome
     * and inside the panel's rounded border. */
    position: absolute;
    top: 3.25rem;
    right: 0.5rem;
    width: 280px;
    max-height: min(420px, calc(100% - 5rem));
    background: #fff;
    border: 1px solid #e2e8f0;
    border-radius: 0.5rem;
    box-shadow: 0 10px 30px -8px rgba(15, 23, 42, 0.18), 0 4px 10px -4px rgba(15, 23, 42, 0.08);
    z-index: 50;
    display: none;
    flex-direction: column;
    overflow: hidden;
}

.ct-agent-history-popover.is-open {
    display: flex;
}

.ct-agent-history-popover__header {
    padding: 0.625rem 0.875rem;
    border-bottom: 1px solid #e2e8f0;
    background: #f8fafc;
    flex: 0 0 auto;
}

.ct-agent-history-popover__title {
    font-size: 0.8125rem;
    font-weight: 700;
    color: #0f172a;
}

.ct-agent-history-popover__list {
    flex: 1 1 auto;
    overflow-y: auto;
    padding: 0.375rem;
    display: flex;
    flex-direction: column;
    gap: 0.125rem;
    scrollbar-width: thin;
    scrollbar-color: #cbd5e1 transparent;
}

.ct-agent-history-popover__list::-webkit-scrollbar { width: 4px; }
.ct-agent-history-popover__list::-webkit-scrollbar-thumb {
    background-color: #cbd5e1;
    border-radius: 3px;
}

/* Each history row is a flex container so the title + timestamp pair
 * can grow while the delete button stays a fixed icon on the right.
 * The button only fades in on hover (or always on touch devices) so
 * the resting state stays clean. */
.ct-agent-history__item {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.5rem 0.625rem;
    border-radius: 0.375rem;
    background: transparent;
    border: 1px solid transparent;
    text-align: left;
    cursor: pointer;
    color: #0f172a;
    transition: background 0.15s ease, border-color 0.15s ease;
}

.ct-agent-history__item:hover {
    background: #f8fafc;
    border-color: #e2e8f0;
}

.ct-agent-history__item.is-active {
    background: #eef2ff;
    border-color: #c7d2fe;
}

.ct-agent-history__item-text {
    flex: 1 1 auto;
    display: flex;
    flex-direction: column;
    gap: 0.125rem;
    min-width: 0;
}

.ct-agent-history__item-title {
    font-size: 0.8125rem;
    font-weight: 600;
    color: #0f172a;
    line-height: 1.3;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
    word-break: break-word;
}

.ct-agent-history__item-time {
    font-size: 0.6875rem;
    color: #64748b;
}

.ct-agent-history__delete {
    flex: 0 0 auto;
    border: none;
    background: transparent;
    color: #94a3b8;
    width: 26px;
    height: 26px;
    border-radius: 0.25rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    opacity: 0;
    transition: opacity 0.15s ease, background 0.15s ease, color 0.15s ease;
}

.ct-agent-history__item:hover .ct-agent-history__delete,
.ct-agent-history__item:focus-within .ct-agent-history__delete {
    opacity: 1;
}

.ct-agent-history__delete:hover {
    background: #fee2e2;
    color: #b91c1c;
}

.ct-agent-history__empty {
    padding: 1rem 0.75rem;
    font-size: 0.75rem;
    color: #94a3b8;
    text-align: center;
    line-height: 1.4;
}

/* Shared confirm dialog. Fixed full-screen overlay; the dialog box is
 * centered. Backdrop click and Cancel dismiss with `cancel`; Delete /
 * Enter resolves with `accept`. Hidden by default; agents.js toggles
 * the `.is-open` modifier. */
.ct-agent-confirm {
    position: fixed;
    inset: 0;
    display: none;
    align-items: center;
    justify-content: center;
    z-index: 1080; /* above Bootstrap modals (1055) so it stacks on top. */
}

.ct-agent-confirm.is-open { display: flex; }

.ct-agent-confirm__backdrop {
    position: absolute;
    inset: 0;
    background: rgba(15, 23, 42, 0.45);
}

.ct-agent-confirm__dialog {
    position: relative;
    background: #fff;
    border-radius: 0.625rem;
    box-shadow: 0 20px 50px -12px rgba(15, 23, 42, 0.35), 0 6px 16px -6px rgba(15, 23, 42, 0.18);
    padding: 1.25rem 1.25rem 1rem;
    width: min(420px, calc(100vw - 2rem));
}

.ct-agent-confirm__title {
    margin: 0 0 0.5rem;
    font-size: 1rem;
    font-weight: 700;
    color: #0f172a;
}

.ct-agent-confirm__message {
    margin: 0 0 1rem;
    font-size: 0.875rem;
    color: #475569;
    line-height: 1.45;
    /* `word-break: break-word` is needed because the title we splice
     * into the message can be a long un-spaced LLM-generated string. */
    word-break: break-word;
}

.ct-agent-confirm__actions {
    display: flex;
    justify-content: flex-end;
    gap: 0.5rem;
}

.ct-agent-confirm__btn {
    border: 1px solid transparent;
    border-radius: 0.4375rem;
    padding: 0.5rem 0.875rem;
    font-size: 0.875rem;
    font-weight: 600;
    cursor: pointer;
    transition: background 0.15s ease, border-color 0.15s ease, color 0.15s ease;
}

.ct-agent-confirm__btn--cancel {
    background: #fff;
    color: #0f172a;
    border-color: #cbd5e1;
}

.ct-agent-confirm__btn--cancel:hover {
    background: #f8fafc;
    border-color: #94a3b8;
}

.ct-agent-confirm__btn--accept {
    background: #dc2626;
    color: #fff;
    border-color: #dc2626;
}

.ct-agent-confirm__btn--accept:hover {
    background: #b91c1c;
    border-color: #b91c1c;
}


/* ─── Agent chat (RD-1800) ──────────────────────────────────────
 *
 * Lives inside .ct-agent-side-panel__feed. Three message kinds:
 * user (blue, right-aligned), assistant (gray, left-aligned, markdown),
 * thinking (animated dots while waiting). Tool-call rows render between
 * a user turn and the assistant reply, one per call. Inline approval
 * row appends Approve/Reject buttons under the assistant bubble when
 * the agent queues a proposal. */

.ct-chat-msg {
    display: flex;
    flex-direction: column;
    max-width: 100%;
}

.ct-chat-msg--user     { align-items: flex-end; }
.ct-chat-msg--assistant,
.ct-chat-msg--thinking,
.ct-chat-msg--error    { align-items: flex-start; }

.ct-chat-msg__bubble {
    padding: 0.5rem 0.75rem;
    border-radius: 0.625rem;
    font-size: 0.875rem;
    line-height: 1.4;
    word-wrap: break-word;
    max-width: 90%;
}

.ct-chat-msg--user .ct-chat-msg__bubble {
    background: #4338ca;
    color: #fff;
    white-space: pre-wrap;
}

.ct-chat-msg--assistant .ct-chat-msg__bubble {
    background: #f1f5f9;
    color: #0f172a;
}

.ct-chat-msg--error .ct-chat-msg__bubble {
    background: #fef2f2;
    color: #b91c1c;
    border: 1px solid #fecaca;
}

/* Markdown bubble: tighten the default paragraph/list margins so the
 * bubble doesn't bloat when the LLM emits a list. */
.ct-chat-msg__bubble--md > :first-child { margin-top: 0; }
.ct-chat-msg__bubble--md > :last-child  { margin-bottom: 0; }
.ct-chat-msg__bubble--md p { margin: 0 0 0.5rem; }
.ct-chat-msg__bubble--md ul,
.ct-chat-msg__bubble--md ol { margin: 0.25rem 0 0.5rem; padding-left: 1.25rem; }
.ct-chat-msg__bubble--md li { margin: 0.125rem 0; }
.ct-chat-msg__bubble--md h1,
.ct-chat-msg__bubble--md h2,
.ct-chat-msg__bubble--md h3,
.ct-chat-msg__bubble--md h4 {
    margin: 0.625rem 0 0.375rem;
    font-weight: 600;
    line-height: 1.25;
}
.ct-chat-msg__bubble--md h1 { font-size: 1.0625rem; }
.ct-chat-msg__bubble--md h2 { font-size: 1rem; }
.ct-chat-msg__bubble--md h3,
.ct-chat-msg__bubble--md h4 { font-size: 0.9375rem; }
.ct-chat-msg__bubble--md code {
    background: #e2e8f0;
    padding: 0.0625rem 0.3125rem;
    border-radius: 0.25rem;
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 0.85em;
}
.ct-chat-msg__bubble--md pre {
    background: #1e293b;
    color: #f1f5f9;
    padding: 0.625rem 0.75rem;
    border-radius: 0.375rem;
    overflow-x: auto;
    margin: 0.5rem 0;
}
.ct-chat-msg__bubble--md pre code { background: transparent; color: inherit; padding: 0; font-size: 0.8125rem; }
.ct-chat-msg__bubble--md a { color: #4338ca; text-decoration: underline; }
.ct-chat-msg__bubble--md table { border-collapse: collapse; margin: 0.375rem 0; font-size: 0.8125rem; }
.ct-chat-msg__bubble--md th,
.ct-chat-msg__bubble--md td { border: 1px solid #e2e8f0; padding: 0.25rem 0.5rem; text-align: left; }
.ct-chat-msg__bubble--md th { background: #f1f5f9; font-weight: 600; }

/* Thinking dots — animated bouncing trio. */
.ct-chat-msg--thinking .ct-chat-msg__bubble {
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
}
.ct-chat-thinking-dot {
    width: 6px; height: 6px;
    border-radius: 50%;
    background: #94a3b8;
    animation: ct-chat-bounce 1.2s infinite ease-in-out both;
}
.ct-chat-thinking-dot:nth-child(2) { animation-delay: 0.15s; }
.ct-chat-thinking-dot:nth-child(3) { animation-delay: 0.3s; }
@keyframes ct-chat-bounce {
    0%, 80%, 100% { transform: scale(0.6); opacity: 0.5; }
    40%           { transform: scale(1);   opacity: 1;   }
}
.ct-plan-stages {
    border: 1px solid #e4e9f2;
    border-radius: 12px;
    background: linear-gradient(180deg, #fbfbff, #ffffff);
    padding: 14px 16px;
    margin: 4px 0 18px;
    animation: ctPlanStagesRise 0.24s var(--ct-ease-out);
}
.ct-plan-stages__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 6px;
}
.ct-plan-stages__title {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    font-size: 12.5px;
    font-weight: 700;
    color: #4338ca;
}
.ct-plan-stages__spark { display: inline-flex; }
.ct-plan-stages__title svg { display: block; width: 15px; height: 15px; }
/* Tabular figures so the seconds column doesn't jitter as it counts. */
.ct-plan-stages__timer {
    font-size: 11.5px;
    font-weight: 600;
    color: #5b6b8c;
    font-variant-numeric: tabular-nums;
}
@keyframes ctPlanSheen { to { background-position: -200% 0; } }
/* Status line that changes as the agent works; a short fade on each swap so
   the copy replacing itself reads as one line changing, not two lines
   flickering. */
.ct-plan-stages__status {
    font-size: 12.5px;
    font-weight: 600;
    color: #1C2E62;
}
.ct-plan-stages__status.is-swap { animation: ctPlanStatusIn 0.28s var(--ct-ease-out); }
@keyframes ctPlanStatusIn { from { opacity: 0.25; transform: translateY(3px); } }
/* Status line left, the don't-refresh hint right — one row. */
.ct-plan-stages__row {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 12px;
}
/* The hint absorbed the old sub-line's warning, so it is now a sentence rather
   than a three-word aside and `white-space: nowrap` would push it out of the
   panel on a narrow viewport. It wraps instead, stays right-aligned, and keeps
   a floor so the status line to its left cannot squeeze it to one word a row. */
.ct-plan-stages__hint {
    font-size: 11px;
    color: #8494b3;
    text-align: right;
    flex: 0 1 auto;
    min-width: 12ch;
    line-height: 1.35;
}
.ct-plan-stages.is-leaving {
    opacity: 0;
    transform: translateY(-4px);
    transition: opacity 0.18s var(--ct-ease-out), transform 0.18s var(--ct-ease-out);
}
@keyframes ctPlanStagesRise { from { opacity: 0; transform: translateY(8px); } }

/* Tool-call rows — one per invocation, between turns. */
.ct-chat-tools {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
    margin-left: 0.25rem;
}
.ct-chat-tool-row {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.25rem 0.5rem;
    background: #f8fafc;
    border: 1px solid #e2e8f0;
    border-radius: 0.375rem;
    font-size: 0.75rem;
    color: #475569;
}
.ct-chat-tool-row--err { background: #fef2f2; border-color: #fecaca; color: #b91c1c; }
.ct-chat-tool-row__icon { font-size: 0.875rem; }
.ct-chat-tool-row__label { font-weight: 600; }
.ct-chat-tool-row__arg {
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    color: #64748b;
}
.ct-chat-tool-row__status {
    margin-left: auto;
    font-size: 0.6875rem;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    font-weight: 700;
}

/* Approval row — under the assistant bubble that queued the proposal. */
.ct-chat-approval {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    margin-top: 0.5rem;
    padding: 0.5rem 0.625rem;
    background: #fef9c3;
    border: 1px solid #fde68a;
    border-radius: 0.375rem;
}
.ct-chat-approval__hint {
    flex: 1 1 auto;
    font-size: 0.75rem;
    color: #854d0e;
    font-weight: 600;
}
.ct-chat-approval__btn {
    padding: 0.25rem 0.625rem;
    border: 1px solid transparent;
    border-radius: 0.375rem;
    font-size: 0.75rem;
    font-weight: 600;
    cursor: pointer;
    transition: background 0.15s ease, color 0.15s ease;
}
.ct-chat-approval__btn--approve {
    background: #16a34a;
    color: #fff;
    border-color: #16a34a;
}
.ct-chat-approval__btn--approve:hover:not(:disabled) { background: #15803d; }
.ct-chat-approval__btn--reject {
    background: #fff;
    color: #b91c1c;
    border-color: #fecaca;
}
.ct-chat-approval__btn--reject:hover:not(:disabled) { background: #fef2f2; }
.ct-chat-approval__btn:disabled { opacity: 0.5; cursor: not-allowed; }

.ct-chat-approval__status {
    font-size: 0.75rem;
    font-weight: 700;
}
.ct-chat-approval__status--approved { color: #15803d; }
.ct-chat-approval__status--rejected { color: #b91c1c; }
.ct-chat-approval__status--err      { color: #b91c1c; }

/* RD-1867: inline preview of a propose_create_insight tool call so the
 * user can read the proposal before clicking Approve. Replaces the
 * one-line JSON-dump row for propose_* tools. */
.ct-chat-proposal {
    margin-top: 0.25rem;
    padding: 0.625rem 0.75rem;
    background: #faf5ff;
    border: 1px solid #e9d5ff;
    border-radius: 0.5rem;
    font-size: 0.8125rem;
    line-height: 1.45;
    color: #1f2937;
    display: block;
}
.ct-chat-proposal__label {
    font-size: 0.6875rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: #7c3aed;
    margin-bottom: 0.375rem;
}
.ct-chat-proposal__title {
    font-weight: 700;
    font-size: 0.9375rem;
    color: #111827;
    margin-bottom: 0.375rem;
}
.ct-chat-proposal__meta {
    display: flex;
    flex-wrap: wrap;
    gap: 0.375rem;
    margin-bottom: 0.5rem;
    font-size: 0.6875rem;
}
.ct-chat-proposal__meta > span {
    padding: 0.125rem 0.4375rem;
    border-radius: 0.25rem;
    background: #ede9fe;
    color: #5b21b6;
    text-transform: capitalize;
}
.ct-chat-proposal__sev--high   { background: #fee2e2 !important; color: #991b1b !important; }
.ct-chat-proposal__sev--medium { background: #fef3c7 !important; color: #92400e !important; }
.ct-chat-proposal__sev--low    { background: #dcfce7 !important; color: #166534 !important; }
.ct-chat-proposal__period {
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace !important;
    background: #f1f5f9 !important;
    color: #475569 !important;
}
.ct-chat-proposal__summary {
    font-weight: 600;
    color: #374151;
    margin-bottom: 0.5rem;
}
.ct-chat-proposal__narrative {
    color: #4b5563;
    margin-bottom: 0.5rem;
    white-space: pre-line;
}
.ct-chat-proposal__resolution {
    margin-top: 0.5rem;
    padding-top: 0.5rem;
    border-top: 1px dashed #d8b4fe;
}
.ct-chat-proposal__resolution-title {
    font-weight: 600;
    color: #6b21a8;
    margin-bottom: 0.375rem;
}
.ct-chat-proposal__steps {
    margin: 0;
    padding-left: 1.25rem;
    color: #4b5563;
}
.ct-chat-proposal__steps li {
    margin-bottom: 0.25rem;
}
/* Collapsed-by-default disclosure that wraps narrative + resolution
 * steps so the card stays compact. */
.ct-chat-proposal__more {
    margin-top: 0.25rem;
}
.ct-chat-proposal__more > summary {
    cursor: pointer;
    user-select: none;
    font-size: 0.75rem;
    font-weight: 600;
    color: #7c3aed;
    padding: 0.25rem 0;
    list-style: none;
}
.ct-chat-proposal__more > summary::-webkit-details-marker { display: none; }
.ct-chat-proposal__more > summary::before {
    content: '▸ ';
    display: inline-block;
    transition: transform 0.15s ease;
    margin-right: 0.125rem;
}
.ct-chat-proposal__more[open] > summary::before {
    transform: rotate(90deg);
}

/* Mirror the active-feed override that lets the explorer flow at its
 * natural height (instead of inheriting the legacy viewport-cap that
 * produced an internal scroll on the evidence card body). Without
 * this, the Early Warning evidence becomes scrollable
 * inside the card even though there's plenty of page space. */
#bottleneck-insights-explorer-ip .card.card-flush.h-xl-100 {
    height: auto !important;
}

@keyframes ctPlanStatusPulse {
    0%   { transform: scale(0.85); opacity: 0.7; }
    70%  { transform: scale(1.35); opacity: 0; }
    100% { transform: scale(1.35); opacity: 0; }
}
@keyframes ctWizardSectionReveal {
    0%   { opacity: 0; transform: translateY(-10px); }
    100% { opacity: 1; transform: translateY(0); }
}
@keyframes ctWizardSectionCollapse {
    0%   { opacity: 1; transform: translateY(0); }
    100% { opacity: 0; transform: translateY(-10px); }
}
@keyframes ctSpinnerPulse {
    0%, 80%, 100% { opacity: 0.3; transform: scale(0.85); }
    40%           { opacity: 1;   transform: scale(1);    }
}

/* When the planning side panel is moved into the wizard host, it
 * loses its floating panel shell and behaves as a static block that
 * fills the host. The original side-panel layout still applies for
 * Research and for v1 insights (which never embed). */
.ct-agent-side-panel.is-embedded {
    position: static;
    display: flex !important;
    flex-direction: column;
    width: 100% !important;
    max-width: none !important;
    height: 100%;
    min-height: 0;
    border: none;
    border-radius: 0;
    background: transparent;
    box-shadow: none;
    padding: 0;
}
.ct-agent-side-panel.is-embedded .ct-agent-side-panel__header {
    flex: 0 0 auto;
    padding: 10px 14px;
    background: linear-gradient(135deg, #4338ca 0%, #6366f1 100%);
    color: #ffffff;
    border-radius: 12px 12px 0 0;
}
.ct-agent-side-panel.is-embedded .ct-agent-side-panel__title {
    color: #ffffff;
    font-size: 13px;
}
.ct-agent-side-panel.is-embedded .ct-agent-side-panel__title-icon {
    color: #ffffff;
}
.ct-agent-side-panel.is-embedded .ct-agent-side-panel__badge {
    background: rgba(255, 255, 255, 0.18);
    color: #ffffff;
    border-color: transparent;
}
/* Hint is informational only; keep it small + single-line where
 * possible so the feed gets the visual budget. */
.ct-agent-side-panel.is-embedded .ct-agent-side-panel__hint {
    flex: 0 0 auto;
    background: #f8fafc;
    color: #475569;
    padding: 6px 14px;
    border-bottom: 1px solid #e2e8f0;
    margin: 0;
    font-size: 11.5px;
    line-height: 1.4;
}
.ct-agent-side-panel.is-embedded .ct-agent-side-panel__feed {
    /* `flex: 1 1 0` (not `auto`) — when the feed is empty we still
     * want it to take all the remaining vertical space so the composer
     * sticks to the bottom of the chat box. `min-height: 0` lets the
     * feed shrink inside its flex container so the composer never
     * overflows. */
    flex: 1 1 0;
    min-height: 0;
    padding: 14px 16px;
    background: #ffffff;
    overflow-y: auto;
}
/* Compact composer — horizontal layout, single-line textarea by
 * default that grows up to `max-height` on multi-line input. The
 * previous default styled the composer for a 460-px tall side panel,
 * which felt oversized once embedded next to the wizard content. */
.ct-agent-side-panel.is-embedded .ct-agent-side-panel__composer {
    flex: 0 0 auto;
    display: flex;
    align-items: flex-end;
    gap: 8px;
    padding: 10px 12px;
    background: #ffffff;
    border-top: 1px solid #e2e8f0;
    box-shadow: 0 -2px 8px -6px rgba(15, 23, 42, 0.06);
}
.ct-agent-side-panel.is-embedded .ct-agent-side-panel__input {
    flex: 1 1 auto;
    min-height: 38px;
    max-height: 110px;
    padding: 8px 12px;
    font-size: 13px;
    line-height: 1.4;
    border: 1px solid #cbd5e1;
    border-radius: 8px;
    resize: none;
}
.ct-agent-side-panel.is-embedded .ct-agent-side-panel__input:focus {
    border-color: #4338ca;
    box-shadow: 0 0 0 3px rgba(67, 56, 202, 0.12);
}
.ct-agent-side-panel.is-embedded .ct-agent-side-panel__send {
    flex: 0 0 auto;
    width: 38px;
    height: 38px;
    padding: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: #4338ca;
    color: #ffffff;
    border: none;
    border-radius: 8px;
    transition: background 0.15s ease;
}
.ct-agent-side-panel.is-embedded .ct-agent-side-panel__send:hover {
    background: #3730a3;
}
.ct-agent-side-panel.is-embedded .ct-agent-side-panel__minimize {
    /* Embedded mode: the chat sits inside the wizard card, so the
     * minimize button is not meaningful — hide it to avoid suggesting
     * a side-panel affordance. */
    display: none;
}
/* The context bar surfaces "Insight: <title>" / "Open an insight to
 * talk to the Resolution Agent" — useful in list mode but redundant
 * here because the embedded chat only renders inside the insight's
 * own detail view. Hide it to remove the misleading "Open an insight
 * …" placeholder. */
.ct-agent-side-panel.is-embedded .ct-agent-side-panel__context {
    display: none !important;
}
@keyframes ctWatchPulse {
    0%, 100% { opacity: 1; transform: scale(1); }
    50%      { opacity: 0.35; transform: scale(0.75); }
}

/* "Plan ready — review on the Resolution panel above" notice that
 * stays in the chat feed when the agent emits a propose_resolution_plan.
 * The full preview lives in the Proposal card now. */
.ct-chat-tool-row--plan-notice {
    background: #eef2ff;
    color: #4338ca;
    border: 1px solid #c7d2fe;
    border-radius: 8px;
}

/* Entry motion: each new surface fades and rises in once, mirroring the
 * detail panel's ct-detail-enter. Decided chips swap in place with no
 * animation so collapsing never feels jumpy. */
@keyframes ct-ar-enter {
    from {
        opacity: 0;
        transform: translateY(8px);
    }
    to {
        opacity: 1;
        transform: translateY(0);
    }
}

/* ─── RD-2342: Insight Reports (Deep analysis) ─── */

/* Prominent AI launcher, PINNED to the card-header top-right (far right of the
   evidence card, at the height of the EVIDENCE / target-badge row). */
.ct-report-btn {
    position: absolute;
    top: 50%;
    right: 24px;
    transform: translateY(-50%);
    display: inline-flex;
    align-items: center;
    gap: 7px;
    min-width: 148px;
    justify-content: center;
    padding: 8px 16px;
    font-size: 12.5px;
    font-weight: 600;
    color: #fff;
    background: linear-gradient(135deg, #419FFF 0%, #4338ca 100%);
    border: none;
    border-radius: 8px;
    cursor: pointer;
    box-shadow: 0 3px 10px -2px rgba(67, 56, 202, 0.5);
    /* GPU-friendly: animate filter/shadow, never layout. */
    transition: filter 0.18s var(--ct-ease-out), box-shadow 0.18s var(--ct-ease-out), opacity 0.18s var(--ct-ease-out);
}
.ct-report-btn__ic {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 19px;
    height: 19px;
    flex: 0 0 19px;
    color: #fff; /* the sparkles / spinner render white */
}
.ct-report-btn__ic svg { display: block; width: 19px; height: 19px; }
.ct-report-btn__ic .spinner-border-sm { width: 0.9rem; height: 0.9rem; border-width: 2px; }
@media (hover: hover) and (pointer: fine) {
    .ct-report-btn:hover { filter: brightness(1.07); box-shadow: 0 8px 20px -4px rgba(67, 56, 202, 0.6); }
}
.ct-report-btn:active { filter: brightness(0.97); }
.ct-report-btn:focus-visible { outline: 2px solid #4338ca; outline-offset: 2px; }
.ct-report-btn:disabled { cursor: default; }
.ct-report-btn.is-thinking { opacity: 0.9; }
/* Ready state: subtle solid indigo so "View analysis" reads as a distinct,
   settled state rather than the same call-to-action as before. */
.ct-report-btn.is-ready { background: #4338ca; }

/* The report popup is wider than the details modal so the card breathes. */
.ct-details-modal--report { max-width: 880px; width: 94%; }
.ct-details-modal--report .ct-details-modal__body { padding: 0; background: #F9F9F9; }
.ct-details-modal--report .ct-report { border: none; box-shadow: none; border-radius: 0; }
.ct-report-modal__spark { display: inline-flex; margin-right: 8px; color: #4338ca; }

/* ─── RD-2455: report-popup footer ────────────────────────────────
 * The details-modal shell is shared with openDetailsModal() and this file is
 * loaded globally from head.php, so EVERY rule below is scoped to
 * .ct-details-modal--report. The base shell has no __footer element and must
 * not grow one. */

/* The body paints #F9F9F9 edge-to-edge and the footer paints white, so both
   have to be clipped by the shell's 12px radius or the bottom corners square
   off. The scroll lives on __body, so hiding overflow here costs nothing. */
.ct-details-modal--report { overflow: hidden; }

/* Outside the scroll container (the modal is a flex column, __body owns
   flex: 1 + overflow-y: auto), so the footer stays put while the artifact
   scrolls under it. flex-shrink: 0 mirrors __header. */
.ct-details-modal--report .ct-details-modal__footer,
.ct-details-modal--report .ct-report-modal__footer {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: 12px;
    flex-shrink: 0;
    padding: 13px 20px;
    background: #fff;
    border-top: 1px solid #e2e8f0;
}

/* Primary CTA — same celeste→indigo mark as .ct-report-btn, so the launcher,
   the popup and its one action read as a single AI surface. */
.ct-details-modal--report .ct-report-modal__cta {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 7px;
    padding: 8px 18px;
    -webkit-appearance: none;
    appearance: none;
    font-family: inherit;
    font-size: 12.5px;
    font-weight: 600;
    line-height: 1.4;
    color: #fff;
    background: linear-gradient(135deg, #419FFF 0%, #4338ca 100%);
    border: none;
    border-radius: 8px;
    cursor: pointer;
    box-shadow: 0 3px 10px -2px rgba(67, 56, 202, 0.5);
    /* GPU-friendly: filter/shadow/transform only, never layout. */
    transition: filter 0.18s var(--ct-ease-out),
                box-shadow 0.18s var(--ct-ease-out),
                transform 0.12s var(--ct-ease-out);
}
.ct-details-modal--report .ct-report-modal__cta::-moz-focus-inner { border: 0; padding: 0; }
.ct-details-modal--report .ct-report-modal__cta svg { display: block; width: 16px; height: 16px; }
@media (hover: hover) and (pointer: fine) {
    .ct-details-modal--report .ct-report-modal__cta:hover {
        filter: brightness(1.07);
        box-shadow: 0 8px 20px -4px rgba(67, 56, 202, 0.6);
    }
}
.ct-details-modal--report .ct-report-modal__cta:active {
    transform: scale(0.97);
    filter: brightness(0.97);
}
.ct-details-modal--report .ct-report-modal__cta:focus-visible {
    outline: 2px solid var(--ct-accent);
    outline-offset: 2px;
}

@media (prefers-reduced-motion: reduce) {
    .ct-details-modal--report .ct-report-modal__cta { transition: none; }
    .ct-details-modal--report .ct-report-modal__cta:hover,
    .ct-details-modal--report .ct-report-modal__cta:active { filter: none; }
    .ct-details-modal--report .ct-report-modal__cta:active { transform: none; }
}

/* ─── RD-2903: the regeneration's progress, above the old artifact ───
 * The artifact body is server-rendered and paints edge to edge on #F9F9F9
 * (__body has padding: 0), so the stages host mounted above it has to bring its
 * own gutters — the same 20px the header and footer use, so the panel lines up
 * with the chrome instead of with the artifact card's own inset.
 *
 * :not(:empty) because the host exists for the whole run and beyond: an empty
 * host with padding would leave a dead band above the artifact. */
.ct-details-modal--report .ct-report-modal__regen-host:not(:empty) {
    padding: 14px 20px 0;
}
@media (max-width: 640px) {
    .ct-details-modal--report .ct-report-modal__regen-host:not(:empty) {
        padding: 12px 16px 0;
    }
}

/* ─── RD-2903: "Add context" — the footer's quiet second action ───
 * Same footer row as the CTA, opposite end. The left placement is bought with
 * margin-right: auto on the BUTTON, not by changing the footer's
 * justify-content, so a footer with only a CTA is untouched.
 *
 * Deliberately not a second gradient: two filled AI-gradient buttons in one row
 * would both claim to be the thing to press. This one is a light button —
 * present, findable, and clearly one step back from "Resolve with agent". */
.ct-details-modal--report .ct-report-modal__footer {
    /* The composer hangs off this element (position: absolute, bottom: 100%),
       which is also what puts the footer above the scrolling body in paint
       order once it is positioned. */
    position: relative;
}

.ct-details-modal--report .ct-report-modal__secondary {
    margin-right: auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 7px;
    padding: 8px 14px;
    -webkit-appearance: none;
    appearance: none;
    font-family: inherit;
    font-size: 12.5px;
    font-weight: 600;
    line-height: 1.4;
    color: #2F3437;
    background: #fff;
    border: 1px solid #E2E8F0;
    border-radius: 8px;
    cursor: pointer;
    transition: background-color 0.14s var(--ct-ease-out),
                border-color 0.14s var(--ct-ease-out),
                transform 0.12s var(--ct-ease-out);
}
.ct-details-modal--report .ct-report-modal__secondary::-moz-focus-inner { border: 0; padding: 0; }

/* Fixed glyph box so the label sits on the same line whatever the icon's
   intrinsic width is — the same treatment the share rows give theirs. */
.ct-details-modal--report .ct-report-modal__secondary i {
    width: 14px;
    font-size: 13px;
    text-align: center;
    color: #64748B;
    transition: color 0.14s var(--ct-ease-out);
}

@media (hover: hover) and (pointer: fine) {
    .ct-details-modal--report .ct-report-modal__secondary:hover {
        background: #F5F7FA;
        border-color: #CBD5E1;
    }
    .ct-details-modal--report .ct-report-modal__secondary:hover i {
        color: var(--ct-accent);
    }
}
.ct-details-modal--report .ct-report-modal__secondary:active {
    transform: scale(0.97);
}
.ct-details-modal--report .ct-report-modal__secondary:focus-visible {
    outline: 2px solid var(--ct-accent);
    outline-offset: 2px;
}

/* ── the composer ──
 * Not a Bootstrap modal on top of the popup: the popup is its own overlay at
 * z-index 1050, and nesting a .modal inside it fights that backdrop. This is a
 * panel parented to the footer, so it opens over the artifact it is about and
 * goes out with the popup that owns it. */
.ct-details-modal--report .ct-context-composer {
    position: absolute;
    /* Opens UPWARD, out of the button: from the bottom of the shell there is
       nothing below to open into. */
    bottom: calc(100% + 8px);
    left: 20px;
    z-index: 6;
    width: min(420px, calc(100% - 40px));
    /* Scales out of the corner the trigger sits in, never out of its own
       centre. */
    transform-origin: bottom left;
    opacity: 0;
    visibility: hidden;
    transform: translateY(6px) scale(0.97);
    /* Exit quicker than entry: the system getting out of the way, not the user
       deciding. visibility is held to the end of the fade so the panel stays
       hit-testable through its own exit. */
    transition: opacity 0.12s var(--ct-ease-out),
                transform 0.12s var(--ct-ease-out),
                visibility 0s linear 0.12s;
}
.ct-details-modal--report .ct-context-composer.is-open {
    opacity: 1;
    visibility: visible;
    transform: none;
    transition: opacity 0.165s var(--ct-ease-out),
                transform 0.165s var(--ct-ease-out),
                visibility 0s linear 0s;
}

.ct-details-modal--report .ct-context-composer__panel {
    padding: 14px 15px 12px;
    background: #FFFFFF;
    border: 1px solid #E2E8F0;
    border-radius: 10px;
    box-shadow: 0 10px 30px -8px rgba(15, 23, 42, 0.18),
                0 4px 10px -4px rgba(15, 23, 42, 0.08);
    /* The ceiling openContextComposer measures: the room that actually exists
       above the footer. Without it a short artifact clips the panel's own title
       against the shell's overflow: hidden. `none` is the fallback so the panel
       is never smaller than its content if the measurement never ran. */
    max-height: var(--ct-composer-max-h, none);
    /* Under that ceiling the title and the buttons stay put and the flexible
       middle gives way — the alternative, scrolling the whole panel, hides
       "Add" below a fold in the one situation where the panel is already
       cramped. */
    display: flex;
    flex-direction: column;
    overflow: hidden;
}

/* The two that may give: the paragraph scrolls, the textarea shrinks. Both need
   min-height: 0 or flexbox refuses to take them below their content size. */
.ct-details-modal--report .ct-context-composer__copy,
.ct-details-modal--report .ct-context-composer__input {
    min-height: 0;
}
.ct-details-modal--report .ct-context-composer__copy {
    flex: 0 1 auto;
    overflow-y: auto;
    overscroll-behavior: contain;
}
.ct-details-modal--report .ct-context-composer__input {
    flex: 1 1 auto;
}
.ct-details-modal--report .ct-context-composer__title,
.ct-details-modal--report .ct-context-composer__actions {
    flex: 0 0 auto;
}

.ct-details-modal--report .ct-context-composer__title {
    font-size: 12.5px;
    font-weight: 600;
    line-height: 1.35;
    color: #2F3437;
}

.ct-details-modal--report .ct-context-composer__copy {
    margin: 3px 0 9px;
    font-size: 11.5px;
    line-height: 1.45;
    color: #64748B;
}

.ct-details-modal--report .ct-context-composer__input {
    display: block;
    width: 100%;
    padding: 8px 10px;
    font-family: inherit;
    font-size: 12.5px;
    line-height: 1.5;
    color: #2F3437;
    background: #F9F9F8;
    border: 1px solid #E2E8F0;
    border-radius: 8px;
    /* Vertical only: a horizontal drag would push the panel past the shell,
       which clips at overflow: hidden. */
    resize: vertical;
    transition: border-color 0.14s var(--ct-ease-out),
                background-color 0.14s var(--ct-ease-out);
}
.ct-details-modal--report .ct-context-composer__input:focus {
    background: #FFFFFF;
    border-color: #419FFF;
    outline: none;
}
.ct-details-modal--report .ct-context-composer__input::placeholder {
    color: #94A3B8;
}

.ct-details-modal--report .ct-context-composer__actions {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-top: 10px;
}

/* Pushes the two buttons to the right and takes the leftover row itself, so the
   count never moves as the number of digits changes. */
.ct-details-modal--report .ct-context-composer__count {
    margin-right: auto;
    font-size: 10.5px;
    color: #94A3B8;
    font-variant-numeric: tabular-nums;
}
.ct-details-modal--report .ct-context-composer__count.is-near-limit {
    color: var(--ct-sev-medium);
}

/* The pair is a text button + a filled one, not two filled ones: Cancel is a
   way out, not an alternative worth the same weight. */
.ct-details-modal--report .ct-context-composer__btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 6px 13px;
    -webkit-appearance: none;
    appearance: none;
    font-family: inherit;
    font-size: 12px;
    font-weight: 600;
    line-height: 1.4;
    color: #64748B;
    background: transparent;
    border: none;
    border-radius: 7px;
    cursor: pointer;
    transition: background-color 0.14s var(--ct-ease-out),
                color 0.14s var(--ct-ease-out),
                filter 0.14s var(--ct-ease-out),
                transform 0.12s var(--ct-ease-out);
}
.ct-details-modal--report .ct-context-composer__btn::-moz-focus-inner { border: 0; padding: 0; }

.ct-details-modal--report .ct-context-composer__btn--primary {
    color: #fff;
    background: #419FFF;
}

/* Saving: the button holds its label and stops answering. No spinner — the
   round trip is one small write and a spinner appearing for 200ms reads worse
   than a button that simply does not bounce back. */
.ct-details-modal--report .ct-context-composer__btn:disabled {
    cursor: default;
    opacity: 0.6;
}

@media (hover: hover) and (pointer: fine) {
    .ct-details-modal--report .ct-context-composer__btn:not(:disabled):hover {
        background: #F5F7FA;
        color: #2F3437;
    }
    .ct-details-modal--report .ct-context-composer__btn--primary:not(:disabled):hover {
        background: #419FFF;
        color: #fff;
        filter: brightness(1.06);
    }
}
.ct-details-modal--report .ct-context-composer__btn:not(:disabled):active {
    transform: scale(0.97);
}
.ct-details-modal--report .ct-context-composer__btn:focus-visible {
    outline: 2px solid var(--ct-accent);
    outline-offset: 2px;
}

@media (prefers-reduced-motion: reduce) {
    /* Opacity aids comprehension and stays; the movement goes. */
    .ct-details-modal--report .ct-context-composer {
        transform: none;
        transition: opacity 0.12s linear, visibility 0s linear 0.12s;
    }
    .ct-details-modal--report .ct-context-composer.is-open {
        transition: opacity 0.12s linear, visibility 0s linear 0s;
    }
    .ct-details-modal--report .ct-report-modal__secondary,
    .ct-details-modal--report .ct-context-composer__btn { transition: none; }
    .ct-details-modal--report .ct-report-modal__secondary:active,
    .ct-details-modal--report .ct-context-composer__btn:not(:disabled):active { transform: none; }
    .ct-details-modal--report .ct-context-composer__btn--primary:not(:disabled):hover { filter: none; }
}

/* ─── RD-2455: report popup on small screens ──────────────────────
 * The shell ships zero media queries and the artifact's own <style> uses 26px
 * section padding and a 52px hero stat, so on a phone the popup overflows.
 * Scoped to --report so the shared shell keeps its current behaviour at every
 * breakpoint. Each override out-specifies the partial's inline rules by the
 * single extra .ct-details-modal--report class. */
@media (max-width: 640px) {
    .ct-details-modal--report {
        width: calc(100% - 20px);
        max-width: none;
        max-height: 88vh;
        max-height: 88dvh;
    }
    .ct-details-modal--report .ct-details-modal__header { padding: 13px 16px; }
    .ct-details-modal--report .ct-details-modal__title { font-size: 14px; }

    .ct-details-modal--report .ct-details-modal__footer,
    .ct-details-modal--report .ct-report-modal__footer { padding: 12px 16px; }
    .ct-details-modal--report .ct-report-modal__cta { width: 100%; }

    /* RD-2903: side by side there is no room for two full-width buttons, so the
       row becomes a stack — CTA on top, because it is still the primary action.
       `order` and not DOM order: on a wide screen the secondary must stay FIRST
       in the markup so it lands on the left and takes the tab stop before it. */
    .ct-details-modal--report .ct-report-modal__footer { flex-wrap: wrap; }
    .ct-details-modal--report .ct-report-modal__cta { order: 1; }
    .ct-details-modal--report .ct-report-modal__secondary {
        order: 2;
        margin-right: 0;
        width: 100%;
    }
    .ct-details-modal--report .ct-context-composer {
        left: 16px;
        width: calc(100% - 32px);
    }

    /* Artifact chrome: 26px gutters → 16px, headline and hero stat down a step
       so the numbers still lead without pushing the card sideways. */
    .ct-details-modal--report .ct-report .r-head { padding: 16px 16px 14px; }
    .ct-details-modal--report .ct-report .rb-sec { padding: 15px 16px; }
    .ct-details-modal--report .ct-report h3.r-headline { font-size: 20px; line-height: 1.25; }
    .ct-details-modal--report .ct-report .r-sub { font-size: 13px; }
    .ct-details-modal--report .ct-report .rb-sec--hero .rb-hero__stat { font-size: 38px; }
    .ct-details-modal--report .ct-report .rb-hero__stat small { font-size: 17px; }

    /* Bars: the 3-column grid starves the label under ~380px, so the label
       takes its own row and the track keeps the full width. */
    .ct-details-modal--report .ct-report .rb-bars__row {
        grid-template-columns: 1fr auto;
        gap: 5px 10px;
    }
    .ct-details-modal--report .ct-report .rb-bars__label { grid-column: 1 / -1; }

    /* KPI cards stop at minmax(150px, 1fr) — one per row reads better than two
       cramped ones. */
    .ct-details-modal--report .ct-report .rb-kpis { grid-template-columns: 1fr; }
    .ct-details-modal--report .ct-report .rb-kpi { padding: 11px 12px; }

    .ct-details-modal--report .ct-report .rb-d { padding: 10px 12px; gap: 10px; }
    .ct-details-modal--report .ct-report .rb-callout { padding: 11px 13px; }
    .ct-details-modal--report .ct-report .rb-table__wrap { -webkit-overflow-scrolling: touch; }
    .ct-details-modal--report .ct-report-error { padding: 22px 16px; }
}

@media (max-width: 400px) {
    .ct-details-modal--report .ct-report .r-head { padding: 14px 14px 12px; }
    .ct-details-modal--report .ct-report .rb-sec { padding: 14px; }
    .ct-details-modal--report .ct-report h3.r-headline { font-size: 18px; }
    .ct-details-modal--report .ct-report .rb-sec--hero .rb-hero__stat { font-size: 32px; }
    .ct-details-modal--report .ct-report .rb-hero__stat small { font-size: 15px; }
}

.ct-report-error {
    padding: 28px 24px;
    text-align: center;
    color: #64748b;
    font-size: 13.5px;
}

@media (prefers-reduced-motion: reduce) {
    .ct-report-btn { transition: none; }
    .ct-report-btn:hover, .ct-report-btn:active { filter: none; }
}

/* ─── RD-2342 fase 2: on-entry auto-generation surfaces ─── */

/* Feed chip: mirrors the generation state on the insight card. */
.ct-report-chip {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    /* RD-2448: rhythm now comes from the .ct-insight-card__tags row. */
    margin-top: 0;
    padding: 3px 10px;
    border-radius: 999px;
    font-size: 11px;
    font-weight: 600;
    line-height: 1.4;
    color: #4338ca;
    background: #f3f2ff;
    border: 1px solid #e2e0fb;
    animation: ctReportRise 0.2s var(--ct-ease-out);
}
.ct-report-chip--ready {
    color: #1C2E62;
    background: #eaf4ff;
    border-color: #d3e8ff;
}
.ct-report-chip svg { display: block; width: 12px; height: 12px; }
.ct-report-chip__ic { display: inline-flex; }
.ct-report-chip__spin {
    flex: none;
    width: 10px;
    height: 10px;
    border: 1.5px solid #d6d2fb;
    border-top-color: #4338ca;
    border-radius: 50%;
    animation: ctReportSpin 0.9s linear infinite;
}
@keyframes ctReportSpin { to { transform: rotate(360deg); } }
@keyframes ctReportRise { from { opacity: 0; transform: translateY(8px); } }

/* RD-2478: the "Analysis ready" chip is an inert state label again.

   RD-2455 had promoted it to a real <button> — it was the only way into the
   artifact, and as a state label nobody clicked it. That whole layer lived
   here: a UA reset so the <button> rendered like the <div> it replaced, a
   hover/active/focus affordance, and a chevron that said "this opens" before
   the text was read. The card is the entry point now, so all of it is gone
   and the chip is back to inheriting .ct-report-chip and the tags-row rule.

   Neither modifier is interactive, so both just declare it. */
.ct-report-chip--ready,
.ct-report-chip--thinking { cursor: default; }

/* "Investigating…" staged panel — sits above Evidence while the agent runs,
   so the wait reads as work happening, not as a stuck page. */
.ct-report-stages {
    border: 1px solid #e4e9f2;
    border-radius: 12px;
    background: linear-gradient(180deg, #fbfbff, #ffffff);
    padding: 14px 16px;
    margin-bottom: 14px;
    animation: ctReportRise 0.24s var(--ct-ease-out);
}
.ct-report-stages__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 6px;
}
.ct-report-stages__title {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    font-size: 12.5px;
    font-weight: 700;
    color: #4338ca;
}
.ct-report-stages__spark { display: inline-flex; }
.ct-report-stages__title svg { display: block; width: 15px; height: 15px; }
.ct-report-stages__timer {
    font-size: 11.5px;
    font-weight: 600;
    color: #5b6b8c;
    font-variant-numeric: tabular-nums;
}
/* Progress bar: an eased time-based fill (JS drives width), gradient in the
   brand celeste→indigo. A soft sheen sweeps across it so it reads as "working"
   even while the width is momentarily static. */
.ct-report-progress {
    height: 6px;
    border-radius: 999px;
    background: #eceff5;
    overflow: hidden;
    margin: 4px 0 10px;
}
.ct-report-progress__fill {
    display: block;
    width: 0;
    height: 100%;
    border-radius: 999px;
    background: linear-gradient(90deg, #419FFF, #4338ca);
    background-size: 200% 100%;
    transition: width 0.9s var(--ct-ease-out);
    animation: ctReportSheen 1.6s linear infinite;
}
@keyframes ctReportSheen { to { background-position: -200% 0; } }
/* Status line that changes as the agent works; a tiny fade on each swap. */
.ct-report-stages__status {
    font-size: 12.5px;
    font-weight: 600;
    color: #1C2E62;
}
.ct-report-stages__status.is-swap { animation: ctReportStatusIn 0.28s var(--ct-ease-out); }
@keyframes ctReportStatusIn { from { opacity: 0.25; transform: translateY(3px); } }
/* Status line left, "Usually takes 1–2 min" right — one row, same height. */
.ct-report-stages__row {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 12px;
}
.ct-report-stages__hint { margin-top: 0; font-size: 11px; color: #8494b3; white-space: nowrap; }

/* Quiet inline error — Evidence is still right there below. */
.ct-report-stages--error {
    display: flex;
    align-items: center;
    gap: 12px;
    background: #fff;
    font-size: 12.5px;
    color: #5b6b8c;
}
.ct-report-retry {
    border: 1px solid #e4e9f2;
    background: #fff;
    border-radius: 8px;
    padding: 4px 12px;
    font-size: 12px;
    font-weight: 600;
    color: #1C2E62;
    cursor: pointer;
    transition: border-color 0.15s ease, transform 0.12s var(--ct-ease-out);
}
.ct-report-retry:hover { border-color: #419FFF; }
.ct-report-retry:active { transform: scale(0.97); }
.ct-report-retry:focus-visible { outline: 2px solid #419FFF; outline-offset: 2px; }

@media (prefers-reduced-motion: reduce) {
    .ct-report-chip,
    .ct-report-stages,
    .ct-report-stages__status.is-swap { animation: none; }
    .ct-report-progress__fill { animation: none; transition: none; }
    /* Keep the progress spinner meaningful, just calmer. */
    .ct-report-chip__spin { animation-duration: 1.8s; }
    /* RD-2495 — the plan panel is the same component on the Resolution step,
       so it gets the same treatment: no rise, no sheen, no swap animation and
       no animated width. The copy still changes and the bar still moves — the
       information survives, only the motion goes. Its exit keeps an opacity
       fade (no translate), because a panel vanishing between frames is more
       disorienting than one that dims out. */
    .ct-plan-stages, .ct-plan-stages__status.is-swap { animation: none; }
    .ct-plan-stages.is-leaving {
        transform: none;
        transition: opacity 0.18s linear;
    }
}

/* ─── RD-2478: the evidence body of the card popup ────────────────
 * Clicking a feed card always opens a popup: the deep-analysis artifact when
 * one is ready, otherwise the evidence. Both are the same shell, so this
 * layers on top of .ct-details-modal--report and only overrides what differs
 * — the artifact ships its own chrome and paints edge-to-edge, the evidence
 * is bare panels that need gutters. */
.ct-details-modal--insight-evidence { max-width: 1040px; }
.ct-details-modal--insight-evidence .ct-details-modal__body {
    padding: 18px 20px 20px;
    background: #fff;
}

.ct-modal-evidence__head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 14px;
    margin-bottom: 14px;
}

.ct-modal-evidence__ident {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 8px;
    min-width: 0;
}

/* The shared rule reserves 170px for the detail card-header's launcher button.
   Here the launcher is a flex sibling, so the reservation only truncates the
   subtitle early. The bottom margin belongs to __head. */
.ct-modal-evidence__ident .ct-evidence-subtitle {
    max-width: none;
    margin-bottom: 0;
    font-weight: 600;
    color: #334155;
}

/* .ct-report-btn is absolutely positioned + translateY(-50%) to sit in the
   detail card-header's vertical centre. Here it is a flex sibling of the
   subtitle, so both have to go or the button escapes the row entirely. Only
   the positioning is reset — the gradient, sizing and hover states carry over
   so the launcher reads identically on both surfaces. */
.ct-modal-evidence__launch {
    position: static;
    transform: none;
    flex-shrink: 0;
}

.ct-modal-evidence__body > * + * { margin-top: 14px; }

/* 450px of graph plus the narrative and the KPI row makes the popup scroll
   before the user has read anything. The graph keeps its aspect but gives back
   ~90px; the detail view, which has the room, is untouched. */
.ct-details-modal--insight-evidence .ct-graph-container { height: 360px; }

/* The narrative box is capped at 220px on the detail surface because the graph
   sits under it in a fixed-height card. The popup scrolls, so let it run —
   a scroll container nested inside a scrolling modal is the worse read.
   Mirrors .ct-anomaly-evidence-panel--expanded. */
.ct-details-modal--insight-evidence .ct-anomaly-evidence-panel {
    max-height: none;
    overflow-y: visible;
}

/* The popup can open the View Details drill-down on top of itself. Both use
   .ct-details-modal-overlay, so without this the stack order is decided by
   DOM insertion order — correct today, but only by accident. 1052 keeps the
   drill-down under Bootstrap's own modals (1055). */
.ct-details-modal-overlay:not(#ct-report-modal) { z-index: 1052; }

@media (max-width: 640px) {
    .ct-details-modal--insight-evidence .ct-details-modal__body { padding: 14px 16px 16px; }
    .ct-modal-evidence__head { flex-direction: column; gap: 10px; }
    .ct-modal-evidence__launch { align-self: flex-start; }
    .ct-details-modal--insight-evidence .ct-graph-container { height: 260px; }
}

/* ─── RD-2481 / RD-2513: public-link control on the artifact popup ─────
 * Scoped to --report so the shared modal shell is untouched at every
 * breakpoint, the same containment RD-2455 used for the responsive block.
 *
 * RD-2513 moved this out of a bar of its own under the artifact and into the
 * HEADER, left of the close X, and replaced the printed URL with a panel that
 * drops down off the button. The footer lost on discoverability: bottom-left is
 * the one corner of a modal the eye never visits. */

/* The panel drops over the scrolling artifact body, so the header has to sit in
   front of it. The base .ct-details-modal__header is loaded globally and carries
   no stacking context of its own, hence the --report scope. */
.ct-details-modal--report .ct-details-modal__header {
    position: relative;
    z-index: 3;
}

.ct-details-modal--report .ct-report-share {
    /* Anchor for the absolutely positioned panel. */
    position: relative;
    display: inline-flex;
    align-items: center;
    /* The header is justify-content: space-between with the title and the X as
       its only children. An auto margin here drives all the free space to the
       LEFT of this row, so the title keeps the left edge and share + X travel
       together to the right — instead of space-between stranding this in the
       middle of the header.
       The right margin is the whole point of the placement: the X dismisses the
       artifact, and a mis-click on a dismiss control is expensive. 14px is a
       deliberate gap, not incidental spacing. */
    margin-left: auto;
    margin-right: 14px;
}

/* Sized for the HEADER, not the footer: the title is 15px and the X is a 22px
   glyph, so a 33px button would set the header's height and visibly fatten it.
   This lands at ~27px — it adds about 5px to the bar and reads as chrome rather
   than as a second primary action competing with the CTA. */
.ct-details-modal--report .ct-report-share__create,
.ct-details-modal--report .ct-report-share__toggle {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 4px 10px;
    -webkit-appearance: none;
    appearance: none;
    font-family: inherit;
    /* 12px x 1.4 line-height + 8px padding + 2px border = ~27px tall. */
    font-size: 12px;
    font-weight: 600;
    line-height: 1.4;
    /* Indigo accent, not celeste: this is a chrome affordance, and celeste is
       the card's own data colour. */
    color: #4A51C9;
    background: #FFFFFF;
    border: 1px solid #E2E8F0;
    border-radius: 6px;
    cursor: pointer;
    /* Colour and transform only — never layout. */
    transition: background-color 0.18s var(--ct-ease-out),
                border-color 0.18s var(--ct-ease-out),
                color 0.18s var(--ct-ease-out),
                transform 0.12s var(--ct-ease-out);
}
.ct-details-modal--report .ct-report-share__create::-moz-focus-inner,
.ct-details-modal--report .ct-report-share__toggle::-moz-focus-inner {
    border: 0;
    padding: 0;
}
.ct-details-modal--report .ct-report-share__create i,
.ct-details-modal--report .ct-report-share__toggle i {
    font-size: 11px;
}

@media (hover: hover) and (pointer: fine) {
    .ct-details-modal--report .ct-report-share__create:hover,
    .ct-details-modal--report .ct-report-share__toggle:hover {
        background: #F7F8FC;
        border-color: #CBD2F0;
    }
}
.ct-details-modal--report .ct-report-share__create:active,
.ct-details-modal--report .ct-report-share__toggle:active {
    transform: scale(0.97);
}
.ct-details-modal--report .ct-report-share__create:focus-visible,
.ct-details-modal--report .ct-report-share__toggle:focus-visible {
    outline: 2px solid var(--ct-accent);
    outline-offset: 2px;
}

/* Shared state reads as done rather than as another call to action — the same
   pale-green semantic pair the copy confirmation uses. Declared after the
   generic hover block so it wins the tie. */
.ct-details-modal--report .ct-report-share__toggle {
    color: #346538;
    background: #F4F8F3;
    border-color: #D7E6D7;
}
@media (hover: hover) and (pointer: fine) {
    .ct-details-modal--report .ct-report-share__toggle:hover {
        background: #EDF3EC;
        border-color: #CBDFCB;
    }
}

/* Points down while closed, because down is where the panel is about to go from
   the header; flips up once open, because up is where it will retreat to. */
.ct-details-modal--report .ct-report-share__chevron {
    font-size: 9px;
    transition: transform 0.18s var(--ct-ease-out);
}
.ct-details-modal--report .ct-report-share__toggle.is-open .ct-report-share__chevron {
    transform: rotate(180deg);
}

/* ── the panel ──
 * No per-row stagger: this is a menu the sharer reopens to copy the link again,
 * and cascading three rows in front of them every time would tax a repeated
 * action for a one-time flourish. The panel's own origin-aware scale is the
 * motion. */
.ct-details-modal--report .ct-report-share__panel {
    position: absolute;
    /* Drops from under the trigger — from the header there is nothing above to
       open into. Pinned by its RIGHT edge so it grows leftward, INTO the modal:
       the trigger sits near the right edge, so a left-pinned panel would run out
       past the shell and be clipped by its overflow: hidden. */
    top: calc(100% + 8px);
    right: 0;
    z-index: 5;
    min-width: 194px;
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding: 6px;
    background: #FFFFFF;
    border: 1px solid #E2E8F0;
    border-radius: 10px;
    box-shadow: 0 10px 30px -8px rgba(15, 23, 42, 0.18),
                0 4px 10px -4px rgba(15, 23, 42, 0.08);
    /* Scales out of the button it hangs off, never out of its own centre. */
    transform-origin: top right;
    opacity: 0;
    visibility: hidden;
    transform: translateY(-4px) scale(0.96);
    /* Exit is quicker than entry: the system getting out of the way, not the
       user deciding. visibility is held to the end of the fade so the panel
       stays hit-testable for the whole of its own exit. */
    transition: opacity 0.12s var(--ct-ease-out),
                transform 0.12s var(--ct-ease-out),
                visibility 0s linear 0.12s;
}
.ct-details-modal--report .ct-report-share__panel.is-open {
    opacity: 1;
    visibility: visible;
    transform: none;
    transition: opacity 0.165s var(--ct-ease-out),
                transform 0.165s var(--ct-ease-out),
                visibility 0s linear 0s;
}

/* Names what the three actions act on, now that the URL is not there to do it.
   Matches .ct-feed-filters-popover__title. */
.ct-details-modal--report .ct-report-share__panel-title {
    padding: 4px 8px 6px;
    font-size: 10.5px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: #64748B;
}

.ct-details-modal--report .ct-report-share__action {
    display: flex;
    align-items: center;
    gap: 9px;
    width: 100%;
    padding: 7px 8px;
    -webkit-appearance: none;
    appearance: none;
    font-family: inherit;
    font-size: 12.5px;
    font-weight: 500;
    line-height: 1.4;
    text-align: left;
    text-decoration: none;
    color: #2F3437;
    background: transparent;
    border: none;
    border-radius: 6px;
    cursor: pointer;
    transition: background-color 0.14s var(--ct-ease-out),
                color 0.14s var(--ct-ease-out),
                transform 0.12s var(--ct-ease-out);
}
.ct-details-modal--report .ct-report-share__action::-moz-focus-inner {
    border: 0;
    padding: 0;
}
/* Fixed width on the glyph box so the three labels start on one vertical line
   whatever each icon's intrinsic width is. */
.ct-details-modal--report .ct-report-share__action i {
    width: 13px;
    font-size: 12px;
    text-align: center;
    color: #94A3B8;
    transition: color 0.14s var(--ct-ease-out);
}

@media (hover: hover) and (pointer: fine) {
    .ct-details-modal--report .ct-report-share__action:hover {
        background: #F5F7FA;
    }
    .ct-details-modal--report .ct-report-share__action:hover i {
        color: #64748B;
    }
    /* Revoke is terminal, so it declares itself on approach rather than only in
       the confirm dialog. */
    .ct-details-modal--report .ct-report-share__revoke:hover {
        background: #FDEBEC;
        color: #9F2F2D;
    }
    .ct-details-modal--report .ct-report-share__revoke:hover i {
        color: #9F2F2D;
    }
}
.ct-details-modal--report .ct-report-share__action:active {
    transform: scale(0.97);
}
.ct-details-modal--report .ct-report-share__action:focus-visible {
    outline: 2px solid var(--ct-accent);
    /* Inset: the panel's own padding is 6px, so an outward ring would clip. */
    outline-offset: -1px;
}

/* The copy confirmation is a state on the row itself — the pale-green semantic
   pair, so it reads as done without shouting. After the hover block so it holds
   while the pointer is still on the row that was just clicked. */
.ct-details-modal--report .ct-report-share__copy.is-copied {
    background: #EDF3EC;
    color: #346538;
}
.ct-details-modal--report .ct-report-share__copy.is-copied i {
    color: #346538;
}

@media (prefers-reduced-motion: reduce) {
    /* Opacity aids comprehension and stays; the movement goes. */
    .ct-details-modal--report .ct-report-share__panel {
        transform: none;
        transition: opacity 0.12s linear, visibility 0s linear 0.12s;
    }
    .ct-details-modal--report .ct-report-share__panel.is-open {
        transition: opacity 0.12s linear, visibility 0s linear 0s;
    }
    .ct-details-modal--report .ct-report-share__chevron { transition: none; }
    .ct-details-modal--report .ct-report-share__create:active,
    .ct-details-modal--report .ct-report-share__toggle:active,
    .ct-details-modal--report .ct-report-share__action:active { transform: none; }
}

@media (max-width: 640px) {
    /* The header now carries three things — title, share, X — and at this width
       the title alone can wrap. The label goes and the trigger becomes the icon
       pair; the button's `title` attribute takes over as the accessible name once
       the span is display:none, which is why it is a title and not an aria-label.
       The chevron stays: without it the control does not read as expandable. */
    .ct-details-modal--report .ct-report-share__trigger-label { display: none; }
    .ct-details-modal--report .ct-report-share {
        /* Tighter, but still a gap — the reason for the gap does not go away on a
           touch screen, it gets worse. */
        margin-right: 10px;
    }
    .ct-details-modal--report .ct-report-share__create,
    .ct-details-modal--report .ct-report-share__toggle {
        padding: 5px 8px;
    }
    /* The trigger is now ~34px wide against a ~190px panel, so right-pinning is
       what keeps the panel inside the shell. */
    .ct-details-modal--report .ct-report-share__panel {
        min-width: 186px;
    }
}

/* ══════════════════════════════════════════════════════════════════
 * RD-2608 — Problem map (cluster × view matrix)
 *
 * Same section chrome the Alerts section gets: this section owns
 * neither the scope bar nor the Insights state tabs, so the whole
 * sticky header goes (an empty .lm-sticky-tabs would still paint its
 * .lm-stuck hairline on scroll).
 * ══════════════════════════════════════════════════════════════════ */
[data-hub-section="matrix"] .lm-sticky-tabs {
    display: none;
}

[data-hub-section="matrix"] .tab-content {
    padding-top: 10px;
}

/* No max-width: the matrix must be able to use whatever the viewport
 * gives it, otherwise zooming out leaves views hidden behind a scrollbar
 * while empty space sits to the right. The table caps its own width per
 * column count (see .ct-matrix-table + applyWidth in cluster-matrix.js). */
/* ── the stat band above the map (RD-2803) ──
 *
 * The cards themselves ARE the hub's KPI card (.ct-feed-metric, unchanged) —
 * the same component as the feed topbar, so the two sections of the hub
 * cannot drift into two different card looks. Only the container differs:
 * .ct-feed-metrics caps each card at 340px and packs them left, which under a
 * full-width matrix leaves the band floating in the left third of the screen.
 * Three equal columns spanning the map's width instead.
 *
 * This rule sits AFTER .ct-feed-metrics in the file (line ~4173), so at equal
 * specificity it wins the grid — which is why the element carries both
 * classes rather than duplicating the KPI styles.
 *
 * The 12px bottom gap is the band's distance to the map card it heads. */
.ct-matrix-stats {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    justify-content: stretch;
    gap: 12px;
    margin-bottom: 12px;
}

.ct-matrix-stats[hidden] { display: none; }

/* RD-2908 — the band's three cards are <button>s.
 *
 * `.ct-feed-metric` was written for a <div>, so all this modifier adds is what
 * a user agent puts on a button and has to be taken back off: its own type,
 * the centred text, the shrink-to-content width and the native appearance. The
 * look, the hover, the focus ring and the press still come from
 * `.ct-feed-metric` + `--clickable`, which is the point — the hub's pressable
 * KPI card already existed (RD-2090), and this is not a second one. */
.ct-matrix-stat {
    width: 100%;
    margin: 0;
    font: inherit;
    color: inherit;
    text-align: left;
    border: 1px solid #e2e8f0;
    -webkit-appearance: none;
    appearance: none;
}

/* The tooltip's trigger on a signal card (RD-2908).
 *
 * It started as the whole card and that was the wrong target: crossing the band
 * on the way down to the map popped bubbles nobody asked for — two at once,
 * covering the very matrix the tooltips describe. So the trigger is this mark,
 * sitting next to the name, and hovering the card itself now does nothing but
 * light the card.
 *
 * Same drawing, size and hover as `.ct-matrix-help` (the mark beside the map's
 * own title) minus the button resets, because this one is a <span>: nested
 * interactive content is invalid inside the <button> the card now is. `cursor`
 * is left alone on purpose — the card underneath is pressable, and a `help`
 * cursor on a 16px island inside it would say the opposite. */
.ct-matrix-stat__help {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: none;
    width: 20px;
    height: 20px;
    margin-left: -2px;
    border-radius: 6px;
    color: #b5b9c9;
    font-size: 13px;
    line-height: 1;
    transition: color 160ms var(--ct-ease-out), background-color 160ms var(--ct-ease-out);
}

/* Hover gated to real pointers, like the KPI card's own: on touch there is no
 * hover to paint and a stuck one reads as a selected state. The mark answers to
 * a hover anywhere on the card, not only on itself — it is the card's own mark,
 * so it should not look inert while the card is lit. */
@media (hover: hover) and (pointer: fine) {
    .ct-matrix-stat:hover .ct-matrix-stat__help {
        color: #8f95a8;
    }

    .ct-matrix-stat__help:hover {
        color: var(--ct-accent);
        background: var(--ct-accent-soft);
    }
}

/* The receipt a click on a lens card gets (RD-2908).
 *
 * The card is not what displays the lens — the toggle is — so the click has to
 * make the TOGGLE speak. And it has to speak even when the mode did not change:
 * pressing "Cost" while already on Cost moves nothing on its own, and that is
 * exactly where an unanswered click reads as a broken button.
 *
 * A ring that grows and leaves in 520ms on whichever button ended up active.
 * `box-shadow` and nothing else — no transform, so it cannot nudge its
 * neighbour or fight the `is-on` state it sits on.
 *
 * A keyframe rather than a transition because this is a one-shot that is fired
 * and forgotten (the JS strips the class when it ends), not an interruptible
 * state. */
@keyframes ct-matrix-lens-ack {
    0%   { box-shadow: 0 0 0 0 var(--ct-accent-border); }
    35%  { box-shadow: 0 0 0 4px var(--ct-accent-border); }
    /* Transparent cut of --ct-accent-border (#c7d2fe): the spread is already
     * back to 0 here, and fading the colour too keeps the last frame from
     * flashing an opaque hairline on a subpixel boundary. */
    100% { box-shadow: 0 0 0 0 rgba(199, 210, 254, 0); }
}

.ct-matrix-toggle__btn.is-lens-ack {
    animation: ct-matrix-lens-ack 520ms var(--ct-ease-out);
}

@media (prefers-reduced-motion: reduce) {
    .ct-matrix-toggle__btn.is-lens-ack {
        animation: none;
    }
}

/* Same breakpoint the preview row under the map uses, so the two bands
 * collapse to one column at the same width. */
@media (max-width: 900px) {
    .ct-matrix-stats {
        grid-template-columns: minmax(0, 1fr);
    }
}

.ct-matrix-card {
    background: #ffffff;
    border: 1px solid #e9edf3;
    border-radius: 12px;
    padding: 24px 28px 20px;
}

.ct-matrix-card__header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 18px;
    flex-wrap: wrap;
    margin-bottom: 18px;
}

.ct-matrix-card__actions {
    display: flex;
    align-items: center;
    gap: 10px;
}

/* ── the proposal modal (RD-2943) ──
 *
 * Nearly the whole viewport, because what is inside is a document with a
 * 960px measure and three acts — a small dialog would turn it into a
 * scroll-tube. The body carries no padding: the iframe IS the body, and the
 * document brings its own gutters. */
.ct-proposal-modal .modal-dialog {
    max-width: min(1140px, 94vw);
    width: min(1140px, 94vw);
    height: 92vh;
    margin-inline: auto;
}

.ct-proposal-modal .modal-content {
    height: 100%;
    border: 0;
    border-radius: 14px;
    overflow: hidden;
}

.ct-proposal-modal .modal-header {
    align-items: center;
    gap: 16px;
    padding: 14px 18px;
    border-bottom: 1px solid #e9edf3;
}

.ct-proposal-modal .modal-title {
    font-weight: 650;
    letter-spacing: -0.01em;
    color: #071437;
}

.ct-proposal-modal .modal-body {
    position: relative;
    padding: 0;
    overflow: hidden;
}

.ct-proposal-modal__tools {
    display: flex;
    align-items: center;
    gap: 6px;
    margin-left: auto;
}

.ct-proposal-modal__link,
.ct-proposal-modal__print {
    padding: 6px 12px;
    border: 1px solid #e9edf3;
    border-radius: 8px;
    background: #ffffff;
    color: #071437;
    font-size: 12px;
    font-weight: 600;
    text-decoration: none;
    cursor: pointer;
    transition: border-color 160ms var(--ct-ease-out), background-color 160ms var(--ct-ease-out), transform 160ms var(--ct-ease-out);
}

.ct-proposal-modal__link:hover,
.ct-proposal-modal__print:hover {
    border-color: #cfd5e1;
    background: #fbfbfc;
    color: #071437;
}

.ct-proposal-modal__link:active,
.ct-proposal-modal__print:active {
    transform: scale(0.97);
}

.ct-proposal-modal__frame {
    width: 100%;
    height: 100%;
    border: 0;
    display: block;
    background: #f9f9f9;
}

.ct-proposal-modal__loading {
    position: absolute;
    inset: 0;
    z-index: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 10px;
    background: #f9f9f9;
    color: #78829d;
    font-size: 13px;
    font-weight: 500;
}

.ct-proposal-modal__spinner {
    width: 15px;
    height: 15px;
    border: 2px solid var(--ct-accent-border);
    border-top-color: var(--ct-accent);
    border-radius: 50%;
    animation: ct-proposal-spin 620ms linear infinite;
}

@keyframes ct-proposal-spin {
    to { transform: rotate(360deg); }
}

@media (prefers-reduced-motion: reduce) {
    .ct-proposal-modal__spinner { animation-duration: 1600ms; }
}

@media (max-width: 767.98px) {
    .ct-proposal-modal .modal-dialog {
        max-width: 100vw;
        width: 100vw;
        height: 100vh;
        margin: 0;
    }

    .ct-proposal-modal .modal-content { border-radius: 0; }
    .ct-proposal-modal .modal-header { flex-wrap: wrap; }
    .ct-proposal-modal__tools { width: 100%; margin-left: 0; }
}

/* Title and hint on one line, wrapping to two on a narrow card rather than
 * squeezing the title. */
.ct-matrix-card__heading {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
    min-width: 0;
}

.ct-matrix-card__title {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: 16px;
    font-weight: 650;
    letter-spacing: -0.01em;
    color: #071437;
    margin: 0;
}

/* The "every cell is a door" hint (RD-2654). Deliberately a chip beside the
 * title and not a banner over the table: it is one instruction, and it competes
 * with the map for attention every second it is on screen. Muted indigo, one
 * line, no heading, no second sentence — it says the verb and gets out of the
 * way, and the tile's hover face is what actually teaches the interaction. */
.ct-matrix-hint {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    padding: 3px 9px 3px 7px;
    border-radius: 999px;
    border: 1px solid var(--ct-accent-border);
    background: var(--ct-accent-soft);
    color: var(--ct-accent);
    font-size: 11px;
    font-weight: 600;
    line-height: 1.45;
}

.ct-matrix-hint svg { flex: none; }

.ct-matrix-help {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 24px;
    height: 24px;
    padding: 0;
    border: 0;
    border-radius: 6px;
    background: transparent;
    color: #a1a5b7;
    font-size: 15px;
    cursor: pointer;
    transition: color 160ms var(--ct-ease-out), background-color 160ms var(--ct-ease-out);
}

.ct-matrix-help:hover {
    color: var(--ct-accent);
    background: var(--ct-accent-soft);
}

.ct-matrix-help:focus-visible {
    outline: 2px solid var(--ct-accent-border);
    outline-offset: 1px;
}

/* ── the preview row (RD-2676) ──
 *
 * Three doors with a taste of what is behind them, right under the map. They
 * are quieter than the map on purpose — hairline cards, no fills — because the
 * map is the argument and these only answer "and where do I go next?".
 * Same entrance as the drawer's evidence rows (rise + stagger), same neutrals
 * as the matrix card. (The tinted foot strip that used to sit between the two
 * — mapped totals in words plus the Review-insights CTA — went in the RD-2676
 * review: the insights card says the same thing in concrete rows and carries
 * the exit itself now.) */
.ct-matrix-previews {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 10px;
    margin-top: 14px;
}

.ct-matrix-previews[hidden] { display: none; }

@media (max-width: 900px) {
    .ct-matrix-previews {
        grid-template-columns: minmax(0, 1fr);
    }
}

.ct-matrix-previews__card {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    padding: 11px 13px 12px;
    border: 1px solid #e9edf3;
    border-radius: 10px;
    background: #ffffff;
    opacity: 0;
    transform: translateY(6px);
    animation: ct-activate-rise 260ms var(--ct-ease-out) forwards;
    animation-delay: calc(var(--ct-stagger, 0) * 60ms);
}

.ct-matrix-previews__head {
    display: flex;
    align-items: center;
    gap: 6px;
    margin-bottom: 8px;
    font-size: 10.5px;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: #99a1b7;
}

.ct-matrix-previews__head i,
.ct-matrix-previews__head svg {
    font-size: 13px;
    color: #aab3c7;
    flex: none;
}

.ct-matrix-previews__value {
    font-size: 15px;
    font-weight: 650;
    color: #071437;
    line-height: 1.3;
}

.ct-matrix-previews__muted {
    margin: 2px 0 0;
    font-size: 12px;
    line-height: 1.5;
    color: #78829d;
    text-wrap: pretty;
}

/* The Insights card's reading of the map, as a plate with its own heading.
 * Loose paragraphs in a card read as a caption for whatever sits above them;
 * the plate makes the sentences a THING you are meant to read. It wears the
 * product's AI clothes — accent-soft tint, sparkle by the heading — so it
 * files as a generated diagnosis (round 6, Felipe's call over neutral grey). */
.ct-matrix-previews__diag {
    width: 100%;
    box-sizing: border-box;
    margin-top: 8px;
    padding: 10px 12px 12px;
    border-radius: 9px;
    background: var(--ct-accent-soft);
    box-shadow: inset 0 0 0 1px #dfe3fb;
}

.ct-matrix-previews__diag-head {
    display: flex;
    align-items: center;
    gap: 6px;
    margin-bottom: 5px;
    font-size: 12.5px;
    font-weight: 650;
    color: var(--ct-accent);
}

.ct-matrix-previews__diag-head svg {
    flex: none;
}

/* Short sentences, one per line of thought, in the body ink rather than the
 * muted grey — this IS the card's content. (It replaced a three-row list of
 * narrated insight titles, whose per-row button styles went with it.) */
.ct-matrix-previews__summary {
    margin: 3px 0 0;
    font-size: 12.5px;
    line-height: 1.5;
    color: #4b5675;
    text-wrap: pretty;
}

/* A section heading INSIDE a card — "Últimos disparos", "Donde más cuesta".
 * Same micro-caps as the card head one step lighter, because its job is to say
 * what the block under it is: without it, three metric names under a headline
 * read as the rules themselves rather than as their history. */
.ct-matrix-previews__sublabel {
    margin-top: 11px;
    font-size: 9.5px;
    font-weight: 700;
    letter-spacing: 0.09em;
    text-transform: uppercase;
    color: #aab3c7;
}

/* Eats the card's slack so every button lands on the same bottom line. It is a
 * real element rather than `margin-top: auto` on the button because an auto
 * margin gives the alignment and then collapses to ZERO once the rows fill the
 * card — which is how the button ended up pressed against the last insight. */
.ct-matrix-previews__spacer {
    flex: 1 1 auto;
    min-height: 0;
}

/* The card's one exit, on the bottom line whatever the content above did. A
 * real (soft) button, not a text link: it is the card's only action, and a bare
 * underlined line next to two cards full of buttons read as an afterthought.
 * Hover commits — soft face to filled accent — the same grammar as every
 * primary press in the module. */
.ct-matrix-previews__link {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    margin-top: 13px;
    padding: 6px 12px;
    border: 1px solid var(--ct-accent-border);
    border-radius: 7px;
    background: #ffffff;
    font-size: 12.5px;
    font-weight: 600;
    color: var(--ct-accent);
    cursor: pointer;
    transition: background-color 140ms var(--ct-ease-out), color 140ms var(--ct-ease-out),
                border-color 140ms var(--ct-ease-out), transform 140ms var(--ct-ease-out);
}

.ct-matrix-previews__link i {
    font-size: 11px;
    transition: transform 140ms var(--ct-ease-out);
}

@media (hover: hover) and (pointer: fine) {
    .ct-matrix-previews__link:hover {
        background: var(--ct-accent);
        border-color: var(--ct-accent);
        color: #ffffff;
    }

    .ct-matrix-previews__link:hover i {
        transform: translateX(2px);
    }
}

.ct-matrix-previews__link:focus-visible {
    outline: 2px solid var(--ct-accent-border);
    outline-offset: 2px;
}

.ct-matrix-previews__link:active {
    transform: translateY(1px) scale(0.98);
}

/* The alerts card's history: one fire per bullet — what crossed and where, then
 * the value that breached on its own line. Inert rows (there is no per-rule
 * page to land on), so no hover face.
 *
 * Separate bullets, NOT a connected timeline: a rail joining them implied the
 * three fires were one sequence, when they are unrelated events from different
 * rules that happen to be the last three. What makes the block read as history
 * is the heading above it and the elapsed time on every row. */
.ct-matrix-previews__log {
    width: 100%;
    margin-top: 5px;
    padding-left: 13px;
}

.ct-matrix-previews__event {
    position: relative;
    display: flex;
    flex-direction: column;
    width: 100%;
    padding: 5px 0;
}

.ct-matrix-previews__event + .ct-matrix-previews__event {
    border-top: 1px solid #f1f4f9;
}

/* Aligned to the first line's text, not to the row's box: the row is two lines
 * tall now, and a dot centred on the box would sit against the value. One
 * colour for every dot — a lighter tail read as "these matter less", which is
 * not what recency means. */
.ct-matrix-previews__event-dot {
    position: absolute;
    left: -13px;
    top: 11px;
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: var(--ct-accent);
}

.ct-matrix-previews__event-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 8px;
    width: 100%;
    min-width: 0;
}

/* The number that breached, under the name it belongs to. On one line it
 * fought the metric name for the same row and lost every time to truncation. */
.ct-matrix-previews__event-value {
    margin-top: 1px;
    font-size: 12.5px;
    font-weight: 700;
    color: #071437;
    font-variant-numeric: tabular-nums;
}

.ct-matrix-previews__event-label {
    flex: 1;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: 12.5px;
    color: #1f2a44;
}

/* The view is context, not the subject: it rides the same line one step back
 * in the greys so the metric stays the thing you read. */
.ct-matrix-previews__event-view {
    color: #99a1b7;
}

.ct-matrix-previews__event-ago {
    flex: none;
    font-size: 11.5px;
    color: #78829d;
    font-variant-numeric: tabular-nums;
}

/* The agents card's recommendation (shown only when nothing is running): the
 * costliest uncovered cell, as a small tinted plate — the one place on this row
 * that is an OFFER rather than a report, so it carries the accent's soft face
 * the way the map's own tiles do. */
.ct-matrix-previews__pick {
    /* One horizontal offer: the text stack on the left, the button on the
     * right — stacked, the plate grew tall against a dead right half. The
     * button wraps under the text only when the card is too narrow to hold
     * both (flex-wrap), instead of crushing the agent's name. */
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 8px 12px;
    width: 100%;
    /* Declared, not inherited from a global reset: this is the only block in
     * the row with horizontal padding AND a full width, so it is the only one
     * that spills out of its card when the reset is not there. */
    box-sizing: border-box;
    margin-top: 5px;
    padding: 10px 12px;
    border-radius: 8px;
    background: var(--ct-accent-soft);
    box-shadow: inset 0 0 0 1px #dfe3fb;
}

.ct-matrix-previews__pick-body {
    display: flex;
    flex-direction: column;
    gap: 1px;
    flex: 1 1 150px;
    min-width: 0;
}

/* The tag that frames the plate as an OFFER. It replaced a section heading
 * outside the box ("Donde más cuesta"), which described how the cell was
 * ranked instead of saying what the block is. */
.ct-matrix-previews__pick-tag {
    align-self: flex-start;
    margin-bottom: 5px;
    padding: 2px 8px;
    border-radius: 999px;
    background: var(--ct-accent);
    color: #ffffff;
    font-size: 9.5px;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
}

.ct-matrix-previews__pick-agent {
    font-size: 12.5px;
    font-weight: 650;
    color: #312a9e;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.ct-matrix-previews__pick-where {
    font-size: 12px;
    color: #4b5675;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.ct-matrix-previews__pick-metric {
    margin-top: 2px;
    font-size: 12.5px;
    font-weight: 700;
    color: #071437;
    font-variant-numeric: tabular-nums;
}

/* The offer's own button, INSIDE the plate: it belongs to this recommendation,
 * not to the card, and at the card's foot it read as a third generic exit
 * beside "Revisar insights" and "Revisar alertas". Filled accent, because on a
 * tinted plate a soft button would be a second shade of the same colour. */
.ct-matrix-previews__pick-cta {
    display: inline-flex;
    align-items: center;
    flex: none;
    gap: 6px;
    white-space: nowrap;
    padding: 6px 12px;
    border: 0;
    border-radius: 7px;
    background: var(--ct-accent);
    font-size: 12.5px;
    font-weight: 600;
    color: #ffffff;
    cursor: pointer;
    transition: background-color 140ms var(--ct-ease-out), transform 140ms var(--ct-ease-out),
                box-shadow 140ms var(--ct-ease-out);
}

.ct-matrix-previews__pick-cta i {
    font-size: 11px;
    transition: transform 140ms var(--ct-ease-out);
}

@media (hover: hover) and (pointer: fine) {
    .ct-matrix-previews__pick-cta:hover {
        background: #3730a3;
        box-shadow: 0 2px 8px -2px rgba(67, 56, 202, 0.55);
    }

    .ct-matrix-previews__pick-cta:hover i {
        transform: translateX(2px);
    }
}

.ct-matrix-previews__pick-cta:focus-visible {
    outline: 2px solid var(--ct-accent);
    outline-offset: 2px;
}

.ct-matrix-previews__pick-cta:active {
    transform: translateY(1px) scale(0.98);
}

/* The agents card: one line per agent, its state as a chip on the right —
 * the same palette the matrix chips use, so "solicitado" looks like the cell
 * it came from. */
.ct-matrix-previews__agent {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    width: 100%;
    padding: 4px 0;
}

.ct-matrix-previews__agent-name {
    flex: 1;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: 12.5px;
    color: #1f2a44;
}

.ct-matrix-previews__agent-mult {
    font-size: 11px;
    font-weight: 650;
    color: #99a1b7;
}

.ct-matrix-previews__state {
    flex: none;
    padding: 2px 8px;
    border-radius: 999px;
    font-size: 10.5px;
    font-weight: 650;
    line-height: 1.5;
}

.ct-matrix-previews__state.is-requested {
    background: #ffffff;
    color: #4338ca;
    box-shadow: inset 0 0 0 1px #a5b4fc;
}

.ct-matrix-previews__state.is-active {
    background: #eef2ff;
    color: #4338ca;
    box-shadow: inset 0 0 0 1px #c7d2fe;
}

.ct-matrix-previews__state.is-pending_approval {
    background: #fff8dd;
    color: #8a6410;
    box-shadow: inset 0 0 0 1px #f5de9c;
}

.ct-matrix-previews__state.is-paused {
    background: #f7f9fc;
    color: #4b5675;
    box-shadow: inset 0 0 0 1px #dbdfe9;
}

/* Segmented Frequency/Cost toggle. Fast (160ms) because it is a
 * high-frequency control; press feedback via scale, never layout. */
.ct-matrix-toggle {
    display: inline-flex;
    flex: 0 0 auto;
    background: #f1f4f9;
    border-radius: 8px;
    padding: 3px;
    gap: 2px;
}

.ct-matrix-toggle__btn {
    font-size: 12px;
    font-weight: 600;
    color: #78829d;
    background: transparent;
    border: 0;
    border-radius: 6px;
    padding: 6px 14px;
    cursor: pointer;
    transition: color 160ms var(--ct-ease-out), background-color 160ms var(--ct-ease-out), transform 160ms var(--ct-ease-out);
}

.ct-matrix-toggle__btn:hover {
    color: #071437;
}

.ct-matrix-toggle__btn:active {
    transform: scale(0.97);
}

.ct-matrix-toggle__btn:focus-visible {
    outline: 2px solid var(--ct-accent-border);
    outline-offset: 1px;
}

.ct-matrix-toggle__btn.is-on {
    background: #ffffff;
    color: #071437;
    box-shadow: 0 1px 4px rgba(7, 20, 55, 0.12);
}

/* ── table ── */

.ct-matrix-scroll {
    overflow-x: auto;
    /* No lateral padding: sticky cells anchor to the scrollport edge, and a
     * padding strip is exactly where scrolled content would peek through. */
    padding-bottom: 2px;
}

/* border-spacing would let scrolled cells show through the 2px gaps under
 * the sticky columns, so the gap is drawn as an OPAQUE white border on each
 * cell instead: same look, nothing bleeds through. Width bounds are set
 * per column count by applyWidth() in cluster-matrix.js. */
.ct-matrix-table {
    width: 100%;
    /* Every view column the same width, and none of them narrower than the
     * hover label needs (RD-2654). Auto layout sized each column by its
     * content, so a view called "Todos" got a tile half the width of one called
     * "Babies SENA Team" — the heat ramp then read as a size difference that
     * meant nothing, and the narrow tiles could not fit "Crear agente".
     *
     * Fixed layout takes the widths from the first row: the two rails declare
     * theirs below and the view columns split what is left, equally. The floor
     * is `CELL_MIN_PX` in the table's min-width (applyWidth), so when the views
     * stop fitting the map scrolls sideways instead of squeezing them. */
    table-layout: fixed;
    border-collapse: separate;
    border-spacing: 0;
    font-variant-numeric: tabular-nums;
    font-feature-settings: "tnum" 1;
    animation: ct-matrix-fade 240ms var(--ct-ease-out);
}

/* ── axes: the table names its own rows, and each column IS its view ── */

/* The column head is ONE target: the link wraps icon + name and fills the
 * cell, so hovering anywhere lights the whole thing and clicking anywhere
 * opens the view. The header cell drops its own padding — the link carries it,
 * otherwise the padded strip around the link would be dead space inside what
 * looks like a button. */
.ct-matrix-table__col-head {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 3px;
    padding: 6px 6px 8px;
    border-radius: 8px;
    color: inherit;
    text-decoration: none;
    transition: background-color 140ms var(--ct-ease-out), color 140ms var(--ct-ease-out);
}

/* The view icon is the sidebar's own Performance Delivery glyph — the visual
 * link between "column" and the row in the menu the user clicked to get here.
 * Solid, not outline: the outline cut of element-11 draws two of its four
 * squares hollow, which reads as a different icon.
 * (`i` prefix: style.bundle.css colours bare ki-* glyphs at 0-1-1.) */
i.ct-matrix-table__col-icon {
    font-size: 13px;
    line-height: 1;
    color: #aab3c7;
    transition: color 140ms var(--ct-ease-out);
}

/* Hover has to be unmistakable — this is the only cue that the header opens
 * something. Tinted plate, accent text AND accent glyph, all at once. */
.ct-matrix-table__col-head--link {
    cursor: pointer;
}

.ct-matrix-table__col-head--link:hover {
    background: var(--ct-accent-soft);
    color: var(--ct-accent);
}

.ct-matrix-table__col-head--link:hover i.ct-matrix-table__col-icon {
    color: var(--ct-accent);
}

.ct-matrix-table__col-head--link:hover .ct-matrix-table__col-name {
    text-decoration: underline;
    text-underline-offset: 2px;
}

.ct-matrix-table__col-head--link:active {
    transform: scale(0.98);
}

.ct-matrix-table__col-head--link:focus-visible {
    outline: 2px solid var(--ct-accent-border);
    outline-offset: 1px;
}

.ct-matrix-table__axis-rows {
    display: block;
    font-size: 10.5px;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--ct-accent);
    text-align: left;
}

/* ── sticky rails: the problem names and the total never scroll away ── */

.ct-matrix-table__corner,
.ct-matrix-table__row-head {
    position: sticky;
    left: 0;
    z-index: 3;
    background: #ffffff;
}

.ct-matrix-table__col--total,
.ct-matrix-table__cell--total,
.ct-matrix-table__cell--grand {
    position: sticky;
    right: 0;
    z-index: 3;
    background: #ffffff;
}

/* Only while something is actually clipped, so a matrix that fits shows no
 * stray rails. .is-scrollable is toggled by cluster-matrix.js. */
.ct-matrix-scroll.is-scrollable .ct-matrix-table__corner,
.ct-matrix-scroll.is-scrollable .ct-matrix-table__row-head {
    box-shadow: 6px 0 8px -6px rgba(7, 20, 55, 0.18);
}

.ct-matrix-scroll.is-scrollable .ct-matrix-table__col--total,
.ct-matrix-scroll.is-scrollable .ct-matrix-table__cell--total,
.ct-matrix-scroll.is-scrollable .ct-matrix-table__cell--grand {
    box-shadow: -6px 0 8px -6px rgba(7, 20, 55, 0.18);
}

@keyframes ct-matrix-fade {
    from { opacity: 0; transform: translateY(6px); }
    to   { opacity: 1; transform: translateY(0); }
}

.ct-matrix-table__corner {
    width: 210px;
    min-width: 180px;
    vertical-align: bottom;
    padding: 0 12px 8px 4px;
}

/* Padding lives on the head inside, not here: the hover plate has to reach
 * the edges of the hit area (see .ct-matrix-table__col-head). */
.ct-matrix-table__col {
    font-size: 10.5px;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: #78829d;
    text-align: center;
    padding: 0 2px 6px;
    vertical-align: bottom;
}

/* Ellipsised against the column it is IN, not against a hard 110px: the columns
 * are equal now and how much of a view's name fits is whatever that share
 * allows. The full name stays reachable — the header is a link with a title. */
.ct-matrix-table__col-name,
.ct-matrix-table__col--total span {
    display: inline-block;
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* The right rail declares its width for the same reason the corner does: under
 * fixed layout an undeclared column would take an equal share of the view
 * columns' space. 104px matches FIXED_COLS_PX in cluster-matrix.js. */
.ct-matrix-table__col--total {
    width: 104px;
    color: #071437;
    border-left: 1px solid #e9edf3;
    padding: 0 14px 8px;
    text-align: center;
}

/* Stays a real table-cell (a flex th drops out of the table layout
 * algorithm and misaligns the row); the chip is inline-block instead. */
.ct-matrix-table__row-head {
    font-size: 12.5px;
    font-weight: 600;
    color: #071437;
    text-align: left;
    white-space: nowrap;
    vertical-align: middle;
    padding: 0 12px 0 4px;
    height: 36px;
    /* Fixed layout will not widen this column for a long problem name, so the
     * name has to know how to end. Its tooltip already carries the description;
     * the label itself is short by design. */
    overflow: hidden;
    text-overflow: ellipsis;
}

/* (The C1..C7 chip that used to sit here was removed in RD-2654 — internal
 * vocabulary. The row now carries a description tooltip instead; its styling
 * lives with the rest of that ticket's rules at the end of this file.) */

.ct-matrix-table__cell {
    text-align: center;
    height: 36px;
    padding: 0 6px;
    border: 2px solid #ffffff;
    border-radius: 7px;
    background-clip: padding-box;
    font-size: 12px;
    font-weight: 650;
    cursor: default;
    outline: none;
}

.ct-matrix-table__cell:focus-visible {
    outline: 2px solid #071437;
    outline-offset: -2px;
}

/* Heat ramp on the product's celeste family (#419FFF), light → navy.
 * Text flips to white once the background crosses mid-intensity.
 *
 * RD-2654: the ramp is now a VARIABLE, not a background. A data cell paints
 * its heat on the button inside it (so the tile is a physical, pressable
 * object with an edge and a highlight) while the <td> stays white gutter.
 * `--edge` and `--top` are the plate's rim and its top highlight, alpha-only
 * so one rule works across the whole ramp — they flip from dark to light past
 * h4, where the tile itself goes dark. `color` stays here: the button inherits
 * it, so white ink on the deep steps keeps working. */
.ct-matrix-table__cell.is-blank { --heat: #fafbfd; --edge: rgba(7, 20, 55, 0.10); --top: rgba(255, 255, 255, 0.65); }
.ct-matrix-table__cell.h0 { --heat: #f0f6fe; --edge: rgba(7, 20, 55, 0.10); --top: rgba(255, 255, 255, 0.70); color: #3a4a60; }
.ct-matrix-table__cell.h1 { --heat: #dcebfd; --edge: rgba(7, 20, 55, 0.10); --top: rgba(255, 255, 255, 0.70); color: #22303f; }
.ct-matrix-table__cell.h2 { --heat: #b9d8fa; --edge: rgba(7, 20, 55, 0.11); --top: rgba(255, 255, 255, 0.55); color: #12243a; }
.ct-matrix-table__cell.h3 { --heat: #8cbdf4; --edge: rgba(7, 20, 55, 0.12); --top: rgba(255, 255, 255, 0.45); color: #0b1b3a; }
.ct-matrix-table__cell.h4 { --heat: #419fff; --edge: rgba(255, 255, 255, 0.34); --top: rgba(255, 255, 255, 0.28); color: #ffffff; }
.ct-matrix-table__cell.h5 { --heat: #2b7cd8; --edge: rgba(255, 255, 255, 0.34); --top: rgba(255, 255, 255, 0.26); color: #ffffff; }
.ct-matrix-table__cell.h6 { --heat: #1c5aa8; --edge: rgba(255, 255, 255, 0.34); --top: rgba(255, 255, 255, 0.24); color: #ffffff; }

/* The totals never carried a heat class and never became doors, so they keep
 * painting flat on the <td> — which is exactly the contrast that sorts the
 * table into "tiles you can press" and "numbers printed into the page". */

/* Symmetric padding, unlike the data cells' 0 6px: every number in this
 * column — plain totals, door totals, the grand total — must centre on the
 * SAME axis as the TOTAL header (14px both sides), or the column reads as
 * jittered. The old 14px/6px split shifted everything 4px off the header. */
.ct-matrix-table__cell--total {
    border-radius: 0;
    border-left: 1px solid #e9edf3;
    padding: 0 14px;
    font-weight: 700;
    color: #071437;
    white-space: nowrap;
}

/* The row total as a door to the evidence (RD-2676): the cells open the agent,
 * this opens the Insights feed filtered to the row's pattern.
 *
 * The button IS the quadrant: it fills the cell and at rest paints nothing, so
 * its number sits exactly where the plain totals sit (the arrow is absolutely
 * positioned — even invisible it must not reserve flow space, which is what
 * used to push the door numbers off the column's axis). On hover it becomes a
 * tile in the same grammar as the map's cells — plate, lift, 1px rise — just
 * in the indigo the module uses for every road to Insights. */
.ct-matrix-table__total-btn {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    /* Bleeds SYMMETRICALLY into the cell's 14px padding (the same trick as
     * the preview cards' rows use): the tile covers the quadrant like a map
     * cell does, while the centred number stays on the column's axis. */
    width: calc(100% + 20px);
    margin: 0 -10px;
    height: 100%;
    min-height: 30px;
    border: 0;
    padding: 0;
    border-radius: 7px;
    background: transparent;
    font: inherit;
    color: inherit;
    cursor: pointer;
    transition: color 140ms var(--ct-ease-out), background-color 140ms var(--ct-ease-out),
                box-shadow 140ms var(--ct-ease-out), transform 140ms var(--ct-ease-out);
}

/* Centred by stretching, not by translateY: the reduced-motion block zeroes
 * transforms on this element, and a transform-centred arrow would sink.
 *
 * It is an inline SVG at 13px with a 2.4 stroke, not the 10px icon-font glyph
 * it started as — that one faded in as a grey hairline nobody could see on the
 * tinted tile, which defeats the purpose of an affordance that only exists on
 * hover. `right: 6px` keeps it clear of the tile's rounded corner. */
.ct-matrix-table__total-arrow {
    position: absolute;
    top: 0;
    bottom: 0;
    right: 6px;
    margin: auto 0;
    opacity: 0;
    transform: translateX(-3px);
    transition: opacity 140ms var(--ct-ease-out), transform 140ms var(--ct-ease-out);
}

@media (hover: hover) and (pointer: fine) {
    .ct-matrix-table__total-btn:hover {
        color: var(--ct-accent);
        background: var(--ct-accent-soft);
        box-shadow: inset 0 0 0 1px #c7d2fe, inset 0 1px 0 rgba(255, 255, 255, 0.7),
                    0 2px 6px -1px rgba(7, 20, 55, 0.16);
        transform: translateY(-1px);
    }

    .ct-matrix-table__total-btn:hover .ct-matrix-table__total-arrow {
        opacity: 1;
        transform: none;
    }
}

.ct-matrix-table__total-btn:focus-visible {
    outline: 2px solid var(--ct-accent);
    outline-offset: -2px;
    color: var(--ct-accent);
}

.ct-matrix-table__total-btn:focus-visible .ct-matrix-table__total-arrow {
    opacity: 1;
    transform: none;
}

/* The same press the cells answer with: down 1px, lift off. */
.ct-matrix-table__total-btn:active {
    transform: translateY(1px) scale(0.985);
    box-shadow: inset 0 0 0 1px #c7d2fe;
}

.ct-matrix-table__totals .ct-matrix-table__row-head--total {
    font-size: 11.5px;
    font-weight: 600;
    color: #78829d;
    padding-top: 6px;
    height: 32px;
}

.ct-matrix-table__cell--foot {
    background: #ffffff;
    border-top: 1px solid #e9edf3;
    border-radius: 0;
    font-weight: 600;
    color: #78829d;
    height: 34px;
}

.ct-matrix-table__cell--grand {
    border-left: 1px solid #e9edf3;
    padding: 0 14px;
    font-weight: 700;
    color: #071437;
    white-space: nowrap;
}

.ct-matrix-table__unit {
    font-size: 10px;
    font-weight: 600;
    color: #78829d;
    margin-left: 1px;
}

/* ── loading skeleton (shaped like the matrix, no spinner) ── */

.ct-matrix-skeleton__row {
    display: grid;
    grid-template-columns: 170px 1fr;
    gap: 14px;
    align-items: center;
    padding: 5px 0;
}

.ct-matrix-skeleton__label,
.ct-matrix-skeleton__cells {
    display: block;
    height: 22px;
    border-radius: 5px;
    background: linear-gradient(90deg, #f1f4f9 25%, #e9edf3 42%, #f1f4f9 60%);
    background-size: 220% 100%;
    animation: ct-matrix-shimmer 1.3s linear infinite;
}

.ct-matrix-skeleton__label { width: 70%; }

@keyframes ct-matrix-shimmer {
    from { background-position: 130% 0; }
    to   { background-position: -90% 0; }
}

/* ── empty / error ── */

.ct-matrix-empty {
    text-align: center;
    padding: 48px 20px 42px;
}

.ct-matrix-empty__title {
    font-size: 14px;
    font-weight: 650;
    color: #071437;
    margin-bottom: 5px;
}

.ct-matrix-empty__text {
    font-size: 12.5px;
    color: #78829d;
    max-width: 420px;
    margin: 0 auto;
}

.ct-matrix-error {
    font-size: 12.5px;
    color: #9f2f2d;
    background: #fdebec;
    border: 1px solid #f5c9ca;
    border-radius: 8px;
    padding: 12px 16px;
}

@media (prefers-reduced-motion: reduce) {
    .ct-matrix-table {
        animation: none;
    }

    .ct-matrix-skeleton__label,
    .ct-matrix-skeleton__cells {
        animation: none;
    }

    .ct-matrix-toggle__btn:active {
        transform: none;
    }
}

@media (max-width: 767.98px) {
    .ct-matrix-card {
        padding: 18px 16px 16px;
    }

    .ct-matrix-card__header {
        flex-direction: column;
    }
}

/* ══════════════════════════════════════════════════════════════════
 * RD-2608 — Cluster filter chips (Insights popover)
 *
 * The problem patterns the matrix maps, reused as a feed filter.
 * Deliberately the same chip vocabulary as .ct-type-filter (a filter
 * is a filter), with the C1..C7 code carried as a small leading badge
 * so the chip and the matrix row read as the same object.
 * ══════════════════════════════════════════════════════════════════ */
.ct-cluster-filters {
    display: flex;
    align-items: center;
    gap: 6px;
    flex-wrap: wrap;
    max-width: 460px;
}

.ct-cluster-filter {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: 12px;
    font-weight: 500;
    color: #475569;
    background: #f8fafc;
    border: 1px solid #e2e8f0;
    border-radius: 999px;
    padding: 5px 12px;
    cursor: pointer;
    transition: color 150ms var(--ct-ease-out), background-color 150ms var(--ct-ease-out),
                border-color 150ms var(--ct-ease-out), opacity 150ms var(--ct-ease-out),
                transform 150ms var(--ct-ease-out);
}

.ct-cluster-filter:hover {
    background: #f1f5f9;
    border-color: #cbd5e1;
}

.ct-cluster-filter:active {
    transform: scale(0.97);
}

.ct-cluster-filter:focus-visible {
    outline: 2px solid var(--ct-accent-border);
    outline-offset: 1px;
}

.ct-cluster-filter.is-active {
    background: #eef2ff;
    border-color: #818cf8;
    color: #4f46e5;
}

/* A pattern this feed does not contain: still clickable (it would just
 * empty the list), but visibly not worth the trip. */
.ct-cluster-filter.is-empty {
    opacity: 0.45;
}

/* (`.ct-cluster-filter__code` lived here until RD-2654 dropped the C-code
 * badge from the chips, for the same reason the matrix dropped it.) */

.ct-cluster-filter__count {
    font-size: 10px;
    background: #e2e8f0;
    color: #475569;
    border-radius: 4px;
    padding: 0 5px;
    min-width: 18px;
    text-align: center;
    font-variant-numeric: tabular-nums;
}

.ct-cluster-filter.is-active .ct-cluster-filter__count {
    background: #c7d2fe;
    color: #4338ca;
}

@media (prefers-reduced-motion: reduce) {
    .ct-cluster-filter:active {
        transform: none;
    }
}

/* ══════════════════════════════════════════════════════════════════
 * RD-2654 — Agent activation from the Problem map
 *
 * Three surfaces, one visual language: the cell (row agent + chip), the
 * drawer that opens from it, and the wizard modal. They reuse the module's
 * own tokens (--ct-accent indigo, --ct-sev-*, --ct-ease-out) and the matrix
 * card's neutrals (#e9edf3 hairlines, #071437 ink, #78829d muted) so the
 * flow reads as one more part of the hub, not a bolted-on product.
 *
 * Motion policy, since this is where it is easiest to get wrong:
 *   · the drawer travels with the iOS-style curve below and exits FASTER
 *     than it enters (the system responding beats the system deciding);
 *   · only transform and opacity animate, ever;
 *   · nothing entering starts from scale(0) — 0.96 is the floor;
 *   · the evidence list staggers 40ms per row and stops there. The chip on a
 *     cell has NO entrance animation on purpose: the matrix re-renders on
 *     every Frequency/Cost toggle, and a chip that popped each time would
 *     draw the eye to something that did not change.
 * ══════════════════════════════════════════════════════════════════ */
:root {
    /* Ionic's drawer curve: leaves immediately, lands without a bounce. */
    --ct-ease-drawer: cubic-bezier(0.32, 0.72, 0, 1);
}

/* ── the cell, now a door ── */

/* (The row-head height override lived here while the row carried a second line
 * naming the agent. The row is one line again, so it keeps the 36px of the
 * base rule further up.) */

/* The row name explains what the pattern groups, on hover or on focus. The
 * dotted rule is PERMANENT and deliberately faint: a definition that only
 * announces itself once you are already hovering it is a definition nobody
 * finds — the same lesson the cells' hover ring taught. `help` rather than
 * `pointer` because reading it goes nowhere; the cell beside it is the door. */
.ct-matrix-table__row-label {
    cursor: help;
    border-bottom: 1px dotted #c8ccd8;
    transition: border-color 150ms var(--ct-ease-out), color 150ms var(--ct-ease-out);
}

.ct-matrix-table__row-label:hover,
.ct-matrix-table__row-label:focus-visible {
    color: var(--ct-accent);
    border-bottom-color: var(--ct-accent);
}

.ct-matrix-table__row-label:focus-visible {
    outline: 2px solid var(--ct-accent-border);
    outline-offset: 2px;
    border-radius: 3px;
}

/* THE TILE PLATE.
 *
 * A heat-map cell is a canonical read-only object: coloured rectangles flush
 * against each other read as "a chart". Nothing contradicted that until you
 * hovered — and a hover cue can only be found by someone who already suspects
 * there is something to find. That is the same trap the rejected hint line was
 * written to patch.
 *
 * So the data cell stops being a table cell and becomes a physical chip: the
 * <td> is white gutter, and the BUTTON carries the heat, a 1px rim, a 1px top
 * highlight and a hairline drop shadow. The decisive part is the contrast
 * inside the same table — plated data cells against a flat Total row and Total
 * column — which sorts the grid into "things sitting on the surface" and
 * "numbers printed into it" without a word of copy, in the first look, with no
 * cursor, no mode and no first-visit gate.
 *
 * `.is-door` is emitted by cluster-matrix.js on data cells ONLY. Every rule
 * below is scoped to it, which is also what fixed a live bug: the old
 * `.ct-matrix-table__cell:hover` matched the base class, so the totals lit an
 * indigo ring and then did nothing when clicked. */
.ct-matrix-table__cell.is-door {
    padding: 0;
    background: #ffffff;
    cursor: pointer;
    height: 40px;
}

.ct-matrix-table__cell-btn {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 5px;
    width: 100%;
    height: 100%;
    min-height: 34px;
    padding: 0 6px;
    border: 0;
    border-radius: 5px;
    background: transparent;
    color: inherit;
    font: inherit;
    cursor: pointer;
    transition: transform 140ms var(--ct-ease-out);
}

/* Four independent shadow slots, because they must compose: the hover ring,
 * the "an agent works here" ring, the plate and the lift all live on the same
 * box-shadow. Stacking them as one declaration is what broke before — an
 * unscoped `.has-agent { box-shadow }` and `:hover { box-shadow }` have equal
 * specificity, so a cell with an agent never once showed a hover ring.
 *
 * Every slot keeps its inset-ness constant so box-shadow still interpolates.
 * WARNING: setting `box-shadow` directly on this element instead of the vars
 * silently erases the plate. */
.ct-matrix-table__cell.is-door .ct-matrix-table__cell-btn {
    position: relative;
    border-radius: 7px;
    background: var(--heat, #ffffff);
    --plate: inset 0 0 0 1px var(--edge), inset 0 1px 0 var(--top);
    --ring: inset 0 0 0 0 rgba(67, 56, 202, 0);
    --agent: inset 0 0 0 0 rgba(67, 56, 202, 0);
    --lift: 0 1px 1px rgba(7, 20, 55, 0.05);
    box-shadow: var(--ring), var(--agent), var(--plate), var(--lift);
    transition: box-shadow 140ms var(--ct-ease-out), transform 140ms var(--ct-ease-out);
}

@media (hover: hover) and (pointer: fine) {
    .ct-matrix-table__cell.is-door:hover .ct-matrix-table__cell-btn {
        --ring: inset 0 0 0 2px rgba(67, 56, 202, 0.55);
        --lift: 0 2px 6px -1px rgba(7, 20, 55, 0.16);
        transform: translateY(-1px);
    }
}

/* The press: the tile goes down 1px, the lift switches off, and the plate's
 * top HIGHLIGHT flips to an inner top SHADOW — the same pixel convex, then
 * concave. That flip is the whole trick, and it only works now that the paint
 * is on the button: the old `scale(.96)` shrank the digit while the coloured
 * tile stayed behind, which is why pressing never confirmed anything.
 * Out at 90ms, back at 140ms — the system answers faster than it relaxes. */
.ct-matrix-table__cell.is-door .ct-matrix-table__cell-btn:active {
    transform: translateY(1px) scale(0.985);
    --lift: 0 0 0 rgba(7, 20, 55, 0);
    --plate: inset 0 0 0 1px var(--edge), inset 0 2px 3px rgba(7, 20, 55, 0.12);
    transition-duration: 90ms;
}

/* Focus speaks the same language as hover. It used to be ink-black while hover
 * was indigo, so the two discovery paths taught two different vocabularies. */
.ct-matrix-table__cell-btn:focus-visible {
    outline: 2px solid var(--ct-accent);
    outline-offset: -2px;
}

/* An empty cell is still a door ("no signals here — start observing"), so it
 * keeps its plate and its pointer; only the ink is muted. */
.ct-matrix-table__cell.is-blank .ct-matrix-table__cell-btn {
    color: #b5bdcc;
}

.ct-matrix-table__cell-value {
    font-variant-numeric: tabular-nums;
}

/* A cell with an agent carries a hairline ring — the "after" state the whole
 * feature exists to show. Its own slot, so hover still lands on top of it. */
.ct-matrix-table__cell.has-agent .ct-matrix-table__cell-btn {
    --agent: inset 0 0 0 1.5px rgba(67, 56, 202, 0.55);
}

/* ── the hover face: the number gives way to the verb (RD-2654) ──
 *
 * An overlay rather than a text swap, for two reasons that both bit earlier
 * versions of this cell: absolute positioning means the tile cannot resize when
 * a two-word label replaces a one-digit number (a matrix that reflows under the
 * cursor is unusable), and the number stays in the DOM, so the tooltip and the
 * accessible name keep working while the paint changes.
 *
 * Filled indigo, not a tint: this is the one moment the map stops reporting and
 * starts offering, and a subtle version of that reads as a hover artefact. */
.ct-matrix-table__cell-cta {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 4px;
    border-radius: 7px;
    background: var(--ct-accent);
    color: #ffffff;
    /* COMPACT is the default, and that is the safe direction: a cell can be as
     * narrow as 62px (CELL_MIN_PX in cluster-matrix.js), where "Crear agente"
     * beside an icon does not fit. The roomy form below is opted into by
     * measurement, so a browser that never runs the JS still reads correctly. */
    font-size: 9.5px;
    font-weight: 700;
    letter-spacing: -0.005em;
    line-height: 1;
    white-space: nowrap;
    overflow: hidden;
    opacity: 0;
    transform: scale(0.985);
    /* The tile below owns the click; this is paint, never a target. */
    pointer-events: none;
    transition: opacity 120ms var(--ct-ease-out), transform 120ms var(--ct-ease-out);
}

/* The mark costs ~17px of a tile that may only have 62. Words first: the verb
 * is what teaches the interaction, the robot only decorates it. */
.ct-matrix-table__cell-cta svg {
    display: none;
}

.ct-matrix-table.is-cta-full .ct-matrix-table__cell-cta {
    font-size: 10.5px;
    letter-spacing: 0.01em;
    gap: 4px;
}

.ct-matrix-table.is-cta-full .ct-matrix-table__cell-cta svg {
    display: inline-flex;
    flex: none;
}

@media (hover: hover) and (pointer: fine) {
    .ct-matrix-table__cell.is-door:hover .ct-matrix-table__cell-cta {
        opacity: 1;
        transform: none;
    }

    /* The number and the chip step aside instead of being covered: the fill is
     * opaque anyway, and fading them out keeps the swap from looking like two
     * layers fighting during the 120ms. */
    .ct-matrix-table__cell.is-door:hover .ct-matrix-table__cell-value,
    .ct-matrix-table__cell.is-door:hover .ct-matrix-agent-chip {
        opacity: 0;
        transition: opacity 120ms var(--ct-ease-out);
    }
}

/* Keyboard gets the same face. It is the only way a tab-only user learns the
 * cell is a door, since they never see hover. */
.ct-matrix-table__cell-btn:focus-visible .ct-matrix-table__cell-cta {
    opacity: 1;
    transform: none;
}

.ct-matrix-table__cell-btn:focus-visible .ct-matrix-table__cell-value,
.ct-matrix-table__cell-btn:focus-visible .ct-matrix-agent-chip {
    opacity: 0;
}

.ct-matrix-agent-chip {
    display: inline-flex;
    align-items: center;
    gap: 2px;
    flex: none;
    padding: 1px 5px 1px 3px;
    border-radius: 999px;
    font-size: 9.5px;
    font-weight: 700;
    letter-spacing: 0.02em;
    line-height: 1.5;
}

.ct-matrix-agent-chip i {
    font-size: 10px;
    line-height: 1;
}

.ct-matrix-agent-chip.is-active {
    background: #eef2ff;
    color: #4338ca;
    box-shadow: inset 0 0 0 1px #c7d2fe;
}

/* Requested: the only state v1 writes. Indigo like `is-active` but hollow —
 * something has been asked for here, and nothing is running yet. */
.ct-matrix-agent-chip.is-requested {
    background: #ffffff;
    color: #4338ca;
    box-shadow: inset 0 0 0 1px #a5b4fc;
}

.ct-matrix-agent-chip.is-pending {
    background: #fff8dd;
    color: #8a6410;
    box-shadow: inset 0 0 0 1px #f5de9c;
}

.ct-matrix-agent-chip.is-paused {
    background: #f1f4f9;
    color: #5e6278;
    box-shadow: inset 0 0 0 1px #dbdfe9;
}

/* On the deep end of the heat ramp the chip sits on navy, so it flips to a
 * translucent white plate instead of its tinted one. */
.ct-matrix-table__cell.h4 .ct-matrix-agent-chip,
.ct-matrix-table__cell.h5 .ct-matrix-agent-chip,
.ct-matrix-table__cell.h6 .ct-matrix-agent-chip {
    background: rgba(255, 255, 255, 0.94);
    box-shadow: none;
}

/* ── drawer ── */

/* 1035/1040 — deliberately BELOW Bootstrap's backdrop (1050) and modal
 * (1055), so the wizard opens over a dimmed drawer and the evidence the
 * manager was reading stays where they left it. */
.ct-activate-scrim {
    position: fixed;
    inset: 0;
    z-index: 1035;
    background: rgba(7, 20, 55, 0.36);
    opacity: 0;
    transition: opacity 200ms var(--ct-ease-out);
}

.ct-activate-scrim.is-on {
    opacity: 1;
}

/* `display: flex` below beats the UA's `[hidden] { display: none }` on
 * specificity, so the attribute has to be restored explicitly — otherwise a
 * "hidden" drawer stays mounted off-screen, keeping its content in the tab
 * order and the accessibility tree with `aria-modal="true"` permanently on.
 * Same reason for the toast further down. */
.ct-activate-drawer[hidden],
.ct-activate-toast[hidden] {
    display: none;
}

.ct-activate-drawer {
    position: fixed;
    top: 0;
    right: 0;
    bottom: 0;
    z-index: 1040;
    display: flex;
    flex-direction: column;
    width: min(460px, 94vw);
    background: #ffffff;
    border-left: 1px solid #e9edf3;
    box-shadow: -14px 0 34px rgba(7, 20, 55, 0.10);
    transform: translateX(100%);
    transition: transform 200ms var(--ct-ease-out);
}

.ct-activate-drawer.is-on {
    transform: translateX(0);
    transition: transform 260ms var(--ct-ease-drawer);
}

.ct-activate-drawer__head {
    position: relative;
    flex: none;
    padding: 22px 52px 16px 24px;
    border-bottom: 1px solid #f1f4f9;
}

.ct-activate-drawer__kicker {
    display: block;
    font-size: 10.5px;
    font-weight: 700;
    letter-spacing: 0.09em;
    text-transform: uppercase;
    color: #99a1b7;
    margin-bottom: 6px;
}

.ct-activate-drawer__title {
    margin: 0 0 4px;
    font-size: 17px;
    font-weight: 650;
    letter-spacing: -0.015em;
    color: #071437;
}

.ct-activate-drawer__sub {
    margin: 0;
    font-size: 12.5px;
    line-height: 1.5;
    color: #4b5675;
    text-wrap: pretty;
}

.ct-activate-drawer__close {
    position: absolute;
    top: 14px;
    right: 14px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 30px;
    height: 30px;
    padding: 0;
    border: 0;
    border-radius: 8px;
    background: transparent;
    color: #99a1b7;
    font-size: 13px;
    cursor: pointer;
    transition: color 150ms var(--ct-ease-out), background-color 150ms var(--ct-ease-out), transform 150ms var(--ct-ease-out);
}

.ct-activate-drawer__close:hover {
    background: #f1f4f9;
    color: #071437;
}

.ct-activate-drawer__close:active {
    transform: scale(0.94);
}

.ct-activate-drawer__close:focus-visible {
    outline: 2px solid var(--ct-accent-border);
    outline-offset: 1px;
}

.ct-activate-drawer__body {
    flex: 1;
    overflow-y: auto;
    padding: 18px 24px 24px;
}

.ct-activate-drawer__foot {
    flex: none;
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 14px 24px;
    border-top: 1px solid #f1f4f9;
    background: #fbfcfe;
}

/* ── skeleton (shaped like the panel, no spinner) ── */

.ct-activate-skel__stats {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 8px;
    margin-bottom: 18px;
}

.ct-activate-skel__box,
.ct-activate-skel__row {
    display: block;
    border-radius: 8px;
    background: linear-gradient(90deg, #f1f4f9 25%, #e9edf3 42%, #f1f4f9 60%);
    background-size: 220% 100%;
    animation: ct-matrix-shimmer 1.3s linear infinite;
}

.ct-activate-skel__box { height: 58px; }

.ct-activate-skel__row {
    height: 44px;
    margin-bottom: 8px;
}

/* ── stats ── */

/* Two tiles now (RD-2654): the high-severity count was a third number that
 * mostly read "0" and competed with the two that carry the decision. */
.ct-activate-stats {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 8px;
    margin-bottom: 18px;
}

.ct-activate-stat {
    padding: 10px 12px;
    border: 1px solid #e9edf3;
    border-radius: 10px;
    background: #fbfcfe;
}

.ct-activate-stat__value {
    display: block;
    font-size: 17px;
    font-weight: 700;
    letter-spacing: -0.02em;
    color: #071437;
    font-variant-numeric: tabular-nums;
}

.ct-activate-stat__label {
    display: block;
    margin-top: 1px;
    font-size: 10.5px;
    line-height: 1.35;
    color: #99a1b7;
}

.ct-activate-stat.is-alert .ct-activate-stat__value {
    color: #b91c1c;
}

/* ── active agent block ── */

.ct-activate-agentstate {
    padding: 13px 15px;
    border: 1px solid var(--ct-accent-border);
    border-radius: 10px;
    background: var(--ct-accent-soft);
    margin-bottom: 18px;
}

.ct-activate-agentstate.is-requested {
    border-color: #c7d2fe;
    background: #ffffff;
}

.ct-activate-agentstate.is-pending {
    border-color: #f5de9c;
    background: #fff8dd;
}

.ct-activate-agentstate.is-paused {
    border-color: #dbdfe9;
    background: #f7f9fc;
}

.ct-activate-agentstate__head {
    display: flex;
    align-items: center;
    gap: 7px;
    font-size: 13.5px;
    font-weight: 650;
    color: var(--ct-accent);
}

.ct-activate-agentstate.is-requested .ct-activate-agentstate__head { color: var(--ct-accent); }
.ct-activate-agentstate.is-pending .ct-activate-agentstate__head { color: #8a6410; }
.ct-activate-agentstate.is-paused .ct-activate-agentstate__head { color: #4b5675; }

.ct-activate-agentstate__meta,
.ct-activate-agentstate__note {
    margin: 4px 0 0;
    font-size: 12px;
    line-height: 1.5;
    color: #4b5675;
}

.ct-activate-agentstate__note {
    color: #78829d;
}

/* ── the pitch (RD-2654) ──
 *
 * The one block of the drawer that is about buying, so it is the one block that
 * looks different: three cards on a celeste-tinted panel, ordered by position
 * (problem → solution → outcome) rather than numbered, because the reading
 * order IS the argument.
 *
 * It sits between the state of the cell and its evidence on purpose: after the
 * numbers that make the cell worth a look, before the list that proves them. */

/* Brand palette, not the module accent (RD-2654). Everything else in this
 * drawer is indigo because indigo is the FEED's accent; the pitch is the one
 * block that speaks for Leanmote itself, so it uses the logo's own colours
 * (`--lm-*` in custom.css): celeste #419FFF, navy #1C2E62, red #FF3366, green
 * #40D390. The `-ink` variants are those hues darkened until 11px bold type
 * clears contrast on white — the brand colour paints marks and edges, its ink
 * paints text. Declared locally so this block does not depend on load order
 * with custom.css. */
.ct-agent-pitch {
    --ct-lm-navy: #1c2e62;
    --ct-lm-blue: #419fff;
    --ct-lm-blue-ink: #1668cc;
    --ct-lm-red: #ff3366;
    --ct-lm-red-ink: #d5104a;
    --ct-lm-green: #40d390;
    --ct-lm-green-ink: #0f8054;

    margin: 0 0 18px;
    padding: 14px;
    border: 1px solid #cfe3ff;
    border-radius: 14px;
    background:
        linear-gradient(180deg, #eff6ff 0%, #ffffff 62%);
    opacity: 0;
    transform: translateY(6px);
    transition: opacity 260ms var(--ct-ease-out), transform 260ms var(--ct-ease-out);
}

.ct-agent-pitch.is-on {
    opacity: 1;
    transform: translateY(0);
}

.ct-agent-pitch[hidden] { display: none; }

/* Navy type, celeste mark: the logo's own pairing. */
.ct-agent-pitch__head {
    display: flex;
    align-items: center;
    gap: 7px;
    margin-bottom: 11px;
    color: var(--ct-lm-navy);
}

.ct-agent-pitch__head svg { color: var(--ct-lm-blue); }

.ct-agent-pitch__head-title {
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
}

.ct-agent-pitch__card {
    position: relative;
    padding: 12px 13px;
    border: 1px solid #e9edf3;
    border-radius: 11px;
    background: #ffffff;
    box-shadow: 0 1px 2px rgba(15, 23, 42, 0.04);
    opacity: 0;
    transform: translateY(6px);
    animation: ct-activate-rise 280ms var(--ct-ease-out) forwards;
    animation-delay: calc(var(--ct-stagger, 0) * 70ms);
}

.ct-agent-pitch__card + .ct-agent-pitch__card { margin-top: 8px; }

/* The left edge is what makes the three read as one sequence: same card, three
 * moments. Colour carries the meaning — the problem, the fix, the payoff. */
.ct-agent-pitch__card::before {
    content: '';
    position: absolute;
    inset: 10px auto 10px 0;
    width: 3px;
    border-radius: 0 3px 3px 0;
    background: currentColor;
    opacity: 0.9;
}

/* `color` is the card's BRAND hue — it paints the edge and the badge, both of
 * which are fills. `--ct-pitch-ink` is the same hue darkened, for anything that
 * is type: a 11px bold kicker in #419FFF or #40D390 on white is unreadable. */
.ct-agent-pitch__card.is-problem {
    color: var(--ct-lm-red);
    --ct-pitch-ink: var(--ct-lm-red-ink);
}

.ct-agent-pitch__card.is-solution {
    color: var(--ct-lm-blue);
    --ct-pitch-ink: var(--ct-lm-blue-ink);
}

.ct-agent-pitch__card.is-outcome {
    color: var(--ct-lm-green);
    --ct-pitch-ink: var(--ct-lm-green-ink);
}

.ct-agent-pitch__card-head {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-bottom: 6px;
}

.ct-agent-pitch__badge {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 24px;
    height: 24px;
    border-radius: 7px;
    background: currentColor;
    flex: 0 0 auto;
}

.ct-agent-pitch__badge i {
    font-size: 13px;
    color: #ffffff;
}

.ct-agent-pitch__kicker {
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.07em;
    text-transform: uppercase;
    color: var(--ct-pitch-ink, currentColor);
}

/* The card's content: the same prose the paragraph carried, split into bullets
 * so it scans (the team's ask). The dot takes the card's brand hue — the ink
 * stays neutral, because three cards of coloured TEXT would read as three
 * warnings, not three steps of one argument. */
.ct-agent-pitch__points {
    margin: 0;
    padding: 0;
    list-style: none;
}

.ct-agent-pitch__points li {
    position: relative;
    padding-left: 15px;
    font-size: 13px;
    line-height: 1.5;
    color: #1f2a44;
    text-wrap: pretty;
}

.ct-agent-pitch__points li + li { margin-top: 5px; }

.ct-agent-pitch__points li::before {
    content: '';
    position: absolute;
    left: 3px;
    top: 7px;
    width: 5px;
    height: 5px;
    border-radius: 50%;
    background: currentColor;
    opacity: 0.7;
}

.ct-agent-pitch__label {
    display: block;
    margin: 10px 0 5px;
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: #99a1b7;
}

/* (The bullet lists under "problem" and "solution" were removed — the card's
 * paragraph already said it. The metrics chips are the only list left.) */

.ct-agent-pitch__chips {
    display: flex;
    flex-wrap: wrap;
    gap: 5px;
}

.ct-agent-pitch__chip {
    display: inline-flex;
    align-items: center;
    padding: 3px 9px;
    border-radius: 999px;
    /* Ink, not the brand hue, on both the type and the hairline: a chip outlined
     * in #40D390 with #40D390 text is a pill you have to squint at. */
    border: 1px solid;
    background: #ffffff;
    font-size: 11.5px;
    font-weight: 600;
    color: var(--ct-pitch-ink, currentColor);
}

/* The money that closes the argument. Green like the card it sits in, and
 * separated by a hairline because it is a different KIND of statement from the
 * sentence above it: that one is what we expect, this one is what the cell
 * measures today. Label and figure only — the line that used to explain it said
 * what the drawer's "impacto estimado" tile already says about the same
 * number. */
.ct-agent-pitch__impact {
    margin-top: 10px;
    padding-top: 10px;
    border-top: 1px dashed #e9edf3;
}

.ct-agent-pitch__impact .ct-agent-pitch__label {
    margin-top: 0;
}

.ct-agent-pitch__impact-value {
    display: block;
    font-size: 19px;
    font-weight: 700;
    letter-spacing: -0.015em;
    line-height: 1.2;
    color: var(--ct-pitch-ink, #0f8054);
    font-variant-numeric: tabular-nums;
    font-feature-settings: "tnum" 1;
}


/* Loading: the cards are already there, only their content is missing — so the
 * panel does not resize when the text lands. */
.ct-agent-pitch__card.is-skeleton {
    padding: 12px 13px;
}

.ct-agent-pitch__card.is-skeleton::before { display: none; }

.ct-agent-pitch__card.is-skeleton .ct-activate-skel__row {
    height: 11px;
    margin-bottom: 7px;
}

.ct-agent-pitch__card.is-skeleton .ct-activate-skel__row.is-short {
    width: 62%;
    margin-bottom: 0;
}

/* ── shared type ── */

.ct-activate-section {
    margin: 20px 0 9px;
    font-size: 10.5px;
    font-weight: 700;
    letter-spacing: 0.09em;
    text-transform: uppercase;
    color: #99a1b7;
}

.ct-activate-muted {
    margin: 0;
    font-size: 12px;
    line-height: 1.55;
    color: #78829d;
    text-wrap: pretty;
}

.ct-activate-hint {
    margin: 7px 0 0;
    font-size: 11.5px;
    line-height: 1.5;
    color: #99a1b7;
    text-wrap: pretty;
}

.ct-activate-link {
    margin-top: 9px;
    padding: 0;
    border: 0;
    background: none;
    color: var(--ct-accent);
    font-size: 12.5px;
    font-weight: 600;
    cursor: pointer;
    transition: opacity 150ms var(--ct-ease-out);
}

.ct-activate-link:hover { opacity: 0.75; }

.ct-activate-link:focus-visible {
    outline: 2px solid var(--ct-accent-border);
    outline-offset: 2px;
    border-radius: 4px;
}

/* ── evidence ── */

.ct-activate-evidence {
    list-style: none;
    margin: 0;
    padding: 0;
}

/* RD-2721 — the entry animation moved off the frame and onto the <li> that
 * holds it. The frame is a button now, and its hover/press transforms would
 * have been overwritten by an `animation … forwards` sitting on the same
 * element: the row would light up and then refuse to move. Two elements, one
 * transform each. */
.ct-activate-evidence__row {
    margin-bottom: 7px;
    opacity: 0;
    transform: translateY(6px);
    animation: ct-activate-rise 260ms var(--ct-ease-out) forwards;
    animation-delay: calc(var(--ct-stagger, 0) * 40ms);
}

@keyframes ct-activate-rise {
    to { opacity: 1; transform: translateY(0); }
}

/* The frame — the same box whether the row opens an insight or not, so the
 * list reads identically when the feed module is not on the page to answer a
 * press. The button resets live here rather than on a modifier for that same
 * reason: only the affordances below are conditional. */
.ct-activate-evidence__item {
    display: flex;
    width: 100%;
    gap: 9px;
    padding: 11px 13px;
    border: 1px solid #e9edf3;
    border-radius: 10px;
    background: #ffffff;
    font: inherit;
    color: inherit;
    text-align: left;
    -webkit-appearance: none;
    appearance: none;
}

button.ct-activate-evidence__item {
    cursor: pointer;
    transition: border-color 140ms var(--ct-ease-out), background-color 140ms var(--ct-ease-out),
                box-shadow 140ms var(--ct-ease-out), transform 140ms var(--ct-ease-out),
                opacity 140ms var(--ct-ease-out);
}

/* The affordance is the row itself and nothing added to it: tint, border, a 1px
 * lift and the title going accent. A trailing arrow was tried and dropped
 * (product decision) — on a wide block of text a glyph fading in at the right
 * edge reads as a second thing happening rather than as the row answering. */
@media (hover: hover) and (pointer: fine) {
    button.ct-activate-evidence__item:hover {
        border-color: var(--ct-accent-border);
        background: var(--ct-accent-soft);
        box-shadow: 0 2px 6px -1px rgba(7, 20, 55, 0.10);
        transform: translateY(-1px);
    }

    button.ct-activate-evidence__item:hover .ct-activate-evidence__title {
        color: var(--ct-accent-strong);
    }
}

button.ct-activate-evidence__item:focus-visible {
    outline: 2px solid var(--ct-accent);
    outline-offset: -2px;
}

/* The same press the map's cells and row totals answer with: down 1px, lift
 * off. It is also the whole affordance on touch, where there is no hover to
 * paint one — the row answers under the finger. */
button.ct-activate-evidence__item:active {
    transform: translateY(1px) scale(0.99);
    box-shadow: none;
}

/* Waiting on the popup. A cell of a column the feed never loaded costs a
 * round-trip, and a row that goes on looking idle through it reads as a click
 * that missed. It dims and refuses a second press (the handler guards on this
 * class too). */
button.ct-activate-evidence__item.is-busy {
    cursor: progress;
    opacity: 0.6;
}

.ct-activate-evidence__body {
    display: block;
    min-width: 0;
}

.ct-activate-evidence__title {
    display: block;
    font-size: 12.5px;
    font-weight: 500;
    line-height: 1.45;
    color: #071437;
    text-wrap: pretty;
}

.ct-activate-evidence__meta {
    display: block;
    margin-top: 3px;
    font-size: 11px;
    color: #99a1b7;
    font-variant-numeric: tabular-nums;
}

/* Severity as a dot: the colour is a second cue, the tooltip is the first,
 * so this never carries the meaning alone. */
.ct-activate-sev {
    flex: none;
    width: 8px;
    height: 8px;
    margin-top: 5px;
    border-radius: 50%;
    background: var(--ct-sev-medium);
}

.ct-activate-sev.is-high { background: var(--ct-sev-high); }
.ct-activate-sev.is-low  { background: var(--ct-sev-low); }

/* RD-2908 — where a row of the high-severity list lives.
 *
 * That list is cross-cutting (any pattern, any view), so without saying WHERE
 * each insight sits the reader has to open it just to place it. It goes ABOVE
 * the title and not in the meta line underneath: it is the row's heading, and
 * the meta already carries the age and the cost — three facts on one line
 * truncate, and the one that loses is always the last.
 *
 * The cell drawer has no such line because its header already names the pattern
 * and the view: there, both are constants of the list. */
.ct-activate-evidence__scope {
    display: block;
    margin-bottom: 3px;
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 0.07em;
    text-transform: uppercase;
    color: #99a1b7;
}

/* The view half in the accent, so the eye can group rows by view down the list
 * without reading either half. */
.ct-activate-evidence__scope-view {
    color: var(--ct-accent);
}

/* ── coverage ── */

.ct-activate-coverage__row {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-top: 10px;
}

.ct-activate-coverage__label {
    flex: none;
    width: 118px;
    font-size: 12px;
    color: #4b5675;
}

.ct-activate-coverage__bar {
    flex: 1;
    height: 7px;
    border-radius: 99px;
    background: #eef1f6;
    overflow: hidden;
}

.ct-activate-coverage__bar i {
    display: block;
    height: 100%;
    border-radius: 99px;
    background: var(--ct-accent);
}

.ct-activate-coverage__bar i.is-partial { background: #ecb22e; }
.ct-activate-coverage__bar i.is-none    { background: #dbdfe9; }

.ct-activate-coverage__pct {
    flex: none;
    width: 38px;
    text-align: right;
    font-size: 12px;
    font-weight: 650;
    color: #071437;
    font-variant-numeric: tabular-nums;
}

.ct-activate-coverage__state {
    margin-left: auto;
    font-size: 15px;
    line-height: 1;
    color: #99a1b7;
}

.ct-activate-coverage__state.is-ok   { color: #17c653; }
.ct-activate-coverage__state.is-none { color: #ecb22e; }

.ct-activate-coverage__detail {
    margin: 3px 0 0 128px;
    font-size: 11px;
    color: #99a1b7;
}

/* ── notes ── */

.ct-activate-note {
    display: flex;
    align-items: flex-start;
    gap: 8px;
    margin-top: 14px;
    padding: 10px 13px;
    border: 1px solid #e9edf3;
    border-left: 3px solid #dbdfe9;
    border-radius: 8px;
    background: #fbfcfe;
    font-size: 11.5px;
    line-height: 1.55;
    color: #4b5675;
    text-wrap: pretty;
}

.ct-activate-note i {
    flex: none;
    margin-top: 1px;
    font-size: 13px;
    color: #99a1b7;
}

.ct-activate-note.is-warn {
    border-color: #f5de9c;
    border-left-color: #ecb22e;
    background: #fff8dd;
    color: #8a6410;
}

.ct-activate-note.is-warn i { color: #ecb22e; }

.ct-activate-note.is-policy {
    border-left-color: #17c653;
}

.ct-activate-empty {
    padding: 26px 6px;
    text-align: center;
}

.ct-activate-empty__title {
    font-size: 13.5px;
    font-weight: 650;
    color: #071437;
    margin-bottom: 6px;
}

.ct-activate-empty__text {
    margin: 0 auto;
    max-width: 320px;
    font-size: 12px;
    line-height: 1.55;
    color: #78829d;
    text-wrap: pretty;
}

/* ── buttons ── */

.ct-activate-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    padding: 10px 16px;
    border: 1px solid #dbdfe9;
    border-radius: 8px;
    background: #ffffff;
    color: #4b5675;
    font-size: 12.5px;
    font-weight: 600;
    cursor: pointer;
    transition: background-color 160ms var(--ct-ease-out), border-color 160ms var(--ct-ease-out),
                color 160ms var(--ct-ease-out), transform 160ms var(--ct-ease-out);
}

.ct-activate-btn i { font-size: 13px; }

/* The agent mark. Optically 1px low against a cap-height label, which is the
 * usual correction for a square glyph beside text. */
.ct-activate-bot {
    flex: none;
    margin-top: 1px;
}

.ct-activate-btn:hover {
    border-color: #99a1b7;
    color: #071437;
}

.ct-activate-btn:active {
    transform: scale(0.97);
}

.ct-activate-btn:focus-visible {
    outline: 2px solid var(--ct-accent-border);
    outline-offset: 1px;
}

.ct-activate-btn--primary {
    background: var(--ct-accent);
    border-color: var(--ct-accent);
    color: #ffffff;
}

.ct-activate-btn--primary:hover {
    background: var(--ct-accent-strong);
    border-color: var(--ct-accent-strong);
    color: #ffffff;
}

.ct-activate-btn--primary:disabled {
    background: #c8ccd8;
    border-color: #c8ccd8;
    color: #ffffff;
    cursor: not-allowed;
    transform: none;
}

.ct-activate-btn--wide {
    flex: 1;
}

.ct-activate-btn--ghost {
    border-color: transparent;
    background: transparent;
    color: #78829d;
}

.ct-activate-btn--ghost:hover {
    background: #f1f4f9;
    border-color: transparent;
    color: #071437;
}

.ct-activate-btn--quiet {
    border-color: transparent;
    background: transparent;
    color: #78829d;
}

.ct-activate-btn--quiet:hover {
    background: #f1f4f9;
    border-color: transparent;
    color: #071437;
}

/* ── wizard modal ── */

.ct-activate-modal__dialog {
    max-width: 560px;
}

.ct-activate-modal__content {
    border-radius: 14px;
    border: 0;
    box-shadow: 0 24px 60px rgba(7, 20, 55, 0.22);
    overflow: hidden;
}

.ct-activate-modal__head {
    position: relative;
    padding: 20px 52px 0 24px;
}

.ct-activate-modal__title {
    margin: 0 0 3px;
    font-size: 16.5px;
    font-weight: 650;
    letter-spacing: -0.015em;
    color: #071437;
}

.ct-activate-modal__sub {
    margin: 0;
    font-size: 12.5px;
    line-height: 1.5;
    color: #78829d;
    text-wrap: pretty;
}

.ct-activate-modal__close {
    top: 16px;
    right: 16px;
}

/* Steps: a rule under each label that fills as the card advances. No numbered
 * circles — the labels already say where you are, and four circles on one line
 * is the most generic wizard chrome there is.
 *
 * The rail came back after the one-screen version: three blocks stacked was
 * everything at once, and "everything at once" is what people skip. Four light
 * steps read faster than one dense screen even though the total is identical. */
.ct-activate-steps {
    display: flex;
    gap: 6px;
    list-style: none;
    margin: 16px 0 0;
    padding: 0 24px;
}

.ct-activate-steps__item {
    flex: 1;
    padding-bottom: 9px;
    border-bottom: 2.5px solid #eef1f6;
    text-align: center;
    transition: border-color 200ms var(--ct-ease-out), color 200ms var(--ct-ease-out);
}

.ct-activate-steps__label {
    font-size: 11.5px;
    font-weight: 500;
    color: #99a1b7;
    transition: color 200ms var(--ct-ease-out);
}

.ct-activate-steps__item.is-done {
    border-bottom-color: #17c653;
}

.ct-activate-steps__item.is-done .ct-activate-steps__label {
    color: #118059;
}

.ct-activate-steps__item.is-current {
    border-bottom-color: var(--ct-accent);
}

.ct-activate-steps__item.is-current .ct-activate-steps__label {
    color: var(--ct-accent);
    font-weight: 650;
}

/* Inside a step there is only ever one block, so it carries no rule and no
 * bottom spacing — the step boundary is the separation now. */
.ct-activate-block {
    padding-bottom: 0;
    margin-bottom: 0;
    border-bottom: 0;
}

.ct-activate-block + .ct-activate-block {
    margin-top: 18px;
}

/* A step is short by design; this keeps a long signal list from making the
 * modal taller than the shortest step. */
.ct-activate-modal__body {
    min-height: 210px;
}

.ct-activate-modal__body {
    max-height: min(60vh, 520px);
    overflow-y: auto;
    padding: 20px 24px 22px;
}

/* Step change: opacity plus 4px of travel. Short (200ms) because the user is
 * mid-task and pressed a button expecting the next thing, not a transition. */
.ct-activate-modal__body.is-entering {
    animation: ct-activate-step 200ms var(--ct-ease-out);
}

@keyframes ct-activate-step {
    from { opacity: 0; transform: translateY(4px); }
    to   { opacity: 1; transform: translateY(0); }
}

.ct-activate-modal__foot {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    padding: 12px 24px;
    border-top: 1px solid #f1f4f9;
    background: #fbfcfe;
}

.ct-activate-modal__foot-right {
    display: flex;
    align-items: center;
    gap: 8px;
    flex: none;
}

/* What happens when you press the button. It sits ABOVE the footer rather than
 * beside it: with Back on the left and two buttons on the right there is no
 * room for a sentence, and squeezing it in is how footers become unreadable. */
.ct-activate-modal__note {
    display: block;
    padding: 0 24px 12px;
    font-size: 11.5px;
    line-height: 1.45;
    color: #78829d;
    background: #fbfcfe;
    text-wrap: pretty;
}

.ct-activate-modal__note:empty {
    display: none;
}

/* ── the configuration card: three blocks, one screen ── */

/* Each block is separated by a rule rather than boxed: four nested cards inside
 * a modal is the "cards inside cards" look, and the label already groups them. */
.ct-activate-block {
    padding-bottom: 16px;
    margin-bottom: 16px;
    border-bottom: 1px solid #f1f4f9;
}

.ct-activate-block:last-child {
    padding-bottom: 0;
    margin-bottom: 0;
    border-bottom: 0;
}

.ct-activate-block__label {
    display: block;
    margin-bottom: 9px;
    font-size: 10.5px;
    font-weight: 700;
    letter-spacing: 0.09em;
    text-transform: uppercase;
    color: #99a1b7;
}

/* Block A is a sentence, not a field list: what the agent works on is a fact we
 * resolved, not a question. The disclosure at the end of it is the only control. */
.ct-activate-sentence {
    margin: 0;
    font-size: 13px;
    line-height: 1.6;
    color: #071437;
    text-wrap: pretty;
}

.ct-activate-disclose {
    padding: 0;
    border: 0;
    background: none;
    color: var(--ct-accent);
    font: inherit;
    font-weight: 600;
    cursor: pointer;
    white-space: nowrap;
}

.ct-activate-disclose:hover {
    text-decoration: underline;
    text-underline-offset: 2px;
}

.ct-activate-disclose:focus-visible {
    outline: 2px solid var(--ct-accent-border);
    outline-offset: 2px;
    border-radius: 4px;
}

.ct-activate-reveal {
    margin-top: 10px;
}

/* The rule line: the threshold label and its field on one row, so it reads as a
 * sentence with a number in it rather than as a form field with a caption. */
.ct-activate-rule {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 14px;
    flex-wrap: wrap;
}

.ct-activate-rule__label {
    font-size: 13px;
    color: #071437;
}

.ct-activate-rule .ct-activate-input {
    flex: none;
    width: 132px;
}

/* What we guarantee, as opposed to what the user sets. Deliberately NOT a
 * bordered note: it is a statement in the flow of the block, and a boxed
 * callout would read as a warning about the number above it. */
.ct-activate-stated {
    display: flex;
    align-items: flex-start;
    gap: 7px;
    margin: 10px 0 14px;
    font-size: 11.5px;
    line-height: 1.5;
    color: #4b5675;
}

.ct-activate-stated i {
    flex: none;
    margin-top: 1px;
    font-size: 13px;
    color: #17c653;
}

/* The approver, revealed under the rung that needs it. */
.ct-activate-field--nested {
    margin: -1px 0 7px 26px;
    padding: 11px 13px;
    border: 1px solid var(--ct-accent-border);
    border-top: 0;
    border-radius: 0 0 10px 10px;
    background: var(--ct-accent-soft);
}

/* A field that follows a block of its own, inside a step. */
.ct-activate-field--spaced {
    margin-top: 16px;
}

/* ── form primitives ── */

.ct-activate-field {
    margin-bottom: 16px;
}

.ct-activate-field:last-child {
    margin-bottom: 0;
}

.ct-activate-label {
    display: block;
    margin-bottom: 6px;
    font-size: 12px;
    font-weight: 650;
    color: #4b5675;
}

.ct-activate-grid2 {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 0 18px;
}

.ct-activate-input {
    position: relative;
    display: flex;
    align-items: center;
}

.ct-activate-input input,
.ct-activate-field select {
    width: 100%;
    padding: 9px 11px;
    border: 1px solid #dbdfe9;
    border-radius: 8px;
    background: #ffffff;
    color: #071437;
    font-size: 13px;
    font-weight: 500;
    font-variant-numeric: tabular-nums;
    transition: border-color 150ms var(--ct-ease-out), box-shadow 150ms var(--ct-ease-out);
}

.ct-activate-input input:focus,
.ct-activate-field select:focus {
    outline: none;
    border-color: var(--ct-accent);
    box-shadow: 0 0 0 3px var(--ct-accent-soft);
}

.ct-activate-input__unit {
    position: absolute;
    right: 12px;
    font-size: 11.5px;
    font-weight: 600;
    color: #99a1b7;
    pointer-events: none;
}

.ct-activate-input input[type="number"] {
    padding-right: 58px;
}

.ct-activate-signal {
    display: flex;
    align-items: flex-start;
    gap: 9px;
    padding: 10px 12px;
    border: 1px solid #e9edf3;
    border-radius: 9px;
    margin-bottom: 7px;
    cursor: pointer;
    transition: border-color 150ms var(--ct-ease-out), background-color 150ms var(--ct-ease-out);
}

.ct-activate-signal:hover {
    border-color: #dbdfe9;
    background: #fbfcfe;
}

.ct-activate-signal input {
    flex: none;
    margin-top: 2px;
    accent-color: var(--ct-accent);
}

.ct-activate-signal .ct-activate-sev {
    margin-top: 6px;
}

.ct-activate-signal__body {
    min-width: 0;
}

.ct-activate-signal__title {
    display: block;
    font-size: 12.5px;
    font-weight: 500;
    line-height: 1.45;
    color: #071437;
    text-wrap: pretty;
}

.ct-activate-signal__meta {
    display: block;
    margin-top: 2px;
    font-size: 11px;
    color: #99a1b7;
}

/* ── autonomy ladder ── */

/* A column, not a 2x2 grid: four cards side by side forced the single screen
 * to scroll, and a ladder reads as a ladder when it is vertical. */
.ct-activate-levels {
    display: flex;
    flex-direction: column;
    gap: 7px;
}

.ct-activate-level {
    position: relative;
    display: flex;
    align-items: flex-start;
    gap: 10px;
    width: 100%;
    padding: 10px 13px 11px;
    border: 1.5px solid #dbdfe9;
    border-radius: 10px;
    background: #ffffff;
    text-align: left;
    cursor: pointer;
    transition: border-color 160ms var(--ct-ease-out), background-color 160ms var(--ct-ease-out), transform 160ms var(--ct-ease-out);
}

/* The radio dot is drawn, not an <input>: the row IS the control (a real
 * <button aria-pressed>), and a nested input would be a second target for the
 * same decision. Locked rows put a padlock in the same 16px slot, so the
 * column of marks reads as one scale. */
.ct-activate-level__mark {
    flex: none;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 16px;
    height: 16px;
    margin-top: 2px;
    border: 1.5px solid #c8ccd8;
    border-radius: 50%;
    color: #99a1b7;
    font-size: 9px;
}

.ct-activate-level.is-selected .ct-activate-level__mark {
    border-color: var(--ct-accent);
    background: var(--ct-accent);
    box-shadow: inset 0 0 0 2.5px #ffffff;
}

.ct-activate-level.is-locked .ct-activate-level__mark {
    border-style: dashed;
    border-radius: 5px;
}

.ct-activate-level__body {
    min-width: 0;
}

.ct-activate-level__head {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: 7px;
}

.ct-activate-level:hover {
    border-color: #99a1b7;
}

.ct-activate-level:active {
    transform: scale(0.99);
}

.ct-activate-level:focus-visible {
    outline: 2px solid var(--ct-accent-border);
    outline-offset: 2px;
}

.ct-activate-level.is-selected {
    border-color: var(--ct-accent);
    background: var(--ct-accent-soft);
}

/* Locked is visible and explained, never hidden: the ladder teaches the model
 * even where it does not hand it over. */
.ct-activate-level.is-locked {
    border-style: dashed;
    background: #fbfcfe;
    cursor: default;
    opacity: 0.72;
}

.ct-activate-level__flag {
    display: inline-flex;
    align-items: center;
    gap: 3px;
    padding: 2px 8px;
    border-radius: 999px;
    font-size: 9.5px;
    font-weight: 700;
    letter-spacing: 0.03em;
    text-transform: uppercase;
}

.ct-activate-level__flag.is-rec {
    background: #dfffea;
    border: 1px solid #40d390;
    color: #118059;
}

.ct-activate-level__flag.is-lock {
    background: #f7f9fc;
    border: 1px solid #dbdfe9;
    color: #99a1b7;
}

.ct-activate-level__flag i { font-size: 9px; }

.ct-activate-level__code {
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 0.06em;
    color: #99a1b7;
}

.ct-activate-level__name {
    font-size: 13.5px;
    font-weight: 650;
    color: #071437;
}

.ct-activate-level__desc,
.ct-activate-level__note {
    display: block;
    margin-top: 2px;
    font-size: 11.5px;
    line-height: 1.5;
    color: #4b5675;
    text-wrap: pretty;
}

.ct-activate-level__note {
    margin-top: 6px;
    padding-top: 6px;
    border-top: 1px dashed #e9edf3;
    color: #78829d;
}

/* ── scopes ── */

.ct-activate-scopes {
    border: 1px solid #e9edf3;
    border-radius: 10px;
    overflow: hidden;
}

.ct-activate-scope {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 11px 14px;
    border-bottom: 1px solid #f1f4f9;
    font-size: 12.5px;
    color: #071437;
    cursor: pointer;
}

.ct-activate-scope:last-child {
    border-bottom: 0;
}

.ct-activate-scope:first-child {
    background: #fbfcfe;
    cursor: default;
}

.ct-activate-scope__right {
    display: inline-flex;
    align-items: center;
    gap: 9px;
}

.ct-activate-scope input {
    accent-color: var(--ct-accent);
}

.ct-activate-tag {
    padding: 2px 7px;
    border-radius: 6px;
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 0.02em;
}

.ct-activate-tag.is-read {
    background: #dfffea;
    color: #118059;
}

.ct-activate-tag.is-write {
    background: #fff8dd;
    color: #8a6410;
}

/* ── toast ── */

.ct-activate-toast {
    position: fixed;
    left: 50%;
    bottom: 26px;
    z-index: 1080;
    display: flex;
    align-items: center;
    gap: 10px;
    max-width: min(520px, 92vw);
    padding: 13px 18px;
    border-radius: 11px;
    background: #1c2e62;
    color: #ffffff;
    font-size: 12.5px;
    line-height: 1.5;
    box-shadow: 0 14px 34px rgba(7, 20, 55, 0.28);
    opacity: 0;
    transform: translate(-50%, 10px);
    transition: opacity 180ms var(--ct-ease-out), transform 180ms var(--ct-ease-out);
    /* Nothing in it is interactive, and it sits over the bottom of the page:
     * without this it would swallow clicks on whatever is behind it. */
    pointer-events: none;
}

.ct-activate-toast.is-on {
    opacity: 1;
    transform: translate(-50%, 0);
    transition: opacity 220ms var(--ct-ease-out), transform 220ms var(--ct-ease-drawer);
}

.ct-activate-toast__icon {
    flex: none;
    font-size: 15px;
    color: #40d390;
}

.ct-activate-toast.is-error {
    background: #9f2f2d;
}

.ct-activate-toast.is-error .ct-activate-toast__icon {
    color: #ffd3df;
}

/* ── responsive ── */

@media (max-width: 767.98px) {
    .ct-activate-drawer {
        width: 100vw;
        border-left: 0;
    }

    .ct-activate-stats,
    .ct-activate-skel__stats {
        grid-template-columns: 1fr;
    }

    .ct-activate-grid2 {
        grid-template-columns: 1fr;
    }

    .ct-activate-steps {
        padding: 0 16px;
    }

    .ct-activate-steps__label {
        font-size: 10px;
    }

    .ct-activate-coverage__detail {
        margin-left: 0;
    }
}

/* Reduced motion: keep the fades that explain what changed, drop every
 * translation and scale. */
@media (prefers-reduced-motion: reduce) {
    .ct-activate-drawer,
    .ct-activate-drawer.is-on {
        transition: opacity 160ms linear;
        transform: none;
    }

    .ct-activate-drawer:not(.is-on) {
        opacity: 0;
    }

    .ct-activate-evidence__row,
    .ct-agent-pitch__card {
        animation: none;
        opacity: 1;
        transform: none;
    }


    .ct-agent-pitch {
        transition: none;
        opacity: 1;
        transform: none;
    }

    .ct-activate-modal__body.is-entering {
        animation: none;
    }

    .ct-activate-toast,
    .ct-activate-toast.is-on {
        transform: translate(-50%, 0);
        transition: opacity 160ms linear;
    }

    .ct-activate-btn:active,
    .ct-activate-level:active,
    .ct-activate-drawer__close:active,
    button.ct-activate-evidence__item:hover,
    button.ct-activate-evidence__item:active,
    .ct-matrix-table__total-btn:hover,
    .ct-matrix-table__total-btn:active,
    .ct-matrix-table__total-arrow,
    .ct-matrix-previews__link:active,
    .ct-matrix-previews__pick-cta:active,
    .ct-matrix-previews__pick-cta:hover i,
    .ct-matrix-table__cell.is-door:hover .ct-matrix-table__cell-btn,
    .ct-matrix-table__cell.is-door .ct-matrix-table__cell-btn:active {
        transform: none;
    }

    .ct-matrix-previews__card {
        animation: none;
        opacity: 1;
        transform: none;
    }

    /* The plate, the ring and the press SHADOWS all stay — they are the cue
     * that a cell opens something, and they involve no movement. Only the
     * travel and the fade go away. */
    .ct-matrix-table__cell.is-door .ct-matrix-table__cell-btn {
        transition: none;
    }

    /* The hover face still appears — it is the label that teaches the verb, not
     * an effect. It just appears instantly and without the scale. */
    .ct-matrix-table__cell-cta {
        transition: none;
        transform: none;
    }

    .ct-activate-skel__box,
    .ct-activate-skel__row {
        animation: none;
    }
}

/* ─── Analysis context modal (RD-2689) ───────────────────────────────
 * Contexto en texto libre que el usuario escribe para guiar el análisis.
 * Usa los tokens del módulo (--ct-accent*, --ct-ease-out) — no introduce
 * paleta ni tipografía propias. */

.ct-context-btn {
    transition: transform 160ms var(--ct-ease-out), background-color 140ms var(--ct-ease-out);
}

/* Los estados de hover se gatean: en touch, :hover se dispara al tocar y
 * deja el botón "pegado" en su estado activo. */
@media (hover: hover) and (pointer: fine) {
    .ct-context-btn:hover {
        color: var(--ct-accent);
    }
}

.ct-context-btn:active,
.ct-context-save:active {
    transform: scale(0.97);
}

.ct-context-save {
    transition: transform 160ms var(--ct-ease-out), opacity 140ms var(--ct-ease-out);
}

.ct-context-save[disabled] {
    opacity: 0.6;
    transform: none;
}

.ct-context-textarea {
    /* El alto lo fija `rows`; resize vertical para textos largos, nunca
     * horizontal (rompería el ancho del modal). */
    resize: vertical;
    min-height: 84px;
}

.ct-context-count {
    font-variant-numeric: tabular-nums;
    font-feature-settings: "tnum" 1;
    text-align: right;
}

/* El contador solo grita cuando queda poco: el cap de 2000 trunca en
 * silencio del lado del servidor, así que el aviso tiene que llegar antes. */
.ct-context-count.is-near-limit {
    color: var(--ct-sev-medium);
}

.ct-context-toggles {
    display: flex;
    flex-wrap: wrap;
    gap: 1.5rem;
    margin-top: 0.75rem;
}

.ct-context-toggle {
    align-items: center;
    gap: 0.5rem;
}

.ct-context-toggle .form-check-input:focus-visible {
    outline: 2px solid var(--ct-accent-border);
    outline-offset: 2px;
}

.ct-context-advanced-toggle {
    transition: transform 160ms var(--ct-ease-out);
}

.ct-context-advanced-toggle:active {
    transform: scale(0.99);
}

.ct-context-advanced-toggle[aria-expanded="true"] i {
    transform: rotate(180deg);
}

.ct-context-advanced-toggle i {
    display: inline-block;
    transition: transform 200ms var(--ct-ease-out);
}

/* Las filas por vista entran escalonadas cuando se abre el colapsable.
 * Nunca desde scale(0): nada en el mundo real aparece de la nada. */
.ct-context-view {
    opacity: 0;
    transform: translateY(6px);
    animation: ct-context-row-in 260ms var(--ct-ease-out) forwards;
    animation-delay: calc(var(--ct-row-index, 0) * 40ms);
    padding-bottom: 1.25rem;
    border-bottom: 1px solid var(--bs-gray-200, #eaeaea);
    margin-bottom: 1.25rem;
}

.ct-context-view:last-child {
    border-bottom: 0;
    margin-bottom: 0;
    padding-bottom: 0;
}

@keyframes ct-context-row-in {
    to {
        opacity: 1;
        transform: translateY(0);
    }
}

/* Estado de carga: esqueleto con la forma de las filas reales, no un spinner
 * genérico — el usuario ve dónde va a aparecer el contenido. */
.ct-context-skeleton {
    height: 74px;
    border-radius: 6px;
    margin-bottom: 1rem;
    background: linear-gradient(90deg, #f2f2f2 25%, #e9e9e9 37%, #f2f2f2 63%);
    background-size: 400% 100%;
    animation: ct-context-skeleton-sweep 1.4s ease-in-out infinite;
}

@keyframes ct-context-skeleton-sweep {
    0%   { background-position: 100% 50%; }
    100% { background-position: 0 50%; }
}

.ct-context-empty {
    padding: 1.5rem 0;
    text-align: center;
}

/* Reduced motion: se conservan opacidad y color (ayudan a entender el cambio)
 * y se quita todo lo que es desplazamiento. */
@media (prefers-reduced-motion: reduce) {
    .ct-context-view {
        opacity: 1;
        transform: none;
        animation: none;
    }

    .ct-context-btn,
    .ct-context-save,
    .ct-context-advanced-toggle,
    .ct-context-advanced-toggle i {
        transition: none;
    }

    .ct-context-btn:active,
    .ct-context-save:active,
    .ct-context-advanced-toggle:active {
        transform: none;
    }

    .ct-context-skeleton {
        animation: none;
    }
}

/* ══════════════════════════════════════════════════════════════════
 * RD-2724 — the cost detail drawer
 *
 * The other half of the badge. Everything the hover tooltip used to cram into
 * 340px of transient overlay lives here instead, in a panel that stays put
 * while the manager checks a figure against the dashboard behind it.
 *
 * The layout is the argument, not decoration. Five blocks answer five
 * questions in the order they arrive:
 *
 *   1. the HERO — how much, and over what horizon;
 *   2. the EQUATION — hours × rate = cost, drawn as one multiplication
 *      rather than three rows that happen to multiply. This is the block
 *      that earns the panel: a reader who studies it and nothing else can
 *      still argue with the number, which is the only test that matters;
 *   3. the RANGE bar — how much room the estimate has to be wrong. It used
 *      to close the tooltip as the tail of a footnote, where it read as a
 *      disclaimer instead of as data;
 *   4. the INPUTS — every value traceable to something on the dashboard;
 *   5. the METHOD — the caveats, last, because they qualify the rest.
 *
 * Visual language is the module's own: --ct-accent indigo, the matrix card's
 * neutrals (#e9edf3 hairlines, #071437 ink, #4b5675 body, #78829d/#99a1b7
 * muted, #f1f4f9 fills), the drag-cost red and the early-warning amber. No
 * new palette — this is one more part of the hub, not a bolted-on product.
 *
 * Motion policy, same as the agent drawer above:
 *   · the drawer travels with the iOS curve and EXITS FASTER than it enters
 *     (the system responding beats the system deciding);
 *   · only transform and opacity animate, ever;
 *   · nothing entering starts from scale(0) — the cards rise 8px and fade;
 *   · the card stagger is 50ms and stops after the fifth. It exists because
 *     five blocks arriving at once read as a wall; it does not exist to be
 *     noticed.
 *
 * Z-index contract, and the reason it is written down: 1085 (scrim) / 1090
 * (drawer), ABOVE Bootstrap's modal (1055) and the View Details overlay
 * (1050). That is the OPPOSITE of the agent drawer's contract two thousand
 * lines up, deliberately — the badge that opens this one lives inside the
 * detail popup on two of its three surfaces, so a drawer that stacked below
 * would open underneath its own trigger.
 * ══════════════════════════════════════════════════════════════════ */

/* `display: flex` below beats the UA's `[hidden] { display: none }` on
 * specificity, so the attribute has to be restored explicitly — otherwise a
 * "hidden" drawer stays mounted off-screen with `aria-modal="true"` and its
 * content permanently in the tab order. */
.ct-cost-drawer[hidden],
.ct-cost-scrim[hidden] {
    display: none;
}

.ct-cost-scrim {
    position: fixed;
    inset: 0;
    z-index: 1085;
    background: rgba(7, 20, 55, 0.36);
    opacity: 0;
    transition: opacity 200ms var(--ct-ease-out);
}

.ct-cost-scrim.is-on {
    opacity: 1;
}

.ct-cost-drawer {
    position: fixed;
    top: 0;
    right: 0;
    bottom: 0;
    z-index: 1090;
    display: flex;
    flex-direction: column;
    width: min(468px, 94vw);
    background: #ffffff;
    border-left: 1px solid #e9edf3;
    box-shadow: -14px 0 34px rgba(7, 20, 55, 0.12);
    transform: translateX(100%);
    /* Exit: 200ms, plain ease-out. */
    transition: transform 200ms var(--ct-ease-out);
}

.ct-cost-drawer.is-on {
    transform: translateX(0);
    /* Enter: 260ms on Ionic's drawer curve — leaves immediately, lands
     * without a bounce. */
    transition: transform 260ms var(--ct-ease-drawer);
}

/* ── head ── */

.ct-cost-drawer__head {
    position: relative;
    flex: none;
    padding: 22px 52px 15px 22px;
    border-bottom: 1px solid #f1f4f9;
}

.ct-cost-drawer__kicker {
    display: block;
    font-size: 10.5px;
    font-weight: 700;
    letter-spacing: 0.09em;
    text-transform: uppercase;
    color: #99a1b7;
    margin-bottom: 6px;
}

/* The panel covers the row it was opened from, so the header has to re-state
 * which insight this cost belongs to — otherwise the reader is looking at a
 * derivation with no subject. */
.ct-cost-drawer__title {
    margin: 0 0 4px;
    font-size: 17px;
    font-weight: 650;
    letter-spacing: -0.015em;
    line-height: 1.3;
    color: #071437;
    text-wrap: pretty;
}

.ct-cost-drawer__sub {
    margin: 0;
    font-size: 12.5px;
    line-height: 1.5;
    color: #4b5675;
    text-wrap: pretty;
}

.ct-cost-drawer__sub[hidden] {
    display: none;
}

.ct-cost-drawer__close {
    position: absolute;
    top: 14px;
    right: 14px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 30px;
    height: 30px;
    padding: 0;
    border: 0;
    border-radius: 8px;
    background: transparent;
    color: #99a1b7;
    font-size: 13px;
    cursor: pointer;
    transition: color 150ms var(--ct-ease-out),
                background-color 150ms var(--ct-ease-out),
                transform 150ms var(--ct-ease-out);
}

.ct-cost-drawer__close:hover {
    background: #f1f4f9;
    color: #071437;
}

.ct-cost-drawer__close:active {
    transform: scale(0.94);
}

.ct-cost-drawer__close:focus-visible {
    outline: 2px solid var(--ct-accent-border);
    outline-offset: 1px;
}

/* ── body ── */

/* Tinted ground so the white cards read as cards. `overscroll-behavior`
 * because a panel that hands its momentum to the feed underneath it loses
 * the reader's place in both. */
.ct-cost-drawer__body {
    flex: 1;
    display: flex;
    flex-direction: column;
    gap: 10px;
    overflow-y: auto;
    overscroll-behavior: contain;
    padding: 16px 20px 28px;
    background: #fbfcfe;
}

/* Five blocks arriving at once read as a wall. 50ms apart and 8px of travel
 * is enough to make them arrive in reading order without anyone noticing
 * that they did. */
.ct-cost-drawer.is-on .ct-cost-drawer__body > * {
    animation: ct-cost-rise 300ms var(--ct-ease-out) both;
}

.ct-cost-drawer.is-on .ct-cost-drawer__body > *:nth-child(1) { animation-delay: 60ms; }
.ct-cost-drawer.is-on .ct-cost-drawer__body > *:nth-child(2) { animation-delay: 110ms; }
.ct-cost-drawer.is-on .ct-cost-drawer__body > *:nth-child(3) { animation-delay: 160ms; }
.ct-cost-drawer.is-on .ct-cost-drawer__body > *:nth-child(4) { animation-delay: 210ms; }
.ct-cost-drawer.is-on .ct-cost-drawer__body > *:nth-child(5) { animation-delay: 260ms; }

@keyframes ct-cost-rise {
    from { opacity: 0; transform: translateY(8px); }
    to   { opacity: 1; transform: none; }
}

/* ── 1. hero ── */

.ct-cost-hero {
    padding: 16px 18px 15px;
    border: 1px solid rgba(220, 38, 38, 0.18);
    border-radius: 12px;
    background: #fff6f6;
}

/* Early Warnings carry a forecast, not a bill. Amber says "not yet" in the
 * same place red says "already". */
.ct-cost-hero--at-risk {
    border-color: rgba(245, 158, 11, 0.32);
    background: #fffaf0;
}

.ct-cost-hero__label {
    display: block;
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 0.09em;
    text-transform: uppercase;
    color: #b91c1c;
    margin-bottom: 5px;
}

.ct-cost-hero--at-risk .ct-cost-hero__label {
    color: #b45309;
}

.ct-cost-hero__figure {
    display: flex;
    align-items: baseline;
    gap: 3px;
    margin: 0 0 7px;
}

.ct-cost-hero__amount {
    font-size: 34px;
    font-weight: 700;
    letter-spacing: -0.03em;
    line-height: 1;
    color: #dc2626;
    font-variant-numeric: tabular-nums;
}

.ct-cost-hero__unit {
    font-size: 15px;
    font-weight: 600;
    color: #dc2626;
    opacity: 0.72;
}

.ct-cost-hero--at-risk .ct-cost-hero__amount,
.ct-cost-hero--at-risk .ct-cost-hero__unit {
    color: #b45309;
}

/* One sentence saying what the figure claims to be. The badge could never
 * carry it and the tooltip buried it under thirty rows. */
.ct-cost-hero__claim {
    margin: 0;
    font-size: 12.5px;
    line-height: 1.5;
    color: #4b5675;
    text-wrap: pretty;
}

/* A rate means nothing until it is stated at the horizon someone plans on.
 * The 1px grid gap doubles as the divider — the tinted background shows
 * through between the two white tiles. */
.ct-cost-hero__horizons {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 1px;
    margin: 13px 0 0;
    background: rgba(220, 38, 38, 0.16);
    border-radius: 9px;
    overflow: hidden;
}

.ct-cost-hero--at-risk .ct-cost-hero__horizons {
    background: rgba(245, 158, 11, 0.28);
}

.ct-cost-horizon {
    padding: 9px 11px 10px;
    background: #ffffff;
}

.ct-cost-horizon dt {
    margin-bottom: 3px;
    font-size: 10.5px;
    font-weight: 600;
    line-height: 1.3;
    color: #78829d;
}

.ct-cost-horizon dd {
    margin: 0;
    font-size: 16px;
    font-weight: 680;
    letter-spacing: -0.02em;
    color: #071437;
    font-variant-numeric: tabular-nums;
}


/* ── cards ── */

.ct-cost-card {
    padding: 15px 18px 16px;
    background: #ffffff;
    border: 1px solid #e9edf3;
    border-radius: 12px;
}

/* The method card is commentary on the four above it, so it steps back onto
 * the body's own ground instead of claiming another white surface. */
.ct-cost-card--method {
    background: #fbfcfe;
}

.ct-cost-card__title {
    margin: 0 0 12px;
    font-size: 10.5px;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: #99a1b7;
}

/* ── 2. the equation ── */

.ct-cost-eq {
    display: grid;
    grid-template-columns: 1fr auto 1fr auto 1fr;
    align-items: start;
    gap: 8px;
    padding: 13px 8px 12px;
    border-radius: 10px;
    background: #f8fafc;
}

.ct-cost-eq__term {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 4px;
    min-width: 0;
    text-align: center;
}

.ct-cost-eq__value {
    font-size: 15px;
    font-weight: 680;
    letter-spacing: -0.02em;
    line-height: 1.1;
    color: #071437;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.ct-cost-eq__caption {
    font-size: 9.5px;
    font-weight: 600;
    letter-spacing: 0.04em;
    line-height: 1.3;
    text-transform: uppercase;
    color: #99a1b7;
    text-wrap: balance;
}

.ct-cost-eq__op {
    padding-top: 2px;
    font-size: 13px;
    font-weight: 500;
    color: #b5b9c7;
}

.ct-cost-eq__term--result .ct-cost-eq__value {
    font-size: 17px;
    color: #dc2626;
}

.ct-cost-eq--at-risk .ct-cost-eq__term--result .ct-cost-eq__value {
    color: #b45309;
}

/* When the team-capacity cap fires, the equation's result is NOT the number
 * on the badge — it is what the model said before the clip. Greying it here
 * and showing the swap below is what keeps the arithmetic closing: a manager
 * who reads `1705h × $30 = $51k` and then sees `$18k` on the card, with
 * nothing in between, stops trusting the tool in the first thirty seconds. */
.ct-cost-eq__term--superseded .ct-cost-eq__value {
    color: #78829d;
}

/* ── the derivation line under the equation ── */

.ct-cost-derivation {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 6px;
    margin: 11px 0 0;
    font-size: 11px;
}

.ct-cost-derivation__key {
    font-style: italic;
    color: #99a1b7;
}

.ct-cost-derivation__value {
    padding: 2px 6px;
    border-radius: 5px;
    background: #f1f4f9;
    font-family: 'SFMono-Regular', Menlo, Monaco, Consolas, 'Courier New', monospace;
    font-size: 10.5px;
    color: #4b5675;
    word-break: break-word;
}

/* ── the team-capacity cap disclosure ── */

.ct-cost-cap {
    margin-top: 13px;
    padding: 12px 13px 11px;
    border: 1px solid rgba(245, 158, 11, 0.30);
    border-radius: 10px;
    background: #fffaf0;
}

.ct-cost-cap__head {
    display: flex;
    align-items: center;
    gap: 6px;
    margin: 0 0 9px;
    font-size: 11.5px;
    font-weight: 650;
    color: #b45309;
}

.ct-cost-cap__head i {
    font-size: 13px;
}

.ct-cost-cap__swap {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: 8px;
    margin: 0 0 9px;
    font-variant-numeric: tabular-nums;
}

.ct-cost-cap__swap s {
    font-size: 14px;
    font-weight: 600;
    color: #a1a5b7;
}

.ct-cost-cap__arrow {
    font-size: 13px;
    color: #c8ccd8;
}

.ct-cost-cap__swap strong {
    font-size: 19px;
    font-weight: 700;
    letter-spacing: -0.02em;
    color: #b45309;
}

.ct-cost-cap__formula {
    display: block;
    padding: 6px 8px;
    border-radius: 6px;
    background: rgba(245, 158, 11, 0.10);
    font-family: 'SFMono-Regular', Menlo, Monaco, Consolas, 'Courier New', monospace;
    font-size: 10.5px;
    line-height: 1.5;
    color: #92400e;
    word-break: break-word;
}

.ct-cost-cap__why {
    margin: 9px 0 0;
    font-size: 11px;
    line-height: 1.5;
    color: #8a7350;
    text-wrap: pretty;
}

/* ── 3. the range bar ── */

.ct-cost-range {
    padding: 0 2px;
}

/* The caption carries the flag's clearance: the estimate marker sits ABOVE
 * the track, so the space has to come from somewhere and a margin here is
 * cheaper than a wrapper nobody else needs. */
.ct-cost-range__caption {
    margin: 0;
    font-size: 11px;
    color: #78829d;
}

/* The clearance for the flag lives here, on the track, not on the caption's
 * margin — the flag is two lines tall and anchored to the track, so the space
 * it needs belongs to the thing it is anchored to. */
.ct-cost-range__track {
    margin-top: 42px;
    position: relative;
    height: 6px;
    border-radius: 999px;
    background: var(--ct-accent-soft);
    border: 1px solid var(--ct-accent-border);
}

.ct-cost-range__dot {
    position: absolute;
    top: 50%;
    width: 12px;
    height: 12px;
    margin-left: -6px;
    border-radius: 50%;
    background: var(--ct-accent);
    border: 2px solid #ffffff;
    box-shadow: 0 1px 3px rgba(7, 20, 55, 0.22);
    transform: translateY(-50%);
}

.ct-cost-range__flag {
    position: absolute;
    bottom: calc(100% + 10px);
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 1px;
    transform: translateX(-50%);
    white-space: nowrap;
}

.ct-cost-range__flag-label {
    font-size: 9px;
    font-weight: 700;
    letter-spacing: 0.07em;
    text-transform: uppercase;
    color: #99a1b7;
}

.ct-cost-range__flag-value {
    font-size: 13px;
    font-weight: 680;
    letter-spacing: -0.015em;
    color: var(--ct-accent);
    font-variant-numeric: tabular-nums;
}

.ct-cost-range__scale {
    display: flex;
    justify-content: space-between;
    margin-top: 8px;
    font-size: 11px;
    font-weight: 600;
    color: #78829d;
    font-variant-numeric: tabular-nums;
}

/* ── rows ── */

.ct-cost-rows {
    margin-top: 13px;
}

.ct-cost-card__title + .ct-cost-rows,
.ct-cost-eq + .ct-cost-rows {
    margin-top: 12px;
}

.ct-cost-row {
    padding: 7px 0;
    border-bottom: 1px solid #f4f6fa;
}

.ct-cost-row:last-child {
    padding-bottom: 0;
    border-bottom: 0;
}

.ct-cost-row__main {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 14px;
    font-size: 12px;
}

.ct-cost-row__label {
    flex: 0 1 auto;
    color: #78829d;
    text-wrap: pretty;
}

.ct-cost-row__value {
    flex: 0 0 auto;
    font-weight: 650;
    color: #071437;
    text-align: right;
    font-variant-numeric: tabular-nums;
}

.ct-cost-row__value--mono {
    font-family: 'SFMono-Regular', Menlo, Monaco, Consolas, 'Courier New', monospace;
    font-size: 11px;
    font-weight: 500;
    color: #4b5675;
}

/* Informational, not the effective figure. The pre-cap cost is the only row
 * that takes this. */
.ct-cost-row--struck .ct-cost-row__label,
.ct-cost-row--struck .ct-cost-row__value {
    color: #a1a5b7;
    text-decoration: line-through;
}

/* Rides with its row, inside it, so the row's bottom hairline never lands
 * between a value and the annotation that qualifies it. */
.ct-cost-row__hint {
    margin: 2px 0 0;
    font-size: 10.5px;
    font-style: italic;
    line-height: 1.4;
    color: #a1a5b7;
    text-align: right;
}

/* ── notes ── */

.ct-cost-note {
    margin: 12px 0 0;
    font-size: 11.5px;
    line-height: 1.55;
    color: #78829d;
    text-wrap: pretty;
}

.ct-cost-card__title + .ct-cost-note,
.ct-cost-rows + .ct-cost-note {
    margin-top: 12px;
}

.ct-cost-note + .ct-cost-note {
    margin-top: 9px;
}

/* A thin sample is the one caveat that changes what you should do with the
 * number, so it is the one that gets a colour. */
.ct-cost-note--tier-low {
    color: #b45309;
}

/* The closing caveat: it qualifies everything above it rather than adding one
 * more note to the list, so it gets a hairline of its own. */
.ct-cost-note--methodology {
    margin-top: 12px;
    padding-top: 10px;
    border-top: 1px solid #eef1f6;
    font-size: 11px;
    color: #99a1b7;
}

/* ── narrow viewports ── */

@media (max-width: 575.98px) {
    .ct-cost-drawer {
        width: 100vw;
        border-left: 0;
    }

    .ct-cost-drawer__head {
        padding: 18px 48px 13px 16px;
    }

    .ct-cost-drawer__body {
        padding: 14px 15px 24px;
    }

    .ct-cost-hero,
    .ct-cost-card {
        padding-left: 15px;
        padding-right: 15px;
    }

    .ct-cost-hero__amount {
        font-size: 30px;
    }

    /* The three terms stop fitting side by side well before the drawer does.
     * Stacked they still read as one multiplication because the operators
     * stay on the line between them. */
    .ct-cost-eq {
        grid-template-columns: 1fr;
        gap: 6px;
        padding: 12px 10px;
    }

    .ct-cost-eq__op {
        padding-top: 0;
        text-align: center;
    }
}

/* ── reduced motion ──
 *
 * Fewer and gentler, not zero: the fades stay because they carry the
 * relationship between the badge and the panel it opened. Everything that
 * TRAVELS goes away. */
@media (prefers-reduced-motion: reduce) {
    .ct-cost-drawer,
    .ct-cost-drawer.is-on {
        transition: none;
    }

    .ct-cost-drawer.is-on .ct-cost-drawer__body > * {
        animation: none;
    }

    .ct-drag-tooltip {
        transform: none;
        transition: opacity 100ms linear;
    }

    .ct-drag-tooltip.is-visible {
        transform: none;
    }

    .ct-insight-card__drag-cost,
    .ct-cost-drawer__close {
        transition: color 150ms linear, background-color 150ms linear;
    }

    .ct-insight-card__drag-cost[data-ct-cost]:active,
    .ct-cost-drawer__close:active {
        transform: none;
    }
}

/* ═══════════════════════════════════════════════════════════════════
 * The Agents section (RD-2815)
 *
 * A list a person reads, not a map they scan, so the surface is quieter
 * than the matrix: no heat, no chips competing for the eye, one accent
 * used only where a row is the one being pointed at. The tokens are the
 * module's own — the accent is indigo here, and the easing curves are
 * already declared at the top of this file.
 * ═══════════════════════════════════════════════════════════════════ */

/* Same section chrome Alerts and the map get: this section owns neither
 * the scope bar nor the Insights state tabs, so the whole sticky header
 * goes rather than just its contents — an empty .lm-sticky-tabs still
 * satisfies sticky-tabs.js's pin test and paints a stray .lm-stuck
 * hairline under the app header on every scroll.
 *
 * Missing this rule is what left "Inbox / Active / Closed" on screen
 * above the agents table, with whichever Insights state was last read
 * still marked active — three tabs that belong to another section,
 * claiming to describe this one. */
[data-hub-section="agents"] .lm-sticky-tabs {
    display: none;
}

[data-hub-section="agents"] .tab-content {
    padding-top: 10px;
}

.ct-agents {
    padding: 4px 2px 8px;
}

/* ── header ── */

.ct-agents__header {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 20px;
    flex-wrap: wrap;
    margin-bottom: 22px;
}

.ct-agents__title {
    margin: 0 0 4px;
    font-size: 17px;
    font-weight: 700;
    letter-spacing: -0.01em;
    color: #1e2129;
    text-wrap: balance;
}

.ct-agents__subtitle {
    margin: 0;
    max-width: 62ch;
    font-size: 13px;
    line-height: 1.55;
    color: #78829d;
    text-wrap: pretty;
}

/* The filter banner. Present only when a cell sent the reader here, and
 * it has to be reversible in one click or the dimmed rows read as a bug. */
.ct-agents__filter {
    display: inline-flex;
    align-items: center;
    gap: 10px;
    padding: 5px 6px 5px 12px;
    border: 1px solid var(--ct-accent-border);
    border-radius: 999px;
    background: var(--ct-accent-soft);
}

.ct-agents__filter[hidden] {
    display: none;
}

.ct-agents__filter-label {
    font-size: 12px;
    font-weight: 600;
    color: var(--ct-accent-strong);
}

.ct-agents__filter-clear {
    padding: 3px 10px;
    border: 0;
    border-radius: 999px;
    background: #ffffff;
    font-size: 11.5px;
    font-weight: 600;
    color: var(--ct-accent);
    cursor: pointer;
    transition: background-color 140ms var(--ct-ease-out), transform 140ms var(--ct-ease-out);
}

@media (hover: hover) and (pointer: fine) {
    .ct-agents__filter-clear:hover {
        background: #f5f3ff;
    }
}

.ct-agents__filter-clear:focus-visible {
    outline: 2px solid var(--ct-accent);
    outline-offset: 2px;
}

.ct-agents__filter-clear:active {
    transform: scale(0.97);
}

/* ── table ── */

/* The wrapper scrolls, not the section: the row of columns is what can
 * outgrow a narrow viewport, and letting the page body scroll sideways
 * would take the sidebar with it. */
.ct-agents__table-wrap {
    overflow-x: auto;
    border: 1px solid #eef0f5;
    border-radius: 12px;
    background: #ffffff;
}

.ct-agents__table {
    width: 100%;
    min-width: 720px;
    border-collapse: collapse;
    font-size: 13px;
}

.ct-agents__table thead th {
    padding: 11px 16px;
    border-bottom: 1px solid #eef0f5;
    background: #fbfbfd;
    font-size: 10.5px;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: #a1a5b7;
    text-align: left;
    white-space: nowrap;
}

.ct-agents__table-num {
    text-align: right;
}

/* Row entry, staggered. A list of four to eight rows mounting at once
 * reads as a flash; 45ms apart reads as a list arriving. Index comes from
 * the partial as an inline custom property, so the cascade does the
 * arithmetic instead of a nth-child ladder that would cap out. */
.ct-agents__row {
    animation: ct-agents-row-in 320ms var(--ct-ease-out) backwards;
    animation-delay: calc(var(--ct-agents-row-index, 0) * 45ms);
    transition: opacity 160ms var(--ct-ease-out), background-color 140ms var(--ct-ease-out);
}

@keyframes ct-agents-row-in {
    from { opacity: 0; transform: translateY(6px); }
    to   { opacity: 1; transform: none; }
}

.ct-agents__row td,
.ct-agents__row th {
    padding: 13px 16px;
    border-bottom: 1px solid #f4f5f9;
    vertical-align: top;
    font-weight: 400;
    text-align: left;
}

.ct-agents__row:last-child td,
.ct-agents__row:last-child th {
    border-bottom: 0;
}

.ct-agents__row td.ct-agents__table-num {
    text-align: right;
}

/* Dimmed, never hidden. Arriving from one cell and seeing a one-row table
 * teaches nothing about what else is running; the rest staying legible
 * says "there are more, you are looking at one". */
.ct-agents__row--dimmed {
    opacity: 0.38;
}

.ct-agents__name {
    display: block;
    font-size: 13.5px;
    font-weight: 600;
    color: #1e2129;
}

/* "Requested" is a state with a consequence — nothing is being watched —
 * so it is a sentence under the name and not a chip beside it. */
.ct-agents__note {
    display: block;
    margin-top: 3px;
    font-size: 11.5px;
    color: #a1a5b7;
}

.ct-agents__cell-watches {
    color: #5e6278;
}

.ct-agents__level {
    display: inline-block;
    padding: 3px 9px;
    border-radius: 6px;
    background: var(--ct-accent-soft);
    font-size: 11.5px;
    font-weight: 600;
    color: var(--ct-accent-strong);
    white-space: nowrap;
}

/* No level chosen yet. Grey and not indigo: the accent means "this is
 * configured", and a requested agent has nothing configured. */
.ct-agents__level--unset {
    background: #f4f5f9;
    color: #a1a5b7;
}

.ct-agents__cell-approver {
    color: #5e6278;
}

/* An agent at N1 with nobody to sign off does nothing at all, so the gap
 * is named rather than dashed — a dash reads like a value. */
.ct-agents__approver-none,
.ct-agents__pending-none {
    color: #b5b9c9;
    font-style: italic;
}

/* Work waiting. The one number in this table that means "do something", so it
 * is the one thing here that carries the accent — everything else in the row
 * describes configuration. Tabular figures because the column right-aligns and
 * a 9 next to a 12 should line up on the digit, not on the glyph. */
.ct-agents__pending-count {
    display: inline-block;
    padding: 3px 9px;
    border-radius: 6px;
    background: var(--ct-accent-soft);
    font-size: 12px;
    font-weight: 700;
    color: var(--ct-accent-strong);
    white-space: nowrap;
    font-variant-numeric: tabular-nums;
    font-feature-settings: "tnum" 1;
}

/* Structural, not empty. Three of the seven clusters have no early-warning
 * rule at all, so this number can never move — and "nothing right now" would
 * invite waiting for data that will not arrive. Dashed underline because the
 * reason lives in the title attribute and the reader has to know there is one. */
.ct-agents__pending-na {
    color: #a1a5b7;
    font-size: 12px;
    border-bottom: 1px dashed #d7dae5;
    cursor: help;
}

.ct-agents__actionables-body {
    margin: 0;
    max-width: 68ch;
    font-size: 12.5px;
    line-height: 1.6;
    color: #78829d;
    text-wrap: pretty;
}

.ct-agents__actionables-body--failed {
    color: #b02a37;
}

/* The count is a button now, so the browser's own chrome has to go before the
 * pill can survive. Everything visual stays in `.ct-agents__pending-count`
 * above; this only strips it and makes it read as a control.
 *
 * The chevron is why: as a bare pill the count read as a status badge and
 * nobody clicked it. */
button.ct-agents__pending-count {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    border: 0;
    cursor: pointer;
    font-family: inherit;
    transition: transform 160ms var(--ct-ease-out), box-shadow 160ms var(--ct-ease-out);
}

.ct-agents__pending-chevron {
    flex: none;
    /* Turns down when the row is open — the affordance and the state in one
     * mark, which is what `aria-expanded` says to a screen reader. */
    transition: transform 180ms var(--ct-ease-out);
}

button.ct-agents__pending-count[aria-expanded="true"] .ct-agents__pending-chevron {
    transform: rotate(90deg);
}

button.ct-agents__pending-count:hover {
    box-shadow: 0 2px 8px rgba(20, 23, 31, .06);
}

button.ct-agents__pending-count:active {
    transform: scale(.97);
}

button.ct-agents__pending-count:focus-visible {
    outline: 2px solid var(--ct-accent);
    outline-offset: 2px;
}

/* ── the expanded row ── */

/* A continuation of the row above, not another card inside the table: no
 * border box, a tinted ground and an accent rail on the left so the eye
 * connects it to the row it came out of. */
.ct-agents__drill-cell {
    padding: 0;
    background: #fbfbfe;
    border-bottom: 1px solid #eef0f4;
}

.ct-agents__drill-inner {
    padding: 14px 18px 16px;
    border-left: 2px solid var(--ct-accent);
    /* Only `opacity` and `transform`: a max-height reveal animates layout and
     * janks a table. The row itself appears at once; its contents settle. */
    animation: ct-agents-drill-in 200ms var(--ct-ease-out) both;
}

@keyframes ct-agents-drill-in {
    from { opacity: 0; transform: translateY(-3px); }
    to   { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
    .ct-agents__drill-inner { animation: none; }
    .ct-agents__pending-chevron { transition: none; }
}

/* The audit line: which view, which window, how many people. Monospaced
 * because it is metadata someone is meant to copy alongside the numbers, and
 * it has to read as machine-stated rather than written. */
.ct-agents__scope {
    margin: 0 0 12px;
    font-family: ui-monospace, "SFMono-Regular", Menlo, monospace;
    font-size: 11px;
    line-height: 1.5;
    color: #a1a5b7;
    text-wrap: pretty;
}

/* ── the actions ── */

.ct-agents__acts {
    list-style: none;
    margin: 0;
    padding: 0;
}

.ct-agents__act {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto auto auto;
    align-items: baseline;
    gap: 4px 14px;
    padding: 9px 0;
    border-bottom: 1px solid #eef0f4;
}

.ct-agents__act:last-child {
    border-bottom: 0;
}

/* Entry is staggered so the list reads as arriving rather than appearing all
 * at once. Capped at ten steps: past that the last rows would wait longer than
 * the read itself took. */
.ct-agents__act {
    animation: ct-agents-act-in 220ms var(--ct-ease-out) both;
    animation-delay: calc(min(var(--ct-act-index, 0), 10) * 26ms);
}

@keyframes ct-agents-act-in {
    from { opacity: 0; transform: translateY(4px); }
    to   { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
    .ct-agents__act { animation: none; }
    button.ct-agents__pending-count { transition: none; }
    button.ct-agents__pending-count:active { transform: none; }
}

.ct-agents__act-do {
    min-width: 0;
    font-size: 12.5px;
    font-weight: 600;
    color: #3f4254;
    overflow-wrap: anywhere;
}

.ct-agents__act-meta,
.ct-agents__act-evidence {
    font-size: 11.5px;
    color: #78829d;
    white-space: nowrap;
}

.ct-agents__act-evidence {
    font-variant-numeric: tabular-nums;
    font-feature-settings: "tnum" 1;
}

.ct-agents__act-owner {
    font-weight: 600;
    color: #5e6278;
}

/* A data gap, not a blocker — see `AgentActionablesService`. Muted enough to
 * read as an aside and not as a warning about the row. */
.ct-agents__act-gap {
    display: inline-block;
    margin-left: 6px;
    padding: 1px 6px;
    border-radius: 3px;
    background: #fbf3db;
    color: #956400;
    font-size: 10px;
    font-weight: 700;
    letter-spacing: .04em;
    text-transform: uppercase;
    white-space: nowrap;
}

.ct-agents__act-open {
    font-size: 11.5px;
    font-weight: 600;
    color: var(--ct-accent);
    text-decoration: none;
    white-space: nowrap;
}

.ct-agents__act-open:hover {
    text-decoration: underline;
}

/* On a narrow viewport the four columns stack: the action keeps its line and
 * the metadata falls under it, rather than the row scrolling sideways. */
@media (max-width: 640px) {
    .ct-agents__act {
        grid-template-columns: minmax(0, 1fr) auto;
    }

    .ct-agents__act-do {
        grid-column: 1 / -1;
    }
}

/* ── la causa (anomalías) ── */

/* NO es otra tarjeta: la elevación no comunica jerarquía acá, y la causa es
 * parte de la misma respuesta que la lista. Un riel a la izquierda y aire,
 * nada más. */
.ct-agents__causes {
    margin-bottom: 14px;
    padding-left: 10px;
    border-left: 2px solid var(--ct-accent-border);
}

.ct-agents__cause-list {
    list-style: none;
    margin: 4px 0 0;
    padding: 0;
}

.ct-agents__cause {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 2px 8px;
    padding: 3px 0;
    font-size: 12.5px;
}

/* La dimensión es la etiqueta ("Autor", "Tipo de ítem"): dice de qué se está
 * hablando, así que va tratada como rótulo y no como dato. */
.ct-agents__cause-dim {
    font-size: 10px;
    font-weight: 700;
    letter-spacing: .08em;
    text-transform: uppercase;
    color: #a1a5b7;
}

.ct-agents__cause-value {
    font-weight: 600;
    color: #3f4254;
    overflow-wrap: anywhere;
}

/* El peso es lo único que lleva el acento: es el número que convierte una
 * mención en una afirmación causal. */
.ct-agents__cause-share {
    padding: 1px 6px;
    border-radius: 3px;
    background: var(--ct-accent-soft);
    color: var(--ct-accent-strong);
    font-size: 11px;
    font-weight: 700;
    white-space: nowrap;
    font-variant-numeric: tabular-nums;
    font-feature-settings: "tnum" 1;
}

/* El antes/después es la evidencia, no el titular. */
.ct-agents__cause-change {
    font-size: 11.5px;
    color: #78829d;
    white-space: nowrap;
    font-variant-numeric: tabular-nums;
    font-feature-settings: "tnum" 1;
}

/* ── what is not on the list ── */

.ct-agents__leftout {
    margin-top: 14px;
    padding-top: 12px;
    border-top: 1px solid #eef0f4;
}

.ct-agents__leftout-label {
    display: block;
    margin-bottom: 5px;
    font-size: 10px;
    font-weight: 700;
    letter-spacing: .08em;
    text-transform: uppercase;
    color: #a1a5b7;
}

.ct-agents__leftout-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 3px;
}

.ct-agents__leftout-list li {
    font-size: 11.5px;
    line-height: 1.5;
    color: #78829d;
    text-wrap: pretty;
}

/* ── empty state ── */

.ct-agents__empty {
    display: flex;
    flex-direction: column;
    align-items: center;
    padding: 52px 24px 56px;
    border: 1px solid #eef0f5;
    border-radius: 12px;
    background: #ffffff;
    text-align: center;
}

.ct-agents__empty-mark {
    display: grid;
    place-items: center;
    width: 52px;
    height: 52px;
    margin-bottom: 16px;
    border-radius: 14px;
    background: var(--ct-accent-soft);
    color: var(--ct-accent);
}

.ct-agents__empty-title {
    margin: 0 0 6px;
    font-size: 15px;
    font-weight: 700;
    color: #1e2129;
}

.ct-agents__empty-body {
    margin: 0 0 20px;
    max-width: 48ch;
    font-size: 13px;
    line-height: 1.6;
    color: #78829d;
    text-wrap: pretty;
}

.ct-agents__empty-cta {
    padding: 8px 16px;
    border: 0;
    border-radius: 8px;
    background: var(--ct-accent);
    font-size: 12.5px;
    font-weight: 600;
    color: #ffffff;
    cursor: pointer;
    transition: background-color 140ms var(--ct-ease-out), transform 140ms var(--ct-ease-out),
                box-shadow 140ms var(--ct-ease-out);
}

@media (hover: hover) and (pointer: fine) {
    .ct-agents__empty-cta:hover {
        background: var(--ct-accent-strong);
        box-shadow: 0 2px 8px -2px rgba(67, 56, 202, 0.55);
    }
}

.ct-agents__empty-cta:focus-visible {
    outline: 2px solid var(--ct-accent);
    outline-offset: 2px;
}

.ct-agents__empty-cta:active {
    transform: translateY(1px) scale(0.98);
}

/* ── the drawer's link out ── */

.ct-activate-link--out {
    display: inline-flex;
    align-items: center;
    gap: 6px;
}

.ct-activate-link--out i {
    font-size: 10px;
    transition: transform 140ms var(--ct-ease-out);
}

@media (hover: hover) and (pointer: fine) {
    .ct-activate-link--out:hover i {
        transform: translateX(2px);
    }
}

/* Motion sickness: keep the opacity that carries meaning (the dim), drop
 * the movement. The stagger goes with it — a cascade IS movement. */
@media (prefers-reduced-motion: reduce) {
    .ct-agents__row {
        animation: none;
    }

    .ct-agents__filter-clear:active,
    .ct-agents__empty-cta:active,
    .ct-matrix-previews__foot-cta:active {
        transform: none;
    }

    .ct-matrix-previews__foot-cta:hover i,
    .ct-activate-link--out:hover i {
        transform: none;
    }
}

/* ─── RD-2770 — Slack connect invite ──────────────────────────────
 * The hub's suggestion to connect Slack, for a viewer whose org already uses it.
 * Built on the module's accent tokens so it reads as part of the hub rather than
 * as a system dialog. The Slack mark sits on a neutral accent tile: the brand
 * colour belongs to Slack's own isologo, not to a surface we tint. */
.ct-slack-invite__logo {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 46px;
    height: 46px;
    border-radius: 12px;
    background-color: var(--ct-accent-soft);
    border: 1px solid var(--ct-accent-border);
    flex-shrink: 0;
}

.ct-slack-invite__body {
    font-size: 0.95rem;
    line-height: 1.6;
    color: var(--bs-gray-700);
    margin: 0 0 1.75rem;
}

/* The three reasons. */
.ct-slack-invite__points {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 1.1rem;
}
.ct-slack-invite__points li {
    display: flex;
    align-items: flex-start;
    gap: 0.85rem;
    font-size: 0.925rem;
    line-height: 1.5;
    color: var(--bs-gray-700);
}
/* Sized here, not by a Metronic `fs-` class: the icon has to line up with the
 * cap height of a 0.925rem line, and the fluid fs- scale drifts off it between
 * breakpoints. The fixed column is what gives the three reasons one left edge
 * even when an icon's glyph is narrower than its neighbours. */
.ct-slack-invite__points i {
    color: var(--ct-accent);
    flex-shrink: 0;
    width: 1.25rem;
    font-size: 1.25rem;
    line-height: 1;
    margin-top: 0.05rem;
    display: inline-flex;
    justify-content: center;
}

/* The reasons cascade in behind the modal so the eye is handed one line at a
 * time instead of a wall. Short delays on purpose (50ms): long enough to read as
 * a sequence, too short to be a wait. Bootstrap's own .modal.fade already ran by
 * the time these start. */
.ct-slack-invite.show .ct-slack-invite__points li {
    opacity: 0;
    transform: translateY(6px);
    animation: ct-slack-invite-point 260ms var(--ct-ease-out) forwards;
}
.ct-slack-invite.show .ct-slack-invite__points li:nth-child(1) { animation-delay: 60ms; }
.ct-slack-invite.show .ct-slack-invite__points li:nth-child(2) { animation-delay: 110ms; }
.ct-slack-invite.show .ct-slack-invite__points li:nth-child(3) { animation-delay: 160ms; }

@keyframes ct-slack-invite-point {
    to {
        opacity: 1;
        transform: translateY(0);
    }
}

/* Press feedback on both answers: the modal is a decision point, and a button
 * that does not acknowledge the press reads as one that did not register it. */
.ct-slack-invite__connect,
.ct-slack-invite__never {
    transition: transform 160ms var(--ct-ease-out);
}
.ct-slack-invite__connect:active,
.ct-slack-invite__never:active {
    transform: scale(0.97);
}

/* Reduced motion: keep the modal and its content, drop the movement. The
 * cascade is decoration — the three reasons must still be there instantly. */
@media (prefers-reduced-motion: reduce) {
    .ct-slack-invite.show .ct-slack-invite__points li {
        opacity: 1;
        transform: none;
        animation: none;
    }
    .ct-slack-invite__connect,
    .ct-slack-invite__never {
        transition: none;
    }
    .ct-slack-invite__connect:active,
    .ct-slack-invite__never:active {
        transform: none;
    }
}
