/* ==========================================================================
   Session Rail - Horizontal Carousel
   ========================================================================== */

/* Wraps the rail + session views so we can swap the rail position without
   touching the rail or view components themselves. Four positions:
     rail-top    — rail above messages (default)
     rail-bottom — rail under everything (below the input bar)
     rail-left   — rail down the left side
     rail-right  — rail down the right side
   The DOM is `.dashboard-body > .session-rail-container > .session-rail`
   (with `.session-views-container` as the rail-container's sibling). For
   left/right we make the rail-container the column-sized flex item; for
   top/bottom we flip the dashboard-body's main axis (column / column-reverse). */
.dashboard-body {
    flex: 1;
    display: flex;
    flex-direction: column;
    min-height: 0;
}

.dashboard-body.rail-top    { flex-direction: column; }
.dashboard-body.rail-bottom { flex-direction: column-reverse; }
.dashboard-body.rail-left   { flex-direction: row; }
.dashboard-body.rail-right  { flex-direction: row-reverse; }

.session-rail {
    display: flex;
    gap: 0.5rem;
    padding: 0.75rem 1rem;
    background: var(--bg-darker);
    border-bottom: 1px solid var(--border);
    overflow-x: auto;
}

/* In bottom-position mode the rail sits under the views/input so the
   horizontal divider line lives on its top edge instead of its bottom. */
.dashboard-body.rail-bottom .session-rail {
    border-bottom: none;
    border-top: 1px solid var(--border);
}

/* Vertical positions (left/right): the rail becomes a 240px-wide column.
   The actual direct child of `.dashboard-body` is `.session-rail-container`
   (which wraps the rail plus the floating dropdown and dialogs), so the
   column-sized box is applied to that wrapper. The inner `.session-rail` is
   then styled via a descendant combinator to switch its flex direction and
   scrolling axis.

   `!important` on the rail's flex-direction and overflow keeps the column
   direction sticky against the base rule's `overflow-x: auto`, which on
   some browser cascades has shadowed the override. */
.dashboard-body.rail-left  > .session-rail-container,
.dashboard-body.rail-right > .session-rail-container {
    width: 200px;
    min-width: 200px;
    flex-shrink: 0;
    display: flex;
    flex-direction: column;
    overflow: hidden;
    min-height: 0;
}

.dashboard-body.rail-left  > .session-rail-container { border-right: 1px solid var(--border); }
.dashboard-body.rail-right > .session-rail-container { border-left:  1px solid var(--border); }

.session-rail-container {
    position: relative;
}

.dashboard-body.rail-left .session-rail,
.dashboard-body.rail-right .session-rail {
    flex: 1;
    flex-direction: column !important;
    align-items: stretch;
    gap: 0.35rem;
    padding: 0.75rem 0.5rem;
    border-bottom: none;
    overflow-x: hidden !important;
    overflow-y: auto !important;
    width: auto;
    min-height: 0;
}

.dashboard-body.rail-left .session-pill,
.dashboard-body.rail-right .session-pill {
    /* Same 200px width as horizontal mode — set by the base `.session-pill`
       rule above. We only need to allow per-pill text wrapping here. */
    white-space: normal;
}

/* Flip the active/inactive divider so it reads horizontally inside the
   vertical column rather than vertically across it. */
.dashboard-body.rail-left .session-rail-divider,
.dashboard-body.rail-right .session-rail-divider {
    flex-direction: row;
    align-self: stretch;
    width: 100%;
    justify-content: center;
    padding: 0.25rem 0;
}

.dashboard-body.rail-left .session-rail-divider .divider-line,
.dashboard-body.rail-right .session-rail-divider .divider-line {
    width: auto;
    height: 1px;
    flex: 1;
    margin: 0 0.35rem;
    align-self: center;
}

/* Divider between active and inactive sessions */
.session-rail-divider {
    display: flex;
    align-items: center;
    align-self: stretch;
    gap: 0.25rem;
    padding: 0 0.25rem;
    cursor: pointer;
    flex-shrink: 0;
}

.session-rail-divider .divider-line {
    width: 1px;
    align-self: stretch;
    margin: 0.35rem 0;
    background: var(--border);
}

.session-rail-divider .divider-toggle {
    background: transparent;
    border: 1px solid var(--border);
    border-radius: 4px;
    color: var(--text-secondary);
    font-size: 0.7rem;
    padding: 0.15rem 0.35rem;
    cursor: pointer;
    white-space: nowrap;
    transition: all 0.2s;
}

.session-rail-divider .divider-toggle:hover {
    border-color: var(--text-secondary);
    color: var(--text-primary);
}

.session-rail-divider.collapsed .divider-toggle {
    background: rgba(0, 0, 0, 0.2);
}

/* Per-host section header (opt-in "group rail by host"). Non-interactive
   label rendered inline among the pills. In the horizontal (top/bottom) rail
   it reads as a compact chip that precedes each host's pills; in the vertical
   (left/right) rail it spans the column as a full-width section heading. */

/* A thin, small-text section header sitting in the sliver of space above each
   host's pills: muted uppercase hostname plus a plain "(n)" parenthetical —
   deliberately not a chip/badge, so it reads as a label, not a control. */
