/* ───────────────────────────────────────────────────────────────────────────
   Workgraph HTML viewer — TUI-parity stylesheet.

   The color palette mirrors the TUI exactly:
   - Status colors come from `flash_color_for_status` in
     src/tui/viz_viewer/state.rs (line ~271). RGB triples reproduced here as
     CSS custom properties.
   - Edge highlight colors come from the TUI's selection highlighting in
     src/tui/viz_viewer/render.rs (line ~1500): magenta = upstream
     dependencies, cyan = downstream dependents, yellow = cycle edges.
     The RGB values are the ratatui Color::Magenta / Color::Cyan / Color::Yellow
     mappings from `indexed_color_to_rgb` in render.rs (line ~1027).

   Theme: dark by default, light via prefers-color-scheme media query.
   `[data-theme="dark"]` and `[data-theme="light"]` overrides are applied by
   the manual toggle button (panel.js), persisted in localStorage.
   ─────────────────────────────────────────────────────────────────────────── */

:root {
    /* Status colors — TUI flash_color_for_status (state.rs:271) */
    --status-done: rgb(80, 220, 100);
    --status-failed: rgb(220, 60, 60);
    --status-in-progress: rgb(60, 200, 220);
    --status-open: rgb(200, 200, 80);
    --status-blocked: rgb(180, 120, 60);
    --status-abandoned: rgb(140, 100, 160);
    --status-waiting: rgb(60, 160, 220);
    --status-pending-validation: rgb(60, 160, 220);
    --status-pending-eval: rgb(140, 230, 80);
    --status-incomplete: rgb(255, 165, 0);

    /* Paused: orthogonal modifier — soft blue-gray, distinct from open (yellow),
       abandoned (purple), and blocked (gray). Mirrors ANSI 256-color 110 used
       in the CLI / TUI ascii viz (src/commands/viz/ascii.rs). */
    --status-paused: rgb(150, 165, 195);

    /* Chat agent color — TUI Color::Blue (render.rs:1034: Color::Blue => (36,114,200)) */
    --chat-task: rgb(36, 114, 200);

    /* Edge highlight colors — TUI render.rs:1500-1510 */
    --edge-upstream: rgb(188, 63, 188);   /* Color::Magenta */
    --edge-downstream: rgb(17, 168, 205); /* Color::Cyan */
    --edge-cycle: rgb(229, 229, 16);      /* Color::Yellow */

    /* Dark theme (default) — matches TUI default terminal background */
    --bg: #0a0a0a;
    --bg-elevated: #141414;
    --bg-pre: #0d0d12;
    --bg-panel: #131319;
    --fg: #e5e5e5;
    --fg-muted: #888;
    --fg-dim: #555;
    --border: #2a2a2a;
    --border-strong: #3a3a3a;
    --link: rgb(108, 188, 251);
    --selection-bg: rgba(255, 255, 255, 0.06);
    --edge-default: #4a4a4a;
    --panel-width: 460px;
    --panel-height: 60vh;
}

/* Auto-follow OS light mode unless user has explicitly chosen */
@media (prefers-color-scheme: light) {
    :root:not([data-theme="dark"]) {
        --bg: #ffffff;
        --bg-elevated: #f7f7f7;
        --bg-pre: #fafafa;
        --bg-panel: #f4f4f4;
        --fg: #1a1a1a;
        --fg-muted: #666;
        --fg-dim: #999;
        --border: #ddd;
        --border-strong: #bbb;
        --link: rgb(0, 102, 204);
        --selection-bg: rgba(0, 0, 0, 0.05);
        --edge-default: #b8b8b8;

        /* Status colors — slightly darkened for readability on light bg.
           Matches TUI light-theme intent: "actually styled, not just inverted dark". */
        --status-done: rgb(40, 160, 70);
        --status-failed: rgb(180, 40, 40);
        --status-in-progress: rgb(20, 130, 160);
        --status-open: rgb(140, 110, 30);
        --status-blocked: rgb(140, 90, 30);
        --status-abandoned: rgb(100, 70, 130);
        --status-waiting: rgb(40, 110, 170);
        --status-pending-validation: rgb(40, 110, 170);
        --status-pending-eval: rgb(80, 150, 40);
        --status-incomplete: rgb(200, 110, 0);
        --status-paused: rgb(95, 110, 145);
        --chat-task: rgb(20, 90, 165);

        --edge-upstream: rgb(150, 40, 150);
        --edge-downstream: rgb(15, 130, 165);
        --edge-cycle: rgb(170, 130, 0);
    }
}

/* Manual override — wins over @media (prefers-color-scheme) */
:root[data-theme="light"] {
    --bg: #ffffff;
    --bg-elevated: #f7f7f7;
    --bg-pre: #fafafa;
    --bg-panel: #f4f4f4;
    --fg: #1a1a1a;
    --fg-muted: #666;
    --fg-dim: #999;
    --border: #ddd;
    --border-strong: #bbb;
    --link: rgb(0, 102, 204);
    --selection-bg: rgba(0, 0, 0, 0.05);
    --edge-default: #b8b8b8;

    --status-done: rgb(40, 160, 70);
    --status-failed: rgb(180, 40, 40);
    --status-in-progress: rgb(20, 130, 160);
    --status-open: rgb(140, 110, 30);
    --status-blocked: rgb(140, 90, 30);
    --status-abandoned: rgb(100, 70, 130);
    --status-waiting: rgb(40, 110, 170);
    --status-pending-validation: rgb(40, 110, 170);
    --status-pending-eval: rgb(80, 150, 40);
    --status-incomplete: rgb(200, 110, 0);
    --status-paused: rgb(95, 110, 145);
    --chat-task: rgb(20, 90, 165);

    --edge-upstream: rgb(150, 40, 150);
    --edge-downstream: rgb(15, 130, 165);
    --edge-cycle: rgb(170, 130, 0);
}

