/* AgentCore MCP Chatbot Styles */

/* Font Definitions */
@font-face {
    font-family: 'Amazon Ember';
    src: url('/fonts/AmazonEmber_Th.ttf') format('truetype');
    font-weight: 100;
    font-style: normal;
}

@font-face {
    font-family: 'Amazon Ember Display';
    src: url('/fonts/AmazonEmberDisplay_Lt.ttf') format('truetype');
    font-weight: 300;
    font-style: normal;
}

@font-face {
    font-family: 'Amazon Ember Display';
    src: url('/fonts/AmazonEmberDisplay_Rg.ttf') format('truetype');
    font-weight: 400;
    font-style: normal;
}

@font-face {
    font-family: 'Amazon Ember Display';
    src: url('/fonts/AmazonEmberDisplay_Md.ttf') format('truetype');
    font-weight: 500;
    font-style: normal;
}

@font-face {
    font-family: 'Amazon Ember Display';
    src: url('/fonts/AmazonEmberDisplay_He.ttf') format('truetype');
    font-weight: 600;
    font-style: normal;
}

@font-face {
    font-family: 'Amazon Ember Display';
    src: url('/fonts/AmazonEmberDisplay_Bd.ttf') format('truetype');
    font-weight: 700;
    font-style: normal;
}

@font-face {
    font-family: 'Ember Modern Display';
    src: url('/fonts/EmberModernDisplayV1.1-Regular.otf') format('opentype');
    font-weight: 400;
    font-style: normal;
}

@font-face {
    font-family: 'Ember Modern Display';
    src: url('/fonts/EmberModernDisplayV1.1-Italic.otf') format('opentype');
    font-weight: 400;
    font-style: italic;
}

@font-face {
    font-family: 'Ember Modern Display';
    src: url('/fonts/EmberModernDisplayV1.1-Bold.otf') format('opentype');
    font-weight: 700;
    font-style: normal;
}

@font-face {
    font-family: 'Ember Modern Display';
    src: url('/fonts/EmberModernDisplayV1.1-BoldItalic.otf') format('opentype');
    font-weight: 700;
    font-style: italic;
}

/* ── Theme Variables ─────────────────────────────────────────────── */
:root {
    --bg-primary: #f5f5f5;
    --bg-secondary: #ffffff;
    --bg-header: #232f3e;
    --bg-tools-header: #ff9900;
    --text-primary: #232f3e;
    --text-secondary: #666;
    --text-inverse: #ffffff;
    --border-color: #ddd;
    --shadow: rgba(0, 0, 0, 0.1);
    --message-user-bg: #0073bb;
    --message-user-text: #ffffff;
    --message-assistant-bg: #e9ecef;
    --message-assistant-text: #232f3e;
    --tool-bg: #fff3cd;
    --tool-border: #ff9900;
    --tool-content-bg: #fff;
    --error-bg: #f8d7da;
    --error-text: #721c24;
    --error-border: #f5c6cb;
    --input-border: #ddd;
    --input-focus: #232f3e;
    --scrollbar-track: #f1f1f1;
    --scrollbar-thumb: #888;
    --scrollbar-thumb-hover: #555;
    --sidebar-width: 380px;
    --left-panel-width: 320px;
}

/* ── The palette actually in effect ───────────────────────────────────────────
   app.js forces data-theme="dark" at boot, so this block is the theme and the
   :root above is vestigial.

   BRAND TOKENS FIRST, then the structural variables derived from them. The five
   brand names are the ones the design is specified in, so a future change to the
   palette is a change to five values rather than a hunt through the file.

   DEPTH COMES FROM SHADOW, NOT FROM BORDERS. Every surface in this design is the
   same squid-ink over the same canvas and is separated by a soft directional
   shadow — downward from the header, rightward from the panel, upward from the
   footer, a lift under each message. That is why --border-color is now barely
   visible rather than a drawn line: the lines it used to draw are doing nothing
   the shadows are not doing better, and a 1px #37475A rule under a shadowed
   header reads as a seam.

   Where a border still carries MEANING it keeps its own colour and does not use
   this variable — the per-status left edge on a domain row, and the six
   information-state badges. Those are not decoration. */
[data-theme="dark"] {
    --smile: #FF9900;
    --white: #FFFFFF;
    --squid-ink: #232F3E;
    --alpine: #F0F0F0;
    --oat: #FFF4E0;
    --canvas: #1B2531;

    --bg-primary: var(--canvas);
    --bg-secondary: var(--squid-ink);
    --bg-header: var(--squid-ink);
    --bg-tools-header: var(--squid-ink);
    /* One step darker than any raised surface, for the recessed provenance panel:
       it reads as cut into the message card rather than stacked on it. */
    --bg-recessed: #16202B;
    --bg-raised: #2A3A4E;

    --text-primary: var(--alpine);
    --text-secondary: rgba(240, 240, 240, 0.55);
    --text-inverse: var(--white);

    --border-color: rgba(255, 255, 255, 0.08);
    --shadow: rgba(0, 0, 0, 0.35);

    --message-user-bg: var(--squid-ink);
    --message-user-text: var(--white);
    --message-assistant-bg: var(--squid-ink);
    --message-assistant-text: var(--alpine);

    --tool-bg: var(--squid-ink);
    --tool-border: var(--smile);
    --tool-content-bg: var(--bg-recessed);

    --error-bg: #3f1f1f;
    --error-text: #fca5a5;
    --error-border: #7f1d1d;

    --input-border: rgba(240, 240, 240, 0.2);
    --input-focus: var(--smile);

    --scrollbar-track: transparent;
    --scrollbar-thumb: rgba(255, 255, 255, 0.12);
    --scrollbar-thumb-hover: rgba(255, 255, 255, 0.22);

    /* Narrower than the 320px it was: the panel is a list of chat titles, and at
       320px it took a quarter of a projected screen away from the answer. */
    --left-panel-width: 260px;
}

* { transition: background-color 1s ease, color 1s ease, border-color 1s ease; }
* { box-sizing: border-box; }

body {
    font-family: 'Ember Modern Display', -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Helvetica', 'Arial', sans-serif;
    margin: 0;
    padding: 0;
    background-color: var(--bg-primary);
    color: var(--text-primary);
    line-height: 1.6;
    height: 100vh;
    display: grid;
    grid-template-rows: auto 1fr auto;
    grid-template-columns: 1fr;
    overflow: hidden;
}

/* Middle row: left panel + main side by side */
.app-row {
    display: flex;
    flex-direction: row;
    min-height: 0;
    overflow: hidden;
}

/* ── Unified Left Panel ──────────────────────────────────────────── */
/* Depth via a soft RIGHTWARD shadow, and no right border, for the same reason the
   header dropped its bottom border. */
.left-panel {
    width: var(--left-panel-width);
    flex-shrink: 0;
    display: flex;
    flex-direction: column;
    background-color: var(--bg-secondary);
    transition: width 0.22s ease, box-shadow 0.22s ease;
    overflow: hidden;
    z-index: 150;
    position: relative;
    box-shadow: 4px 0 14px rgba(0, 0, 0, 0.3);
}

.left-panel.collapsed {
    width: 0;
    box-shadow: none;
}

/* The children keep their own layout width so the list does not reflow and rewrap
   on its way to zero. Collapsing a flex column by animating the parent's width
   otherwise squeezes every title through a few pixels of line-breaking first,
   which is visible and ugly at 220ms. */
.left-panel > * {
    width: var(--left-panel-width);
}

/* ── Left Panel Tab Bar ──────────────────────────────────────────── */
.left-panel-tabs {
    display: flex;
    flex-shrink: 0;
    background-color: #161D26;
    border-bottom: 1px solid var(--border-color);
}

.left-panel-tab {
    flex: 1;
    padding: 0.6rem 1rem;
    background: none;
    border: none;
    border-bottom: 2px solid transparent;
    color: var(--text-secondary);
    font-family: inherit;
    font-size: 0.8rem;
    font-weight: 500;
    cursor: pointer;
    transition: color 0.2s, border-color 0.2s;
}

.left-panel-tab:hover {
    color: var(--text-primary);
}

.left-panel-tab.active {
    color: #FF9900;
    border-bottom-color: #FF9900;
}

/* ── Left Panel Content ──────────────────────────────────────────── */
.left-panel-content {
    flex: 1;
    display: flex;
    flex-direction: column;
    min-height: 0;
    overflow: hidden;
}