.session-rail-host-header {
    display: flex;
    align-items: baseline;
    gap: 0.3rem;
    flex-shrink: 0;
    padding: 0.1rem 0.5rem;
    align-self: center;
    color: var(--text-secondary);
    font-size: 0.65rem;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    white-space: nowrap;
}

.session-rail-host-header .host-header-name {
    font-weight: 600;
    overflow: hidden;
    text-overflow: ellipsis;
}

.session-rail-host-header .host-header-count {
    font-weight: 400;
    opacity: 0.75;
}

/* Vertical rail: the header becomes a full-width row with a hairline rule and
   a thin gap above it, so each host group reads as a stacked section. */
.dashboard-body.rail-left .session-rail-host-header,
.dashboard-body.rail-right .session-rail-host-header {
    align-self: stretch;
    width: 100%;
    justify-content: flex-start;
    padding: 0.4rem 0.25rem 0.1rem;
    border-top: 1px solid var(--border);
    margin-top: 0.35rem;
}

/* The first header in a vertical rail shouldn't carry a top rule/margin. */
.dashboard-body.rail-left .session-rail-host-header:first-child,
.dashboard-body.rail-right .session-rail-host-header:first-child {
    border-top: none;
    margin-top: 0;
}

.session-pill {
    --pill-visual-height: 2.5rem;

    position: relative;
    display: flex;
    align-items: center;
    gap: 0.35rem;
    padding: 0.5rem 0.55rem;
    background: rgba(0, 0, 0, 0.3);
    border: 1px solid var(--border);
    border-radius: 20px;
    cursor: pointer;
    white-space: nowrap;
    transition: all 0.2s;
    flex-shrink: 0;
    overflow: hidden;
    width: 180px;
    box-sizing: border-box;
}

/* Agent-type watermark — large logo anchored to the pill's left edge,
   clipped by the pill's overflow:hidden, behind all foreground content.
   Sized at 80% of the previous 80×80 (so 64×64); the new `left: -16px`
   keeps the icon's horizontal center at x=16 in pill coords so the visual
   centering doesn't drift across the resize. Vertical center is held by
   `top: 50%` + `translateY(-50%)`, which is size-agnostic. */
.pill-watermark {
    position: absolute;
    left: -16px;
    top: 50%;
    transform: translateY(-50%);
    width: 64px;
    height: 64px;
    pointer-events: none;
    background-repeat: no-repeat;
    background-position: center;
    background-size: contain;
    opacity: 0.22;
    z-index: 0;
}
.pill-watermark.claude { background-image: url('/anthropic-mark.svg'); }
.pill-watermark.codex  { background-image: url('/openai-mark.png'); }

/* Model-version watermark — the session's compact model number (e.g. "4.8")
   rendered in the same faint grey/opacity treatment as the agent logo. The
   number shares the logo-side anchor, is vertically centered in the pill, and
   scales to 3/5 of the pill's visual height. */
.pill-model-watermark {
    position: absolute;
    left: 0;
    top: 50%;
    transform: translateY(-50%);
    pointer-events: none;
    font-size: calc(var(--pill-visual-height) * 0.6);
    font-weight: 700;
    font-family: monospace;
    line-height: 1;
    color: var(--text-primary);
    opacity: 0.22;
    z-index: 0;
}

.session-pill:hover {
    border-color: var(--accent);
    background: rgba(122, 162, 247, 0.1);
}

.session-pill.focused {
    border-color: var(--accent);
    background: rgba(122, 162, 247, 0.18);
    box-shadow: 0 0 0 2px rgba(122, 162, 247, 0.5);
}

.session-pill.awaiting {
    border-color: var(--error);
}

.session-pill.focused.awaiting {
    /* Focus state dominates — keep the blue ring/background; the indicator
       glyph alone signals "still waiting". */
    border-color: var(--accent);
    background: rgba(122, 162, 247, 0.18);
}

.pill-broadcast-effect {
    position: absolute;
    inset: 3px 6px;
    border-radius: 16px;
    pointer-events: none;
    z-index: 1;
}

.pill-broadcast-strand,
.pill-broadcast-core {
    position: absolute;
    left: 50%;
    top: 50%;
    display: block;
    opacity: 0;
    pointer-events: none;
}