/* Manual dark override — wins over OS light preference */
:root[data-theme="dark"] {
    --bg: #0a0a0a;
    --bg-elevated: #141414;
    --bg-pre: #0d0d12;
    --bg-panel: #131319;
    --fg: #e5e5e5;
    --fg-muted: #888;
    --fg-dim: #555;
    --border: #2a2a2a;
    --border-strong: #3a3a3a;
    --link: rgb(108, 188, 251);
    --selection-bg: rgba(255, 255, 255, 0.06);
    --edge-default: #4a4a4a;
}

* { box-sizing: border-box; }

html, body {
    margin: 0;
    padding: 0;
    background: var(--bg);
    color: var(--fg);
    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
    line-height: 1.5;
}

body {
    padding: 1rem 2rem 4rem;
}

a { color: var(--link); text-decoration: none; }
a:hover { text-decoration: underline; }

code {
    font-family: 'JetBrains Mono', ui-monospace, 'Cascadia Code', 'Source Code Pro', Menlo, Consolas, monospace;
    font-size: 0.88em;
    background: var(--bg-elevated);
    padding: 0.1em 0.3em;
    border-radius: 3px;
}

/* ── Header ─────────────────────────────────────────────────────────────── */

.page-header {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 1rem;
    flex-wrap: wrap;
    margin-bottom: 1rem;
    padding-bottom: 0.5rem;
    border-bottom: 1px solid var(--border);
}

.page-header h1 { margin: 0.5rem 0 0.25rem; font-size: 1.5rem; }

.subtitle { color: var(--fg-muted); margin: 0; font-size: 0.9rem; }

/* ── Project header (above the dependency graph) ─────────────────────────
   Rendered when the user supplied a [project] title/byline/abstract or a
   per-deployment override in html-publish.toml. Omitted entirely when all
   three fields are empty (the page falls back to the minimal "Workgraph /
   N tasks shown" header above). */
.project-header {
    margin: 0 0 1.25rem;
    padding: 0.5rem 0 1rem;
    border-bottom: 1px solid var(--border);
}

.project-title {
    margin: 0 0 0.25rem;
    font-size: 1.85rem;
    font-weight: 600;
    line-height: 1.2;
    color: var(--fg);
}

.project-byline {
    margin: 0 0 0.75rem;
    color: var(--fg-muted);
    font-size: 0.95rem;
    font-style: italic;
}

/* Abstract: rendered markdown body. Reuses .description-rendered styles
   for headings/lists/code/etc. Long abstracts collapse to ~5 lines with
   a "show more" affordance installed by panel.js. */
.project-abstract {
    margin: 0;
    color: var(--fg);
    font-size: 0.95rem;
    line-height: 1.55;
    max-width: 80ch;
}

