/* Praxis Web - Compiled Styles */
/* Auto-generated - edit praxis/web/src/css/ instead */


/* ========== variables.css ========== */
/* Praxis Web - CSS Variables
 * All color, spacing, and size tokens
 */

:root {
    /* Brand accent - ONE hue knob drives every brand color. Flip [data-accent] on
       <html> to re-tint the whole app (both themes); the logs panel sets it to "blue".
       --accent is the bright brand color (was #0B9A6D); --accent-deep the dark one
       (was #18453B). Adjust --accent-hue to re-theme from a single place. */
    --accent-hue: 161;
    --accent-sat: 87%;
    --accent-lum: 32%;
    --accent: hsl(var(--accent-hue) var(--accent-sat) var(--accent-lum));
    --accent-deep: hsl(var(--accent-hue) 49% 18%);

    /* Brand Colors (derived from the accent) */
    --primary: var(--accent-deep);
    --primary-light: var(--accent);
    --primary-dark: var(--accent-deep);

    /* Light Theme */
    --background: #f9f9f9;
    --text: #1f1f1f;
    --light-text: #4f4f4f;
    --user-message-bg: hsl(var(--accent-hue) 45% 92%);
    --assistant-message-bg: #ffffff;
    --border: #d0d0d0;
    --shadow: rgba(0, 0, 0, 0.05);

    /* Terminal palette (light defaults; dark overrides below) */
    --term-bg: #ffffff;
    --term-panel: #f5f5f5;
    --term-panel-alt: #fafafa;
    --term-border: #d0d0d0;
    --term-border-soft: #e0e0e0;
    --term-border-faint: #ececec;
    --term-text: #1f1f1f;
    --term-bright: #000000;
    --term-fg-strong: #222222;
    --term-fg: #555555;
    --term-dim: #6b6b6b;
    --term-dim2: #888888;
    --term-dim3: #9a9a9a;
    --term-faint: #b5b5b5;

    /* Unified spacing variables - carefully calibrated */
    --spacing-xs: 0.25rem;
    --spacing-sm: 0.5rem;
    --spacing-md: 0.75rem;
    --spacing-lg: 1.25rem;
    --spacing-xl: 1.5rem;

    /* Unified border radius - consistent curves */
    --radius-small: 6px;
    --radius-medium: 6px;
    --radius-large: 6px;
    --radius-round: 50%;

    /* Icon and element sizes - pixel-perfect */
    --icon-size: 1.5em;
    --icon-font-size: 0.85em;
    --icon-element-size: 0.85em;  /* Size of the "P" element symbol */
    --icon-padding: 0.5em;
    --seed-font-size: 0.2em;

    /* Functional Layout Constraints - Applied Universally to All Tabs */
    --tab-padding: 1.25rem;
    --tab-padding-mobile: 1rem;
    --content-max-width: 100%;
    --overflow-y: auto;
    --overflow-x: hidden;
}


/* Dark Theme Overrides */
[data-theme="dark"] {
    --primary: var(--accent-deep);
    --primary-light: var(--accent);
    --primary-dark: var(--accent);
    --background: #121212;
    --text: #e0e0e0;
    --light-text: #b0b0b0;
    --user-message-bg: var(--accent-deep);
    --assistant-message-bg: #1e1e1e;
    --border: #333333;
    --shadow: rgba(0, 0, 0, 0.3);
    --input-bg: #1e1e1e;
    --input-text: #e0e0e0;

    /* Terminal palette - dark */
    --term-bg: #0d0d0d;
    --term-panel: #111111;
    --term-panel-alt: #0a0a0a;
    --term-border: #333333;
    --term-border-soft: #2a2a2a;
    --term-border-faint: #222222;
    --term-text: #d4d4d4;
    --term-bright: #ffffff;
    --term-fg-strong: #cccccc;
    --term-fg: #aaaaaa;
    --term-dim: #888888;
    --term-dim2: #666666;
    --term-dim3: #555555;
    --term-faint: #444444;
}


/* Logs mode: shift the entire accent to blue (applies to both light + dark themes).
   Set on <html> while the logs panel is open; everything derived from --accent /
   --accent-hue (lines, links, accents, the Praxis animation) re-tints to blue. */
[data-accent="blue"] {
    --accent-hue: 212;
    --accent-sat: 72%;
    --accent-lum: 50%;
}



/* ========== base.css ========== */
/* Praxis Web - Base Styles
 * Global resets and typography
 */

* {
    box-sizing: border-box;
    margin: 0;
    padding: 0;
}


body, html {
    font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
    height: 100%;
    background-color: var(--background);
    color: var(--text);
    line-height: 1.6;
    margin: 0;
    padding: 0;
}


/* Utility Classes */
.hidden {
    display: none;
}


.header-actions {
    display: flex;
    align-items: center;
}


.loading-placeholder {
    padding: 1.25rem;
    color: var(--light-text);
}


.modal-subheading {
    margin-top: 1.25rem;
    margin-bottom: 0.9375rem;
    font-weight: 500;
    color: var(--text);
}


.button-group {
    display: flex;
    gap: 0.625rem;
    margin-top: 1.25rem;
}



/* ========== layout.css ========== */
/* Praxis Web - Layout
 * Main app container and grid layout
 */

.app-container {
    display: grid;
    grid-template-rows: auto auto 1fr auto;
    grid-template-columns: minmax(0, 1fr);
    height: 100vh;
    height: 100dvh;
    max-width: 56.25rem;
    margin: 0 auto;
    padding: 0 1.375rem;
    overflow: hidden;
    box-sizing: border-box;
}


/* Functional Tab Container - Universal Base Class for All Tabs */
.tab-container {
    flex: 1;
    padding: var(--tab-padding);
    overflow-y: var(--overflow-y);
    overflow-x: var(--overflow-x);
    max-width: var(--content-max-width);
    width: 100%;
    box-sizing: border-box;
    -webkit-overflow-scrolling: touch;
}


/* Tab Content Layout */
.tab-content {
    display: none;
    height: 100%;
    width: 100%;
    max-width: 100%;
    overflow: hidden;
    box-sizing: border-box;
}


.tab-content.active {
    display: flex;
    flex-direction: column;
    overflow-y: auto;
    overflow-x: hidden;
    width: 100%;
    max-width: 100%;
}


/* Conversation toolbar - tool toggles (R/E/P/L) pinned at top of the chat */
.conversation-toolbar {
    flex: 0 0 auto;
    display: flex;
    gap: var(--spacing-sm);
    /* Top padding matches the chat-container's top padding below it, so the
       toolbar sits with equal breathing room above and below. Horizontal
       padding insets the buttons away from the window margin. Bottom border
       mirrors the divider every other tab's second row (.tab-header) has. */
    padding: var(--spacing-md) var(--spacing-lg) var(--spacing-md);
    border-bottom: 1px solid var(--border);
    width: 100%;
    box-sizing: border-box;
}


.tool-toggle {
    background: none;
    border: 1px solid var(--border);
    border-radius: var(--radius-small);
    color: var(--light-text);
    font-family: inherit;
    font-size: 0.75rem;
    font-weight: bold;
    padding: 0.375rem 0.875rem;
    cursor: pointer;
    letter-spacing: 0.03em;
}
@media (hover: hover) { .tool-toggle:hover {
    border-color: var(--primary);
    color: var(--text);
} }


/* Print and Loop are always present and look identical to their siblings - inert
   (clicks are no-ops) until usable, but never greyed or auto-highlighted. Loop
   lights up via .active while a loop is running; otherwise no conditional styling
   here by design. */

/* Live engagement-energy badge. The badge is just a positioning wrapper pushed
   to the toolbar's right edge (all viewports); the visible, content-width chip
   lives in .badge-chip. */
.print-energy-badge {
    margin-left: auto;
    align-self: center;
    display: flex;
}


.print-energy-badge[hidden] {
    display: none;
}


/* The chip - clamps to its content width. Icon-only: white shapes on the chip's
   own gauge background (dark green tint filling from the bottom to the energy
   fraction, muted gray above). */
.badge-chip {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    font-size: 0.72rem;
    font-weight: bold;
    letter-spacing: 0.03em;
    color: var(--accent);
    padding: 0.25rem 0.5rem;
    border-radius: var(--radius-small);
    /* The chip background IS the energy gauge: a hard horizontal split, muted
       gray on top, the usual dark accent tint filling from the bottom up to the
       energy fraction (--energy in [0,1], set by renderPrintButton). Same
       subdued alpha family as the old solid tint. */
    background: linear-gradient(
        to top,
        hsl(var(--accent-hue) var(--accent-sat) var(--accent-lum) / 0.35) calc(var(--energy, 0) * 100%),
        hsl(0 0% 55% / 0.15) calc(var(--energy, 0) * 100%)
    );
}


.badge-chip .harmonic-icon {
    height: 1.7em;        /* square glyph, transparent - the chip shows through */
    width: 1.7em;
    flex: 0 0 auto;
}


/* Every active tool follows the brand accent (green; blue while the LOGS easter egg is
   on), so Read and Evaluate are always the same color when selected - one source, no
   second hardcoded blue. */
/* Accent lives in the border (and a faint fill); text stays neutral so the
   active state reads as a subtle highlight, not a bright label. */
.tool-toggle.active {
    background: hsl(var(--accent-hue) var(--accent-sat) var(--accent-lum) / 0.15);
    border-color: var(--accent);
    color: var(--text);
}


/* Two distinct conversation layouts (toolbar stays order 0 at top):
   - Evaluate (default): texting style - messages fill and scroll above, input
     pinned at the bottom.
   - Read (.mode-read): search style - input floats up under the toolbar, KB
     results/content fill below it. */

/* Evaluate: messages hug the top under the toolbar; the input sits right below
   them (flex 0 = no grow, so it never floats to the viewport bottom). */
.chat-container {
    order: 1;
    flex: 0 1 auto;
    display: flex;
    flex-direction: column;
    overflow-y: auto;
    overflow-x: hidden;
    padding: 0;
    width: 100%;
    max-width: 100%;
    box-sizing: border-box;
}


/* Only space messages from the toolbar when there are messages; an empty chat
   contributes zero height so the input aligns identically in both modes. */
.chat-container:not(:empty) {
    padding-top: var(--spacing-md);
}


/* KB results/content (Read mode) - fill the column below the input. */
.kb-results {
    flex: 1 1 auto;
    /* min-height:0 lets this flex child shrink below its content so overflow-y
       actually scrolls the list, instead of growing the column to fit it all. */
    min-height: 0;
    display: flex;
    flex-direction: column;
    gap: var(--spacing-sm);
    overflow-y: auto;
    padding: var(--spacing-md) 0 0 0;
    width: 100%;
    box-sizing: border-box;
}


/* The inactive panel is hidden per mode. Needs higher specificity than the
   .chat-container / .kb-results display:flex rules above, or the empty panel
   stays in flow as a flex:1 box and shoves the input down. */
.chat-container[hidden],
.kb-results[hidden] { display: none; }


/* Read mode reorders the input above the results. */
.mode-read .input-container { order: 1; }

.mode-read .kb-results { order: 2; }


.kb-empty {
    color: var(--text-muted, var(--text));
    opacity: 0.6;
    padding: var(--spacing-md) 0;
    font-size: 0.9rem;
}


.kb-result {
    display: grid;
    grid-template-columns: auto 1fr;
    grid-template-areas: "type title" "type snippet";
    column-gap: var(--spacing-sm);
    padding: var(--spacing-sm) var(--spacing-md);
    border: 1px solid var(--border);
    border-radius: var(--radius-small);
    text-decoration: none;
    color: var(--text);
    transition: border-color 0.15s, background-color 0.15s;
    /* Rows are independent: containing layout/paint keeps long lists (and
       lazy thumbnails decoding in) from re-laying-out the whole feed. */
    contain: layout paint;
    min-width: 0;
}


/* ── KB Explorer: directory cards fanned downward ──
   Collapsed cards overlap so only header lips show, like a deck of folders
   running down the page. The opened card lifts out and exposes its files.
   Depth nudges children rightward, so nesting reads as a slope into space. */
.kb-explorer {
    display: flex;
    flex-direction: column;
    margin-bottom: var(--spacing-md);
}


.kb-file.current {
    background: var(--accent);
    color: var(--background);
}


/* Deck-style stack (same gesture as the chart-deck fan): ancestors sit
   BEHIND the front card, scaled back toward the vanishing point, each
   peeking a header lip above the card in front. --dir-rank counts back
   from the front card (0 = front). */
.kb-dir-card {
    position: relative;
    border: 1px solid var(--border);
    border-radius: var(--radius-small);
    background: var(--background);
    transform: scale(calc(1 - var(--dir-rank, 0) * 0.045));
    transform-origin: top center;
    margin-bottom: -0.5rem; /* the next card forward overlaps all but the lip */
    z-index: calc(10 - var(--dir-rank, 0));
    box-shadow: 0 -0.3rem 0.7rem rgba(0, 0, 0, 0.18);
    transition: transform 0.18s ease, margin 0.18s ease, box-shadow 0.18s ease;
}


.kb-dir-card:last-child {
    margin-bottom: 0;
}


.kb-dir-card:not(.open) .kb-dir-header {
    opacity: calc(1 - var(--dir-rank, 0) * 0.18); /* recede into the dark */
}