.left-panel-tab-content {
    display: none;
    flex-direction: column;
    flex: 1;
    min-height: 0;
    overflow: hidden;
}

.left-panel-tab-content.active {
    display: flex;
}

/* Tools tab: the sidebar-content inside it scrolls */
#toolsTabContent .sidebar-content {
    flex: 1;
    overflow-y: auto;
    min-height: 0;
}

/* Chats tab: conversation list scrolls */
#chatsTabContent .conversation-list {
    flex: 1;
    overflow-y: auto;
    min-height: 0;
}

/* ── Header ──────────────────────────────────────────────────────── */
/* Depth via a soft DOWNWARD shadow, and no bottom border: the two together read
   as a seam plus a shadow rather than as one raised surface. */
header {
    background-color: var(--bg-header);
    color: var(--text-inverse);
    padding: 0.9rem 1.5rem;
    display: grid;
    grid-template-columns: 1fr auto 1fr;
    align-items: center;
    box-shadow: 0 4px 14px rgba(0, 0, 0, 0.35);
    flex-shrink: 0;
    z-index: 100;
    position: relative;
}

/* Quieter than it was (1.4rem/600 in near-white). The product name is orientation,
   not the thing being read — the answer is — so it recedes deliberately. */
header h1 {
    margin: 0;
    font-size: 1.05rem;
    font-weight: 500;
    font-family: 'Ember Modern Display', sans-serif;
    line-height: 1.6;
    color: rgba(240, 240, 240, 0.65);
}

.header-left-zone {
    display: flex;
    align-items: center;
    gap: 0.65rem;
    justify-self: start;
    cursor: pointer;
    padding: 0.2rem 0.4rem;
    margin: -0.2rem -0.4rem;
    border-radius: 6px;
    transition: background-color 0.15s;
}

.header-left-zone:hover {
    background-color: rgba(255, 255, 255, 0.05);
}

/* A disclosure affordance for the panel, so the control says whether it is open.
   It is in the HEADER while the panel it controls is in the row below, and app.js
   toggles only `.collapsed` on the panel — so the state is read back off the panel
   with :has() rather than by adding a second class for the browser to keep in sync.
   One source of truth for "is the panel open", and no JS change. */
.panel-chevron {
    font-size: 0.65rem;
    color: rgba(240, 240, 240, 0.4);
    transition: transform 0.22s ease;
}

body:has(#leftPanel:not(.collapsed)) .panel-chevron {
    transform: rotate(180deg);
}

.header-starburst-btn {
    width: 20px;
    height: 20px;
    flex-shrink: 0;
    opacity: 0.6;
    transition: opacity 0.2s;
}

.header-starburst-btn.active {
    opacity: 1;
}

.header-left-zone:hover .header-starburst-btn {
    opacity: 1;
}

.header-left-labels {
    display: flex;
    flex-direction: column;
    gap: 0;
    line-height: 1.2;
}

.header-tools-label {
    font-size: 0.8rem;
    font-weight: 400;
    color: rgba(240, 240, 240, 0.55);
    transition: color 0.15s;
}

.header-left-zone:hover .header-tools-label {
    color: var(--alpine);
}

.header-thinking-text {
    font-size: 0.65rem;
    color: rgba(240, 240, 240, 0.55);
    opacity: 0.7;
    white-space: nowrap;
}

.thinking-container-inline {
    display: flex;
    align-items: center;
}

.header-center-zone {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    justify-self: center;
}

.header-logo {
    height: 2.5rem;
    width: auto;
}

.header-right-zone {
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: 0.25rem;
}

/* ── Server Info ─────────────────────────────────────────────────── */
.server-info {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    font-size: 0.75rem;
    color: var(--text-inverse);
    opacity: 0.9;
}

.server-status {
    font-size: 0.6rem;
    animation: pulse 2s ease-in-out infinite;
}

.server-status.connected { color: #00AC00; animation: none; }
.server-status.disconnected { color: #ef4444; animation: none; }

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

.server-text { font-weight: 500; }

/* ── Model Selector ──────────────────────────────────────────────── */
.model-selector-wrapper {
    position: relative;
    display: block;
    text-align: right;
    margin: 0;
    line-height: 1;
}

.model-selector-selected {
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
    cursor: pointer;
    padding: 0 0.35rem;
    border-radius: 3px;
    transition: background-color 0.15s;
    line-height: 1;
}

.model-selector-selected:hover { background-color: rgba(255, 255, 255, 0.08); }

.model-selector-label {
    font-size: 0.55rem;
    font-weight: 500;
    opacity: 0.5;
    color: var(--text-inverse);
    transition: opacity 0.15s;
}

.model-selector-selected:hover .model-selector-label { opacity: 0.8; }

.model-selector-chevron {
    font-size: 0.5rem;
    opacity: 0.35;
    color: var(--text-inverse);
    transition: transform 0.2s, opacity 0.15s;
}

.model-selector-wrapper.open .model-selector-chevron { transform: rotate(180deg); opacity: 0.7; }
.model-selector-selected:hover .model-selector-chevron { opacity: 0.6; }

.model-selector-dropdown {
    position: absolute;
    right: 0;
    top: calc(100% + 4px);
    min-width: 200px;
    background: #1a1a1a;
    border: 1px solid rgba(255, 255, 255, 0.1);
    border-radius: 8px;
    padding: 0.3rem;
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.5);
    z-index: 1000;
    opacity: 0;
    transform: translateY(-4px);
    pointer-events: none;
    transition: opacity 0.15s, transform 0.15s;
}

.model-selector-wrapper.open .model-selector-dropdown {
    opacity: 1;
    transform: translateY(0);
    pointer-events: auto;
}

.model-selector-option {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 0.45rem 0.6rem;
    border-radius: 5px;
    cursor: pointer;
    transition: background-color 0.1s;
}

.model-selector-option:hover { background-color: rgba(255, 255, 255, 0.07); }
.model-selector-option.active { background-color: rgba(139, 92, 246, 0.15); }

.model-option-name {
    font-size: 0.72rem;
    font-weight: 500;
    color: rgba(255, 255, 255, 0.85);
}

.model-selector-option.active .model-option-name { color: rgb(167, 139, 250); }

.model-option-provider {
    font-size: 0.6rem;
    font-weight: 400;
    color: rgba(255, 255, 255, 0.3);
    margin-left: 1rem;
}

/* ── Theme Toggle ────────────────────────────────────────────────── */
.theme-toggle {
    background: none;
    padding: 0.5rem 1rem;
    border-radius: 6px;
    cursor: pointer;
    font-size: 0.9rem;
    font-weight: 600;
    transition: all 0.3s ease;
    display: flex;
    align-items: center;
}

.theme-toggle:hover { background-color: rgba(255, 255, 255, 0.1); transform: scale(1.05); }
.theme-toggle::before { content: '🌙'; font-size: 1.2rem; }
[data-theme="dark"] .theme-toggle::before { content: '☀️'; }


/* ── Main Content Area ───────────────────────────────────────────── */
/* Flat canvas. The gradient it replaced fought the message cards' own shadows:
   two depth cues in the same place, one of which moved as you scrolled. */
main {
    flex: 1;
    display: flex;
    flex-direction: column;
    min-height: 0;
    min-width: 0;
    overflow: hidden;
    background-color: var(--bg-primary);
}

/* WIDE ENOUGH FOR A CHART, with the prose kept narrow separately.
   ----------------------------------------------------------------
   This was 720px, with a comment asserting that "the visualization iframes lay out to
   whatever width they are given, so nothing is cropped by narrowing it". Measured in a real
   browser, that was false in a way that mattered:

       chart column      438px for a 340-day span   (1.3 pixels per day)
       label gutter      184px of a 624px card
       bar widths        35-53px
       clipped labels    49 of 56

   Forty-nine of fifty-six task names inside their bars were ellipsed to "finis...", "inge...",
   "deliv...". The bars laid out, so nothing was "cropped" in the sense the comment meant, and
   the chart was still unreadable. That is the difference between a layout assertion and a
   measurement, and it is why .local-mcp/demo_view.py exists.

   The original concern was real though: a narrative paragraph at this width runs to ~200
   characters a line, which is unreadable from a stand. So the two needs are separated rather
   than traded off — the COLUMN is wide, and the PROSE inside it is capped at a measure (see
   `.answer-text` and `.claim-text`). Charts get the width; sentences do not. */
#conversation {
    flex: 1;
    overflow-y: auto;
    overflow-x: visible;
    padding: 1.75rem 2rem 1.5rem;
    display: flex;
    flex-direction: column;
    gap: 1.4rem;
    max-width: 1320px;
    width: 100%;
    margin: 0 auto;
}