.project-abstract.is-collapsed {
    max-height: 7.75em;   /* ~5 lines at 1.55 line-height */
    overflow: hidden;
    position: relative;
    -webkit-mask-image: linear-gradient(180deg, #000 60%, transparent 100%);
            mask-image: linear-gradient(180deg, #000 60%, transparent 100%);
}

.project-abstract-toggle {
    margin-top: 0.4rem;
    background: transparent;
    color: var(--link);
    border: none;
    padding: 0;
    font: inherit;
    font-size: 0.85rem;
    cursor: pointer;
    text-decoration: underline;
    text-decoration-style: dotted;
}
.project-abstract-toggle:hover { color: var(--fg); }

.header-controls {
    display: flex;
    gap: 0.5rem;
    align-items: center;
}

.theme-toggle,
.legend-toggle {
    background: var(--bg-elevated);
    color: var(--fg);
    border: 1px solid var(--border-strong);
    padding: 0.4rem 0.8rem;
    border-radius: 4px;
    font: inherit;
    font-size: 0.85rem;
    cursor: pointer;
    line-height: 1;
}
.theme-toggle:hover,
.legend-toggle:hover {
    border-color: var(--link);
    color: var(--link);
}

/* Agency-task toggle — web equivalent of TUI period key.
   Visual treatment matches the theme-toggle so the controls feel paired.
   When pressed (data-show-agency=true) we mark it active so it's clear the
   user has opted into the noisier view. */
.agency-toggle {
    background: var(--bg-elevated);
    color: var(--fg);
    border: 1px solid var(--border-strong);
    padding: 0.4rem 0.8rem;
    border-radius: 4px;
    font: inherit;
    font-size: 0.85rem;
    cursor: pointer;
    line-height: 1;
}
.agency-toggle:hover {
    border-color: var(--link);
    color: var(--link);
}
.agency-toggle[aria-pressed="true"] {
    background: var(--bg-pre);
    border-color: var(--link);
    color: var(--link);
}

/* Dual-viz swap: hide the alternate viz block based on toggle state. The
   fallback :not() rule handles the (default) case where no toggle exists yet
   — single-viz pages render `<pre class="viz-pre">` with neither
   modifier and stay visible. */
:root[data-show-agency="false"] .viz-pre.viz-agency { display: none; }
:root[data-show-agency="true"]  .viz-pre.viz-substantive { display: none; }

/* Agency tasks in the task list section. Hidden by default; dimmed when
   the toggle is on so they read as supporting metadata rather than peer
   work. (Matches the spec — same dim treatment the TUI uses on system
   tasks.) */
:root[data-show-agency="false"] .task-list li.is-agency { display: none; }
:root[data-show-agency="true"]  .task-list li.is-agency a {
    opacity: 0.55;
    font-style: italic;
}

/* Agency task-link spans inside the agency viz: dim them so they don't
   visually overwhelm the substantive task labels. The status color still
   shows through so the at-a-glance status read still works. */
.viz-pre.viz-agency .task-link.is-agency {
    opacity: 0.6;
}
.viz-pre.viz-agency .task-link.is-agency:hover {
    opacity: 1;
}

h2 {
    border-bottom: 1px solid var(--border);
    padding-bottom: 0.25rem;
    margin-top: 2rem;
    font-size: 1.1rem;
}

section { margin-bottom: 2rem; }

/* ── Page layout ────────────────────────────────────────────────────────── */

.page-layout {
    display: flex;
    gap: 0;
    align-items: flex-start;
    max-width: 1800px;
}

.main-content {
    flex: 1 1 0;
    min-width: 0;
}

/* ── ASCII viz ──────────────────────────────────────────────────────────── */

.viz-wrap {
    overflow-x: auto;
    background: var(--bg-pre);
    border: 1px solid var(--border);
    border-radius: 6px;
    padding: 0.75rem 1rem;
}

.viz-pre {
    font-family: 'JetBrains Mono', ui-monospace, 'Cascadia Code', 'Source Code Pro', Menlo, Consolas, monospace;
    font-size: 13px;
    line-height: 1.4;
    color: var(--fg);
    margin: 0;
    white-space: pre;
    /* Terminal-cell invariants: every glyph (ASCII letters, spaces, and
       Unicode box-drawing connectors `─ │ ┐ ┘ ├ └ ←`) must occupy exactly
       one fixed-width cell. Without these declarations the browser's text
       layout enables typography features that perturb advance width on
       a per-character basis, so long runs of `─` drift out of column with
       the corner / arrow glyphs they're supposed to land on. Inherits via
       `.viz-pre *` so the per-character `<span class="edge">` wrappers
       stay on the same grid as the surrounding plain text. */
    font-variant-ligatures: none;
    font-feature-settings: "liga" 0, "calt" 0, "clig" 0, "dlig" 0, "hlig" 0;
    font-kerning: none;
    font-synthesis: none;
    font-variant-numeric: tabular-nums;
    text-rendering: geometricPrecision;
    /* Some webfonts ship slightly narrower box-drawing metrics; ensuring a
       fixed advance via font-variant-east-asian: full-width is wrong (it
       affects CJK), so we lean on a strict cell grid here and let the
       fallback chain pick up box-drawing glyphs that share the primary
       font's advance width. */
}

/* All descendants of .viz-pre — task-link / decorator / time-suffix /
   edge spans — must share the same monospace metric grid. Inheritance of
   font-* properties is normal, but explicitly resetting these on the
   children defends against parent rules that flip ligatures back on for
   a subtree (e.g. global typography presets). */
.viz-pre * {
    font-family: inherit;
    font-variant-ligatures: none;
    font-feature-settings: "liga" 0, "calt" 0, "clig" 0, "dlig" 0, "hlig" 0;
    font-kerning: none;
    font-synthesis: none;
}

/* Default edge connector color (tree connectors + arcs).
   Matches TUI 'gray' edge color (\x1b[90m → ratatui DarkGray ≈ rgb(118,118,118)). */
.viz-pre .edge {
    color: var(--edge-default);
    /* The per-character edge wrapper is a no-layout span: it must not
       introduce padding, margin, border, or a box-shadow that pushes
       neighbouring `─` cells out of column. Forcing display: inline
       (the default for <span>) plus zero box metrics keeps long
       connector runs aligned with `wg viz` terminal output. */
    display: inline;
    padding: 0;
    margin: 0;
    border: 0;
    letter-spacing: 0;
    word-spacing: 0;
}

/* Task-id labels. The base label color = the task's status color. */
.viz-pre .task-link {
    cursor: pointer;
    border-bottom: 1px dotted transparent;
    transition: border-color 0.15s;
}
.viz-pre .task-link:hover {
    border-bottom-color: currentColor;
}
.viz-pre .task-link[data-status="done"]                  { color: var(--status-done); }
.viz-pre .task-link[data-status="failed"]                { color: var(--status-failed); }
.viz-pre .task-link[data-status="in-progress"]           { color: var(--status-in-progress); }
.viz-pre .task-link[data-status="open"]                  { color: var(--status-open); }
.viz-pre .task-link[data-status="blocked"]               { color: var(--status-blocked); }
.viz-pre .task-link[data-status="abandoned"]             { color: var(--status-abandoned); }
.viz-pre .task-link[data-status="waiting"]               { color: var(--status-waiting); }
.viz-pre .task-link[data-status="pending-validation"]    { color: var(--status-pending-validation); }
.viz-pre .task-link[data-status="pending-eval"]          { color: var(--status-pending-eval); }
.viz-pre .task-link[data-status="incomplete"]            { color: var(--status-incomplete); }

/* Chat agent tasks (.chat-N / .coordinator-N) — TUI Color::Blue (36,114,200).
   Comes after status rules so it wins the cascade for chat task nodes in the viz. */
.viz-pre .task-link.chat-agent { color: var(--chat-task); }

/* Paused tasks: orthogonal modifier on top of any status. The CLI ascii viz
   already prefixes ‖ before the id; the .paused class re-colors the cluster
   to the muted blue-gray --status-paused so the task reads as "on hold"
   rather than as the underlying status color. Wins over chat-agent so a
   paused chat agent still looks paused. */
.viz-pre .task-link.paused {
    color: var(--status-paused);
    font-style: italic;
}

/* Status-decorator parens — `(in-progress · →44k ←6.6k …)`. Rendered with
   the foreground colour so the decorator stays readable but doesn't share
   the task id's status hue, which would dominate visually. The nested
   selector beats the [data-status] colour (same specificity, declared later
   in source order). */
.viz-pre .task-link .decorator { color: var(--fg); }

/* Trailing relative time suffix (` 5m`, ` 1d`, etc.) — muted so the
   timestamp metadata fades behind the active status info. */
.viz-pre .time-suffix { color: var(--fg-muted); }

/* ── Selection state: edge + node highlighting ──────────────────────────── */

/* When a task is selected, body gets data-selected="<id>".
   The selected task itself gets a subtle highlight, dependency edges/nodes
   get colored, and everything else dims to make the relationship pop. */
body[data-selected] .viz-pre .task-link,
body[data-selected] .viz-pre .edge,
body[data-selected] .viz-pre .text-cell {
    opacity: 0.32;
    transition: opacity 0.15s, color 0.15s, background-color 0.15s;
}

/* The selected node itself: full opacity + subtle highlight. */
body[data-selected] .viz-pre .task-link.is-selected {
    opacity: 1;
    background: var(--selection-bg);
    border-bottom-color: currentColor;
    font-weight: 600;
}

/* Upstream nodes (this task's --after deps) — magenta accent on the label. */
body[data-selected] .viz-pre .task-link.is-upstream {
    opacity: 1;
    color: var(--edge-upstream) !important;
}

/* Downstream nodes (tasks that depend on this task) — cyan accent on the label. */
body[data-selected] .viz-pre .task-link.is-downstream {
    opacity: 1;
    color: var(--edge-downstream) !important;
}

/* Edges in the upstream chain — magenta. */
body[data-selected] .viz-pre .edge.is-upstream {
    opacity: 1;
    color: var(--edge-upstream);
}

/* Edges in the downstream chain — cyan. */
body[data-selected] .viz-pre .edge.is-downstream {
    opacity: 1;
    color: var(--edge-downstream);
}

/* Edges that are part of a cycle involving the selection — yellow.
   Cycle highlighting wins over upstream/downstream (matches TUI priority). */
body[data-selected] .viz-pre .edge.is-cycle {
    opacity: 1;
    color: var(--edge-cycle);
}

/* ── Legend ─────────────────────────────────────────────────────────────── */

.legend {
    list-style: none;
    padding: 0;
    margin: 0;
    display: flex;
    flex-wrap: wrap;
    gap: 0.75rem 1.5rem;
    font-size: 0.85rem;
}

.legend li {
    display: flex;
    align-items: center;
    gap: 0.4rem;
}

.swatch {
    display: inline-block;
    width: 0.85em;
    height: 0.85em;
    border-radius: 2px;
    border: 1px solid var(--border-strong);
    flex-shrink: 0;
}

/* ── Legend in side panel (column layout, more breathing room) ──────────── */

.panel-legend .legend {
    flex-direction: column;
    gap: 0.4rem;
}

.panel-legend .legend li {
    align-items: flex-start;
}

.panel-legend .legend-text {
    list-style: disc;
    padding-left: 1.25rem;
    margin: 0;
    font-size: 0.85rem;
}

.panel-legend .legend-text li {
    margin: 0.3rem 0;
}

.panel-legend kbd {
    background: var(--bg-elevated);
    border: 1px solid var(--border-strong);
    border-radius: 3px;
    padding: 0.1em 0.35em;
    font-size: 0.85em;
    font-family: 'JetBrains Mono', ui-monospace, 'Cascadia Code', 'Source Code Pro', Menlo, Consolas, monospace;
}

/* ── Task list (compact index) ──────────────────────────────────────────── */

.task-list {
    list-style: none;
    padding: 0;
    margin: 0;
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(380px, 1fr));
    gap: 0.4rem;
    font-size: 0.85rem;
}

.task-list li {
    border: 1px solid var(--border);
    background: var(--bg-elevated);
    border-radius: 4px;
}

.task-list a {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.4rem 0.6rem;
    color: var(--fg);
}
.task-list a:hover {
    background: var(--selection-bg);
    text-decoration: none;
}

/* ── Status badges ──────────────────────────────────────────────────────── */

.badge {
    display: inline-block;
    font-size: 0.72rem;
    font-weight: 600;
    padding: 0.1em 0.5em;
    border-radius: 3px;
    border: 1px solid currentColor;
    line-height: 1.4;
    background: transparent;
}
.badge.done                  { color: var(--status-done); }
.badge.failed                { color: var(--status-failed); }
.badge.in-progress           { color: var(--status-in-progress); }
.badge.open                  { color: var(--status-open); }
.badge.blocked               { color: var(--status-blocked); }
.badge.abandoned             { color: var(--status-abandoned); }
.badge.waiting               { color: var(--status-waiting); }
.badge.pending-validation    { color: var(--status-pending-validation); }
.badge.pending-eval          { color: var(--status-pending-eval); }
.badge.incomplete            { color: var(--status-incomplete); }
.badge.paused                { color: var(--status-paused); }

/* Index task list: paused tasks render their row label in italic muted color
   so the visual cue is reinforced beyond the inline ‖ paused badge. */
.task-list li.paused > a > code,
.task-list li.paused > a {
    font-style: italic;
    color: var(--status-paused);
}

/* ── Side panel (detail overlay) ────────────────────────────────────────── */

.side-panel {
    position: fixed;
    top: 0;
    right: 0;
    bottom: 0;
    width: var(--panel-width);
    max-width: 92vw;
    background: var(--bg-panel);
    border-left: 1px solid var(--border-strong);
    box-shadow: -8px 0 24px rgba(0, 0, 0, 0.4);
    overflow-y: auto;
    padding: 1rem 1.25rem 2.5rem;
    z-index: 100;
    transform: translateX(100%);
    transition: transform 0.18s ease-out;
    font-size: 0.9rem;
}
.side-panel.is-open {
    transform: translateX(0);
}

/* ── Resize handle ──────────────────────────────────────────────────────
   The handle is a thin transparent strip overlapping the panel's edge
   (left edge in wide mode, top edge in narrow mode). It catches the
   pointer slightly outside the panel's visible border so it's easy to
   grab. A subtle hover/drag highlight gives feedback without dominating.
   --------------------------------------------------------------------- */
.panel-resize-handle {
    position: absolute;
    top: 0;
    left: -3px;
    width: 6px;
    height: 100%;
    cursor: col-resize;
    background: transparent;
    z-index: 101;
    touch-action: none;
    transition: background-color 0.15s;
}
.panel-resize-handle:hover,
.panel-resize-handle.is-dragging {
    background-color: var(--link);
    opacity: 0.4;
}

/* While dragging, suppress text selection + force the resize cursor
   even when the pointer briefly leaves the handle's hit-area. */
body.is-resizing-panel,
body.is-resizing-panel * {
    user-select: none !important;
    -webkit-user-select: none !important;
}
body.is-resizing-panel.is-resizing-col { cursor: col-resize !important; }
body.is-resizing-panel.is-resizing-row { cursor: row-resize !important; }

.panel-close {
    position: absolute;
    top: 0.6rem;
    right: 0.8rem;
    background: transparent;
    color: var(--fg-muted);
    border: 0;
    font-size: 1.4rem;
    cursor: pointer;
    line-height: 1;
    padding: 0.25rem 0.5rem;
}
.panel-close:hover { color: var(--fg); }

.panel-header {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.5rem;
    padding-right: 2rem;
    margin-bottom: 0.5rem;
}

.panel-id {
    font-size: 1rem;
    background: var(--bg-elevated);
    padding: 0.15em 0.5em;
    border-radius: 4px;
}

.panel-title {
    font-size: 1.05rem;
    font-weight: 600;
    margin: 0.25rem 0 0.75rem;
}

.panel-meta {
    margin: 0.25rem 0 0.75rem;
    font-size: 0.85rem;
    color: var(--fg-muted);
    line-height: 1.6;
}

.panel-meta strong {
    color: var(--fg);
    font-weight: 500;
}

.panel-tags {
    display: flex;
    flex-wrap: wrap;
    gap: 0.25rem;
    margin: 0 0 0.75rem;
}
.panel-tags code {
    font-size: 0.75rem;
}

.side-panel details {
    margin: 0.75rem 0;
    border-top: 1px solid var(--border);
    padding-top: 0.5rem;
}

.side-panel details > summary {
    cursor: pointer;
    font-weight: 600;
    font-size: 0.85rem;
    margin-bottom: 0.4rem;
    list-style: revert;
}

.side-panel details > summary:hover {
    color: var(--link);
}

.panel-desc {
    background: var(--bg-elevated);
    padding: 0.6rem 0.8rem;
    border-radius: 4px;
    overflow-x: auto;
    font-size: 0.78rem;
    line-height: 1.5;
    white-space: pre-wrap;
    word-break: break-word;
    color: var(--fg);
    margin: 0;
    max-height: 360px;
    overflow-y: auto;
}

.panel-deps {
    list-style: none;
    padding: 0;
    margin: 0;
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
}
.panel-deps li {
    font-size: 0.8rem;
}
.panel-deps a {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.15rem 0;
    color: var(--fg);
}
.panel-deps a:hover {
    color: var(--link);
    text-decoration: none;
}

.panel-log {
    list-style: none;
    padding: 0;
    margin: 0;
    font-size: 0.75rem;
    line-height: 1.45;
    max-height: 280px;
    overflow-y: auto;
    background: var(--bg-elevated);
    padding: 0.5rem 0.75rem;
    border-radius: 4px;
}
.panel-log li {
    padding: 0.2rem 0;
    border-bottom: 1px solid var(--border);
    word-break: break-word;
}
.panel-log li:last-child { border-bottom: 0; }
.panel-log .log-ts {
    color: var(--fg-muted);
    font-family: 'JetBrains Mono', ui-monospace, monospace;
    font-size: 0.7rem;
    margin-right: 0.4rem;
}

.eval-score {
    font-size: 1.5rem;
    font-weight: 600;
    margin: 0.25rem 0;
    color: var(--status-pending-eval);
}

.eval-dims {
    width: 100%;
    border-collapse: collapse;
    font-size: 0.8rem;
}
.eval-dims td {
    padding: 0.2rem 0.4rem;
    border-bottom: 1px dotted var(--border);
}
.eval-dims .eval-dim-val {
    text-align: right;
    font-family: 'JetBrains Mono', ui-monospace, monospace;
    color: var(--fg-muted);
}

.panel-deeplink {
    margin-top: 1rem;
    font-size: 0.8rem;
}

/* ── Description toggle button ──────────────────────────────────────────── */

.desc-header {
    display: flex;
    justify-content: flex-end;
    margin-bottom: 0.3rem;
}

.desc-toggle {
    font-size: 0.65rem;
    padding: 0.15em 0.5em;
    background: var(--bg-elevated);
    color: var(--fg-muted);
    border: 1px solid var(--border);
    border-radius: 3px;
    cursor: pointer;
    font-family: inherit;
    line-height: 1;
}
.desc-toggle:hover {
    color: var(--fg);
    border-color: var(--border-strong);
}

/* ── Rendered markdown (description-rendered + panel-desc-html) ──────── */

.description-rendered,
.panel-desc-html {
    background: var(--bg-elevated);
    padding: 0.75rem 1rem;
    border-radius: 4px;
    overflow-x: auto;
    font-size: 0.85rem;
    line-height: 1.6;
    color: var(--fg);
}
.panel-desc-html {
    font-size: 0.78rem;
    padding: 0.6rem 0.8rem;
    max-height: 360px;
    overflow-y: auto;
    margin: 0;
}

/* Headings inside rendered descriptions */
.description-rendered h1, .description-rendered h2, .description-rendered h3,
.description-rendered h4, .description-rendered h5, .description-rendered h6,
.panel-desc-html h1, .panel-desc-html h2, .panel-desc-html h3,
.panel-desc-html h4, .panel-desc-html h5, .panel-desc-html h6 {
    margin: 0.8em 0 0.3em;
    font-weight: 600;
    line-height: 1.3;
    border-bottom: none;
}
.description-rendered h1, .panel-desc-html h1 { font-size: 1.15em; }
.description-rendered h2, .panel-desc-html h2 { font-size: 1.05em; }
.description-rendered h3, .panel-desc-html h3 { font-size: 0.97em; }
.description-rendered h4, .description-rendered h5, .description-rendered h6,
.panel-desc-html h4, .panel-desc-html h5, .panel-desc-html h6 { font-size: 0.9em; }

/* Paragraphs, lists */
.description-rendered p, .panel-desc-html p { margin: 0.4em 0; }
.description-rendered ul, .description-rendered ol,
.panel-desc-html ul, .panel-desc-html ol {
    padding-left: 1.4em;
    margin: 0.3em 0;
}
.description-rendered li, .panel-desc-html li { margin: 0.15em 0; }

/* Task list checkboxes */
.description-rendered input[type="checkbox"],
.panel-desc-html input[type="checkbox"] {
    margin-right: 0.3em;
    pointer-events: none;
}

/* Inline code */
.description-rendered code, .panel-desc-html code {
    font-family: 'JetBrains Mono', ui-monospace, monospace;
    font-size: 0.88em;
    background: var(--bg);
    padding: 0.1em 0.3em;
    border-radius: 3px;
    border: 1px solid var(--border);
}

/* Fenced code blocks */
.description-rendered pre, .panel-desc-html pre {
    background: var(--bg);
    border: 1px solid var(--border-strong);
    border-radius: 4px;
    padding: 0.5em 0.75em;
    overflow-x: auto;
    margin: 0.4em 0;
    white-space: pre;
}
.description-rendered pre code, .panel-desc-html pre code {
    background: none;
    border: none;
    padding: 0;
    font-size: 0.85em;
}

/* Links */
.description-rendered a, .panel-desc-html a { color: var(--link); }

/* Blockquotes */
.description-rendered blockquote, .panel-desc-html blockquote {
    border-left: 3px solid var(--border-strong);
    padding-left: 0.75em;
    margin: 0.4em 0 0.4em 0;
    color: var(--fg-muted);
}

/* Tables */
.description-rendered table, .panel-desc-html table {
    border-collapse: collapse;
    margin: 0.4em 0;
    font-size: 0.9em;
    width: 100%;
}
.description-rendered th, .description-rendered td,
.panel-desc-html th, .panel-desc-html td {
    border: 1px solid var(--border);
    padding: 0.25em 0.5em;
    text-align: left;
}
.description-rendered th, .panel-desc-html th {
    background: var(--bg);
    font-weight: 600;
}

/* Horizontal rules */
.description-rendered hr, .panel-desc-html hr {
    border: 0;
    border-top: 1px solid var(--border);
    margin: 0.75em 0;
}

/* ── Footer ─────────────────────────────────────────────────────────────── */

footer {
    margin-top: 3rem;
    padding-top: 1rem;
    border-top: 1px solid var(--border);
    color: var(--fg-muted);
    font-size: 0.8rem;
}

footer .meta {
    margin-top: 0.5rem;
    font-style: italic;
    color: var(--fg-dim);
}

footer code {
    font-size: 0.75rem;
}

/* ── Per-task page (deeplink target) ────────────────────────────────────── */

body.task-page {
    max-width: 900px;
    margin: 0 auto;
}

.breadcrumb { font-size: 0.85rem; margin-bottom: 0.5rem; }

.task-page h1 {
    margin-top: 0.5rem;
    font-family: 'JetBrains Mono', ui-monospace, monospace;
    font-size: 1.4rem;
    word-break: break-all;
}

.task-page .task-title {
    color: var(--fg);
    font-size: 1.1rem;
    margin: 0 0 1rem;
    font-weight: 600;
}

.meta-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 0.9rem;
}
.meta-table th, .meta-table td {
    text-align: left;
    padding: 0.3rem 0.6rem;
    border-bottom: 1px solid var(--border);
    vertical-align: top;
}
.meta-table th {
    color: var(--fg-muted);
    font-weight: 500;
    width: 9rem;
    white-space: nowrap;
}