.kb-dir-card.open {
    transform: scale(1);
    margin-top: 0.5rem;
    margin-bottom: 0.35rem;
    border-color: var(--accent);
    box-shadow: 0 0.25rem 1rem rgba(0, 0, 0, 0.25);
}


.kb-dir-header {
    display: flex;
    align-items: baseline;
    gap: var(--spacing-sm);
    padding: 0.45rem var(--spacing-md);
    cursor: pointer;
    font-weight: 600;
    font-size: 0.85rem;
}


.kb-dir-name {
    color: var(--accent);
}


.kb-dir-count {
    font-size: 0.7rem;
    opacity: 0.55;
}


.kb-dir-files {
    display: none;
    padding: 0 var(--spacing-md) var(--spacing-sm);
    columns: 14rem;
    column-gap: var(--spacing-md);
}


.kb-dir-card.open .kb-dir-files {
    display: block;
}


.kb-file {
    font-family: monospace;
    font-size: 0.8rem;
    padding: 0.15rem 0.35rem;
    border-radius: var(--radius-small);
    cursor: pointer;
    break-inside: avoid;
}
@media (hover: hover) { .kb-file:hover {
    background: var(--input-bg, var(--background));
    color: var(--accent);
} }


/* Video rows are two true panels: media (chip + thumb) left, text right.
   The thumbnail hugs the chip's bottom edge regardless of title height. */
.kb-result.has-thumb {
    display: flex;
    align-items: flex-start;
    gap: var(--spacing-sm);
}


.kb-result-media {
    flex: 0 0 auto;
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 0.25rem;
}


.kb-result-text {
    flex: 1 1 auto;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 0.1rem;
}


.kb-result-thumb {
    width: 96px;
    aspect-ratio: 16 / 9;
    object-fit: cover;
    border-radius: var(--radius-small);
    opacity: 0.92;
    transition: opacity 0.15s;
}


/* Video descriptions carry hard line breaks; flow them as prose and clamp
   so the thumbnail row stays compact on every viewport. */
.has-thumb .kb-result-snippet {
    white-space: normal;
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 3;
    overflow: hidden;
}


/* The video's address: a quiet shortened link under the title. */
.kb-result-url {
    align-self: flex-start;
    font-size: 0.7rem;
    opacity: 0.55;
    color: inherit;
    text-decoration: none;
}
@media (hover: hover) { .kb-result-url:hover {
    opacity: 0.85;
    text-decoration: underline;
} }
@media (hover: hover) { .kb-result:hover .kb-result-thumb {
    opacity: 1;
} }
@media (hover: hover) { .kb-result:hover {
    border-color: var(--accent);
    background-color: var(--input-bg, var(--background));
} }


.kb-result-type {
    grid-area: type;
    align-self: start;
    font-size: 0.65rem;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--accent);
    border: 1px solid var(--border);
    border-radius: var(--radius-small);
    padding: 0.1rem 0.4rem;
    white-space: nowrap;
}


/* Actionable items (navigate away) get a filled chip; content items (open
   inline) stay outlined - a cue for what a click will do. */
.kb-result-type[data-kb-type="card"],
.kb-result-type[data-kb-type="link"],
.kb-result-type[data-kb-type="agent"] {
    background: var(--accent);
    color: var(--background);
    border-color: var(--accent);
}


/* The label doubles as a filter button: click/tap to append it to the search
   query (comma-delimited). Fill on hover, dim on tap - works on desktop and
   mobile. */
.kb-label-filter {
    cursor: pointer;
    transition: background 0.12s, color 0.12s, border-color 0.12s, opacity 0.12s;
}
@media (hover: hover) { .kb-label-filter:hover {
    background: var(--accent);
    color: var(--background);
    border-color: var(--accent);
} }

.kb-label-filter:active {
    opacity: 0.6;
}


/* Brief highlight when a KB search deep-links to a fleet row on the Stage tab. */
.agent-row.kb-flash {
    animation: kbFlash 1.2s ease;
}


@keyframes kbFlash {
    0%, 100% { background-color: transparent; }
    25% { background-color: var(--input-bg, var(--background)); box-shadow: 0 0 0 2px var(--accent); }
}

.kb-result-title {
    grid-area: title;
    font-weight: 600;
    min-width: 0;
}


/* Provenance chip: where the entry came from (its document, site, or event). */
.kb-result-origin {
    margin-left: var(--spacing-sm);
    font-size: 0.7rem;
    font-weight: 400;
    opacity: 0.55;
}


.kb-result-snippet {
    grid-area: snippet;
    width: 100%;
    min-width: 0;
    font-size: 0.8rem;
    opacity: 0.75;
    white-space: pre-wrap;
    word-break: break-word;
}


.kb-result-snippet mark {
    background: var(--accent);
    color: var(--background);
    border-radius: 2px;
    padding: 0 1px;
}


/* KB content card - a single entry rendered inline from data. */
.kb-card {
    display: flex;
    flex-direction: column;
    height: 100%;
    border: 1px solid var(--border);
    border-radius: var(--radius-small);
    overflow: hidden;
}


.kb-card-header {
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    gap: var(--spacing-sm);
    padding: var(--spacing-sm) var(--spacing-md);
    border-bottom: 1px solid var(--border);
    background: var(--input-bg, var(--background));
}


.kb-card-back {
    background: none;
    border: 1px solid var(--border);
    border-radius: var(--radius-small);
    color: var(--text);
    font-family: inherit;
    font-size: 0.75rem;
    padding: 0.25rem 0.6rem;
    cursor: pointer;
}
@media (hover: hover) { .kb-card-back:hover {
    border-color: var(--accent);
} }


.kb-card-title {
    font-weight: 600;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}


/* Crawled pages: link back to the live original. */
.kb-card-external {
    margin-left: auto;
    color: var(--accent);
    text-decoration: none;
    border: 1px solid var(--border);
    border-radius: var(--radius-small);
    padding: 0.25rem 0.6rem;
    font-size: 0.75rem;
}
@media (hover: hover) { .kb-card-external:hover {
    border-color: var(--accent);
} }


/* Inline player for crawled video pages. */
.kb-card-embed {
    flex: 0 0 auto;
    width: 100%;
    aspect-ratio: 16 / 9;
    max-height: 60%;
    border: none;
    background: #000;
}


.kb-card-body {
    flex: 1 1 auto;
    overflow-y: auto;
    padding: var(--spacing-md) var(--spacing-lg);
    line-height: 1.6;
}


/* Reading typography defaults. Every KB type inherits these; types refine
   below via [data-kb-type] - autonomous, no per-card overrides. */
.kb-card-body {
    font-size: 0.95rem;
    max-width: 46rem;
}


.kb-card-body h1,
.kb-card-body h2,
.kb-card-body h3,
.kb-card-body h4 {
    margin: 1.2em 0 0.45em;
    line-height: 1.3;
}


.kb-card-body h1:first-child,
.kb-card-body h2:first-child,
.kb-card-body h3:first-child {
    margin-top: 0;
}


.kb-card-body h1 { font-size: 1.35rem; }

.kb-card-body h2 {
    font-size: 1.15rem;
    padding-bottom: 0.25em;
    border-bottom: 1px solid var(--border);
}

.kb-card-body h3 { font-size: 1rem; }

.kb-card-body h4 { font-size: 0.9rem; opacity: 0.85; }


.kb-card-body p {
    margin: 0 0 0.75em;
}


/* Lists indent INSIDE the text column - markers must never hang past it. */
.kb-card-body ul,
.kb-card-body ol {
    margin: 0 0 0.75em;
    padding-left: 1.5rem;
}


.kb-card-body li {
    margin: 0.2em 0;
}


.kb-card-body li::marker {
    color: var(--accent);
}


.kb-card-body a {
    color: var(--accent);
}


/* Wiki edges read as internal: dotted underline, no external affordance. */
.kb-card-body .kb-wiki-link {
    text-decoration: underline dotted;
    text-underline-offset: 2px;
}


.kb-card-body pre {
    background: var(--input-bg, var(--background));
    border: 1px solid var(--border);
    border-radius: var(--radius-small);
    padding: var(--spacing-sm) var(--spacing-md);
    margin: 0 0 0.75em;
    overflow-x: auto;
    line-height: 1.45;
}


.kb-card-body code {
    font-family: monospace;
    font-size: 0.85em;
}


.kb-card-body :not(pre) > code {
    background: var(--input-bg, var(--background));
    border: 1px solid var(--border);
    border-radius: 3px;
    padding: 0 0.25em;
}


.kb-card-body blockquote {
    margin: 0 0 0.75em;
    padding: 0.1em var(--spacing-md);
    border-left: 3px solid var(--accent);
    opacity: 0.85;
}


/* Code blocks wrap by default - horizontal scroll is hostile on mobile - and
   scale down gently with the viewport for vertical readability. */
.kb-card-body pre {
    white-space: pre-wrap;
    word-break: break-word;
    font-size: clamp(0.65rem, 0.55rem + 0.4vw, 0.8rem);
}


/* Syntax tinting: token spans only, all driven off theme variables. */
.kb-card-body .tok-kw { color: var(--accent); font-weight: 600; }

.kb-card-body .tok-str { color: var(--primary-dark, #2d7a4f); }

.kb-card-body .tok-com { opacity: 0.5; font-style: italic; }

.kb-card-body .tok-num { color: var(--accent); opacity: 0.8; }


/* Type refinements, driven by the KB type system. */
.kb-card-body[data-kb-type="run"] pre {
    border: none;
    background: none;
    padding: 0;
}


.kb-card-body[data-kb-type="page"] {
    overflow-wrap: anywhere; /* crawled text carries long raw URLs */
}


/* Whole-file views: full width, quiet frame, room to read. */
.kb-card-body[data-kb-type="code"] {
    max-width: none;
}


.kb-card-body[data-kb-type="code"] .code-file {
    border: none;
    background: none;
    padding: 0;
}


.input-container {
    /* Default order 2 = bottom (Evaluate, texting style); Read mode reorders it
       to 1 (under the toolbar). Equal spacing-md above and below balances the
       toolbar's top padding. */
    order: 2;
    flex: 0 0 auto;
    display: flex;
    padding: var(--spacing-md) 0;
    background-color: var(--background);
    width: 100%;
    max-width: 100%;
    box-sizing: border-box;
    z-index: 10;
}


.message-input {
    width: 100%;
    max-width: 100%;
    padding: 0.75rem;
    border: 1px solid var(--border);
    border-radius: var(--radius-small);
    font-size: 1rem;
    resize: none;
    min-height: 2.75rem;
    max-height: 12.5rem;
    overflow-y: hidden;
    transition: border-color 0.2s;
    font-family: inherit;
    background-color: var(--input-bg, var(--background));
    color: var(--input-text, var(--text));
    line-height: 1.5;
    box-sizing: border-box;
}


.message-input:focus {
    outline: none;
    border-color: var(--primary);
}


.input-container.with-messages {
    padding-top: var(--spacing-lg);
    border-top: 1px solid var(--border);
    margin-top: 0;
}



/* ========== components.css ========== */
/* Praxis Web - All Component Styles
 * Single file with all carefully-crafted visual styles preserved
 */

/* ==================== Header ==================== */
.header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 0.25rem 0 1px 0;
    background-color: var(--background);
    /* Positioned so the notification fly-out stacks over tab content
       (e.g. the Gymnasium input box at z-index 10). */
    position: relative;
    z-index: 50;
}


.logo {
    font-size: 1.5rem;
    font-weight: 600;
    color: var(--primary-dark);
    display: flex;
    align-items: center;
    gap: 0.4375rem;
    flex: 1;
}


.prism-logo {
    width: 7rem;
    height: 7rem;
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    flex-grow: 0;
    position: relative;
    overflow: visible;
}


.prism-logo canvas {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    width: 8.75rem;
    height: 8.75rem;
    image-rendering: auto;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
}


.logo-separator {
    color: var(--border);
    font-weight: 300;
    margin: 0 0.625rem;
}


.system-prompt-header {
    font-size: 0.875rem;
    font-weight: 400;
    color: var(--light-text);
    font-style: italic;
    cursor: text;
    padding: 0.25rem 0.5rem 0.25rem 0.875rem;
    position: relative;
    border-radius: var(--radius-small);
    transition: background-color 0.2s, max-width 0.2s;
    outline: none;
    flex: 1;
    max-width: 37.5rem;
    white-space: pre-wrap;
    word-wrap: break-word;
    overflow: hidden;
    text-overflow: ellipsis;
    display: -webkit-box;
    line-clamp: 2;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    line-height: 1.4;
}


@keyframes prompt-cursor-blink {
    0%, 100% { opacity: 1; }
    50% { opacity: 0; }
}

.system-prompt-header.default-prompt::before {
    content: '|';
    position: absolute;
    left: 0.125rem;
    top: 0.25rem;
    line-height: 1.225rem;
    font-style: normal;
    font-size: 1.125rem;
    font-weight: 300;
    color: var(--border);
    animation: prompt-cursor-blink 1s step-end infinite;
}
@media (hover: hover) { .system-prompt-header:hover {
    background-color: rgba(0, 0, 0, 0.02);
    max-width: 50rem;
} }


.system-prompt-header:focus {
    background-color: rgba(0, 0, 0, 0.03);
    box-shadow: 0 0 0 1px var(--border);
    white-space: pre-wrap;
    overflow: visible;
    max-width: 50rem;
    line-clamp: unset;
    -webkit-line-clamp: unset;
    display: block;
}
@media (hover: hover) { [data-theme="dark"] .system-prompt-header:hover {
    background-color: rgba(255, 255, 255, 0.05);
} }


[data-theme="dark"] .system-prompt-header:focus {
    background-color: rgba(255, 255, 255, 0.08);
}


.settings-button,
.theme-toggle-button {
    background: none;
    border: none;
    cursor: pointer;
    color: var(--light-text);
    font-size: 0.875rem;
    display: flex;
    align-items: center;
    gap: 0.3125rem;
    padding: 0.625rem 0.5rem;
    border-radius: var(--radius-small);
    transition: background-color 0.2s;
}


.theme-toggle-button {
    margin-right: 0.125rem;
}
@media (hover: hover) { .settings-button:hover, .theme-toggle-button:hover {
    background-color: rgba(0, 0, 0, 0.05);
} }
@media (hover: hover) { [data-theme="dark"] .settings-button:hover, [data-theme="dark"] .theme-toggle-button:hover {
    background-color: rgba(255, 255, 255, 0.1);
} }


/* Notification bell + dropdown panel (header, left of theme toggle). */
.notification-wrapper {
    position: relative;
    display: flex;
    align-items: center;
}


.notification-button {
    background: none;
    border: none;
    cursor: pointer;
    color: var(--light-text);
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0.625rem 0.5rem;
    border-radius: var(--radius-small);
    transition: background-color 0.2s;
}
@media (hover: hover) { .notification-button:hover {
    background-color: rgba(0, 0, 0, 0.05);
} }
@media (hover: hover) { [data-theme="dark"] .notification-button:hover {
    background-color: rgba(255, 255, 255, 0.1);
} }


.notification-button svg {
    display: block;
}


/* Donations link (pig icon) - styled like the other header icon buttons. */
.donation-button {
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--light-text);
    padding: 0.625rem 0.5rem;
    border-radius: var(--radius-small);
    transition: background-color 0.2s, color 0.2s;
    text-decoration: none;
}
@media (hover: hover) { .donation-button:hover {
    background-color: rgba(0, 0, 0, 0.05);
    color: var(--accent);
} }
@media (hover: hover) { [data-theme="dark"] .donation-button:hover {
    background-color: rgba(255, 255, 255, 0.1);
} }