.pill-broadcast-strand {
    width: 46px;
    height: 2px;
    transform-origin: center;
    background: linear-gradient(90deg, transparent, #d8b4ff 18%, #bb9af7 50%, #d8b4ff 82%, transparent);
    box-shadow:
        0 0 7px rgba(216, 180, 255, 0.95),
        0 0 15px rgba(187, 154, 247, 0.78);
}

.pill-broadcast-core {
    width: 28px;
    height: 18px;
    margin-left: -14px;
    margin-top: -9px;
    border: 1px solid rgba(187, 154, 247, 0.82);
    border-radius: 4px;
    background: rgba(26, 27, 38, 0.72);
    box-shadow:
        inset 0 0 8px rgba(187, 154, 247, 0.24),
        0 0 14px rgba(187, 154, 247, 0.42);
}

.strand-left-top {
    --strand-shift: 45px;

    margin-left: -68px;
    margin-top: -13px;
    transform: rotate(8deg) scaleX(0.25);
}

.strand-left-bottom {
    --strand-shift: 45px;

    margin-left: -68px;
    margin-top: 12px;
    transform: rotate(-8deg) scaleX(0.25);
}

.strand-right-top {
    --strand-shift: -45px;

    margin-left: 22px;
    margin-top: -13px;
    transform: rotate(-8deg) scaleX(0.25);
}

.strand-right-bottom {
    --strand-shift: -45px;

    margin-left: 22px;
    margin-top: 12px;
    transform: rotate(8deg) scaleX(0.25);
}

.session-pill.broadcast-sender .pill-broadcast-strand {
    animation: pill-strands-gather 980ms cubic-bezier(0.25, 0.8, 0.25, 1) forwards;
}

.session-pill.broadcast-sender .pill-broadcast-core {
    animation: pill-core-gather 980ms cubic-bezier(0.25, 0.8, 0.25, 1) forwards;
}

.session-pill.broadcast-receiver .pill-broadcast-strand {
    animation: pill-strands-release 980ms cubic-bezier(0.25, 0.8, 0.25, 1) 520ms forwards;
}

.session-pill.broadcast-receiver .pill-broadcast-core {
    border-color: rgba(216, 180, 255, 0.92);
    box-shadow:
        inset 0 0 10px rgba(216, 180, 255, 0.32),
        0 0 18px rgba(187, 154, 247, 0.55);
    animation: pill-core-release 980ms cubic-bezier(0.25, 0.8, 0.25, 1) 520ms forwards;
}

.agent-broadcast-layer {
    position: absolute;
    pointer-events: none;
    z-index: 6;
}

.agent-broadcast-layer.horizontal {
    left: 0;
    right: 0;
    top: 50%;
    height: 34px;
    transform: translateY(-50%);
}

.agent-broadcast-layer.vertical {
    top: 0;
    bottom: 0;
    left: 50%;
    width: 34px;
    transform: translateX(-50%);
}

.agent-broadcast-path {
    /* Plasma tunables + the three seeded 0..1 variation values, which
       `plasma_seeds` in session_rail/broadcast.rs sets inline per broadcast.
       See the plasma block below for how they're consumed. */
    --pl-size: 18px;
    --pl-pulse: 360ms;
    --pl-travel: 1350ms;
    --pl-hot: #fff;
    --pl-s1: 0.5;
    --pl-s2: 0.5;
    --pl-s3: 0.5;

    /* Signed -0.5..+0.5 deviations. */
    --j1: calc(var(--pl-s1) - 0.5);
    --j2: calc(var(--pl-s2) - 0.5);
    --j3: calc(var(--pl-s3) - 0.5);

    position: absolute;
    display: block;
    opacity: 0;
    animation: agent-path-fade 1.35s ease-out;
}

.agent-broadcast-layer.horizontal .agent-broadcast-path {
    top: 0;
    height: 34px;
}

.agent-broadcast-layer.vertical .agent-broadcast-path {
    left: 0;
    width: 34px;
}

@keyframes pill-strands-gather {
    0% {
        opacity: 0;
        translate: 0 0;
        scale: 0.4 1;
    }
    22% {
        opacity: 1;
        scale: 1 1;
    }
    58% {
        opacity: 0.9;
        translate: var(--strand-shift) 0;
        scale: 0.35 1;
    }
    100% {
        opacity: 0;
        translate: var(--strand-shift) 0;
        scale: 0.1 1;
    }
}

@keyframes pill-strands-release {
    0% {
        opacity: 0;
        translate: var(--strand-shift) 0;
        scale: 0.1 1;
    }
    24% {
        opacity: 0.95;
        translate: var(--strand-shift) 0;
        scale: 0.35 1;
    }
    70% {
        opacity: 1;
        translate: 0 0;
        scale: 1 1;
    }
    100% {
        opacity: 0;
        translate: 0 0;
        scale: 0.25 1;
    }
}

@keyframes agent-path-fade {
    0%,
    12% {
        opacity: 0;
    }
    22%,
    72% {
        opacity: 1;
    }
    100% {
        opacity: 0;
    }
}

@keyframes pill-core-gather {
    0% {
        opacity: 0;
        scale: 0.72;
    }
    26%,
    58% {
        opacity: 1;
        scale: 1;
    }
    100% {
        opacity: 0;
        scale: 0.65;
    }
}

@keyframes pill-core-release {
    0% {
        opacity: 0;
        scale: 0.65;
    }
    20%,
    62% {
        opacity: 1;
        scale: 1;
    }
    100% {
        opacity: 0;
        scale: 1.2;
    }
}

/* ---------------------------------------------------------------------------
   Agent-to-agent broadcast: a pulsating ball of plasma that coalesces at the
   sending pill's centre, crosses the rail, and dissolves into the receiving
   pill's centre.

   Per-instance variation comes from --pl-s1/--pl-s2/--pl-s3, three 0..1 values
   set inline by `plasma_seeds` in session_rail/broadcast.rs. They are hashed
   from the message identity, not random: the rail re-renders throughout the
   2.4s broadcast window, and random seeds would reshuffle the orbits mid-flight.
   --------------------------------------------------------------------------- */

/* Agent identity is carried by hue rather than by a glyph — a mark inside a
   plasma ball fights the effect. Claude: Anthropic orange. Codex: sampled from
   its mark (#3f3aff indigo -> #7193fe -> #b49cfc lavender). */
.agent-broadcast-orb.claude {
    --pl-core: #ffc9a8;
    --pl-edge: #d97757;
}

.agent-broadcast-orb.codex {
    --pl-core: #b9a8ff;
    --pl-edge: #5b57fb;
}

.agent-broadcast-orb {
    position: absolute;
    width: var(--pl-size);
    height: var(--pl-size);
    display: grid;
    place-items: center;
    opacity: 0;

    /* The orbits are real 3D rotations; without a perspective origin and a
       preserved 3D context the tilted rings flatten into squashed ellipses and
       read as 2D. Short perspective exaggerates near/far at this small size. */
    perspective: calc(var(--pl-size) * 5);
    transform-style: preserve-3d;
}

/* The path spans centre-to-centre (see view_for_positions), so the orb is
   offset by half its diameter in both axes to sit exactly on each endpoint. */
.agent-broadcast-layer.horizontal .agent-broadcast-orb {
    top: 50%;
    margin-top: calc(var(--pl-size) / -2);
    margin-left: calc(var(--pl-size) / -2);
    animation: agent-orb-x calc(var(--pl-travel) * (1 + var(--j1) * 0.28))
        cubic-bezier(0.3, 0.7, 0.2, 1) forwards;
}

.agent-broadcast-layer.vertical .agent-broadcast-orb {
    left: 50%;
    margin-left: calc(var(--pl-size) / -2);
    margin-top: calc(var(--pl-size) / -2);
    animation: agent-orb-y calc(var(--pl-travel) * (1 + var(--j1) * 0.28))
        cubic-bezier(0.3, 0.7, 0.2, 1) forwards;
}

.agent-broadcast-layer.horizontal .agent-broadcast-path.reverse .agent-broadcast-orb {
    animation-name: agent-orb-x-reverse;
}

.agent-broadcast-layer.vertical .agent-broadcast-path.reverse .agent-broadcast-orb {
    animation-name: agent-orb-y-reverse;
}

/* Radial gradients rather than a border: the hard bordered edge is what made
   the previous packet read as an envelope. The core holds the centre — the
   orbits supply the motion — so it only breathes and never drifts. */
.agent-broadcast-core {
    position: absolute;
    inset: 0;
    border-radius: 50%;
    background: radial-gradient(circle at 50% 50%,
        var(--pl-hot) 0%,
        var(--pl-core) 32%,
        var(--pl-edge) 58%,
        color-mix(in srgb, var(--pl-edge) 40%, transparent) 76%,
        transparent 100%);
    box-shadow:
        0 0 calc(var(--pl-size) * 0.5) color-mix(in srgb, var(--pl-core) 85%, transparent),
        0 0 calc(var(--pl-size) * 1.15) color-mix(in srgb, var(--pl-edge) 65%, transparent),
        0 0 calc(var(--pl-size) * 2) color-mix(in srgb, var(--pl-edge) 32%, transparent);
    animation: agent-orb-pulse calc(var(--pl-pulse) * (1 + var(--j1) * 0.55)) ease-in-out infinite;
    animation-delay: calc(var(--pl-s2) * -900ms);
}

/* Comet tail, horizontal only — on the vertical rail it would point sideways. */
.agent-broadcast-core::after {
    content: "";
    position: absolute;
    top: 50%;
    right: 55%;
    width: calc(var(--pl-size) * (1.35 + var(--pl-s3) * 0.6));
    height: calc(var(--pl-size) * 0.42);
    transform: translateY(-50%);
    border-radius: 50%;
    background: linear-gradient(90deg, transparent, color-mix(in srgb, var(--pl-edge) 45%, transparent));
    filter: blur(2px);
    opacity: calc(0.7 + var(--pl-s1) * 0.3);
}

.agent-broadcast-path.reverse .agent-broadcast-core::after {
    right: auto;
    left: 55%;
    rotate: 180deg;
}

.agent-broadcast-layer.vertical .agent-broadcast-core::after {
    display: none;
}

/* Conic rings masked to thin arcs. Each keeps a fixed seeded tilt and spins
   about its OWN normal: rotateZ sits innermost in the keyframes so the spin
   happens inside the already-tilted frame, which is what makes it orbit through
   the plane instead of spinning flat on screen. */
.agent-broadcast-arc {
    position: absolute;
    border-radius: 50%;
    opacity: 0;
    pointer-events: none;
    transform-style: preserve-3d;
}

.agent-broadcast-arc.a1 {
    inset: calc(var(--pl-size) * -0.3);
    background: conic-gradient(from 0deg,
        transparent 0deg 24deg, var(--pl-core) 30deg 36deg,
        transparent 42deg 150deg, var(--pl-hot) 156deg 162deg,
        transparent 168deg 268deg, var(--pl-core) 274deg 282deg,
        transparent 288deg 360deg);
    mask: radial-gradient(circle, transparent 58%, #000 62%, #000 84%, transparent 88%);

    --tx: calc(64deg + var(--j1) * 54deg);
    --ty: calc(10deg + var(--j2) * 44deg);

    animation:
        agent-orbit-a calc(var(--pl-pulse) * (3.4 + var(--j1) * 2.4)) linear infinite,
        agent-arc-flicker calc(var(--pl-pulse) * (0.62 + var(--j2) * 0.4)) steps(2, end) infinite;
    animation-delay: calc(var(--pl-s1) * -4000ms), calc(var(--pl-s3) * -700ms);
}

.agent-broadcast-arc.a2 {
    inset: calc(var(--pl-size) * -0.52);
    background: conic-gradient(from 90deg,
        transparent 0deg 60deg, var(--pl-edge) 66deg 74deg,
        transparent 80deg 196deg, var(--pl-core) 202deg 210deg,
        transparent 216deg 360deg);
    mask: radial-gradient(circle, transparent 66%, #000 70%, #000 88%, transparent 92%);

    --tx: calc(-46deg + var(--j2) * 50deg);
    --ty: calc(36deg + var(--j3) * 42deg);

    animation:
        agent-orbit-b calc(var(--pl-pulse) * (5.1 + var(--j3) * 2.8)) linear infinite,
        agent-arc-flicker calc(var(--pl-pulse) * (0.83 + var(--j1) * 0.5)) steps(2, end) infinite;
    animation-delay: calc(var(--pl-s2) * -5200ms), calc(var(--pl-s1) * -900ms);
}

/* The outermost arc's presence is seeded, so some broadcasts crackle wide and
   others stay tight. */
.agent-broadcast-arc.a3 {
    --arc-scale: calc(0.35 + var(--pl-s3) * 0.9);

    inset: calc(var(--pl-size) * -0.78);
    background: conic-gradient(from 200deg,
        transparent 0deg 40deg, var(--pl-hot) 44deg 49deg,
        transparent 54deg 240deg, var(--pl-edge) 246deg 252deg,
        transparent 258deg 360deg);
    mask: radial-gradient(circle, transparent 74%, #000 78%, #000 94%, transparent 97%);

    --tx: calc(16deg + var(--j3) * 48deg);
    --ty: calc(-58deg + var(--j1) * 46deg);

    animation:
        agent-orbit-c calc(var(--pl-pulse) * (6.8 + var(--j2) * 3.6)) linear infinite,
        agent-arc-flicker-faint calc(var(--pl-pulse) * (1.07 + var(--j3) * 0.6)) steps(2, end) infinite;
    animation-delay: calc(var(--pl-s3) * -6000ms), calc(var(--pl-s2) * -1400ms);
}

.agent-broadcast-mark {
    position: relative;
    width: calc(var(--pl-size) * 0.5);
    height: calc(var(--pl-size) * 0.5);
    background-repeat: no-repeat;
    background-position: center;
    background-size: contain;
    opacity: 0.9;
    mix-blend-mode: overlay;
    filter: drop-shadow(0 0 3px rgba(255, 255, 255, 0.6));
}

.agent-broadcast-orb.claude .agent-broadcast-mark {
    background-image: url('/anthropic-mark.svg');
}

.agent-broadcast-orb.codex .agent-broadcast-mark {
    background-image: url('/openai-mark.png');
}

/* Travel, with a coalesce/dissolve envelope. The envelope lives here on the
   orb rather than on the core because the core's infinite pulse already owns
   scale and filter — two animations on one property clobber each other. Putting
   it on the orb also scales and blurs the orbits, so it coalesces as one body. */
@keyframes agent-orb-x {
    0% {
        left: 0%;
        opacity: 0;
        scale: 2.1;
        filter: blur(7px);
    }
    9% {
        opacity: 1;
        scale: 1.12;
        filter: blur(1.5px);
    }
    16% {
        scale: 1;
        filter: blur(0);
    }
    82% {
        opacity: 1;
        scale: 1;
        filter: blur(0);
    }
    92% {
        opacity: 0.85;
        scale: 1.45;
        filter: blur(3.5px);
    }
    100% {
        left: 100%;
        opacity: 0;
        scale: 2.5;
        filter: blur(10px);
    }
}

@keyframes agent-orb-x-reverse {
    0% {
        left: 100%;
        opacity: 0;
        scale: 2.1;
        filter: blur(7px);
    }
    9% {
        opacity: 1;
        scale: 1.12;
        filter: blur(1.5px);
    }
    16% {
        scale: 1;
        filter: blur(0);
    }
    82% {
        opacity: 1;
        scale: 1;
        filter: blur(0);
    }
    92% {
        opacity: 0.85;
        scale: 1.45;
        filter: blur(3.5px);
    }
    100% {
        left: 0%;
        opacity: 0;
        scale: 2.5;
        filter: blur(10px);
    }
}

@keyframes agent-orb-y {
    0% {
        top: 0%;
        opacity: 0;
        scale: 2.1;
        filter: blur(7px);
    }
    9% {
        opacity: 1;
        scale: 1.12;
        filter: blur(1.5px);
    }
    16% {
        scale: 1;
        filter: blur(0);
    }
    82% {
        opacity: 1;
        scale: 1;
        filter: blur(0);
    }
    92% {
        opacity: 0.85;
        scale: 1.45;
        filter: blur(3.5px);
    }
    100% {
        top: 100%;
        opacity: 0;
        scale: 2.5;
        filter: blur(10px);
    }
}

@keyframes agent-orb-y-reverse {
    0% {
        top: 100%;
        opacity: 0;
        scale: 2.1;
        filter: blur(7px);
    }
    9% {
        opacity: 1;
        scale: 1.12;
        filter: blur(1.5px);
    }
    16% {
        scale: 1;
        filter: blur(0);
    }
    82% {
        opacity: 1;
        scale: 1;
        filter: blur(0);
    }
    92% {
        opacity: 0.85;
        scale: 1.45;
        filter: blur(3.5px);
    }
    100% {
        top: 0%;
        opacity: 0;
        scale: 2.5;
        filter: blur(10px);
    }
}

/* Uneven stops: size and brightness move together so it reads as unstable
   energy rather than a metronomic throb. */
@keyframes agent-orb-pulse {
    0% {
        scale: 0.92;
        filter: brightness(0.95) saturate(1);
    }
    38% {
        scale: 1.12;
        filter: brightness(1.4) saturate(1.3);
    }
    55% {
        scale: 1.01;
        filter: brightness(1.1) saturate(1.1);
    }
    72% {
        scale: 1.08;
        filter: brightness(1.28) saturate(1.22);
    }
    100% {
        scale: 0.92;
        filter: brightness(0.95) saturate(1);
    }
}

/* Tilt held constant across every stop, only rotateZ sweeps, so the ring
   travels through its own plane. Uneven stops under linear timing give varying
   angular velocity — a bit accelerates near and coasts far. */
@keyframes agent-orbit-a {
    0% { transform: rotateX(var(--tx)) rotateY(var(--ty)) rotateZ(0deg); }
    34% { transform: rotateX(var(--tx)) rotateY(var(--ty)) rotateZ(158deg); }
    61% { transform: rotateX(var(--tx)) rotateY(var(--ty)) rotateZ(203deg); }
    100% { transform: rotateX(var(--tx)) rotateY(var(--ty)) rotateZ(360deg); }
}

@keyframes agent-orbit-b {
    0% { transform: rotateX(var(--tx)) rotateY(var(--ty)) rotateZ(360deg); }
    29% { transform: rotateX(var(--tx)) rotateY(var(--ty)) rotateZ(232deg); }
    68% { transform: rotateX(var(--tx)) rotateY(var(--ty)) rotateZ(141deg); }
    100% { transform: rotateX(var(--tx)) rotateY(var(--ty)) rotateZ(0deg); }
}

@keyframes agent-orbit-c {
    0% { transform: rotateX(var(--tx)) rotateY(var(--ty)) rotateZ(0deg); }
    42% { transform: rotateX(var(--tx)) rotateY(var(--ty)) rotateZ(198deg); }
    73% { transform: rotateX(var(--tx)) rotateY(var(--ty)) rotateZ(241deg); }
    100% { transform: rotateX(var(--tx)) rotateY(var(--ty)) rotateZ(360deg); }
}

@keyframes agent-arc-flicker {
    0%, 41% { opacity: 0.85; }
    42%, 57% { opacity: 0.22; }
    58%, 66% { opacity: 1; }
    67% { opacity: 0.4; }
    100% { opacity: 0.62; }
}

@keyframes agent-arc-flicker-faint {
    0%, 33% { opacity: calc(0.5 * var(--arc-scale)); }
    34%, 52% { opacity: calc(0.1 * var(--arc-scale)); }
    53%, 61% { opacity: calc(0.9 * var(--arc-scale)); }
    100% { opacity: calc(0.3 * var(--arc-scale)); }
}

@media (prefers-reduced-motion: reduce) {
    .pill-broadcast-strand,
    .pill-broadcast-core,
    .agent-broadcast-path,
    .agent-broadcast-orb,
    .agent-broadcast-core,
    .agent-broadcast-arc {
        animation: none;
    }

    .session-pill.broadcast-sender,
    .session-pill.broadcast-receiver {
        box-shadow: 0 0 0 2px rgba(187, 154, 247, 0.35);
    }

    .agent-broadcast-layer {
        display: none;
    }
}

.pill-indicator {
    font-size: 0.7rem;
    color: var(--text-secondary);
}

.session-pill.awaiting .pill-indicator {
    color: var(--error);
}

.pill-name {
    display: flex;
    flex-direction: column;
    flex: 1;
    min-width: 0;
    overflow: hidden;
    position: relative;
    z-index: 1;
}

.pill-session-name {
    font-size: 0.85rem;
    color: var(--text-primary);
    font-weight: 500;
    overflow: hidden;
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
    line-height: 1.18;
    overflow-wrap: anywhere;
}

.pill-repo {
    font-size: 0.7rem;
    color: var(--text-secondary);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.pill-branch {
    font-size: 0.7rem;
    color: var(--accent);
    font-style: italic;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 100%;
    direction: rtl;
    text-align: left;
}



.pill-branch.pill-no-vcs {
    color: var(--text-muted);
    font-style: normal;
    font-size: 0.65rem;
}

/* Open-PR numbers on the collapsed pill: one span per PR, each carrying its
   branch name as a hover tooltip. LTR so "#34 #35" reads left-to-right (the
   plain branch view uses rtl to keep the tail visible when truncated). */
.pill-branch.pill-prs {
    direction: ltr;
    font-style: normal;
}

.pill-pr-num {
    margin-inline-end: 5px;
    cursor: help;
}

.pill-pr-num:last-child {
    margin-inline-end: 0;
}

.session-pill.status-disconnected .pill-branch {
    color: var(--text-secondary);
}

/* Sparkline - activity ticks at bottom of pill */
.pill-sparkline {
    position: absolute;
    bottom: 0;
    left: 0;
    right: 0;
    height: 5px;
    pointer-events: none;
}

.sparkline-tick {
    position: absolute;
    bottom: 0;
    width: 3px;
    height: 100%;
    opacity: 1;
    border-radius: 1px;
    transform: translateZ(0);
}

.sparkline-tick.tick-assistant { background: var(--accent); }
.sparkline-tick.tick-user { background: var(--success); }
.sparkline-tick.tick-result { background: #e0af68; }
.sparkline-tick.tick-portal { background: #7dcfff; }
.sparkline-tick.tick-error { background: var(--error); }
.sparkline-tick.tick-other { background: var(--text-secondary); }

.sparkline-range {
    position: absolute;
    bottom: 0;
    height: 100%;
    opacity: 0.5;
    border-radius: 1px;
    transform: translateZ(0);
}

.sparkline-range.tick-compaction { background: var(--text-secondary); }
.sparkline-range.tick-task { background: #bb9af7; }

.session-pill.hidden .sparkline-tick,
.session-pill.hidden .sparkline-range {
    opacity: 0.2;
}

/* Top context-usage bar: the counterpart to the bottom activity ticks, but a
   single fill sized to how full the model's context window is. Clipped to the
   pill's rounded top by the pill's own `overflow: hidden`, like the sparkline
   at the bottom. */
.pill-context-bar {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    height: 4px;
    pointer-events: none;
    background: rgba(192, 202, 245, 0.06);
    overflow: hidden;
}

.pill-context-fill {
    position: absolute;
    top: 0;
    left: 0;
    height: 100%;
    border-radius: 1px;
    transform: translateZ(0);
    background: var(--success);

    /* Grows as context fills; drains smoothly when a compaction shrinks it. */
    transition: width 0.6s ease, background-color 0.6s ease;
}

.pill-context-fill.ctx-mid {
    background: #e0af68;
}

.pill-context-fill.ctx-high {
    background: var(--error);

    /* Ambient warning pulse when close to the window limit. */
    animation: ctx-near-full 1.8s ease-in-out infinite;
}

@keyframes ctx-near-full {
    0%,
    100% {
        opacity: 1;
    }
    50% {
        opacity: 0.5;
    }
}

/* Brief flash the moment a compaction lands (context drains). */
.pill-context-bar.compacting {
    animation: ctx-compact-flash 0.8s ease-out;
}

@keyframes ctx-compact-flash {
    0% {
        box-shadow: 0 0 6px 1px var(--accent);
    }
    100% {
        box-shadow: none;
    }
}

.session-pill.hidden .pill-context-fill {
    opacity: 0.2;
}

.pill-status {
    font-size: 0.6rem;
    line-height: 1;
}

.pill-status.connected {
    color: var(--success);
}

.pill-status.disconnected {
    color: var(--text-secondary);
}

/* Disconnected session (DB status) styling */
.session-pill.status-disconnected {
    opacity: 0.6;
    background: rgba(127, 132, 156, 0.1);
    border-color: var(--text-secondary);
}

.session-pill.status-disconnected:hover {
    opacity: 0.8;
    background: rgba(127, 132, 156, 0.15);
}

.session-pill.status-disconnected.focused {
    opacity: 0.85;
    border-color: var(--text-secondary);
    box-shadow: 0 0 0 2px rgba(127, 132, 156, 0.3);
}

.session-pill.status-disconnected .pill-session-name,
.session-pill.status-disconnected .pill-repo,
.session-pill.status-disconnected .pill-name {
    color: var(--text-secondary);
}

/* Hidden session styling - more greyed out, no awaiting highlight */
.session-pill.hidden {
    opacity: 0.35;
    border-style: dashed;
    filter: grayscale(50%);
}

.session-pill.hidden:hover {
    opacity: 0.55;
    filter: grayscale(30%);
}

.session-pill.hidden.focused {
    opacity: 0.5;
    filter: grayscale(40%);
}

/* Hidden sessions should NOT show awaiting (red) highlighting */
.session-pill.hidden.awaiting {
    border-color: var(--border);
    animation: none;
    box-shadow: none;
}

.session-pill.hidden.focused.awaiting {
    border-color: var(--text-secondary);
    background: rgba(127, 132, 156, 0.15);
}

.pill-hidden-badge {
    font-size: 0.7rem;
    color: var(--text-secondary);
    font-weight: 600;
}

.pill-agent-badge {
    font-size: 0.6rem;
    font-weight: 600;
    padding: 0 0.3rem;
    border-radius: 3px;
    flex-shrink: 0;
}

.pill-agent-badge.codex {
    color: #0a4;
    background: rgba(0, 170, 68, 0.15);
}

.pill-agent-badge.cron {
    color: var(--accent);
    background: rgba(125, 207, 255, 0.15);
}

.pill-agent-badge.paused {
    color: #e0af68;
    background: rgba(224, 175, 104, 0.15);
}

/* Token expiry warning badge */
.pill-token-warning {
    font-size: 0.75rem;
    flex-shrink: 0;
    cursor: help;
    color: #e0af68;
}

.pill-token-warning.critical {
    color: #ff9e64;
}

.pill-token-warning.expired {
    color: #f7768e;
}

/* Pill menu toggle (▼ button). Positioned above pill-name in stacking order
   so overflowing folder/branch text can't visually cover the dropdown affordance. */
.pill-menu-toggle {
    background: transparent;
    border: none;
    color: var(--text-secondary);
    font-size: 0.6rem;
    cursor: pointer;
    padding: 0.2rem 0.3rem;
    border-radius: 4px;
    transition: color 0.15s, background 0.15s;
    flex-shrink: 0;
    position: relative;
    z-index: 2;
}

.pill-menu-toggle:hover {
    color: var(--text-primary);
    background: rgba(122, 162, 247, 0.2);
}

/* Pill dropdown menu - fixed positioning to escape rail overflow clipping.
   Always in DOM, toggled by .open class (same pattern as send button dropdown). */
.pill-dropdown {
    position: fixed;
    background: var(--bg-darker);
    border: 1px solid var(--border);
    border-radius: 6px;
    min-width: 160px;
    display: none;
    flex-direction: column;
    overflow: visible;
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
    z-index: 100;
}

.pill-dropdown.open {
    display: flex;
}

.pill-menu-option {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    padding: 0.6rem 0.8rem;
    background: transparent;
    border: none;
    color: var(--text-primary);
    font-size: 0.85rem;
    font-weight: 500;
    cursor: pointer;
    text-align: left;
    transition: background 0.15s;
}

.pill-menu-option:hover {
    background: rgba(122, 162, 247, 0.15);
}

.pill-menu-option.stop {
    color: var(--error);
}

.pill-menu-option.stop:hover {
    background: rgba(247, 118, 142, 0.15);
}

.pill-menu-option.stop.confirming {
    background: rgba(247, 118, 142, 0.2);
    font-weight: 600;
}

.pill-menu-option.hide.active {
    color: var(--success);
}

.pill-menu-option.hide.active:hover {
    background: rgba(158, 206, 106, 0.15);
}

.pill-menu-option.leave {
    color: #e0af68;
}

.pill-menu-option.leave:hover {
    background: rgba(224, 175, 104, 0.15);
}

.pill-menu-option.copy-id .option-hint {
    font-family: monospace;
}

.pill-menu-option.copy-id.copied {
    color: var(--success);
}

a.pill-menu-option.pr-link {
    text-decoration: none;
    color: #7aa2f7;
}

a.pill-menu-option.pr-link:hover {
    background: rgba(122, 162, 247, 0.15);
}

.pill-menu-submenu {
    position: relative;
}

.pill-menu-submenu > .pill-menu-option {
    width: 100%;
}

.repo-submenu-trigger {
    position: relative;
    padding-right: 1.8rem;
}

.repo-submenu-trigger::after {
    content: "›";
    position: absolute;
    right: 0.7rem;
    top: 50%;
    transform: translateY(-50%);
    color: var(--text-muted);
    font-size: 1.1rem;
    line-height: 1;
}

.pill-submenu-panel {
    position: absolute;
    left: calc(100% + 4px);
    top: 0;
    display: none;
    min-width: 220px;
    max-width: 320px;
    max-height: min(360px, calc(100vh - 2rem));
    overflow-y: auto;
    flex-direction: column;
    background: var(--bg-darker);
    border: 1px solid var(--border);
    border-radius: 6px;
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
}

.pill-menu-submenu:hover .pill-submenu-panel,
.pill-menu-submenu:focus-within .pill-submenu-panel {
    display: flex;
}

.pill-menu-option.disabled {
    color: var(--text-muted);
    cursor: default;
}

.pill-menu-option.disabled:hover {
    background: transparent;
}

.pill-menu-option .option-hint {
    font-size: 0.7rem;
    font-weight: 400;
    color: var(--text-muted);
    margin-top: 0.1rem;
}

/* Number annotation for nav mode (1-9 keys) */
.pill-number {
    display: none;
    position: absolute;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
    font-size: 1rem;
    font-weight: 700;
    color: var(--bg-primary);
    background: var(--accent);
    width: 1.5rem;
    height: 1.5rem;
    border-radius: 50%;
    font-family: monospace;
    align-items: center;
    justify-content: center;
    z-index: 10;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
}

.session-pill.nav-mode .pill-number {
    display: flex;
}

/* Nav mode selection highlight */
.session-pill.nav-mode.focused {
    border-color: var(--accent);
    background: rgba(122, 162, 247, 0.25);
    box-shadow: 0 0 0 3px rgba(122, 162, 247, 0.4);
}

/* ==========================================================================
   Role Badge (for shared sessions)
   ========================================================================== */

.pill-role-badge {
    font-size: 0.65rem;
    padding: 0.15rem 0.4rem;
    border-radius: 4px;
    text-transform: uppercase;
    font-weight: 600;
    letter-spacing: 0.3px;
}

.pill-role-badge.role-editor {
    background: rgba(224, 175, 104, 0.3);
    color: #e0af68;
}

.pill-role-badge.role-viewer {
    background: rgba(127, 132, 156, 0.3);
    color: var(--text-secondary);
}

/* ==========================================================================
   Share / Leave Buttons
   ========================================================================== */

.pill-share {
    background: transparent;
    border: none;
    color: var(--text-secondary);
    cursor: pointer;
    font-size: 0.9rem;
    padding: 0.2rem 0.4rem;
    border-radius: 4px;
    transition: background 0.15s, color 0.15s;
    touch-action: manipulation;
    -webkit-tap-highlight-color: transparent;
}

.pill-share:hover {
    background: rgba(122, 162, 247, 0.2);
    color: var(--accent);
}

.pill-share:active {
    background: rgba(122, 162, 247, 0.3);
}