.deps {
    list-style: none;
    padding: 0;
    margin: 0;
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
}
.deps li.none { color: var(--fg-muted); font-style: italic; }
.deps .note { color: var(--fg-muted); font-size: 0.85em; }

.description {
    background: var(--bg-elevated);
    padding: 0.75rem 1rem;
    border-radius: 4px;
    overflow-x: auto;
    font-size: 0.85rem;
    line-height: 1.55;
    white-space: pre-wrap;
    word-break: break-word;
}

.task-log {
    list-style: none;
    padding: 0;
    margin: 0;
    font-size: 0.82rem;
    background: var(--bg-elevated);
    padding: 0.5rem 0.75rem;
    border-radius: 4px;
    max-height: 480px;
    overflow-y: auto;
}
.task-log li { padding: 0.2rem 0; border-bottom: 1px solid var(--border); word-break: break-word; }
.task-log li:last-child { border-bottom: 0; }
.task-log .log-ts {
    color: var(--fg-muted);
    font-family: 'JetBrains Mono', ui-monospace, monospace;
    font-size: 0.75rem;
    margin-right: 0.4rem;
}

/* ── Chat transcripts (--chat) ─────────────────────────────────────────── */

.chat-banner {
    margin: 0.4rem 0 0;
    color: var(--fg-muted);
    font-size: 0.85rem;
}
.chat-conversation { margin-top: 1rem; }
.chat-meta {
    color: var(--fg-muted);
    font-size: 0.8rem;
    margin: 0 0 0.5rem;
}
.chat-messages {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 0.6rem;
}
.chat-msg {
    border: 1px solid var(--border);
    border-radius: 6px;
    padding: 0.55rem 0.75rem;
    background: var(--bg-elevated);
}
.chat-msg-user { border-left: 3px solid rgb(60, 200, 220); }
.chat-msg-agent { border-left: 3px solid rgb(80, 220, 100); }
.chat-msg-other { border-left: 3px solid var(--fg-muted); }
.chat-msg-head {
    display: flex;
    align-items: baseline;
    gap: 0.6rem;
    margin-bottom: 0.35rem;
}
.chat-role {
    font-weight: 600;
    text-transform: uppercase;
    font-size: 0.7rem;
    letter-spacing: 0.05em;
}
.chat-ts {
    color: var(--fg-muted);
    font-family: 'JetBrains Mono', ui-monospace, monospace;
    font-size: 0.7rem;
}
.chat-text, .chat-code {
    margin: 0.25rem 0;
    padding: 0.4rem 0.6rem;
    font-family: 'JetBrains Mono', ui-monospace, monospace;
    font-size: 0.8rem;
    white-space: pre-wrap;
    word-break: break-word;
    background: var(--bg);
    border-radius: 4px;
    border: 1px solid var(--border);
}
.chat-code {
    background: rgba(0, 0, 0, 0.25);
    border-color: var(--border-strong);
}
.chat-hidden-note {
    color: var(--fg-muted);
    font-style: italic;
}