.donation-button svg {
    display: block;
}


.notification-badge {
    position: absolute;
    top: 0.125rem;
    right: 0.125rem;
    min-width: 0.875rem;
    height: 0.875rem;
    padding: 0 0.1875rem;
    border-radius: 0.4375rem;
    background: var(--primary, #0b9a6d);
    color: #fff;
    font-size: 0.625rem;
    font-weight: 700;
    line-height: 0.875rem;
    text-align: center;
}


.notification-panel {
    position: absolute;
    top: calc(100% + 0.5rem);
    right: 0;
    width: 20rem;
    max-height: 22.5rem;
    overflow-y: auto;
    background: var(--background);
    border: 1px solid var(--border);
    border-radius: var(--radius-medium);
    box-shadow: 0 0.5rem 1.5rem rgba(0, 0, 0, 0.25);
    z-index: 50;
    padding: 0.25rem;
}


/* Desktop: the pop-out shows on hover of the bell OR the panel itself, and lingers a
   beat after the mouse leaves so brushing past the gap (or a small mis-move) doesn't
   snap it shut. A click does nothing - hover is the whole interaction. */
@media (hover: hover) {
    .notification-panel {
        opacity: 0;
        visibility: hidden;
        transform: translateY(-0.25rem);
        transition: opacity 0.15s ease, transform 0.15s ease, visibility 0.15s ease;
        transition-delay: 0.45s;   /* linger before hiding */
    }
@media (hover: hover) { .notification-wrapper:hover .notification-panel, .notification-panel:hover {
        opacity: 1;
        visibility: visible;
        transform: translateY(0);
        transition-delay: 0s;       /* appear immediately */
    } }

}

/* Touch: no hover, so a tap toggles .open (see TOGGLE_NOTIFICATIONS). */
@media (hover: none) {
    .notification-panel { display: none; }

    .notification-panel.open { display: block; }

}

.notification-item {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: 0.5rem;
    padding: 0.5rem 0.625rem;
    border-radius: var(--radius-small);
    font-size: 0.8125rem;
}


.notification-item + .notification-item {
    border-top: 1px solid var(--border);
}


.notification-item .notification-message {
    flex: 1;
    word-break: break-word;
}


.notification-item .notification-age {
    flex-shrink: 0;
    font-size: 0.6875rem;
    color: var(--light-text);
    font-variant-numeric: tabular-nums;
}


.notification-empty {
    padding: 0.75rem 0.625rem;
    font-size: 0.8125rem;
    color: var(--light-text);
    text-align: center;
}


/* ==================== Messages ==================== */
.message {
    margin-bottom: var(--spacing-md);
    max-width: 75%;
    padding: var(--spacing-sm) var(--spacing-md);
    border-radius: var(--radius-medium);
    box-shadow: 0 1px 0.125rem var(--shadow);
    position: relative;
    animation: fadeIn 0.3s forwards;
    box-sizing: border-box;
    word-wrap: break-word;
    overflow-wrap: break-word;
}


.message.user {
    align-self: flex-end;
    background-color: var(--user-message-bg);
}


.message.assistant {
    align-self: flex-start;
    background-color: var(--assistant-message-bg);
    border: 1px solid var(--border);
}


.message:last-child {
    margin-bottom: var(--spacing-lg);
}


.message-header {
    display: flex;
    align-items: center;
    gap: 0.375rem;
    margin-bottom: 0.25rem;
    font-size: 0.875rem;
    font-weight: 600;
}


.thinking-status {
    font-weight: normal;
    color: var(--light-text);
    font-style: italic;
}


/* Muted footnote under a message (e.g. the Print reward shown alongside the
   answer rather than as its own bubble). */
.message-caption {
    margin-top: 0.25rem;
    font-size: 0.72rem;
    color: var(--light-text);
    opacity: 0.75;
    letter-spacing: 0.02em;
}


/* Loop mode: the want->need score slider - the human signal under a looped
   output. Continuous control instead of a binary vote. */
.joke-score {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    margin-top: 0.5rem;
}


.joke-score-end {
    font-size: 0.65rem;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    opacity: 0.7;
}


.joke-slider {
    flex: 1 1 auto;
    min-width: 0;
    accent-color: var(--accent);
    cursor: pointer;
}


.joke-score-val {
    font-size: 0.7rem;
    font-weight: bold;
    color: var(--accent);
    min-width: 2.4em;
    text-align: right;
    font-variant-numeric: tabular-nums;
}


.error-header {
    color: #cc0000;
    font-weight: 600;
}


[data-theme="dark"] .error-header {
    color: #ff6666;
}


.message.user .message-header {
    color: #1a5a1a;
}


.message.assistant .message-header {
    color: #333333;
}


[data-theme="dark"] .message.user .message-header {
    color: #a8d5a8;
}


[data-theme="dark"] .message.assistant .message-header {
    color: #b0b0b0;
}


.message-content {
    word-wrap: break-word;
    white-space: pre-wrap;
}


.message.user .message-content {
    color: #1a1a1a;
}


.message.assistant .message-content {
    color: #333333;
    line-height: 1.7;
}


[data-theme="dark"] .message.user .message-content {
    color: #ffffff;
}


[data-theme="dark"] .message.assistant .message-content {
    color: #e0e0e0;
}


.reroll-button {
    position: absolute;
    top: 0.625rem;
    right: 0.625rem;
    background: var(--background);
    border: 1px solid var(--border);
    border-radius: var(--radius-small);
    padding: 0.375rem 0.625rem;
    cursor: pointer;
    font-size: 0.75rem;
    color: var(--light-text);
    opacity: 0;
    transition: opacity 0.2s, background-color 0.2s;
    display: flex;
    align-items: center;
    gap: 0.25rem;
}
@media (hover: hover) { .reroll-button:hover {
    background-color: var(--user-message-bg);
    color: var(--text);
} }
@media (hover: hover) { .message.assistant:last-of-type:hover .reroll-button {
    opacity: 1;
} }


.dots {
    display: inline-flex;
    gap: 0.25rem;
    margin-left: 0.8rem;
}


.dot {
    width: 0.5rem;
    height: 0.5rem;
    border-radius: var(--radius-round);
    background-color: var(--primary);
    opacity: 0.7;
}


.dot:nth-child(1) { animation: bounce 1.2s infinite 0s; }

.dot:nth-child(2) { animation: bounce 1.2s infinite 0.2s; }

.dot:nth-child(3) { animation: bounce 1.2s infinite 0.4s; }


/* ==================== Tabs ==================== */
.tab-nav {
    display: flex;
    gap: 0.625rem;
    padding: 0.3125rem 1.25rem 0.625rem 1.25rem;
    border-bottom: 1px solid var(--border);
    background-color: var(--background);
    position: relative;
    justify-content: space-between;
    align-items: center;
}


.tab-buttons {
    display: flex;
    gap: 0.625rem;
    min-width: 0;
}


.tab-button {
    padding: 0.625rem 1rem;
    background: var(--surface);
    border: none;
    color: var(--light-text);
    cursor: pointer;
    font-size: 0.875rem;
    font-weight: 500;
    border-radius: var(--radius-small);
    transition: all 0.2s;
    position: relative;
    white-space: nowrap;
    flex: 0 1 auto;
    min-width: 0;
    display: flex;
    align-items: center;
    justify-content: center;
}
@media (hover: hover) { .tab-button:hover {
    background-color: rgba(0, 0, 0, 0.05);
    color: var(--text);
} }
@media (hover: hover) { [data-theme="dark"] .tab-button:hover {
    background-color: rgba(255, 255, 255, 0.1);
} }


.tab-button.active {
    color: var(--primary-dark);
    background-color: var(--user-message-bg);
}


/* Beautiful glowing tab effect (dark mode) */
[data-theme="dark"] .tab-button.active {
    color: #FFFFFF;
    background-color: rgba(255, 255, 255, 0.08);
    text-shadow:
        0 0 0.375rem rgba(255, 255, 255, 0.4),
        0 0 0.75rem rgba(255, 255, 255, 0.2);
    animation: subtleGlow 3s ease-in-out infinite alternate;
}


[data-theme="dark"] .tab-button.active::before {
    content: '';
    position: absolute;
    top: -2px;
    left: -2px;
    right: -2px;
    bottom: -2px;
    background: radial-gradient(ellipse at center, rgba(255, 255, 255, 0.1) 0%, transparent 60%);
    border-radius: var(--radius-small);
    z-index: -1;
    opacity: 0.5;
}


.tab-button.active::after {
    content: '';
    position: absolute;
    bottom: -11px;
    left: 0;
    right: 0;
    height: 0.125rem;
    background-color: var(--primary);
}


[data-theme="dark"] .tab-button.active::after {
    background: linear-gradient(90deg,
        transparent,
        #FFFFFF 10%,
        #FFFFFF 90%,
        transparent
    );
    box-shadow:
        0 0 0.375rem rgba(255, 255, 255, 0.5),
        0 -1px 0.25rem rgba(255, 255, 255, 0.3);
}


.terminal-status {
    position: absolute;
    right: 1.25rem;
    top: calc(50% - 0.15625rem);
    transform: translateY(-50%);
    display: flex;
    align-items: center;
    z-index: 20;
    background: transparent;
    pointer-events: none;
}


.status-indicator {
    width: 0.625rem;
    height: 0.625rem;
    border-radius: var(--radius-round);
    background-color: #666;
    transition: background-color 0.3s;
    pointer-events: auto;
}


.status-indicator.connected {
    background-color: var(--accent-deep);
    box-shadow: 0 0 0.25rem rgba(24, 69, 59, 0.4);
}


/* ==================== Modal ==================== */
.settings-modal {
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background-color: rgba(0, 0, 0, 0.4);
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 1000;
    opacity: 0;
    visibility: hidden;
    transition: opacity 0.3s, visibility 0.3s;
    padding: 1.25rem;
    overflow-y: auto;
}


.settings-modal.open {
    opacity: 1;
    visibility: visible;
}


.modal-content {
    background-color: var(--background);
    padding: 1.875rem;
    border-radius: var(--radius-small);
    width: 90%;
    max-width: 31.25rem;
    max-height: 90vh;
    overflow-y: auto;
    box-shadow: 0 0.25rem 1.25rem rgba(0, 0, 0, 0.15);
    border: 1px solid var(--border);
    color: var(--text);
    margin: auto;
    position: relative;
}


[data-theme="dark"] .modal-content {
    box-shadow: 0 0.25rem 1.25rem rgba(0, 0, 0, 0.5);
}


.modal-header {
    display: flex;
    justify-content: space-between;
    margin-bottom: 1.25rem;
}


.modal-title {
    font-size: 1.125rem;
    font-weight: 600;
    color: var(--text);
}


.close-button {
    background: none;
    border: none;
    cursor: pointer;
    font-size: 1.25rem;
    color: var(--light-text);
}
@media (hover: hover) { .close-button:hover {
    color: var(--text);
} }


.form-group {
    margin-bottom: 1.25rem;
}


.form-group label {
    display: block;
    margin-bottom: 0.5rem;
    font-weight: 500;
    font-size: 0.875rem;
    color: var(--text);
}


.form-group label input[type="checkbox"] {
    width: auto;
    margin: 0;
    margin-right: 0.625rem;
    cursor: pointer;
    vertical-align: middle;
}


.form-group input:not([type="checkbox"]) {
    width: 100%;
    padding: 0.625rem 0.75rem;
    border: 1px solid var(--border);
    border-radius: var(--radius-small);
    font-size: 0.875rem;
    background-color: var(--input-bg, var(--assistant-message-bg));
    color: var(--input-text, #333333);
}


.form-group input:focus {
    outline: none;
    border-color: var(--primary);
}


.save-button, .reset-button {
    color: white;
    border: none;
    padding: 0.625rem 1rem;
    border-radius: var(--radius-small);
    font-weight: 500;
    cursor: pointer;
    transition: background-color 0.2s;
}


.save-button {
    background-color: var(--primary);
}
@media (hover: hover) { .save-button:hover {
    background-color: var(--primary-dark);
} }


.reset-button {
    background-color: #d32f2f;
}
@media (hover: hover) { .reset-button:hover {
    background-color: #b71c1c;
} }


.save-confirmation {
    display: none;
    margin-top: 0.9375rem;
    padding: 0.75rem;
    background-color: var(--primary-light);
    color: white;
    border-radius: var(--radius-small);
    text-align: center;
    font-size: 0.875rem;
    font-weight: 500;
    animation: fadeIn 0.3s ease-in;
}


.save-confirmation.show {
    display: block;
}


/* ==================== Terminal ==================== */
.terminal-container {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: flex-start;
    background-color: var(--background);
    color: var(--term-text);
    font-family: 'Cascadia Code', 'Fira Code', 'Monaco', 'Courier New', monospace;
    font-size: 0.625rem;
    line-height: 1.0;
    padding: 0.625rem;
    white-space: pre;
    position: relative;
    max-width: 100%;
    overflow-x: hidden;
    overflow-y: visible;
    /* Fill the tab so the dashboard's max-height resolves against the viewport,
       but let the dashboard inside size to its own content. */
    flex: 1 1 auto;
    min-height: 0;
}


.terminal-line {
    min-height: 1.4em;
    white-space: pre-wrap;
    word-wrap: break-word;
    background-color: transparent;
    color: var(--text);
    padding: 0.125rem 0.3125rem;
}


.dashboard-wrapper {
    max-width: 100%;
    width: 100%;
    position: relative;
    background-color: var(--term-bg);
    overflow-x: auto;
    overflow-y: hidden;
}


.dashboard-frame {
    background-color: var(--term-bg);
    color: var(--term-text);
    padding: 0.5rem;
    border-radius: var(--radius-small);
    box-shadow: 0 0.125rem 0.5rem rgba(0, 0, 0, 0.2);
    display: inline-block;
    font-family: 'Courier New', Courier, 'Lucida Console', monospace;
    font-size: 0.625rem;
    line-height: 1.2;
    letter-spacing: 0;
    font-variant-ligatures: none;
    -webkit-font-smoothing: none;
    -moz-osx-font-smoothing: auto;
    text-rendering: optimizeSpeed;
    font-feature-settings: "liga" 0, "calt" 0;
}


.dashboard-line {
    white-space: pre;
    margin: 0;
    padding: 0;
    background-color: transparent !important;
    color: inherit !important;
    font-family: inherit;
    font-size: inherit;
    line-height: inherit;
    letter-spacing: inherit;
}


/* ==================== Live Dashboard ==================== */
.live-dashboard {
    font-family: 'Cascadia Code', 'Fira Code', 'Courier New', monospace;
    font-size: 0.8125rem;
    line-height: 1.5;
    background-color: var(--term-bg);
    color: var(--term-text);
    padding: 1rem;
    border-radius: var(--radius-small);
    width: 100%;
    box-sizing: border-box;
    /* Flex column so the header (+system/logs) and footer stay pinned while the
       body (context blocks + sparkline) flexes to fill and scrolls internally. */
    display: flex;
    flex-direction: column;
    /* Grow to fit content, but never past the viewport - then the body scrolls
       internally while the header and footer stay pinned. */
    max-height: 100%;
    min-height: 360px;
}


.ld-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.5rem;
    border-bottom: 1px solid var(--term-border);
    padding-bottom: 0.5rem;
    margin-bottom: 0.75rem;
    flex-shrink: 0;   /* pinned */
}


.ld-header-left {
    color: var(--term-dim);
}


.ld-hash {
    color: var(--term-fg-strong);
    font-weight: bold;
}


.ld-label {
    color: var(--term-dim3);
}


.ld-header-metrics {
    display: flex;
    gap: 1rem;
    flex-wrap: wrap;
}


.ld-metric {
    color: var(--term-dim);
    font-size: 0.75rem;
    text-transform: uppercase;
}


.ld-val {
    color: var(--primary-light);
    font-weight: bold;
}


.ld-body {
    /* Every block (context, loss, or future) is a half-width cell paired two-by-two.
       Rows are a bounded height (blocks don't stretch to full height); the body fills
       between the pinned header and footer and scrolls if there are more than two rows. */
    display: grid;
    grid-template-columns: 1fr 1fr;
    grid-auto-rows: 11rem;
    align-content: start;
    gap: 0.75rem;
    margin-bottom: 0.75rem;
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    scrollbar-width: none;
}


.ld-body::-webkit-scrollbar {
    display: none;
}


.ld-panel {
    border: 1px solid var(--term-border-soft);
    border-radius: 4px;
    padding: 0.5rem;
    background: var(--term-panel);
    display: flex;
    flex-direction: column;
    align-items: stretch;
}


.ld-panel-title {
    font-size: 0.6875rem;
    color: var(--term-dim3);
    text-transform: uppercase;
    letter-spacing: 0.05em;
    margin-bottom: 0.375rem;
    border-bottom: 1px solid var(--term-border-faint);
    padding-bottom: 0.25rem;
    flex-shrink: 0;
}


/* Each block is a grid cell (half-width); its text scrolls within the bounded cell. */
.ld-context {
    min-height: 0;
    overflow: hidden;
}


.ld-context .ld-panel-title {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: 0.5rem;
}


.ld-context-meta {
    color: var(--term-bright);
    text-transform: none;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}


.ld-meta-sep {
    color: var(--term-dim3);
    margin: 0 0.4em;
}


.ld-meta-tok {
    color: var(--term-dim);
}


.ld-context-desc {
    font-size: 0.625rem;
    color: var(--term-dim2);
    margin-bottom: 0.25rem;
    flex-shrink: 0;
}


.ld-status-text {
    white-space: pre-wrap;
    word-wrap: break-word;
    overflow-y: auto;
    flex: 1 1 0;
    min-height: 0;
    font-size: 0.625rem;
    color: var(--term-fg);
    line-height: 1.4;
    scrollbar-width: none;
}


.ld-status-text::-webkit-scrollbar {
    display: none;
}


/* The loss sparkline is just another half-width block in the 2-by-2 grid. */
.ld-panel-chart {
    display: flex;
    flex-direction: column;
}


.ld-sparkline-canvas {
    width: 100%;
    flex: 1 1 0;
    min-height: 0;
}


.ld-info-panel {
    border: 1px solid var(--term-border-soft);
    border-radius: 4px;
    padding: 0.5rem;
    background: var(--term-panel);
    margin-bottom: 0.75rem;
    display: flex;
    flex-direction: column;
    align-items: stretch;
    flex-shrink: 0;   /* pinned */
}


/* Logs view tints the card black (the terminal log surface), keeping the card's
   border, padding, and tab header so the switcher stays in place. */
.ld-info-panel--logs {
    background: var(--term-panel-alt);
}


/* System panel title carries the LOGS toggle, floated to its right. */
.ld-info-panel .ld-panel-title {
    display: flex;
    justify-content: space-between;
    align-items: center;
}


.ld-info-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
    gap: 0.125rem 1rem;
}