/* ── Footer ───────────────────────────────────────────────────────── */
/* Depth via a soft UPWARD shadow, and no top border. */
.app-footer {
    background-color: var(--bg-header);
    flex-shrink: 0;
    box-shadow: 0 -4px 14px rgba(0, 0, 0, 0.3);
    z-index: 100;
    position: relative;
}

/* Aligned to the column above it, so the caret sits under the transcript rather than under a
   narrower box floating inside it. Matches #conversation's max-width for that reason. */
.input-area {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    padding: 1rem 2rem;
    max-width: 1320px;
    margin: 0 auto;
    width: 100%;
}

/* A ruled line rather than a boxed field: one border instead of four, and the
   focus state becomes the accent moving onto that line. A 2px box in a footer that
   is already a distinct surface is a second frame around nothing. */
#messageInput {
    flex: 1;
    padding: 0.6rem 0;
    border: none;
    border-bottom: 1px solid var(--input-border);
    border-radius: 0;
    font-family: inherit;
    font-size: 0.9rem;
    resize: none;
    height: 1.8rem;
    min-height: 1.8rem;
    max-height: 1.8rem;
    background-color: transparent;
    color: var(--text-primary);
    outline: none;
    transition: border-color 0.2s;
    line-height: 1.4;
    overflow: hidden;
    box-sizing: border-box;
}

#messageInput::placeholder {
    color: rgba(240, 240, 240, 0.4);
}

#messageInput:focus {
    border-bottom-color: var(--input-focus);
}

/* A pill with a faint glow of its own colour: the one saturated element on the
   page, and the only control whose job is to be found without looking. */
#sendButton {
    padding: 0.5rem 1.3rem;
    background-color: var(--smile);
    color: var(--squid-ink);
    border: none;
    border-radius: 20px;
    cursor: pointer;
    font-family: inherit;
    font-size: 0.85rem;
    font-weight: 700;
    box-shadow: 0 2px 8px rgba(255, 153, 0, 0.25);
    transition: background-color 0.2s, opacity 0.2s;
}

#sendButton:hover { background-color: #e88600; }
#sendButton:disabled { opacity: 0.5; cursor: not-allowed; box-shadow: none; }

/* The vendor service-icon strip (.service-icons-row / .service-icon / serviceGlow) was
   REMOVED on adaptation with the markup and the tool-name classifier that drove it
   (Requirement 11.3). */

/* ── Message Bubbles ─────────────────────────────────────────────── */
/* Each message is its own raised card: same surface colour as the header and the
   footer, lifted off the canvas by its own shadow. */
.message {
    max-width: 78%;
    word-wrap: break-word;
    animation: fadeIn 0.3s ease;
    line-height: 1.65;
    flex-shrink: 0;
}

.message.user {
    padding: 0.65rem 0.95rem;
    /* Asymmetric: the tight corner points back at the sender. */
    border-radius: 14px 14px 3px 14px;
    font-size: 0.92rem;
    background-color: var(--message-user-bg);
    color: var(--message-user-text);
    align-self: flex-end;
    box-shadow: 0 3px 10px rgba(0, 0, 0, 0.3);
}

/* Full width of the column, not 78%: an answer carries provenance rows and embedded
   visualizations, and indenting all of that to leave room for a gutter nothing occupies costs
   the views their width.

   `align-self: stretch`, and that is the property that actually delivers what the paragraph
   above claims. This rule said `max-width: 100%` with `align-self: flex-start`, and in a COLUMN
   flex container those two do not combine the way they read: `flex-start` sizes the item to its
   CONTENT on the cross axis, so `max-width` never binds and the article is as wide as its
   contents ask to be. An iframe has no intrinsic width, so it landed on a browser default.

   Measured: widening the column from 720px to 1320px moved the chart by 51 pixels, because the
   column was never the constraint. `max-width: 100%` is not `width: 100%`, and in a flex
   container neither is `align-self`. */
.message.assistant {
    align-self: stretch;
    max-width: 100%;
    padding: 0.9rem 1.1rem;
    border-radius: 14px;
    font-size: 0.95rem;
    background-color: var(--message-assistant-bg);
    color: var(--message-assistant-text);
    box-shadow: 0 3px 10px rgba(0, 0, 0, 0.3);
}

.message.assistant.streaming {
    opacity: 0.7;
    font-style: italic;
    white-space: pre-wrap;
}

.message.assistant.assistant-thought {
    opacity: 0.5;
    font-size: 0.85rem;
    font-style: italic;
    padding-left: 0.75rem;
    box-shadow: none;
}

/* Welcome message — wide enough for 2+ thumbnail columns */
.message.assistant.welcome-message {
    max-width: none;
    min-width: min(100%, 960px);
}

/* ── Message Content Styles ──────────────────────────────────────── */
.message p { margin: 0.25rem 0; }
.message p:first-child { margin-top: 0; }
.message p:last-child { margin-bottom: 0; }

.message code {
    background-color: rgba(0, 0, 0, 0.15);
    padding: 0.15rem 0.35rem;
    border-radius: 3px;
    font-size: 0.85em;
}

.message pre {
    background-color: #1e1e1e;
    padding: 0.75rem;
    border-radius: 6px;
    overflow-x: auto;
    margin: 0.5rem 0;
}

.message pre code {
    background: none;
    padding: 0;
    font-size: 0.85em;
    color: #d4d4d4;
}

.message a { color: #FF9900; text-decoration: none; }
.message a:hover { text-decoration: underline; }

.message table {
    border-collapse: collapse;
    margin: 0.5rem 0;
    width: 100%;
    font-size: 0.9em;
}

.message th, .message td {
    border: 1px solid var(--border-color);
    padding: 0.4rem 0.6rem;
    text-align: left;
}

.message th {
    background-color: rgba(0, 0, 0, 0.1);
    font-weight: 600;
}

.message img {
    max-width: 100%;
    border-radius: 4px;
}

/* The thumbnail surfaces of NAB's {{IMG_N}} convention were REMOVED on adaptation
   (Requirement 11.3) together with their handlers in app.js: the .image-grid /
   .image-grid-item / .image-caption layout, the .no-thumb-placeholder fallback,
   .filmstrip-active scrubbing, and the .grid-size-* thumbnail size slider. */

/* ── Tool Invocations (inside left panel) ────────────────────────── */
.tool-invocation {
    padding: 0.75rem;
    margin: 0.5rem;
    background-color: var(--tool-bg);
    border-radius: 6px;
    font-size: 0.8rem;
    animation: fadeIn 0.3s ease;
    box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
    position: relative;
}

.tool-header {
    display: flex;
    align-items: flex-start;
    gap: 0.5rem;
    margin-bottom: 0.25rem;
}

.tool-header-text {
    display: flex;
    flex-direction: column;
    min-width: 0;
}

.tool-name {
    display: flex;
    align-items: center;
    font-weight: 600;
    color: var(--text-primary);
}

.tool-name-icon {
    position: absolute;
    top: 0.5rem;
    right: 0.5rem;
    width: 32px;
    height: 32px;
    object-fit: contain;
    flex-shrink: 0;
    opacity: 0.4;
}

.tool-name-icon-emoji {
    position: absolute;
    top: 0.4rem;
    right: 0.5rem;
    font-size: 1.4rem;
    opacity: 0.4;
}

.tool-section-collapsible {
    margin-top: 0.4rem;
}

.tool-section-collapsible summary {
    cursor: pointer;
    font-size: 0.75rem;
    font-weight: 500;
    color: var(--text-secondary);
    user-select: none;
    padding: 0.15rem 0;
}

.tool-section-collapsible summary:hover {
    color: var(--text-primary);
}

.tool-summary-error {
    color: #ef4444 !important;
}

.tool-summary-skipped {
    color: #f59e0b !important;
}

.tool-parameters {
    margin-top: 0.25rem;
    max-height: 200px;
    overflow-y: auto;
}

.tool-parameters pre {
    margin: 0;
    font-size: 0.65rem;
    background: var(--tool-content-bg);
    padding: 0.4rem;
    border-radius: 4px;
    overflow-x: auto;
}

.tool-result, .tool-error {
    margin-top: 0.25rem;
    padding: 0.4rem;
    border-radius: 4px;
    font-size: 0.65rem;
    max-height: 300px;
    overflow-y: auto;
    word-break: break-word;
    background: var(--tool-content-bg);
}

.tool-error {
    color: #ef4444;
    border: 1px solid rgba(239, 68, 68, 0.3);
}

.tool-skipped {
    opacity: 0.6;
}

.tool-timing {
    font-size: 0.75em;
    color: #888;
}

.tool-phase {
    font-size: 0.65em;
    color: #f59e0b;
    font-style: italic;
    margin-top: 1px;
}

.tool-elapsed {
    color: #f59e0b;
}

/* ── JSON Tree ───────────────────────────────────────────────────── */
.json-tree {
    font-family: monospace;
    font-size: 0.8rem;
    line-height: 1.4;
}

.json-toggle {
    cursor: pointer;
    user-select: none;
}

.json-content {
    padding-left: 1rem;
}

/* ── Error Display ───────────────────────────────────────────────── */
#errorDisplay {
    padding: 0 1rem;
}