/* ── Responsive: narrow screens push the panel under the viz ─────────── */

@media (max-width: 900px) {
    .side-panel {
        position: fixed;
        top: auto;
        right: 0;
        left: 0;
        bottom: 0;
        width: 100%;
        max-width: 100%;
        height: var(--panel-height);
        max-height: 92vh;
        border-left: 0;
        border-top: 1px solid var(--border-strong);
        transform: translateY(100%);
        box-shadow: 0 -8px 24px rgba(0, 0, 0, 0.4);
    }
    .side-panel.is-open {
        transform: translateY(0);
    }
    .panel-resize-handle {
        top: -3px;
        left: 0;
        width: 100%;
        height: 6px;
        cursor: row-resize;
    }
}

/* ── Message indicator (parity with TUI envelope glyph) ─────────────────── */
/* Status-aligned palette mirroring src/messages.rs::CoordinatorMessageStatus
   color() (Unseen=DarkGray, Seen=Yellow, Replied=Green). The "unseen" tone
   reads as a foreground text color rather than dim, so unread messages pop
   against the row text — matching the TUI's bold envelope behavior. */
:root {
    --msg-unseen: var(--status-open);          /* yellow-ish — pending action */
    --msg-seen: var(--fg-muted);               /* gray — neutral */
    --msg-replied: var(--status-done);         /* green — done */
    --msg-bg-unread: rgba(229, 229, 16, 0.08); /* faint highlight on row bg */
}