.ld-info-grid[hidden] {
    display: none;
}


/* Heavy (wrapping) rows begin a fresh grid row at the left, so the leftover
   single-line metrics fill in to their right rather than stranding the tall
   wrapping cell on the right of the bottom row. */
.ld-info-row--heavy {
    grid-column: 1;
}


.ld-info-row {
    display: flex;
    justify-content: space-between;
    gap: 0.5rem;
    align-items: baseline;
    font-size: 0.6875rem;
    padding: 0.125rem 0;
    /* Override the terminal-container's white-space:pre so long values wrap. */
    white-space: normal;
}


.ld-info-key {
    color: var(--term-dim2);
    text-transform: uppercase;
    flex: 0 0 auto;       /* the key keeps its width, pinned left */
    white-space: nowrap;
}


.ld-info-val {
    color: var(--term-fg-strong);
    font-weight: bold;
    /* Let a long value (e.g. multiple comma-joined policies) shrink and wrap
       within its column, breaking mid-token if needed, instead of overflowing. */
    min-width: 0;
    text-align: right;
    overflow-wrap: anywhere;
    word-break: break-word;
}


/* SYSTEM / LOGS card switcher - tabs styled like the Gymnasium tool toggles. */
.ld-card-tabs {
    display: flex;
    gap: 0.375rem;
    margin-bottom: 0.375rem;
}


.ld-card-tab {
    background: none;
    border: 1px solid var(--term-border);
    border-radius: var(--radius-small);
    color: var(--term-dim);
    font-family: inherit;
    font-size: 0.75rem;
    font-weight: bold;
    padding: 0.375rem 0.875rem;
    cursor: pointer;
    letter-spacing: 0.03em;
}
@media (hover: hover) { .ld-card-tab:hover {
    border-color: var(--accent);
    color: var(--term-fg-strong);
} }


/* Active tab follows the brand accent, matching .tool-toggle.active: accent in
   the border + faint fill, neutral text. */
.ld-card-tab.active {
    background: hsl(var(--accent-hue) var(--accent-sat) var(--accent-lum) / 0.15);
    border-color: var(--accent);
    color: var(--term-fg-strong);
}


.ld-log-count {
    font-size: 0.625rem;
    color: var(--term-faint);
}


/* Plain content inside the grey panel - no nested box. Mirrors how the SYSTEM
   grid sits directly in the card, so the tabs stay in the card header in both
   views and the log view is just the card's body. */
.ld-log-view[hidden] {
    display: none;
}


.ld-log-panel-title {
    display: flex;
    justify-content: space-between;
    align-items: center;
    font-size: 0.6875rem;
    color: var(--term-dim3);
    text-transform: uppercase;
    letter-spacing: 0.05em;
    padding: 0 0 0.1875rem 0;
    border-bottom: 1px solid var(--term-border-faint);
}


.ld-log-content {
    max-height: 200px;
    overflow-y: auto;
    padding: 0.1875rem 0 0 0;
}


.ld-log-line {
    font-size: 0.6875rem;
    line-height: 1.4;
    color: var(--term-dim);
    white-space: pre-wrap;
    word-wrap: break-word;
}


.ld-footer {
    border-top: 1px solid var(--term-border);
    padding-top: 0.5rem;
    font-size: 0.6875rem;
    color: var(--term-dim);
    display: flex;
    flex-wrap: wrap;
    gap: 0.25rem;
    align-items: center;
    flex-shrink: 0;   /* pinned */
}


.ld-footer-sep {
    color: var(--term-border);
}


.ld-footer-item {
    white-space: nowrap;
}


.ld-url {
    color: var(--primary-light);
}


/* ==================== Spec Tab ==================== */
.spec-section {
    margin-bottom: 1.875rem;
    max-width: 100%;
    overflow-wrap: break-word;
}


.spec-title {
    font-size: 1.125rem;
    font-weight: 600;
    color: var(--text);
    margin-bottom: 0.75rem;
}


.spec-metadata {
    font-size: 0.875rem;
    color: var(--light-text);
    margin-bottom: 0.5rem;
    font-family: 'Inter', sans-serif;
}


.spec-hash {
    font-family: 'Cascadia Code', 'Fira Code', monospace;
    font-size: 0.8125rem;
    margin-bottom: 1rem;
}


.spec-hash-rest {
    color: var(--text);
}


.spec-code {
    background-color: #f5f5f5;
    border: 1px solid var(--border);
    border-radius: var(--radius-small);
    padding: 1rem;
    font-family: 'Cascadia Code', 'Fira Code', monospace;
    font-size: 0.8125rem;
    color: #333;
    white-space: pre;
    overflow-x: auto;
    overflow-y: hidden;
    width: 100%;
    max-width: 100%;
    box-sizing: border-box;
    -webkit-overflow-scrolling: touch;
}


[data-theme="dark"] .spec-code {
    background-color: #1e1e1e;
    color: #e0e0e0;
}


/* One-click copy for long, hard-to-select JSON (Identity tab). Rides the
   top-right of the scrollable content (sticky, so it stays put as the JSON
   scrolls beneath it) and fades in on hover - always visible on touch
   (responsive.css). The negative bottom margin lets the content flow up under
   it instead of reserving a blank row. */
.spec-copy-btn {
    position: sticky;
    top: 0.5rem;
    float: right;
    z-index: 3;
    margin: 0 0 -2rem 0.5rem;
    padding: 0.25rem 0.6rem;
    font-family: inherit;
    font-size: 0.7rem;
    border: 1px solid var(--border);
    border-radius: var(--radius-small);
    background: var(--background);
    color: var(--text);
    cursor: pointer;
    opacity: 0;
    transition: opacity 0.15s, border-color 0.15s;
}
.spec-copy-btn:focus-visible {
    opacity: 1;
}
@media (hover: hover) { .chart-card.has-copy:hover .spec-copy-btn {
    opacity: 1;
} }
@media (hover: hover) { .spec-copy-btn:hover {
    border-color: var(--accent);
} }