.error-message {
    background-color: var(--error-bg);
    color: var(--error-text);
    border: 1px solid var(--error-border);
    padding: 0.75rem 1rem;
    border-radius: 8px;
    margin: 0.5rem 0;
    animation: fadeIn 0.3s ease, shake 0.3s ease;
}

/* ── Thinking Placeholder (in conversation) ──────────────────────── */
.thinking-placeholder {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    padding: 0.5rem 1rem;
    background: transparent;
    max-width: 400px;
    box-shadow: none;
    border: none;
    position: relative;
    margin-left: 0;
}

.thinking-placeholder .agentcore-spinner {
    position: absolute;
    left: -44px;
    top: 50%;
    transform: translateY(-50%);
    flex-shrink: 0;
}

.thinking-placeholder .thinking-status {
    font-size: 0.8rem;
    color: var(--text-secondary);
    white-space: nowrap;
}

/* ── AgentCore Spinner SVG ───────────────────────────────────────── */
.agentcore-spinner {
    width: 36px;
    height: 36px;
    flex-shrink: 0;
}

.agentcore-spinner .starburst-outer,
.agentcore-spinner .starburst-inner {
    transition: opacity 0.3s;
}

.agentcore-spinner.tool-executing .starburst-outer,
.agentcore-spinner.tool-executing .starburst-inner {
    opacity: 0.3;
}

/* ── Header Starburst ────────────────────────────────────────────── */
.header-starburst {
    width: 20px;
    height: 20px;
    flex-shrink: 0;
    opacity: 0.3;
    transition: opacity 0.3s;
}

.header-starburst.active {
    opacity: 1;
}

/* ── Typing Indicator (in sidebar header) ────────────────────────── */
.typing-indicator {
    display: flex;
    align-items: center;
    gap: 0.3rem;
}

.typing-text {
    font-size: 0.65rem;
    opacity: 0.8;
}

/* ── Composing Grid (placeholder animation) ──────────────────────── */
.composing-grid {
    display: grid;
    grid-template-columns: repeat(3, 40px);
    gap: 4px;
    margin-top: 0.5rem;
}

.composing-grid-cell {
    width: 40px;
    height: 28px;
    border-radius: 3px;
    background: var(--border-color);
    animation: blink 1.5s ease-in-out infinite;
}

.composing-grid-cell:nth-child(2) { animation-delay: 0.2s; }
.composing-grid-cell:nth-child(3) { animation-delay: 0.4s; }
.composing-grid-cell:nth-child(4) { animation-delay: 0.6s; }
.composing-grid-cell:nth-child(5) { animation-delay: 0.8s; }
.composing-grid-cell:nth-child(6) { animation-delay: 1.0s; }

/* ── Streaming Cursor ────────────────────────────────────────────── */
.streaming-cursor::after {
    content: '▊';
    animation: blink 1s step-end infinite;
    color: var(--text-secondary);
}

/* ── Empty State ─────────────────────────────────────────────────── */
.empty-state {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    height: 100%;
    color: var(--text-secondary);
    font-size: 1.1rem;
    gap: 0.5rem;
}

/* ── Scrollbar Styles ────────────────────────────────────────────── */
#conversation::-webkit-scrollbar,
.sidebar-content::-webkit-scrollbar,
.conversation-list::-webkit-scrollbar {
    width: 6px;
}

#conversation::-webkit-scrollbar-track,
.sidebar-content::-webkit-scrollbar-track,
.conversation-list::-webkit-scrollbar-track {
    background: var(--scrollbar-track);
}

#conversation::-webkit-scrollbar-thumb,
.sidebar-content::-webkit-scrollbar-thumb,
.conversation-list::-webkit-scrollbar-thumb {
    background: var(--scrollbar-thumb);
    border-radius: 3px;
}

#conversation::-webkit-scrollbar-thumb:hover,
.sidebar-content::-webkit-scrollbar-thumb:hover,
.conversation-list::-webkit-scrollbar-thumb:hover {
    background: var(--scrollbar-thumb-hover);
}

/* ── Responsive ──────────────────────────────────────────────────── */
@media (max-width: 768px) {
    .left-panel {
        position: fixed;
        left: 0;
        top: 0;
        bottom: 0;
        z-index: 300;
        box-shadow: 2px 0 8px var(--shadow);
    }

    .left-panel.collapsed {
        width: 0;
        box-shadow: none;
    }

    .message { max-width: 95%; }
    
    header h1 { font-size: 0.85rem; }
}

/* ── Keyframes ───────────────────────────────────────────────────── */
@keyframes fadeIn {
    from { opacity: 0; transform: translateY(4px); }
    to { opacity: 1; transform: translateY(0); }
}

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

@keyframes typing {
    0%, 100% { opacity: 0.3; }
    50% { opacity: 1; }
}

@keyframes shake {
    0%, 100% { transform: translateX(0); }
    25% { transform: translateX(-4px); }
    75% { transform: translateX(4px); }
}

@keyframes blink {
    0%, 100% { opacity: 0.3; }
    50% { opacity: 1; }
}

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

/* ── Login Overlay ───────────────────────────────────────────────── */
.login-overlay {
    position: fixed;
    inset: 0;
    z-index: 9999;
    display: flex;
    align-items: center;
    justify-content: center;
    background: rgba(22, 29, 38, 0.95);
    backdrop-filter: blur(8px);
}

.login-overlay.hidden {
    display: none;
}

.login-card {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 1rem;
    padding: 2.5rem 3rem;
    background: var(--bg-secondary);
    border: 1px solid var(--border-color);
    border-radius: 16px;
    box-shadow: 0 8px 32px rgba(0, 0, 0, 0.6);
    max-width: 360px;
    width: 100%;
}

.login-logo-icon {
    width: 56px;
    height: 56px;
    opacity: 0.8;
}

.login-title {
    margin: 0;
    font-size: 1.4rem;
    font-weight: 600;
    color: var(--text-primary);
    font-family: 'Ember Modern Display';
}

.login-subtitle {
    margin: 0;
    font-size: 0.85rem;
    color: var(--text-secondary);
}

/* Sign-in blocked because the bridge served no OIDC client configuration
   (task 1.4). Colour is paired with the alert role app.js sets, so the state is
   not conveyed by colour alone. */
.login-subtitle.login-error {
    color: var(--error-text);
    max-width: 22rem;
    line-height: 1.4;
}

.login-button {
    margin-top: 0.5rem;
    padding: 0.6rem 2rem;
    background-color: #FF9900;
    color: #161D26;
    border: none;
    border-radius: 8px;
    font-family: inherit;
    font-size: 0.95rem;
    font-weight: 600;
    cursor: pointer;
    transition: background-color 0.2s;
}

.login-button:hover {
    background-color: #EC7211;
}

/* ── Conversation List (inside Chats tab) ────────────────────────── */
.conversation-list-header {
    padding: 0.75rem;
    flex-shrink: 0;
}

.new-conversation-btn {
    width: 100%;
    padding: 0.5rem 0.75rem;
    background: transparent;
    color: var(--text-secondary);
    border: 1px dashed var(--border-color);
    border-radius: 6px;
    font-family: inherit;
    font-size: 0.8rem;
    font-weight: 500;
    cursor: pointer;
    transition: background-color 0.2s, color 0.2s, border-color 0.2s;
}

.new-conversation-btn:hover {
    background-color: rgba(255, 255, 255, 0.05);
    color: var(--text-primary);
    border-color: var(--text-secondary);
}