.msg-indicator {
    display: inline-flex;
    align-items: baseline;
    gap: 0.15em;
    margin-left: 0.45em;
    padding: 0 0.3em;
    border-radius: 3px;
    font-family: 'JetBrains Mono', ui-monospace, 'Cascadia Code', 'Source Code Pro', Menlo, Consolas, monospace;
    font-size: 0.85em;
    line-height: 1;
    cursor: pointer;
    user-select: none;
    color: var(--msg-seen);
    border: 1px solid transparent;
    transition: background 120ms, border-color 120ms;
}
.msg-indicator:hover {
    background: var(--selection-bg);
    border-color: var(--border);
    text-decoration: none;
}
.msg-indicator .msg-glyph { font-size: 1.05em; }
.msg-indicator .msg-count { font-variant-numeric: tabular-nums; }

.msg-indicator.msg-unseen {
    color: var(--msg-unseen);
    font-weight: 600;
}
.msg-indicator.msg-seen {
    color: var(--msg-seen);
}
.msg-indicator.msg-replied {
    color: var(--msg-replied);
}
.msg-indicator.has-unread-msg {
    background: var(--msg-bg-unread);
    border-color: rgba(229, 229, 16, 0.25);
}

/* Whole task-list row gets a subtle wash when there are unread messages so
   the user can scan for activity without zeroing in on the indicator alone. */