.spec-code-step {
    margin-bottom: 1rem;
}


.spec-steps {
    margin-bottom: 1rem;
}


.spec-peer-button {
    float: right;
    margin-bottom: 1rem;
    position: relative;
}


.spec-link {
    color: var(--accent);
    font-weight: 600;
    text-decoration: none;
}
@media (hover: hover) { .spec-link:hover {
    text-decoration: underline;
} }


.spec-link-primary {
    color: var(--accent);
    font-weight: 600;
    text-decoration: none;
}


.spec-value-highlight {
    color: var(--accent);
    font-weight: 600;
}


/* ==================== Iframe Tab ==================== */
.iframe-container {
    padding: 0 !important;
    overflow: hidden !important;
}


.iframe-content {
    width: 100%;
    height: 100%;
    border: none;
    display: block;
}


/* ==================== Agents Tab ==================== */
.agents-section {
    margin-bottom: 1.875rem;
    max-width: 100%;
}


.agents-title {
    font-size: 1.125rem;
    font-weight: 600;
    color: var(--text);
    margin-bottom: 0.75rem;
}


.agents-table {
    width: 100%;
    max-width: 100%;
    background-color: var(--assistant-message-bg);
    border-radius: var(--radius-small);
    overflow: hidden;
    box-shadow: 0 0.125rem 0.625rem var(--shadow);
}


.agents-list {
    padding: 0;
}


.agent-row {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 1rem 1.25rem;
    border-bottom: 1px solid var(--border);
    transition: background-color 0.2s;
    width: 100%;
    max-width: 100%;
    box-sizing: border-box;
}
@media (hover: hover) { .agent-row:hover {
    background-color: rgba(0, 0, 0, 0.03);
} }
@media (hover: hover) { [data-theme="dark"] .agent-row:hover {
    background-color: rgba(255, 255, 255, 0.05);
} }


.agent-row:last-child {
    border-bottom: none;
}


.agent-info {
    flex: 1;
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
}


.agent-name {
    font-size: 1rem;
    font-weight: 500;
    color: var(--text);
}


.agent-url {
    font-size: 0.8125rem;
    color: var(--light-text);
    font-family: 'Cascadia Code', 'Fira Code', monospace;
    word-break: break-all;
}


/* Expert metric line: pipe-separated chips, each (with its trailing separator)
   an unbreakable unit so a narrow screen wraps whole metrics instead of chopping
   one mid-word. */
.agent-metrics {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    column-gap: 0.4rem;
    row-gap: 0.15rem;
    font-size: 0.8125rem;
    color: var(--light-text);
    font-family: 'Cascadia Code', 'Fira Code', monospace;
}


.agent-metric {
    white-space: nowrap;
}


/* Pipe separator hangs off each chip (except the last); the container column-gap
   spaces the next chip, so the pipe never starts a wrapped line and disappears
   at the end of a row. */
.agent-metric:not(:last-child)::after {
    content: '|';
    margin-left: 0.4rem;
    color: var(--border);
}


.agent-status {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.375rem 0.75rem;
    border-radius: var(--radius-medium);
    font-size: 0.8125rem;
    font-weight: 500;
    flex-shrink: 0;        /* the badge keeps its size; the info column wraps */
    white-space: nowrap;
}


/* Badge background + text color and the dot color are set inline per-agent by
   renderStatusBadge (status color shaded by commit freshness) - the single
   source of truth. The status classes below only carry the dot's pulse motion,
   which signals "live" and can't be expressed inline. */

.status-dot {
    width: 0.5rem;
    height: 0.5rem;
    border-radius: var(--radius-round);
}


.status-dot.online {
    animation: pulse 2s infinite;
}


.status-dot.ambiguous {
    animation: pulse-harsh 1.5s infinite linear;
}


.agents-empty {
    padding: 2.5rem 1.25rem;
    text-align: center;
    color: var(--light-text);
}


/* ==================== Stage: Swarm Contracts ==================== */

/* The CONTRACTS toggle is solid green (the brand accent) by default, and
   collapses to a neutral grey chip once active/open. Inherits the generic
   .tab-header-button look at rest; the active rule overrides it to grey. */
.contracts-toggle.active {
    background: none !important;
    border: 1px solid var(--border);
    color: var(--light-text) !important;
    transform: none;
}
@media (hover: hover) { .contracts-toggle.active:hover {
    border-color: var(--accent);
    color: var(--text) !important;
    background: none !important;
} }


/* Slide-out contracts panel: collapsed by default, expands above the fleet list
   when CONTRACTS is pressed. The 0fr->1fr grid row animates height without a
   hardcoded max-height. */
.contracts-panel {
    display: grid;
    grid-template-rows: 0fr;
    transition: grid-template-rows 0.28s ease, margin-bottom 0.28s ease, opacity 0.2s ease;
    opacity: 0;
    margin-bottom: 0;
}


.contracts-panel.open {
    grid-template-rows: 1fr;
    opacity: 1;
    margin-bottom: 1.5rem;
}


.contracts-panel-inner {
    overflow: hidden;   /* required for the grid-row collapse to clip content */
    min-height: 0;
}


.contracts-panel-label {
    font-size: 0.8125rem;
    font-weight: 600;
    color: var(--light-text);
    margin-bottom: 0.625rem;
}


.contracts-list {
    background-color: var(--assistant-message-bg);
    border-radius: var(--radius-small);
    overflow: hidden;
    box-shadow: 0 0.125rem 0.625rem var(--shadow);
}


.contract-row {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 1rem;
    padding: 1rem 1.25rem;
    border-bottom: 1px solid var(--border);
}


.contract-row:last-child {
    border-bottom: none;
}


.contract-info {
    flex: 1;
    display: flex;
    flex-direction: column;
    gap: 0.375rem;
}


.contract-name {
    font-size: 1rem;
    font-weight: 600;
    color: var(--text);
}


.contract-guarantee {
    font-size: 0.8125rem;
    font-weight: 500;
    color: var(--accent);
}


.contract-desc {
    font-size: 0.8125rem;
    line-height: 1.45;
    color: var(--light-text);
    max-width: 46rem;
}


.contract-agree-btn {
    flex-shrink: 0;
    padding: 0.5rem 1.25rem;
    background: var(--accent);
    color: white;
    border: none;
    border-radius: var(--radius-small);
    font-size: 0.8125rem;
    font-weight: 700;
    letter-spacing: 0.05em;
    cursor: pointer;
    transition: filter 0.2s, transform 0.1s;
}
@media (hover: hover) { .contract-agree-btn:hover {
    filter: brightness(1.1);
} }


.contract-agree-btn:active {
    transform: translateY(1px);
}


/* "browser" tag next to a ship's name in the fleet list. */
.agent-kind-tag {
    font-size: 0.6875rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--accent);
    border: 1px solid var(--accent);
    border-radius: var(--radius-small);
    padding: 0.05rem 0.35rem;
    margin-left: 0.4rem;
    vertical-align: middle;
}


/* OBSERVE is an active status (passively training on real backend batches), so
   its dot pulses like online to read as "live". IDLE shows no pulse - a calm
   neutral. Colors for both come from the inline freshness style. */
.status-dot.observe {
    animation: pulse 2s infinite;
}


/* Spawned-agent status doubles as a SEVER button: shows the status at rest,
   flips to red SEVER on hover/focus (clicking tears down the connection). */
.agent-sever {
    position: relative;
    font-family: inherit;
    border: none;
    cursor: pointer;
    transition: background-color 0.15s, color 0.15s;
}


.agent-sever .agent-sever-label {
    display: none;
    font-weight: 700;
    letter-spacing: 0.05em;
}
/* `.armed` mirrors hover for touch devices: the first tap arms (reveals SEVER), .agent-sever:focus-visible, .agent-sever.armed {
    background-color: rgba(204, 0, 0, 0.12);
    color: #cc0000;
    outline: none;
}
@media (hover: hover) { the second confirms (see SEVER_AGENT in actions.js). */
.agent-sever:hover {
    background-color: rgba(204, 0, 0, 0.12);
    color: #cc0000;
    outline: none;
} }
[data-theme="dark"] .agent-sever:focus-visible, [data-theme="dark"] .agent-sever.armed {
    background-color: rgba(204, 0, 0, 0.2);
    color: #ff5b5b;
}
@media (hover: hover) { [data-theme="dark"] .agent-sever:hover {
    background-color: rgba(204, 0, 0, 0.2);
    color: #ff5b5b;
} }
/* On hover/focus/armed, .agent-sever:focus-visible .status-dot, .agent-sever.armed .status-dot, .agent-sever:focus-visible .agent-status-label, .agent-sever.armed .agent-status-label {
    display: none;
}
@media (hover: hover) { swap the status (dot + label) for the SEVER label. */
.agent-sever:hover .status-dot, .agent-sever:hover .agent-status-label {
    display: none;
} }
.agent-sever:focus-visible .agent-sever-label, .agent-sever.armed .agent-sever-label {
    display: inline;
}
@media (hover: hover) { .agent-sever:hover .agent-sever-label {
    display: inline;
} }


/* ==================== Tab Headers (Generic & Reusable) ==================== */

/* Remove top padding from containers that have headers */
.tab-container:has(.tab-header) {
    padding-top: 0 !important;
}


.tab-header {
    position: sticky;
    top: 0;
    z-index: 10;
    background: var(--background);
    padding: 0.75rem 0 0.75rem 0;
    margin-bottom: 1.5rem;
    max-width: 100%;
    border-bottom: 1px solid var(--border);
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.02);
}


[data-theme="dark"] .tab-header {
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
}


.tab-header-title-row {
    display: flex;
    justify-content: space-between;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.75rem;
}


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


.tab-header-button {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.5rem 1rem;
    background: var(--primary);
    color: white;
    border: none;
    border-radius: 0.375rem;
    font-size: 0.9rem;
    font-weight: 500;
    cursor: pointer;
    transition: background 0.2s, transform 0.1s;
}
@media (hover: hover) { .tab-header-button:hover {
    background: var(--primary-light);
    transform: translateY(-1px);
} }


.tab-header-button svg {
    width: 16px;
    height: 16px;
}


/* Harmonic sonification control: a small "listen" pill floated into a snapshot
   card's title. Additive playback of the harmonic field's spectrum (Web Audio). */
.harmonic-listen-btn {
    float: right;
    margin-left: 0.5rem;
    padding: 0.1rem 0.55rem;
    font-family: inherit;
    font-size: 0.7rem;
    color: var(--light-text);
    background: transparent;
    border: 1px solid var(--border-color);
    border-radius: var(--radius-small, 4px);
    cursor: pointer;
    transition: border-color 0.15s, color 0.15s, background 0.15s;
}
@media (hover: hover) { .harmonic-listen-btn:hover {
    color: var(--accent);
    border-color: var(--accent);
} }

.harmonic-listen-btn.playing {
    color: var(--accent);
    border-color: var(--accent);
    background: color-mix(in srgb, var(--accent) 16%, transparent);
    animation: harmonic-listen-pulse 1.1s ease-in-out infinite;
}

@keyframes harmonic-listen-pulse {
    0%, 100% { opacity: 1; }
    50% { opacity: 0.55; }
}

.tab-header-metadata {
    display: flex;
    flex-wrap: wrap;
    gap: 1.5rem;
    font-size: 0.9rem;
    color: var(--light-text);
}


/* ==================== Research Tab ==================== */
/* Note: Research now uses generic .tab-header instead */

.training-badge {
    background: var(--primary-light);
    color: white;
    padding: 0.2rem 0.6rem;
    border-radius: 0.75rem;
    font-size: 0.75rem;
    font-weight: 600;
}


.chart-card {
    background: var(--assistant-message-bg);
    border: 1px solid var(--border);
    border-radius: var(--radius-small);
    padding: 1.25rem;
    box-shadow: 0 0.125rem 0.25rem var(--shadow);
    transition: box-shadow 0.2s;
    width: 100%;
    max-width: 100%;
    overflow: hidden;
    box-sizing: border-box;
}
@media (hover: hover) { .chart-card:hover {
    box-shadow: 0 0.25rem 0.75rem rgba(0,0,0,0.08);
} }


[data-theme="dark"] .chart-card {
    background: #1e1e1e;
}


/* Deck cards: the body fill lives on a ::before so the title row can be masked
   to ~78% opacity - the card behind bleeds through faintly at the title (an
   orientation aid for the stack) while the chart body stays fully opaque and
   the title text on top stays crisp. Only applies inside a deck. */
.chart-deck .chart-card {
    background: transparent;
}