.conversation-list {
    flex: 1;
    overflow-y: auto;
    padding: 0 0.5rem 0.5rem;
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.conversation-item,
.conversation-list-item {
    padding: 0.5rem 0.6rem;
    border-radius: 6px;
    cursor: pointer;
    transition: background-color 0.15s;
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.conversation-item:hover,
.conversation-list-item:hover {
    background-color: rgba(255, 255, 255, 0.05);
}

/* Selected by being RAISED, not by an accent stripe. The accent is reserved for the
   one control that acts (Send) and for the simulated-data warning; spending it on a
   list selection makes both of those quieter. */
.conversation-item.active,
.conversation-list-item.active {
    background-color: var(--bg-raised);
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
}

.conversation-item-title {
    font-size: 0.78rem;
    font-weight: 400;
    color: var(--text-secondary);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.conversation-item.active .conversation-item-title,
.conversation-list-item.active .conversation-item-title {
    color: var(--white);
}

.conversation-item-time,
.conversation-item-timestamp {
    font-size: 0.6rem;
    color: var(--text-secondary);
    opacity: 0.7;
}

.conversation-list-empty {
    padding: 1rem 0.6rem;
    font-size: 0.75rem;
    color: var(--text-secondary);
    opacity: 0.6;
    text-align: center;
}

.conversation-list-loading {
    padding: 1.2rem 0.6rem;
    font-size: 0.75rem;
    color: var(--text-secondary);
    opacity: 0.6;
    text-align: center;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
}

.session-loading-dot {
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: var(--text-secondary);
    opacity: 0.5;
    animation: sessionPulse 1.2s ease-in-out infinite;
}

@keyframes sessionPulse {
    0%, 100% { opacity: 0.3; transform: scale(0.8); }
    50% { opacity: 0.8; transform: scale(1.2); }
}

/* The per-session vendor icon strip (.session-service-icons / .session-service-icon)
   was REMOVED on adaptation with the markup that drove it (Requirement 11.3). */

/* ── Interrupt Buttons (human-in-the-loop clarification) ─────────── */
.interrupt-buttons {
    padding: 12px 16px;
    animation: fadeIn 0.3s ease;
}

.interrupt-question {
    font-size: 0.85rem;
    color: var(--text-secondary);
    margin-bottom: 8px;
}

.interrupt-btn-row {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
}

.interrupt-btn {
    padding: 7px 18px;
    border-radius: 18px;
    border: 1px solid var(--border-color);
    background: transparent;
    color: var(--text-primary);
    font-family: inherit;
    font-size: 0.8rem;
    cursor: pointer;
    transition: all 0.15s;
}

.interrupt-btn:hover {
    background: rgba(255, 153, 0, 0.1);
    border-color: #FF9900;
    color: #FF9900;
}

.interrupt-btn.selected {
    background: rgba(255, 153, 0, 0.2);
    border-color: #FF9900;
    color: #FF9900;
}

.interrupt-btn:disabled:not(.selected) {
    opacity: 0.35;
    cursor: default;
}

/* ── User Display (header) ───────────────────────────────────────── */
.user-display {
    display: flex;
    align-items: center;
    gap: 0.4rem;
}

.user-display-name {
    font-size: 0.7rem;
    font-weight: 500;
    color: var(--text-secondary);
    opacity: 0.8;
}

.logout-btn {
    display: flex;
    align-items: center;
    justify-content: center;
    background: none;
    border: 1px solid transparent;
    border-radius: 6px;
    color: var(--text-secondary);
    cursor: pointer;
    padding: 4px;
    opacity: 0.6;
    transition: opacity 0.15s ease, color 0.15s ease, border-color 0.15s ease;
}

.logout-btn:hover {
    opacity: 1;
    color: #ef4444;
    border-color: rgba(239, 68, 68, 0.3);
}

/* ── Rich Content / Chart Container ──────────────────────────────── */
.rich-content {
    width: 100%;
    animation: fadeIn 0.3s ease;
    flex-shrink: 0;
}

.chart-container {
    background: var(--message-assistant-bg);
    border-radius: 12px;
    padding: 1rem;
    border: thin solid #23303e;
    box-shadow: 0 2px 12px rgba(0, 0, 0, 0.4);
}

.chart-container canvas {
    width: 100% !important;
    max-height: 400px;
}

/* Visualization agent HTML iframe container */
.viz-container {
    margin: 0.5rem 0;
    overflow: hidden;
    flex-shrink: 0;
    min-width: 0;
    width: 100%;
}

/* The height here is only what the frame occupies UNTIL its document reports the height
   it needs, which app.js then applies as an inline style. So this is a starting box, not
   a cap — a fifty-six-row Gantt asks for two thousand pixels and gets them rather than
   being scrolled inside a 520px window.

   `min-height` is deliberately gone: it was the floor that made an empty view — one
   sentence saying the payload carried nothing — occupy four hundred pixels of the answer.
   The receiver in app.js keeps its own 80px floor, so a frame can never collapse to
   nothing. */
.viz-iframe {
    display: block;
    width: 100%;
    height: 520px;
    border: none;
    background: transparent;
    flex-shrink: 0;
}

/* Gradient mask for visualization tool icon (starburst purple gradient) */
.tool-name-icon-gradient {
    -webkit-mask-image: url('/images/graphs.svg');
    mask-image: url('/images/graphs.svg');
    -webkit-mask-size: contain;
    mask-size: contain;
    -webkit-mask-repeat: no-repeat;
    mask-repeat: no-repeat;
    -webkit-mask-position: center;
    mask-position: center;
    background: linear-gradient(135deg, #7638fa 0%, #c084fc 50%, #9b6dfa 100%);
    opacity: 0.7;
}

/* Visualization placeholder (shown while viz agent is working) */
.viz-placeholder {
    position: relative;
    width: 100%;
    height: 520px;
    border-radius: 12px;
    background: linear-gradient(135deg, #1a2332 0%, #2a3a4e 50%, #1e2d3d 100%);
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 1rem;
    overflow: hidden;
    border: 1px solid #23303e;
    box-shadow: 0 2px 12px rgba(0, 0, 0, 0.4);
    flex-shrink: 0;
}

.viz-placeholder-icon {
    width: 72px;
    height: 72px;
    opacity: 0.15;
    animation: vizPlaceholderPulse 8s ease-in-out infinite;
}

.viz-placeholder-text {
    font-size: 0.85rem;
    color: var(--text-secondary);
    opacity: 0.6;
    animation: vizPlaceholderPulse 8s ease-in-out infinite;
}

.viz-placeholder-shimmer {
    position: absolute;
    inset: 0;
    background: linear-gradient(
        90deg,
        transparent 0%,
        rgba(255, 255, 255, 0.03) 40%,
        rgba(255, 255, 255, 0.06) 50%,
        rgba(255, 255, 255, 0.03) 60%,
        transparent 100%
    );
    background-size: 200% 100%;
    animation: vizShimmer 8s ease-in-out infinite;
    pointer-events: none;
}

@keyframes vizPlaceholderPulse {
    0%, 100% { opacity: 0.15; }
    50% { opacity: 0.3; }
}

@keyframes vizShimmer {
    0% { background-position: -200% 0; }
    100% { background-position: 200% 0; }
}

/* ── Presenter transcript rendering (spec task 4.1) ───────────────────────────
   Presentation for the elements frontend/render.js builds from the bridge's render
   payload (api/render_payload.py): information-state badges in canonical order,
   per-domain sections with their exact reported status, evidence-bearing claims,
   contributor attribution, the visualization block, and the operating-mode banner.

   Nothing here decides anything: the state classes and data attributes are applied
   from the payload. In particular `[data-presented-as-live="false"]` is set by the
   payload's `presentedAsLive`, so cached, simulated, degraded, partial and
   unavailable information cannot read as current (Requirements 3.3, 9.4).
   ------------------------------------------------------------------------------ */

.answer .field-label {
    display: inline-block;
    margin-right: 0.35rem;
    font-size: 0.65rem;
    font-weight: 500;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--text-secondary);
}

.answer .field-value {
    font-size: 0.8rem;
    color: var(--text-primary);
}

/* ── Information-state badges ─────────────────────────────────────── */

.state-labels {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.35rem;
    margin: 0.35rem 0;
}

.state-badge {
    padding: 0.1rem 0.5rem;
    border: 1px solid var(--border-color);
    border-radius: 10px;
    font-size: 0.65rem;
    font-weight: 600;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    color: var(--text-primary);
    background: rgba(255, 255, 255, 0.04);
}

.state-badge[data-state="live"] { border-color: #2f7d4f; color: #6ee7a0; }
.state-badge[data-state="cached"] { border-color: #8a6d1f; color: #f0c869; }
.state-badge[data-state="simulated"] { border-color: #6d4bb3; color: #c4a6ff; }
.state-badge[data-state="degraded"] { border-color: #9a5a1e; color: #f4a45f; }
.state-badge[data-state="partial"] { border-color: #1f6c8a; color: #7fc9e8; }
.state-badge[data-state="unavailable"] { border-color: #8e3b3b; color: #f18f8f; }

/* Applied from the payload flag, never inferred here. */
.state-labels[data-presented-as-live="false"] .state-live-note {
    padding: 0.1rem 0.45rem;
    border: 1px dashed var(--border-color);
    border-radius: 10px;
    font-size: 0.6rem;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    color: var(--text-secondary);
}

.state-unrecognized {
    display: inline-flex;
    align-items: baseline;
    gap: 0.3rem;
    font-size: 0.65rem;
    color: var(--text-secondary);
}

.state-unrecognized-value {
    font-family: 'SF Mono', Menlo, Consolas, monospace;
    color: var(--text-primary);
}

.state-unrecognized-note { opacity: 0.75; font-style: italic; }

/* ── Answer entry ─────────────────────────────────────────────────── */

.message.assistant.answer {
    display: flex;
    flex-direction: column;
    gap: 0.6rem;
}

.answer-incomplete {
    padding: 0.4rem 0.6rem;
    border-left: 3px solid #f18f8f;
    border-radius: 4px;
    background: rgba(142, 59, 59, 0.15);
    font-size: 0.8rem;
    color: var(--text-primary);
}

.answer-header,
.answer-footer {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.6rem;
}

.answer-footer {
    padding-top: 0.4rem;
    border-top: 1px solid var(--border-color);
    font-size: 0.7rem;
    color: var(--text-secondary);
}

.answer-text[data-unformatted="true"] { white-space: pre-wrap; }

/* THE MEASURE, capped independently of the column.
   #conversation is wide because a Gantt needs the width; a sentence does not. At the full
   1320px a narrative paragraph runs to nearly 200 characters a line, which is what the old
   720px column existed to prevent and is genuinely unreadable from a stand.
   `ch` rather than px, so the cap tracks the font rather than fighting it: ~78 characters is
   the upper end of the usual 45-75 typographic range, chosen because this text is read at
   distance in short bursts rather than at length. */
.answer-text,
.claim-text,
.caveat-note,
.undrawn-view-reason {
    max-width: 78ch;
}

/* ── The answer's single provenance disclosure ─────────────────────────
   The answer is what the presenter came for, so it is the only thing above this.
   Three nested levels, each a <details>: the answer's badges and source count are
   always visible; a domain with its checked sources, reported status and own badges
   is one click in; its claims and their citations are two.

   Collapsing provenance must not collapse honesty, which is why the state badges
   live on the summary line rather than inside. */

.sources-disclosure {
    margin: 0;
    border-top: 1px solid var(--border-color);
}

.sources-summary {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.5rem;
    padding: 0.4rem 0 0.15rem;
    cursor: pointer;
    user-select: none;
    list-style: none;
    font-size: 0.68rem;
    color: var(--text-secondary);
}

.sources-summary::-webkit-details-marker { display: none; }
.sources-summary::marker { content: ''; }

.sources-summary:hover { color: var(--text-primary); }

/* A small oat dot leading the summary line: the answer above it is prose, and this
   line is the one place a presenter is told there is provenance to check. It is
   decoration added in CSS rather than an element in render.js on purpose — the
   renderer emits only what the payload said, and a dot says nothing. */
.sources-summary::before {
    content: '';
    width: 5px;
    height: 5px;
    border-radius: 50%;
    background: var(--oat);
    flex-shrink: 0;
}

/* The badges sit inside a summary, so their own vertical margin would push the
   summary line out of alignment with the count beside them. */
.sources-summary .state-labels { margin: 0; }

.sources-count {
    letter-spacing: 0.04em;
    text-transform: uppercase;
}

/* A disclosure affordance that does not depend on the platform marker, which is
   hidden above so the badges can lead the line. */
.sources-chevron,
.source-entry-chevron {
    margin-left: auto;
    font-size: 0.7rem;
    color: var(--text-secondary);
    transition: transform 0.15s ease, color 0.15s ease;
}

.sources-chevron::before,
.source-entry-chevron::before { content: '\203A'; }

.sources-disclosure[open] > .sources-summary .sources-chevron { transform: rotate(90deg); }
.source-entry[open] > .source-entry-header .source-entry-chevron { transform: rotate(90deg); }

/* RECESSED, not raised. The message card it sits in is already lifted off the
   canvas; stacking another raised box inside it reads as a third layer coming
   forward, when what this is is detail cut into the answer. An inset shadow plus one
   step darker than the card does that in two declarations. */
.sources-list {
    display: flex;
    flex-direction: column;
    margin: 0.6rem 0 0;
    padding: 0.3rem 0.9rem;
    border-radius: 10px;
    background: var(--bg-recessed);
    box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.4), 0 2px 8px rgba(0, 0, 0, 0.2);
}

/* The one sentence render.js writes, and only when the payload says the answer may
   not be presented as current. It points at the badges rather than naming a state. */
.caveat-note {
    margin: 0.5rem 0 0.2rem;
    font-size: 0.7rem;
    line-height: 1.5;
    color: var(--text-secondary);
}

/* ── Per-domain sections ──────────────────────────────────────────── */

.answer-sections {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}

/* A row in the recessed panel, separated by a hairline rather than boxed. The panel
   already has an edge; giving each of four domains its own box inside it produced
   five nested rectangles for one answer.
   
   The per-status LEFT edge stays, and stays 3px. It is the only place the reported
   status is encoded as something you can see without reading, and it is the
   difference between a domain that answered and one that failed — meaning, not
   decoration, which is why it keeps its own colours below rather than using
   --border-color. */
.answer-section {
    padding: 0.55rem 0 0.55rem 0.6rem;
    border: none;
    border-left: 3px solid var(--border-color);
    border-bottom: 1px solid rgba(255, 255, 255, 0.05);
    border-radius: 0;
    background: none;
}

.answer-section:last-child { border-bottom: none; }

/* A domain row is the same box, with its header as the disclosure control. */
.source-entry-header {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.5rem;
    cursor: pointer;
    user-select: none;
    list-style: none;
}

.source-entry-header::-webkit-details-marker { display: none; }
.source-entry-header::marker { content: ''; }

.source-entry-header .state-labels { margin: 0; }

.answer-section[data-status="ok"] { border-left-color: #2f7d4f; }
.answer-section[data-status="partial"] { border-left-color: #1f6c8a; }
.answer-section[data-status="no-data"] { border-left-color: var(--text-secondary); }
.answer-section[data-status="timeout"] { border-left-color: #9a5a1e; }
.answer-section[data-status="error"],
.answer-section[data-status="unavailable"] { border-left-color: #8e3b3b; }

/* Leads the row and takes the slack, so everything after it — the source pills, the
   status, the badges, the chevron — sits right-aligned as a compact strip. */
.section-domain {
    margin: 0;
    flex: 1;
    min-width: 8rem;
    font-size: 0.82rem;
    font-weight: 500;
    color: var(--text-primary);
}

/* The source identifier as a monospace pill beside the domain name, rather than a
   bulleted list under it. `fpt` and `deadline-cloud` are system identifiers, not prose,
   and setting them in mono says so — a presenter reads the row as
   "Production tracking [fpt]" in one glance instead of two lines. */
.source-entry-header .contributor-sources {
    display: flex;
    flex-wrap: wrap;
    gap: 0.3rem;
    margin: 0;
    padding: 0;
    list-style: none;
}

.source-entry-header .contributor-source {
    padding: 0.05rem 0.4rem;
    border: 1px solid var(--border-color);
    border-radius: 8px;
    font-family: 'SF Mono', Menlo, Consolas, monospace;
    font-size: 0.65rem;
    color: var(--text-secondary);
}

.section-status-missing,
.claim-ungrounded,
.freshness-missing,
.viz-view-reason-missing,
.viz-view-kind-unrecognized,
.evidence-uri-blocked-note {
    font-size: 0.7rem;
    font-style: italic;
    color: var(--text-secondary);
}

.freshness {
    display: flex;
    flex-wrap: wrap;
    gap: 0.75rem;
    margin: 0.3rem 0;
    font-size: 0.75rem;
}

.section-error {
    margin: 0.4rem 0;
    padding: 0.4rem 0.6rem;
    border-radius: 4px;
    background: rgba(142, 59, 59, 0.12);
    font-size: 0.75rem;
}

.section-claims,
.claim-evidence,
.contributor-list,
.warning-list,
.checked-source-list,
.supported-domain-list,
.contributor-sources,
.viz-warnings {
    margin: 0.3rem 0 0;
    padding-left: 1.1rem;
}

/* The claims a domain grounded, as a plain list against a rail. The design target asks for
   "two levels, not per-claim evidence fields" — so this is the LEAF of the reading path, and
   the citations below it become a quiet affordance rather than a third block.
   
   Kept in the document rather than removed. A grounded answer whose records cannot be
   reached is not verifiable, and "how do you know?" is the question this demo exists to
   answer; what the target objects to is the noise, which is a styling problem. */
.section-claims {
    list-style: none;
    margin: 0.35rem 0 0.2rem;
    padding: 0 0 0 0.7rem;
    border-left: 2px solid var(--border-color);
}

.section-claims .claim {
    padding: 0.2rem 0 0.2rem 0.4rem;
}

.claim-text {
    font-size: 0.8rem;
    line-height: 1.5;
    color: var(--text-primary);
    opacity: 0.9;
}

.claim-evidence { list-style: none; padding-left: 0.6rem; }

/* ── The domain's pooled records ──────────────────────────────────────
   One collapsed list per DOMAIN, under its claims — not one per claim, between them.
   That was the wall: a production-tracking answer cited 21 records on one claim, each
   six labelled field/value pairs, so three claims put 126 label-value pairs between
   the presenter and the next sentence.

   Nothing is hidden that carries state: the count and one badge per distinct
   integration mode are on the summary line (Requirement 9.2's per-record surface),
   and the row header above holds the information-state labels. Both stay outside the
   fold. What folds away is record identifiers and instants. */

.section-records {
    margin: 0.15rem 0 0.1rem 0.7rem;
}

/* A quiet line, not a block. The platform marker is hidden for the same reason it is on
   the two disclosures above, so the affordance is the count itself, which brightens on
   hover. Applies to the records summary AND to the identical strip that sits inline on
   each claim, where there is nothing to open. */
.evidence-summary {
    display: inline-flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.4rem;
    padding: 0;
    cursor: pointer;
    user-select: none;
    list-style: none;
    font-size: 0.62rem;
    color: var(--text-secondary);
    opacity: 0.75;
    transition: opacity 0.15s, color 0.15s;
}

.evidence-summary::-webkit-details-marker { display: none; }
.evidence-summary::marker { content: ''; }

.evidence-summary:hover {
    color: var(--text-primary);
    opacity: 1;
}

/* On a claim the strip is a `div`, so there is nothing to open and the pointer would be
   lying about that. It still carries the mode badges, which is why it is here at all. */
.claim > .evidence-summary { cursor: default; }
.claim > .evidence-summary:hover { opacity: 0.75; color: var(--text-secondary); }

/* A successful domain's status word is the least informative thing on its row: the row's
   own left edge already encodes the status in colour, and the badges beside it carry the
   information state. So `ok` recedes and every other status keeps full contrast.

   Keyed off the data attribute rather than branched on in render.js, deliberately: the
   renderer holds no status vocabulary, and a comparison against 'ok' there would be the
   first entry in one. Requirement 3.4 constrains the UNSUCCESSFUL statuses — error,
   no-data, timeout, unavailable — and every one of those is untouched here. */
.answer-section[data-status="ok"] > .source-entry-header .section-status {
    opacity: 0.55;
}

.answer-section[data-status="ok"] > .source-entry-header .section-status .field-label {
    display: none;
}

.evidence-count {
    letter-spacing: 0.04em;
    text-transform: uppercase;
}

/* One citation, one line. The fields stay separate elements — each is a distinct
   payload value — but none of them wraps, so a ref reads left to right instead of
   stacking into six rows. */
.evidence-ref {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    column-gap: 0.6rem;
    row-gap: 0.15rem;
    margin: 0.15rem 0;
    padding: 0.15rem 0.5rem;
    border-left: 2px solid var(--border-color);
    font-size: 0.68rem;
}

.evidence-ref > div { white-space: nowrap; }

/* The record id is the useful part, so it is the only one set in mono and at full
   contrast. */
.evidence-ref .evidence-record-id .field-value {
    font-family: 'SF Mono', Menlo, Consolas, monospace;
}

/* The instants are the bulk of the text and the least often read. Dimmed rather
   than truncated: a shortened timestamp is a different timestamp. */
.evidence-observed-at .field-value,
.evidence-retrieved-at .field-value {
    color: var(--text-secondary);
}

.evidence-ref .field-label {
    font-size: 0.58rem;
    letter-spacing: 0.02em;
    opacity: 0.7;
}

.evidence-uri { color: #7fc9e8; word-break: break-all; }

.evidence-uri-blocked {
    display: inline-flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.4rem;
}

.evidence-uri-text {
    padding: 0.05rem 0.3rem;
    border-radius: 3px;
    background: rgba(255, 255, 255, 0.06);
    color: var(--text-secondary);
    word-break: break-all;
}

.warning-item, .checked-source, .supported-domain, .viz-warning {
    font-size: 0.72rem;
    color: var(--text-secondary);
}

/* ── Contributor attribution ──────────────────────────────────────── */

.contributors {
    margin-top: 0.5rem;
    padding-top: 0.4rem;
    border-top: 1px solid var(--border-color);
}

.contributors-heading {
    margin: 0 0 0.2rem;
    font-size: 0.65rem;
    font-weight: 500;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--text-secondary);
}

.contributor-list { list-style: none; padding-left: 0; }

.contributor {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.5rem;
    margin: 0.2rem 0;
}

.contributor-agent { font-size: 0.78rem; font-weight: 500; color: var(--text-primary); }

.contributor-sources { display: flex; flex-wrap: wrap; gap: 0.4rem; list-style: none; padding-left: 0; }

.contributor-source {
    padding: 0.05rem 0.4rem;
    border: 1px solid var(--border-color);
    border-radius: 8px;
    font-size: 0.65rem;
    color: var(--text-secondary);
}

/* ── Visualization block ──────────────────────────────────────────── */

/* ITS OWN BLOCK, AT THE TOP, FULL BLEED.
   When a question asked to be shown something, the thing it asked for leads and the prose
   explains it underneath. The negative margins cancel `.message.assistant`'s 1.1rem
   padding and 0.9rem top padding exactly, so the chart meets the card's own edges and
   reads as a block in the transcript rather than a panel indented inside the answer —
   a fifty-six-row Gantt inset twice and boxed per view read as a thumbnail of a chart
   instead of the chart.

   Its own recessed ground and a rule along the bottom separate it from the prose without
   stacking a third raised layer inside an already-raised card. */
.viz-block {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    margin: -0.9rem -1.1rem 0.75rem;
    padding: 0;
    border-bottom: 1px solid var(--border-color);
    border-radius: 14px 14px 0 0;
    background: var(--bg-recessed);
    overflow: hidden;
}

/* The card holds only drawn views now, so it holds only frames, and a frame needs no
   padding around it: the view's own document has 1rem of its own inside. Padding here was
   a second margin around a chart that already had one. */
.viz-block > .viz-view + .viz-view {
    border-top: 1px solid var(--border-color);
}

/* ── What the visualization boundary said that was not a chart ─────────
   Views that drew nothing, rendering warnings, a refused result. Sits with the provenance
   under the answer, at the provenance's weight, because it is the same kind of thing: true,
   checkable, and not what was asked for. */
.viz-notes {
    display: flex;
    flex-direction: column;
    gap: 0.2rem;
    margin: 0;
    padding-top: 0.3rem;
    border-top: 1px solid var(--border-color);
}

/* --- account views (diagnostic evidence), collapsed beneath the answer's own charts -------
 *
 * Styled as a disclosure rather than a card, because that is what it is: a restatement of the
 * answer with its citations, kept one click away so the chart the question asked for is the
 * first thing on screen. Sized like the sources disclosure it sits beside, not like a chart.
 */
.account-views-disclosure {
    margin: 0.5rem 0 0;
    border-top: 1px solid var(--border-color);
    padding-top: 0.45rem;
}

.account-views-summary {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.15rem 0;
    cursor: pointer;
    user-select: none;
    list-style: none;
    font-size: 0.72rem;
    color: var(--text-secondary);
}

.account-views-summary::-webkit-details-marker { display: none; }
.account-views-summary:hover { color: var(--text-primary); }
.account-views-summary .sources-chevron { margin-left: auto; }
.account-views-disclosure[open] > .account-views-summary .sources-chevron {
    transform: rotate(90deg);
}

.account-views-body {
    margin-top: 0.5rem;
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
}

.undrawn-views-disclosure { margin: 0; }

.undrawn-views-summary {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.15rem 0;
    cursor: pointer;
    user-select: none;
    list-style: none;
    font-size: 0.68rem;
    color: var(--text-secondary);
}

.undrawn-views-summary::-webkit-details-marker { display: none; }
.undrawn-views-summary:hover { color: var(--text-primary); }
.undrawn-views-summary .sources-chevron { margin-left: auto; }
.undrawn-views-disclosure[open] > .undrawn-views-summary .sources-chevron {
    transform: rotate(90deg);
}

.undrawn-view-list {
    list-style: none;
    margin: 0.25rem 0 0;
    padding: 0 0 0 0.1rem;
    display: flex;
    flex-direction: column;
    gap: 0.3rem;
}

/* One row per view: what it was, what state it was checked under, and why it is empty. */
.undrawn-view {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.4rem;
    padding: 0.3rem 0;
    border-top: 1px solid var(--border-color);
    font-size: 0.72rem;
    color: var(--text-secondary);
}

.undrawn-view:first-child { border-top: none; }

.undrawn-view-kind {
    font-family: 'SF Mono', Menlo, Consolas, monospace;
    font-size: 0.68rem;
    color: var(--text-primary);
}

.undrawn-view .state-labels { margin: 0; }
.undrawn-view-reason { flex: 1 1 100%; line-height: 1.45; }

.viz-refusal {
    padding: 0.6rem 0.75rem;
    border: 1px solid #8e3b3b;
    border-radius: 6px;
    background: rgba(142, 59, 59, 0.12);
    font-size: 0.78rem;
}

.viz-refusal-heading { font-weight: 600; margin-bottom: 0.25rem; }

/* Rendering warnings, folded. Six views that each carried no payload produce six
   warnings ahead of the views, which is where the eye lands first. */
.viz-warnings-summary {
    padding: 0.1rem 0;
    cursor: pointer;
    user-select: none;
    font-size: 0.68rem;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--text-secondary);
}

.viz-warnings-summary:hover { color: var(--text-primary); }

/* No box. The view's own document already draws panels with borders and shadows, so a
   border here is a rectangle around a stack of rectangles. What identifies the view is
   its header line; what shows it is the frame. */
.viz-view {
    padding: 0;
    border: none;
    border-radius: 0;
}

.viz-view-title { margin: 0 0 0.2rem; font-size: 0.85rem; font-weight: 600; }

/* The header is metadata about the frame below it, so it reads at the weight of the
   other metadata rows in this card rather than competing with the chart. */
.viz-view-header {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.5rem;
    font-size: 0.68rem;
    color: var(--text-secondary);
}

.viz-view-header .state-labels { margin: 0; }

/* Mount for the sandboxed iframe (spec task 5.1). The frame itself carries the
   inherited .viz-iframe sizing. */
.viz-view-content { margin-top: 0.35rem; min-width: 0; }

/* Shown instead of the frame when nothing was embedded — a stated gap, never a
   plausible-looking substitute for a view (Requirement 4.3). */
.viz-view-not-embedded {
    padding: 1rem;
    border: 1px dashed var(--border-color);
    border-radius: 6px;
    font-size: 0.75rem;
    text-align: center;
    color: var(--text-secondary);
}

.viz-view-not-embedded-note { font-weight: 600; margin-bottom: 0.2rem; }

.viz-view-not-embedded-reason { display: block; }

/* ── Operating-mode banner ────────────────────────────────────────── */

.mode-banner-host { flex-shrink: 0; }

/* A quiet line across the top of the reading column, not a boxed alert. It is on
   screen for the whole demo, and an uppercase bordered banner shouting at a
   presenter for two hours stops being read — which is the opposite of what
   Requirement 9.2 wants from it.
   
   So: sentence case, oat, no border, and a background faint enough to be a tint
   rather than a box. Still unmissable when it changes, because nothing else on the
   canvas is oat. */
.mode-banner {
    padding: 0.55rem 2rem 0;
    font-size: 0.72rem;
    font-weight: 400;
    letter-spacing: 0.01em;
    text-align: center;
    color: var(--oat);
    background: transparent;
}

/* A simulated run is the case this exists for, so it gets the weight and the tint —
   the honest inverse of the usual instinct, which would have styled `live` loudly and
   left the misleading state looking ordinary. */
.mode-banner[data-simulated="true"] {
    padding: 0.5rem 2rem;
    font-weight: 600;
    background: rgba(255, 244, 224, 0.07);
    color: var(--oat);
}

/* Per-source integration-mode badges (Requirement 9.2's second surface).
   The banner above states the mode the *bridge* was deployed in; this states the
   mode each *record* came from, carried by EvidenceRef.integrationMode and applied
   by render.js as a data attribute. The two are independent, which is the point: a
   live-mode banner over a simulated record must still read as simulated.

   Keyed to the evidence envelope's three values (live | simulated | cached), sharing
   the .state-badge palette so the same information state reads the same way wherever
   it appears. A value outside the three keeps the base badge and no colour, because
   colouring a mode this component does not recognize would be a claim about it. */

.evidence-integration-mode[data-integration-mode] {
    display: inline-flex;
    align-items: baseline;
    gap: 0.25rem;
    padding: 0.1rem 0.5rem;
    border: 1px solid var(--border-color);
    border-radius: 10px;
    font-size: 0.65rem;
    font-weight: 600;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    color: var(--text-primary);
    background: rgba(255, 255, 255, 0.04);
}

.evidence-integration-mode[data-integration-mode="live"] {
    border-color: #2f7d4f;
    color: #6ee7a0;
}

.evidence-integration-mode[data-integration-mode="cached"] {
    border-color: #8a6d1f;
    color: #f0c869;
}

.evidence-integration-mode[data-integration-mode="simulated"] {
    border-color: #6d4bb3;
    color: #c4a6ff;
}

.render-refusal {
    padding: 0.6rem 0.75rem;
    border: 1px solid #8e3b3b;
    border-radius: 6px;
    background: rgba(142, 59, 59, 0.12);
    font-size: 0.8rem;
    color: var(--text-primary);
}

/* ==========================================================================
   Persona / Demo_Scenario selectors (spec task 3.1's browser half)
   Replaced NAB's `.model-selector-*` dropdown, whose markup is gone from
   index.html. The two lists come from the bridge's registry routes.
   ========================================================================== */

.query-selectors {
    display: flex;
    align-items: center;
    gap: 0.75rem;
}

.query-selector {
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
}

.query-selector-label {
    font-size: 0.6rem;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    opacity: 0.6;
    color: var(--text-primary);
}

.query-selector-control {
    min-width: 11rem;
    max-width: 15rem;
    padding: 0.3rem 0.45rem;
    border: 1px solid rgba(255, 255, 255, 0.14);
    border-radius: 6px;
    background: rgba(255, 255, 255, 0.05);
    color: var(--text-primary);
    font-size: 0.78rem;
}

.query-selector-control:disabled {
    opacity: 0.5;
}

/* ==========================================================================
   Recorded history entries (spec task 8.1, Requirement 8.3)
   A historical answer is rendered by the same renderer as a live one, so it
   needs no styling of its own -- only the stamp that says it is a record of
   what was true when it was given, rather than the current state.
   ========================================================================== */

.history-entry {
    border-left: 2px solid rgba(255, 255, 255, 0.12);
    margin: 0.75rem 0;
    padding-left: 0.75rem;
}

.history-recorded {
    font-size: 0.68rem;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    opacity: 0.65;
    margin-bottom: 0.35rem;
    color: var(--text-primary);
}

.history-empty {
    padding: 0.75rem;
    font-size: 0.8rem;
    opacity: 0.7;
    color: var(--text-primary);
}