.task-list li.has-unread-msg {
    background: var(--msg-bg-unread);
    border-radius: 3px;
}
.task-list li.has-unread-msg > a {
    /* Keep the link color readable over the wash. */
    color: inherit;
}

/* ── Messages section (per-task page + inspector) ───────────────────────── */
.messages-section {
    margin-top: 1.5rem;
}
.messages-section h2 {
    display: flex;
    align-items: baseline;
    gap: 0.5rem;
    margin-bottom: 0.75rem;
}
.messages-section h2 .msg-glyph { font-size: 1.1em; }
.messages-section.msg-unseen h2 .msg-glyph { color: var(--msg-unseen); }
.messages-section.msg-seen   h2 .msg-glyph { color: var(--msg-seen); }
.messages-section.msg-replied h2 .msg-glyph { color: var(--msg-replied); }
.messages-section .messages-summary {
    font-size: 0.85em;
    color: var(--fg-muted);
    font-weight: normal;
}

.messages-list {
    list-style: none;
    padding: 0;
    margin: 0;
    display: flex;
    flex-direction: column;
    gap: 0.6rem;
}

.msg-row {
    border: 1px solid var(--border);
    border-radius: 6px;
    padding: 0.55rem 0.7rem;
    background: var(--bg-elevated);
}
.msg-row.msg-incoming {
    /* Pull eye toward incoming via a left accent stripe (the action items). */
    border-left: 3px solid var(--msg-unseen);
}
.msg-row.msg-outgoing {
    border-left: 3px solid var(--msg-replied);
    opacity: 0.92;
}
.msg-row.msg-urgent {
    border-color: var(--status-failed);
    background: color-mix(in srgb, var(--status-failed) 6%, var(--bg-elevated));
}