.chart-deck .chart-card::before {
    content: '';
    position: absolute;
    inset: 0;
    z-index: 0;
    border-radius: inherit;
    background: var(--assistant-message-bg);
    -webkit-mask-image: linear-gradient(to bottom, rgba(0,0,0,0.78), rgba(0,0,0,0.78) 3rem, #000 3.3rem);
            mask-image: linear-gradient(to bottom, rgba(0,0,0,0.78), rgba(0,0,0,0.78) 3rem, #000 3.3rem);
}

[data-theme="dark"] .chart-deck .chart-card::before {
    background: #1e1e1e;
}

.chart-deck .chart-card > * {
    position: relative;
    z-index: 1;
}


/* Every deck card scrolls its own body. JS caps each card to the viewport (desktop
   + mobile) so the deck never grows an outer scrollbar; tall content scrolls here. */
.deck-card-scroll {
    overflow-y: auto;
    overscroll-behavior: contain;
}

/* Fan-down sheets (Identity) fade the scroll edges to signal there's more; chart
   cards skip the fade so chart axes/labels aren't clipped. Heights are JS-capped. */
.deck-fan .deck-card-scroll {
    -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 1.25rem, #000 calc(100% - 1.25rem), transparent 100%);
            mask-image: linear-gradient(to bottom, transparent 0, #000 1.25rem, #000 calc(100% - 1.25rem), transparent 100%);
}

/* Card-switching is the ONLY outer navigation: the deck and its scroll region never
   scroll as a whole (no page/outer scrollbar) - tall cards scroll their own body.
   Applies on every viewport (the deck is JS-capped to fit). */
.tab-content.active:has(.chart-deck),
.tab-container:has(.chart-deck) {
    overflow: hidden;
}


/* Mobile gesture surface (created by initChartDeck): spans the band from the
   head card's top to the floor so swipes beside/below short compact cards
   still drive the deck. Inert on desktop; activated in responsive.css. */
.deck-gesture-pad {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    pointer-events: none;
}


/* Card-switcher carousel: chart cards are stacked in place, the active one on
   top at full size with the next few peeking out behind it. Cycling advances
   the deck in place - the page never grows. JS sets per-card transforms and
   the deck height (so compact cards stay compact). */
.chart-deck {
    position: relative;
    width: 100%;
    margin: 0 auto;
    /* The deck owns the vertical gesture (drag/momentum cycling); no native
       scroll or pull-to-refresh interferes. */
    touch-action: none;
    /* Keep the cards' z-indices in their own stacking context. */
    isolation: isolate;
    /* Hidden until the cards are measured + positioned (and charts mounted), so the
       unpositioned card shape never flashes at the top. JS adds .deck-ready to fade
       the stack in once it's laid out in its final place. */
    opacity: 0;
    transition: opacity 0.25s ease;
}


.chart-deck.deck-ready {
    opacity: 1;
}


.chart-deck .chart-card {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    /* top origin so scaled-down upcoming cards keep their title strip aligned
       with the raised-stack offset (rolodex preview from the top). */
    transform-origin: top center;
    /* Motion is driven frame-by-frame from JS (drag + momentum + lift); cards
       never CSS-transition. */
    transition: none;
    will-change: transform, opacity;
    backface-visibility: hidden;
}


/* Per-card index, printed on each card's top-right corner (rides with the
   card). Replaces the old single floating deck counter, which is retired. */
.chart-deck-counter {
    display: none;
}

/* Specific enough to beat `.chart-deck .chart-card > *` (which forces children
   to position:relative); otherwise the number falls into flow as a full-width
   block instead of a corner pill. */
.chart-deck .chart-card .chart-card-number {
    position: absolute;
    top: 0.6rem;
    right: 0.6rem;
    z-index: 2;
    padding: 0.15rem 0.55rem;
    border-radius: 0.75rem;
    font-size: 0.7rem;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    color: var(--light-text);
    background: var(--background);
    border: 1px solid var(--border);
    pointer-events: none;
}

.chart-deck-counter-unused {
    position: absolute;
    top: 0.5rem;
    right: 0.5rem;
    z-index: 9999;
    padding: 0.15rem 0.55rem;
    border-radius: 0.75rem;
    font-size: 0.7rem;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    color: var(--light-text);
    background: var(--background);
    border: 1px solid var(--border);
    pointer-events: none;
}


.chart-title {
    font-size: 1.2rem;
    font-weight: 600;
    margin-bottom: 0.5rem;
    color: var(--text);
}


/* General guard for the recurring "card-number pill overlaps title content" bug.
   The number is an absolute pill in the top-right of every deck card; reserve a
   safe zone on the right of the title so its text wraps early and any floated
   control (e.g. the harmonic listen button) sits to the LEFT of the counter
   instead of underflowing it. Fix once here, not per-card. */
:root { --deck-counter-safe: 3.25rem; }

.chart-deck .chart-card .chart-title {
    padding-right: var(--deck-counter-safe);
}


.chart-subtitle {
    font-size: 0.875rem;
    color: var(--light-text);
    margin-bottom: 1rem;
}


.chart-caller {
    font-size: 0.8rem;
    font-weight: 400;
    color: var(--light-text);
}


.empty-state, .error-message {
    text-align: center;
    padding: 4rem 2rem;
}


.empty-state h3, .error-message h3 {
    font-size: 1.5rem;
    margin-bottom: 0.5rem;
    color: var(--text);
}


.empty-state p, .error-message p {
    color: var(--light-text);
}


.retry-button {
    margin-top: 1rem;
    padding: 0.5rem 1.5rem;
    background: var(--primary);
    color: white;
    border: none;
    border-radius: 0.25rem;
    cursor: pointer;
    font-size: 0.9rem;
    transition: background 0.2s;
}
@media (hover: hover) { .retry-button:hover {
    background: var(--primary-light);
} }


.run-selector-wrapper {
    position: relative;
}


.run-selector-button {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.5rem 1rem;
    background: var(--background);
    color: var(--text);
    border: 1px solid var(--border);
    border-radius: 0.375rem;
    font-size: 0.9rem;
    font-weight: 500;
    cursor: pointer;
    transition: all 0.2s;
}
@media (hover: hover) { .run-selector-button:hover {
    border-color: var(--primary);
    background: var(--primary-light);
    color: white;
} }


[data-theme="dark"] .run-selector-button {
    border-color: #333;
}


.run-selector-dropdown {
    position: absolute;
    top: calc(100% + 0.5rem);
    right: 0;
    background: var(--assistant-message-bg);
    border: 1px solid var(--border);
    border-radius: 0.5rem;
    box-shadow: 0 0.25rem 0.75rem rgba(0,0,0,0.15);
    min-width: 18.75rem;
    max-width: 25rem;
    z-index: 1000;
}


[data-theme="dark"] .run-selector-dropdown {
    background: #1e1e1e;
    box-shadow: 0 0.25rem 0.75rem rgba(0,0,0,0.5);
}


.run-selector-header {
    padding: 0.75rem 1rem;
    border-bottom: 1px solid var(--border);
    font-weight: 600;
    font-size: 0.9rem;
    color: var(--text);
}


.run-selector-list {
    max-height: 25rem;
    overflow-y: auto;
    padding: 0.5rem;
}


.run-selector-item {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    padding: 0.75rem;
    cursor: pointer;
    border-radius: 0.375rem;
    transition: background 0.15s;
}
@media (hover: hover) { .run-selector-item:hover {
    background: rgba(0, 0, 0, 0.03);
} }
@media (hover: hover) { [data-theme="dark"] .run-selector-item:hover {
    background: rgba(255, 255, 255, 0.05);
} }


.run-selector-item input[type="checkbox"] {
    width: 1rem;
    height: 1rem;
    cursor: pointer;
}


.run-color-indicator {
    width: 0.75rem;
    height: 0.75rem;
    border-radius: 50%;
    flex-shrink: 0;
}


.run-label {
    flex: 1;
    font-size: 0.85rem;
    color: var(--text);
    font-family: 'Monaco', 'Courier New', monospace;
}


.run-steps {
    font-size: 0.75rem;
    color: var(--light-text);
    font-weight: 500;
}


.chart-wrapper {
    position: relative;
    height: 25rem;
    width: 100%;
    max-width: 100%;
    overflow: hidden;
}


/* Canvases are inline by default - that leaves a descender gap under them (the
   "proportions feel slightly off") and lets a stale/DPR-scaled bitmap spill past
   the card on the right. Block + max-width pins them to their wrapper. */
.chart-card canvas,
.chart-wrapper canvas {
    display: block;
    max-width: 100%;
}


/* Scrollable custom legend used by the activation charts, where the raw
   curve count (one per depth pass, per block) blows past what Chart.js's
   built-in legend can fit without stealing chart height. Controls stay
   pinned; only the label list scrolls. */
.chart-legend {
    margin-top: 0.75rem;
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    padding: 0.5rem;
    border: 1px solid var(--border-color, #e0e0e0);
    border-radius: var(--radius-small, 4px);
    background: var(--background);
    font-size: 0.75rem;
    color: var(--text);
}


.chart-legend:empty {
    display: none;
}


.chart-legend-controls {
    display: flex;
    gap: 0.5rem;
    flex-wrap: wrap;
}


.chart-legend-control {
    padding: 0.25rem 0.6rem;
    border: 1px solid color-mix(in srgb, var(--text) 25%, transparent);
    border-radius: var(--radius-small, 4px);
    background: transparent;
    color: inherit;
    font: inherit;
    font-size: 0.7rem;
    cursor: pointer;
    transition: background 0.15s ease;
}
@media (hover: hover) { .chart-legend-control:hover {
    background: color-mix(in srgb, var(--text) 10%, transparent);
} }


.chart-legend-items {
    max-height: 9rem;
    overflow-y: auto;
    display: flex;
    flex-wrap: wrap;
    gap: 0.25rem 0.5rem;
}


.chart-legend-item {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.15rem 0.5rem;
    background: transparent;
    border: 1px solid transparent;
    border-radius: var(--radius-small, 4px);
    color: inherit;
    font: inherit;
    cursor: pointer;
    transition: background 0.15s ease, opacity 0.15s ease;
}
@media (hover: hover) { .chart-legend-item:hover {
    background: color-mix(in srgb, var(--text) 10%, transparent);
} }


.chart-legend-item.muted {
    opacity: 0.4;
    text-decoration: line-through;
}


.chart-legend-swatch {
    display: inline-block;
    width: 0.75rem;
    height: 0.75rem;
    border-radius: 2px;
    flex: 0 0 auto;
}


.chart-legend-label {
    white-space: nowrap;
}


@media (max-width: 640px) {
    .chart-legend {
        font-size: 0.7rem;
    }

    .chart-legend-items {
        max-height: 7rem;
    }

}

/* ==================== Copy Fallback Modal ==================== */
.copy-fallback-content {
    background-color: var(--background);
    padding: 1.5rem;
    border-radius: var(--radius-small);
    max-width: 500px;
    width: 100%;
    box-shadow: 0 0.25rem 1.25rem rgba(0, 0, 0, 0.3);
    border: 1px solid var(--border);
    color: var(--text);
}


.copy-fallback-content h3 {
    margin: 0 0 0.5rem 0;
    font-size: 1.2em;
    color: var(--text);
}


.copy-fallback-input {
    width: 100%;
    padding: 0.75rem;
    margin: 1rem 0;
    border: 1px solid var(--border);
    border-radius: var(--radius-small);
    background-color: var(--background-secondary);
    color: var(--text);
    font-family: monospace;
    font-size: 0.9em;
    box-sizing: border-box;
}


.copy-fallback-input:focus {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}


.copy-fallback-close {
    width: 100%;
    padding: 0.75rem;
    background: var(--accent);
    color: white;
    border: none;
    border-radius: var(--radius-small);
    font-size: 1em;
    cursor: pointer;
    font-weight: 500;
}
@media (hover: hover) { .copy-fallback-close:hover {
    background: #0a8660;
} }


.copy-fallback-close:active {
    transform: translateY(1px);
}


/* ==================== Dynamics Controls ==================== */
.dynamics-controls {
    scrollbar-width: none; /* Firefox */
    -ms-overflow-style: none; /* IE/Edge */
}


.dynamics-controls::-webkit-scrollbar {
    display: none; /* Chrome/Safari */
}


/* In-card layer filter: sits between a chart's subtitle and its canvas. */
.dynamics-layer-toggles {
    margin: 0.75rem 0 1rem;
    font-size: 0.875rem;
}


.dynamics-layer-toggles strong {
    margin-right: 0.5rem;
}


.layer-toggle-btn {
    padding: 0.375rem 0.75rem;
    margin-right: 0.5rem;
    background: var(--background);
    border: 1px solid var(--border);
    border-radius: var(--radius-small);
    font-size: 0.875rem;
    font-weight: 500;
    color: var(--light-text);
    cursor: pointer;
    transition: all 0.2s;
}
@media (hover: hover) { .layer-toggle-btn:hover {
    border-color: var(--primary);
    color: var(--text);
} }


.layer-toggle-btn.active {
    background: var(--primary);
    color: white;
    border-color: var(--primary);
}


[data-theme="dark"] .layer-toggle-btn {
    background: rgba(255, 255, 255, 0.05);
    border-color: #333;
}
@media (hover: hover) { [data-theme="dark"] .layer-toggle-btn:hover {
    background: rgba(255, 255, 255, 0.1);
} }


/* ==================== Hybrid Mode (Split Reality) ==================== */
.hybrid-overlay {
    /* Full viewport overlay */
    position: fixed !important;
    top: 0 !important;
    left: 0 !important;
    width: 100vw !important;
    height: 100vh !important;
    overflow: hidden !important;
    z-index: 998 !important;

    /* Non-interactive overlay */
    pointer-events: none !important;

    /* No transitions - we're updating 60fps */
    transition: none !important;

    /* Force identical rendering */
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    text-rendering: optimizeLegibility;

    /* Prevent subpixel shifts */
    transform: translate3d(0, 0, 0);
    backface-visibility: hidden;
}


/* Hybrid overlay inherits light theme - no custom colors needed */
/* The data-theme="light" attribute triggers all existing :root light theme rules */

/* Full-width backgrounds without changing content layout */
body.hybrid-active::before {
    content: '';
    position: fixed;
    top: 0;
    left: 0;
    width: 100vw;
    height: 100vh;
    background-color: var(--background);
    z-index: -1;
}



.hybrid-overlay[data-theme="light"] .message.user {
    background-color: #e8f5e9 !important;
}


.hybrid-overlay[data-theme="light"] .message.assistant {
    background-color: #ffffff !important;
}


/* Header and prism stay on top in hybrid mode */
body.hybrid-active .header {
    position: relative;
    z-index: 10001 !important;
    background-color: transparent !important;
}


body.hybrid-active .logo {
    background-color: transparent !important;
}


body.hybrid-active .prism-logo {
    position: relative;
    z-index: 10002 !important;
}



/* Essential hybrid overlay rules - let data-theme="light" handle colors */

/* Remove dark mode glow effects */
.hybrid-overlay[data-theme="light"] .tab-button.active::before,
.hybrid-overlay[data-theme="light"] .tab-button.active::after {
    display: none !important;
}


/* Solid backgrounds to prevent transparency */
.hybrid-overlay .app-container,
.hybrid-overlay .tab-content,
.hybrid-overlay .tab-nav {
    background-color: var(--background) !important;
}


/* Force pixel-perfect positioning on cloned elements */
.hybrid-overlay .message,
.hybrid-overlay .tab-content,
.hybrid-overlay .terminal-line {
    transform: translateZ(0);
    backface-visibility: hidden;
}


/* Chart canvases in hybrid overlay - invert to approximate light theme */
.hybrid-overlay canvas:not(#prism-canvas) {
    display: block !important;
    /* Invert dark theme chart colors to make them readable on light background */
    filter: invert(1);
}


/* Header spacer - invisible but maintains layout */
.hybrid-overlay .header-spacer-hybrid {
    background: transparent !important;
}


/* Hide any loading placeholders or empty states that create white boxes */
.hybrid-overlay .loading-placeholder {
    display: none !important;
}


/* Remove backgrounds from spacer elements */
.hybrid-overlay .header-spacer-hybrid {
    background: transparent !important;
    background-color: transparent !important;
}


/* Use consistent background throughout overlay */
.hybrid-overlay {
    background: var(--background) !important;
}


.hybrid-overlay .app-container {
    background: var(--background) !important;
}


/* Explicitly define light theme variables on hybrid overlay */
.hybrid-overlay[data-theme="light"] {
    --background: #f9f9f9;
    --text: #1f1f1f;
    --light-text: #4f4f4f;
    --user-message-bg: #e8f5e9;
    --assistant-message-bg: #ffffff;
    --border: #d0d0d0;
    --input-bg: #ffffff;
    --surface: #ffffff;
}


/* Apply background using the variable */
.hybrid-overlay[data-theme="light"] {
    background-color: var(--background) !important;
}


/* Remove all opacity/transparency from hybrid overlay and children */
.hybrid-overlay,
.hybrid-overlay *:not(#prism-canvas) {
    opacity: 1 !important;
}


/* Remove dark mode specific styling that might cause issues */
.hybrid-overlay[data-theme="light"] .tab-button.active::before,
.hybrid-overlay[data-theme="light"] .tab-button.active::after {
    display: none !important;
}

/* Comprehensive light theme fixes for hybrid overlay */

/* Input fields - white background, dark text */
.hybrid-overlay[data-theme="light"] #message-input,
.hybrid-overlay[data-theme="light"] input,
.hybrid-overlay[data-theme="light"] textarea {
    background-color: #ffffff !important;
    color: #1f1f1f !important;
    border: 1px solid #d0d0d0 !important;
}


/* Tab buttons - light theme colors */
.hybrid-overlay[data-theme="light"] .tab-button {
    background: #ffffff !important;
    color: #4f4f4f !important;
    border: 1px solid transparent !important;
}


.hybrid-overlay[data-theme="light"] .tab-button.active {
    background-color: #e8f5e9 !important;
    color: #18453B !important;
    font-weight: 500 !important;
}


/* All text should be dark in light theme */
.hybrid-overlay[data-theme="light"] {
    color: #1f1f1f !important;
}


.hybrid-overlay[data-theme="light"] .message-content {
    color: #1f1f1f !important;
}


.hybrid-overlay[data-theme="light"] .message-header {
    color: #4f4f4f !important;
}


.hybrid-overlay[data-theme="light"] .terminal-line {
    color: #1f1f1f !important;
}


/* Chart backgrounds */
.hybrid-overlay[data-theme="light"] .chart-card {
    background-color: #ffffff !important;
    border: 1px solid #d0d0d0 !important;
}


.hybrid-overlay[data-theme="light"] .chart-wrapper {
    background-color: transparent !important;
}


/* All backgrounds should use light theme color */
.hybrid-overlay[data-theme="light"] .tab-content,
.hybrid-overlay[data-theme="light"] .chat-container,
.hybrid-overlay[data-theme="light"] .terminal-container {
    background-color: #f9f9f9 !important;
}


/* Buttons */
.hybrid-overlay[data-theme="light"] button {
    background-color: transparent !important;
    color: var(--light-text) !important;
}


.hybrid-overlay[data-theme="light"] .spec-peer-button,
.hybrid-overlay[data-theme="light"] button[type="submit"] {
    background-color: var(--accent) !important;
    color: #ffffff !important;
}


/* Fix header text colors in light zone - use darker colors for visibility */
.hybrid-overlay[data-theme="light"] .system-prompt-header {
    color: #4f4f4f !important;
}


.hybrid-overlay[data-theme="light"] .settings-button,
.hybrid-overlay[data-theme="light"] .theme-toggle-button {
    color: #4f4f4f !important;
}


/* Chart text and labels - use proper light theme colors */
.hybrid-overlay[data-theme="light"] .chart-title {
    color: var(--text) !important;
}


.hybrid-overlay[data-theme="light"] .empty-state h3,
.hybrid-overlay[data-theme="light"] .error-message h3 {
    color: var(--text) !important;
}


.hybrid-overlay[data-theme="light"] .empty-state p,
.hybrid-overlay[data-theme="light"] .error-message p {
    color: var(--light-text) !important;
}


/* Business card preview (Identity tab) */
.biz-card {
    display: flex;
    flex-direction: column;
    align-items: center;
}


/* The deck card embodies the business card: full-bleed art, badge overlay. */
.biz-card-sheet {
    padding: 0.75rem;
}


.biz-card-frame {
    position: relative;
    display: flex;
    justify-content: center;
    width: 100%;
}


.biz-card-img {
    width: 96%;
    aspect-ratio: 88.9 / 50.8;
    border-radius: 8px;
    box-shadow: 0 4px 14px rgba(0, 0, 0, 0.3);
    background: var(--background);
    transition: filter 0.15s ease, opacity 0.15s ease;
    cursor: pointer;
}


.biz-card-img.loading {
    filter: grayscale(1) brightness(0.85);
    opacity: 0.55;
}


/* Action strip sits snug in the card's lower-right border region: the same
   small fixed inset from the card's bottom and right edges (px, not %, so
   the two gaps match despite the card's aspect ratio). */
.biz-card-actions {
    position: absolute;
    /* The img is 96% of the frame, centered: its right edge sits 2% in.
       Offset from that edge so both gaps measure from the card itself. */
    right: calc(2% + 6px);
    bottom: 6px;
    z-index: 2;
    display: flex;
    gap: 0.35rem;
}


.biz-btn {
    padding: 0.15rem 0.55rem;
    border: 1px solid var(--border);
    border-radius: 999px;
    background: color-mix(in srgb, var(--background) 75%, transparent);
    backdrop-filter: blur(2px);
    color: var(--text);
    font-size: 0.68rem;
    cursor: pointer;
}
@media (hover: hover) { .biz-btn:hover {
    border-color: var(--accent);
    color: var(--accent);
} }


/* Portal frames: the standard chrome for collectable-type cards. The
   border matches the business card exactly: a paper-colored mat, white in
   light theme and black in dark, 3mm of an 88.9mm card wide - so the mat
   is percentage padding (resolves against width), not a fixed border. */
.portal-frame {
    width: 96%;
    padding: 3.4%;
    border-radius: 8px;
    box-shadow: 0 4px 14px rgba(0, 0, 0, 0.3);
    background: #ffffff;
}


[data-theme="dark"] .portal-frame {
    background: #000000;
}


.portal-window {
    position: relative;
    overflow: hidden;
    border-radius: 3px;
    /* A quiet vertical wash of the theme background - the canvas paints
       its own habitat tints on top. */
    background: linear-gradient(180deg,
        color-mix(in srgb, var(--background) 95%, var(--text)) 0%,
        var(--background) 40%,
        color-mix(in srgb, var(--background) 91%, var(--text)) 100%);
}


/* The Arena (Identity tab): a 3D ecosystem the spider lives in. Pure
   card - no title, no chrome, just the portal window into the scene. */
.arena-sheet {
    padding: 0.75rem;
}


.arena-card {
    display: flex;
    justify-content: center;
}


.arena-frame .portal-window {
    aspect-ratio: 16 / 9;
    max-height: 65vh;
}


.arena-canvas {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    color: var(--text);
}


/* Debug-mode reroll sits snug in the window corner, biz-card style. */
.arena-actions {
    right: 6px;
    bottom: 6px;
}


/* display:flex on .biz-card-actions would defeat [hidden]; restore it. */
.arena-actions[hidden] {
    display: none;
}


/* Single Download button with a pop-out count picker (1 / 10). */
.biz-dl-wrap {
    position: relative;
    display: flex;
}


.biz-dl-menu {
    position: absolute;
    bottom: calc(100% + 6px);
    right: 0;
    display: flex;
    gap: 0.35rem;
}


.biz-dl-menu[hidden] {
    display: none;
}



/* ========== themes.css ========== */
/* Praxis Web - Theme System
 * Theme-specific overrides beyond variables
 * Most theming is handled via CSS variables in variables.css
 */

/* This file is intentionally minimal - most theming happens through CSS variables */
/* Add theme-specific overrides here only when CSS variables aren't sufficient */


/* ========== animations.css ========== */
/* Praxis Web - Animations
 * All keyframe animations
 */

@keyframes fadeIn {
    from { opacity: 0; transform: translateY(10px); }
    to { opacity: 1; transform: translateY(0); }
}

@keyframes fadeOut {
    from { opacity: 1; transform: translateY(0); }
    to { opacity: 0; transform: translateY(10px); }
}

@keyframes bounce {
    0%, 100% { transform: translateY(0); }
    50% { transform: translateY(-6px); }
}

@keyframes pulse {
    0% { opacity: 1; }
    50% { opacity: 0.5; }
    100% { opacity: 1; }
}

@keyframes pulse-harsh {
    0% { opacity: 1; }
    50% { opacity: 0; }
    100% { opacity: 1; }
}

/* Subtle glow animation for dark mode tabs */
@keyframes subtleGlow {
    0% {
        text-shadow:
            0 0 6px rgba(255, 255, 255, 0.4),
            0 0 12px rgba(255, 255, 255, 0.2);
    }

    100% {
        text-shadow:
            0 0 8px rgba(255, 255, 255, 0.5),
            0 0 14px rgba(255, 255, 255, 0.3);
    }

}

@keyframes spin {
    from {
        transform: rotate(0deg);
    }
    to {
        transform: rotate(360deg);
    }
}

/* First-navigation "unroll": a prefetched tab is already built, so stagger its
   deck card elements into place to restore a sense of life (replaces the
   incidental async-load reveal we lost by pre-building). One-shot per tab;
   the .deck-reveal class is added on first activation, then removed. */
@keyframes deckUnroll {
    from { opacity: 0; transform: translateY(10px); }

    to   { opacity: 1; transform: translateY(0); }

}

/* Titles/subtitles are deliberately NOT animated: the card chrome is
   precached and already painted, so text that starts hidden and fades in
   reads as "the title vanished and reloaded", not as a reveal. Only the
   card body unrolls. */
.tab-content.deck-reveal .chart-card > .deck-card-scroll {
    animation: deckUnroll 0.4s ease both;
    animation-delay: 0.12s;
}


@media (prefers-reduced-motion: reduce) {
    .tab-content.deck-reveal .chart-card > * { animation: none; }

}


/* ========== responsive.css ========== */
/* ==================== Mobile Responsive ==================== */
@media (max-width: 768px) {
    body {
        overflow-x: hidden;
    }


    /* The energy badge is icon-only, so it fits in the toolbar row; the desktop
       margin-left:auto keeps it pushed to the right edge beside the buttons. */

    .app-container {
        padding: 0 var(--tab-padding-mobile);
        overflow-x: hidden;
        max-width: 100vw;
    }


    /* Tab containers use full parent width, no horizontal padding */
    .tab-container {
        padding: var(--tab-padding-mobile) 0;
        width: 100%;
        max-width: 100%;
    }


    /* Code blocks: horizontal scroll, no line breaks (blueprints depend on format) */
    .spec-code,
    pre,
    code {
        max-width: 100%;
        overflow-x: auto;
        overflow-y: hidden;
        white-space: pre;
        word-wrap: normal;
        overflow-wrap: normal;
        box-sizing: border-box;
        -webkit-overflow-scrolling: touch;
    }


    /* No hover on touch, so the copy button is always visible. */
    .spec-copy-btn {
        opacity: 1;
    }


    /* Non-code content: wrap normally */
    .agent-url,
    .chart-card,
    .chart-wrapper,
    .agents-table,
    .message {
        max-width: 100%;
        word-wrap: break-word;
        overflow-wrap: break-word;
        box-sizing: border-box;
    }


    .header {
        padding: 0.625rem 0;
    }

    
    .logo {
        font-size: 1.125rem;
        gap: 0.5rem;
    }

    
    .logo-text,
    .logo-separator {
        display: none;
    }

    
    .tab-nav {
        padding: 0.3125rem 0.625rem 0.625rem 0.625rem;
        position: relative;
        overflow: hidden;
    }


    /* Fade indicators - extend to edges, tabs flow under status indicator */
    .tab-nav::before,
    .tab-nav::after {
        content: '';
        position: absolute;
        top: 0.3125rem;
        bottom: 0.625rem;
        width: 2rem;
        pointer-events: none;
        z-index: 10;
        opacity: 0;
        transition: opacity 0.3s;
    }


    .tab-nav::before {
        left: 0;
        background: linear-gradient(to right, var(--background), transparent);
    }


    .tab-nav::after {
        right: 0;
        background: linear-gradient(to left, var(--background), transparent);
    }


    .tab-nav.has-scroll-left::before {
        opacity: 0.9;
    }


    .tab-nav.has-scroll-right::after {
        opacity: 0.9;
    }


    .tab-buttons {
        display: flex;
        gap: 0.375rem;
        max-width: 100%;
        min-width: 0;
        /* Native momentum scroll (smooth, tile-managed - no pop-in). The endless
           loop is the buffer copies + scroll re-center in mobile.js. */
        overflow-x: auto;
        overflow-y: hidden;
        -webkit-overflow-scrolling: touch;
        scrollbar-width: none;
        -ms-overflow-style: none;
        scroll-padding-left: 0;
    }


    .tab-buttons::-webkit-scrollbar {
        display: none;
    }


    /* (No trailing runway: the strip is an endless loop with no last tab.) */

    .tab-button {
        padding: 0.625rem 0.75rem;
        font-size: 0.8125rem;
        flex: 0 0 auto;  /* Prevent buttons from shrinking */
        white-space: nowrap;  /* Keep text on one line */
    }

    
    .logo-icon {
        width: var(--icon-size);
        height: var(--icon-size);
    }

    
    .system-prompt-header {
        font-size: 0.6875rem;
        max-width: none;
        flex: 1;
        line-clamp: 2;
        -webkit-line-clamp: 2;
        line-height: 1.3;
        margin-right: 0.625rem;
    }

    
    .system-prompt-header:focus {
        max-width: none;
        line-clamp: unset;
        -webkit-line-clamp: unset;
        font-size: 0.75rem;
    }


    .message {
        max-width: 85%;
    }

    
    /* Mobile scroll model: the inner-body flex-scroll (desktop) does not reliably
       accept touch on phones. So the terminal-container is the single NATIVE
       scroll surface (touch scrolling that just works), the dashboard grows to
       its content, and the header/footer stay pinned via position:sticky. */
    .terminal-container {
        font-size: 0.5rem;
        /* No vertical padding: the sticky header/footer sit flush with the scroll
           edges, so content can't peek above the header or below the footer and
           the header doesn't jump when it sticks. */
        padding: 0;
        display: flex;
        flex-direction: column;
        /* Bounded to the tab area so it scrolls its overflowing content. */
        flex: 1 1 0%;
        min-height: 0;
        overflow-y: auto;
        overscroll-behavior: contain;  /* no rubber-band reveal past the pins */
        -webkit-overflow-scrolling: touch;
    }


    .dashboard-frame {
        padding: 0.25rem;
        font-size: 0.5rem;
        font-family: monospace, 'Courier', 'Courier New';
        max-width: 100%;
    }


    .dashboard-line {
        font-size: inherit;
        word-wrap: break-word;
        overflow-wrap: break-word;
    }


    .live-dashboard {
        font-size: 0.6875rem;
        /* Horizontal inset only - no top/bottom padding, so the header/footer are
           flush with the scroll edges (solid boundaries, no header shift). */
        padding: 0 0.5rem;
        /* Grow to content; the terminal-container scrolls it. No inner flex
           bounding (that's what blocked touch scrolling on mobile). */
        flex: 0 0 auto;
        min-height: 0;
        max-height: none;
    }


    /* Header and footer stay pinned while the dashboard scrolls beneath them -
       position:sticky against the native scroller (terminal-container). Opaque
       backgrounds and their own vertical padding form solid boundaries the
       scrolling cards can't trail past. */
    .ld-header {
        position: sticky;
        top: 0;
        z-index: 3;
        background: var(--term-bg);
        padding-top: 0.5rem;   /* breathing room (covered by the opaque bg) */
    }


    .ld-footer {
        position: sticky;
        bottom: 0;
        z-index: 3;
        background: var(--term-bg);
        padding-top: 0.4rem;
        padding-bottom: 0.5rem;
    }


    /* Single column; cards render at full height and the whole list scrolls with
       the dashboard (not an internal scroller, which mobile touch wouldn't chain). */
    .ld-body {
        grid-template-columns: 1fr;
        grid-auto-rows: 12rem;
        overflow: visible;
        flex: 0 0 auto;
        min-height: 0;
    }


    /* Each card's rolling text is its own overflow scroller. On mobile a touch
       lands on it and would be captured instead of scrolling the dashboard, so
       make the inner scrollers transparent to touch - the gesture falls through
       to the terminal-container. JS still auto-scrolls them to their latest line. */
    .ld-status-text,
    .ld-log-content {
        pointer-events: none;
    }


    /* Exception: the rolling context boxes ARE meant to be read by hand, so they
       keep their own touch scroller. No overscroll-behavior: contain here - a box
       with overflow scrolls its own text, but one with little content lets the
       swipe chain through to the page instead of swallowing it into a dead zone. */
    .ld-context .ld-status-text {
        pointer-events: auto;
        touch-action: pan-y;
        -webkit-overflow-scrolling: touch;
    }


    /* Context cards and the sparkline keep their own bounded height so text/chart
       scroll within the card and the footer never gets pushed off-screen. */
    .ld-panel-chart {
        min-height: 12rem;
    }


    .ld-info-grid {
        grid-template-columns: 1fr;
    }


    .ld-header {
        flex-direction: column;
        align-items: flex-start;
    }


    .ld-footer {
        font-size: 0.625rem;
    }


    .chart-card {
        padding: 0.65rem;
        width: 100%;
    }


    .chart-wrapper {
        height: 17rem;
        width: 100%;
    }


    .tab-header-title-row {
        flex-direction: column;
        gap: 0.75rem;
        align-items: flex-start;
    }


    .tab-header-controls {
        width: 100%;
        justify-content: flex-start;
    }


    .run-selector-dropdown {
        position: fixed;
        top: auto;
        left: 1rem;
        right: 1rem;
        min-width: 0;
        max-width: none;
        width: auto;
    }


    /* Agent rows: keep the status badge intact on the right and let the info
       column take the remaining width, wrapping whole metric chips beneath the
       name rather than colliding with the badge. Tighter padding + a gap give
       the cramped mobile width room to breathe. */
    .agent-row {
        padding: 0.75rem 0.875rem;
        gap: 0.625rem;
        align-items: flex-start;
    }


    .agent-info {
        min-width: 0;          /* allow the column to shrink so chips wrap */
    }


    .agent-status {
        margin-top: 0.125rem;  /* align with the name's cap height */
    }

}

/* Ultra-compact mode for very small screens */
@media (max-width: 360px) {
    .tab-buttons {
        gap: 0.25rem;
        max-width: calc(100% - 6.25rem);
    }

    
    .tab-button {
        padding: 0.5rem 0.625rem;
        font-size: 0.75rem;
        letter-spacing: -0.3px;
    }

    
    .terminal-status {
        right: 0.5rem;
        padding: 0;
    }


    .status-indicator {
        width: 0.625rem;
        height: 0.625rem;
    }

}

/* Iframe-friendly adjustments */
@media (max-height: 600px) {
    .modal-content {
        max-height: 85vh;
        padding: 1.25rem;
    }


    .form-group {
        margin-bottom: 0.9375rem;
    }

}


/* Mobile deck tabs - opaque-ceiling slotting. The top-level logo header and the
   tab row form one SOLID, opaque chrome band; the scroll region clips directly
   beneath the tab row. Engaging the deck lifts it (wallet 'A') so the active
   card title tucks just under the tabs and the per-tab title strip hides cleanly
   under that ceiling - no translucent seam, no content bleeding under the logo.
   A downward swipe from the deck's top edge drops the lift back ('B') to reveal
   the title strip. Scoped to deck tabs and mobile; desktop is unchanged. */
@media (max-width: 768px) {
    /* The chrome is a solid, opaque layer that paints OVER the deck (positioned
       + z-index, since a static z-index is inert). This is the ceiling: anything
       that reaches it is occluded by an opaque band, not seamed against it. */
    .header,
    .tab-nav {
        position: relative;
        z-index: 30;
        background: var(--background);
    }

    /* Deck z-order, from back to front:
         tab-header (run selector / title / metadata, sticky z-index:10)
       < deck cards (z-index:20)        <- lifted cards render OVER that content
       < chrome header + tab row (z-index:30)  <- the ceiling, cards anchor below.
       The deck MUST outrank the sticky tab-header (10) or the run selector paints
       over the lifted cards.
       Clip the deck content at the deck's OWN top edge: the cards carry
       will-change:transform, so they live on their own compositor layers and a
       distant ancestor's overflow:hidden does NOT clip them on mobile - the clip
       must be on their direct parent. The deck top sits just below the title
       strip (slot A anchors there), so exiting/transiting cards vanish under the
       title instead of covering it. contain:paint forces the compositor clip to
       follow suit. */
    .chart-deck {
        z-index: 20;
        overflow: hidden;
        contain: paint;
        /* The deck anchors just below the title strip; its transparent regions
           still overlap the run selector / metadata row, whose toggles must stay
           tappable. pointer-events:none lets touches in those regions fall through
           to the header beneath; the head card re-enables them (JS sets
           pointer-events:auto on the head only), so you grab the card to
           swipe/lift and tap the header controls. */
        pointer-events: none;
    }

    /* An OPEN run-selector dropdown must paint over the deck, but its z-index
       lives inside the tab-header's stacking context (10), so the deck (20)
       wins no matter how high the dropdown sets it. Lift the whole header
       above the deck only while a dropdown is open - 25 stays under the
       chrome ceiling (30). Keyed off the inline display the JS toggles, so
       every open/close path (all three tabs) is covered by this one rule. */
    .tab-header:has(.run-selector-dropdown[style*="block"]) {
        z-index: 25;
    }

    /* Cards drive the deck (JS narrows pointer-events to the head card); they must
       block native pan so the JS 1:1 drag / inner-scroll owns the gesture. */
    .chart-deck .chart-card,
    .chart-deck .deck-card-scroll {
        touch-action: none;
    }

    /* The no-dead-zones surface: the deck container stays pointer-events:none
       (the header controls under its transparent TOP must remain tappable),
       but from the head card's top down to the floor the pad accepts the
       gesture anywhere - beside and below short compact cards too. Sits
       under the cards (their z runs 95+), so card content still wins. */
    .chart-deck .deck-gesture-pad {
        pointer-events: auto;
        touch-action: none;
        z-index: 1;
    }

    /* Cards must FULLY occlude whatever they lift over (the run selector, the
       title strip), so drop the title-row translucency on mobile - the faint
       card-behind bleed is a desktop orientation aid, not wanted over the chrome. */
    .chart-deck .chart-card::before {
        -webkit-mask-image: none;
                mask-image: none;
    }

    /* (The region overflow:hidden that kills the outer scrollbar is now a base rule
       in components.css - it applies on every viewport, not just mobile.) */
}

/* Reclaim system-prompt width on mobile: let the header actions wrap so the
   Settings button keeps the top row and the icon buttons (bell, theme, donation)
   drop beneath it, instead of all four crowding to the left of Settings. */
@media (max-width: 768px) {
    /* Collapse Settings to its gear icon so the action cluster is icon-width,
       leaving the system prompt the rest of the row. */
    .settings-button {
        font-size: 0;
        gap: 0;
    }


    /* The fly-out lives inside .header, which is a z-index:30 stacking context
       (the opaque chrome band above). A fixed child can't escape that context, so
       it tied with .tab-nav (also 30) and lost on DOM order - painting under it.
       Lift .header above the tab row so its panel always wins; both still outrank
       the deck (20), and they don't otherwise overlap. */
    .header { z-index: 40; }


    /* Pin the fly-out to the viewport's right edge with a capped width: enough to
       clear the left edge on any phone without going full-width. JS sets `top`
       from the bell's live rect (the header height varies - the prompt can wrap). */
    .notification-panel {
        position: fixed;
        top: 4rem;
        right: 0.5rem;
        left: auto;
        width: min(20rem, calc(100vw - 1rem));
    }

}