.msg-head {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    align-items: baseline;
    font-size: 0.82em;
    color: var(--fg-muted);
    margin-bottom: 0.35rem;
}
.msg-head .msg-id {
    font-family: 'JetBrains Mono', ui-monospace, 'Cascadia Code', monospace;
    color: var(--fg-dim);
}
.msg-head .msg-sender {
    font-weight: 600;
    color: var(--fg);
}
.msg-head .msg-status {
    margin-left: auto;
    font-style: italic;
}

.msg-body {
    margin: 0;
    padding: 0.4rem 0.55rem;
    background: var(--bg-pre);
    border-radius: 4px;
    color: var(--fg);
    font-family: 'JetBrains Mono', ui-monospace, 'Cascadia Code', monospace;
    font-size: 0.88em;
    white-space: pre-wrap;
    word-break: break-word;
}

/* Inspector-side variant: tighter padding, smaller fonts. */
.side-panel .messages-section {
    margin-top: 1rem;
}
.side-panel .messages-section h2 {
    font-size: 0.95rem;
}
.side-panel .msg-row {
    padding: 0.45rem 0.55rem;
}
.side-panel .msg-body {
    font-size: 0.82em;
}
/* Highlight pulse when the user clicks the indicator and we scroll to the
   section. The animation runs once via a class added by panel.js. */
.messages-section.msg-flash {
    animation: msg-flash 1.4s ease-out;
}
@keyframes msg-flash {
    0%   { background: var(--msg-bg-unread); }
    100% { background: transparent; }
}
