/* ==========================================================================
   SA Smart Assistant — dark industrial premium
   Register: product UI (control-room tool). Restrained color strategy:
   neutral slate surfaces, one steel-blue accent, semantic colors for state.
   ========================================================================== */

* {
  box-sizing: border-box;
}

:root {
  /* Surfaces (darkest → lightest) */
  --page: #0a0d12;
  --panel: #10141b;
  --panel-strong: #141924;
  --surface: #171d28;
  --surface-soft: #1d2430;
  --raised: #212a38;

  /* Lines */
  --line: rgba(151, 168, 192, 0.13);
  --line-strong: rgba(151, 168, 192, 0.24);

  /* Ink */
  --text: #e9eef5;
  --muted: #9aa7b8;
  --faint: #7f8ea3; /* 5.5:1 on --panel (was #718093 at a borderline 4.6:1) */

  /* Accent — one hue, three duties */
  --accent: #3f7fe8;          /* fills (primary buttons, user bubble) */
  --accent-bright: #79aaff;   /* text, icons, focus on dark */
  --accent-soft: rgba(63, 127, 232, 0.14); /* tints, selection */

  /* Semantic state */
  --ok: #2fbe86;
  --ok-bright: #55dfa6;
  --warn: #dfa63e;
  --warn-bright: #f0c169;
  --bad: #d64545;
  --bad-bright: #ff7a7a;
  --blue: var(--accent);
  --green: var(--ok);
  --amber: var(--warn);
  --red: var(--bad);
  --accent-cyan: var(--accent-bright);
  --accent-magenta: var(--accent-bright);

  /* Components */
  --input-bg: #0d1117;
  --input-text: var(--text);
  --chat-bg: #0d1219;
  --assistant-bubble: #1a212d;
  --user-bubble: var(--accent);
  --button-bg: #202836;
  --button-hover: #273142;
  --surface-glass: var(--panel);

  /* Elevation */
  --shadow: 0 1px 0 rgba(255, 255, 255, 0.03) inset, 0 24px 48px -12px rgba(0, 0, 0, 0.5);
  --panel-shadow: 0 1px 0 rgba(255, 255, 255, 0.03) inset, 0 12px 28px -10px rgba(0, 0, 0, 0.45);

  /* Motion */
  --ease-out: cubic-bezier(0.22, 1, 0.36, 1);
  --t-fast: 150ms;
  --t-med: 220ms;

  /* Radius scale */
  --r-sm: 7px;
  --r-md: 10px;
  --r-lg: 14px;

  /* Z scale */
  --z-sticky: 10;
  --z-overlay: 20;
  --z-toast: 30;
}

body.theme-dark {
  --page: #0a0d12;
  --panel: #10141b;
  --panel-strong: #141924;
  --surface: #171d28;
  --surface-soft: #1d2430;
  --surface-glass: var(--panel);
  --line: rgba(151, 168, 192, 0.13);
  --text: #e9eef5;
  --muted: #9aa7b8;
  --input-bg: #0d1117;
  --input-text: var(--text);
  --chat-bg: #0d1219;
  --assistant-bubble: #1a212d;
  --user-bubble: var(--accent);
  --button-bg: #202836;
  --button-hover: #273142;
}

html,
body {
  margin: 0;
  width: 100%;
  height: 100%;
  font-family: "Inter", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Helvetica, Arial, sans-serif;
  font-feature-settings: "cv05", "tnum" 0;
  background: var(--page);
  color: var(--text);
  overflow: hidden;
  -webkit-font-smoothing: antialiased;
}

body {
  background:
    radial-gradient(1100px 640px at -8% -12%, rgba(63, 127, 232, 0.07), transparent 60%),
    var(--page);
}

body.theme-dark {
  background:
    radial-gradient(1100px 640px at -8% -12%, rgba(63, 127, 232, 0.07), transparent 60%),
    var(--page);
}

::selection {
  background: var(--accent-soft);
  color: var(--text);
}

/* Focus vocabulary — one ring everywhere */
button:focus-visible,
a:focus-visible,
input:focus-visible {
  outline: 2px solid var(--accent-bright);
  outline-offset: 2px;
}

/* --------------------------------------------------------------------------
   Shell
   -------------------------------------------------------------------------- */

.app {
  width: 100%;
  height: 100vh;
  height: 100dvh;
  max-width: 760px;
  display: flex;
  flex-direction: column;
  min-height: 0;
  padding: clamp(10px, 1.6dvh, 20px);
  gap: clamp(8px, 1.25dvh, 16px);
  background: var(--panel);
  border-right: 1px solid var(--line);
  box-shadow: var(--shadow);
  position: relative;
  overflow-x: hidden;
  overflow-y: auto;
  scrollbar-gutter: stable;
}

.app > * {
  position: relative;
  z-index: 1;
}

.app-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: clamp(8px, 1.1dvh, 14px);
  text-align: left;
  margin: 0;
  padding: 2px 2px 0;
  flex-shrink: 0;
}

.app-header h1 {
  margin: 0;
  font-size: clamp(16px, 2dvh, 19px);
  line-height: 1.2;
  font-weight: 650;
  letter-spacing: -0.011em;
}

.app-header p {
  margin: 2px 0 0;
  color: var(--muted);
  font-size: 13px;
}

/* Session-saved toast */
.session-toast {
  position: fixed;
  left: 50%;
  bottom: 22px;
  transform: translate(-50%, 12px);
  z-index: var(--z-toast);
  max-width: min(90vw, 420px);
  padding: 11px 16px;
  border: 1px solid rgba(47, 190, 134, 0.4);
  border-radius: var(--r-md);
  background: color-mix(in srgb, var(--panel-strong) 92%, transparent);
  color: var(--text);
  font-size: 13px;
  font-weight: 550;
  box-shadow: var(--shadow);
  backdrop-filter: blur(10px);
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--t-med) var(--ease-out), transform var(--t-med) var(--ease-out);
}

.session-toast::before {
  content: "";
  display: inline-block;
  width: 8px;
  height: 8px;
  margin-right: 9px;
  border-radius: 50%;
  background: var(--ok-bright);
  vertical-align: 1px;
  box-shadow: 0 0 0 3px rgba(47, 190, 134, 0.18);
}

.session-toast.show {
  opacity: 1;
  transform: translate(-50%, 0);
}

/* System health dots: PLC / SCADA tracker / Smart Assistant */
.system-status {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-top: 5px;
}

.system-status-item {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  color: var(--muted);
  font-size: 10.5px;
  font-weight: 650;
  letter-spacing: 0.05em;
  cursor: default;
}

.system-status-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: #64748b;
  transition: background var(--t-med) var(--ease-out), box-shadow var(--t-med) var(--ease-out);
}

.system-status-item.ok .system-status-dot {
  background: var(--ok-bright);
  box-shadow: 0 0 0 2px rgba(47, 190, 134, 0.16);
}

.system-status-item.warn .system-status-dot {
  background: var(--warn-bright);
  box-shadow: 0 0 0 2px rgba(223, 166, 62, 0.18);
}

.system-status-item.bad .system-status-dot {
  background: var(--bad-bright);
  box-shadow: 0 0 0 2px rgba(214, 69, 69, 0.2);
  animation: pulse 1.4s infinite;
}

.avatar-mode-controls {
  display: flex;
  gap: 6px;
  align-items: center;
  flex-shrink: 0;
}

.avatar-mode-btn,
.external-tool-link {
  min-height: 34px;
  padding: 7px 12px;
  border: 1px solid var(--line-strong);
  background: var(--button-bg);
  color: var(--muted);
  border-radius: var(--r-sm);
  font-size: 12.5px;
  font-weight: 600;
  letter-spacing: 0.01em;
  cursor: pointer;
  transition: background var(--t-fast) var(--ease-out), color var(--t-fast) var(--ease-out), border-color var(--t-fast) var(--ease-out);
}

.external-tool-link {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  text-decoration: none;
  white-space: nowrap;
}

.avatar-mode-btn.active {
  background: var(--accent-soft);
  color: var(--accent-bright);
  border-color: rgba(63, 127, 232, 0.45);
}

.avatar-mode-btn:hover:not(:disabled),
.external-tool-link:hover {
  background: var(--button-hover);
  color: var(--text);
}

.avatar-mode-btn.active:hover:not(:disabled) {
  background: var(--accent-soft);
  color: var(--accent-bright);
}

.avatar-mode-btn:disabled {
  opacity: 0.5;
  cursor: not-allowed;
  transform: none;
}

.main-layout {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: clamp(8px, 1.5dvh, 18px);
  min-height: 0;
}

/* --------------------------------------------------------------------------
   Agent panel
   -------------------------------------------------------------------------- */

.agent-panel {
  display: grid;
  grid-template-columns: minmax(160px, 210px) minmax(0, 1fr);
  grid-template-areas:
    "agent status"
    "agent controls"
    "agent controls";
  align-items: start;
  gap: 12px 16px;
  flex-shrink: 0;
  flex: 0 0 auto;
  padding: clamp(10px, 1.35dvh, 16px);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: linear-gradient(180deg, var(--panel-strong), var(--panel));
  box-shadow: var(--panel-shadow);
  transition:
    flex-basis 0.55s var(--ease-out),
    min-height 0.55s var(--ease-out),
    padding 0.35s ease,
    background 0.35s ease,
    border-color 0.35s ease;
}

.agent-status {
  grid-area: status;
  display: flex;
  align-items: center;
  gap: 9px;
  color: var(--text);
  font-size: 13px;
  font-weight: 550;
  font-variant-numeric: tabular-nums;
  min-height: 34px;
  padding: 7px 11px;
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--surface);
}

.status-dot {
  width: 8px;
  height: 8px;
  background: var(--ok-bright);
  border-radius: 50%;
  box-shadow: 0 0 0 3px rgba(47, 190, 134, 0.16);
  flex-shrink: 0;
  transition: background var(--t-med) var(--ease-out), box-shadow var(--t-med) var(--ease-out);
}

.status-dot.thinking {
  background: var(--warn-bright);
  box-shadow: 0 0 0 3px rgba(223, 166, 62, 0.18);
  animation: pulse 1.1s infinite var(--ease-out);
}

.status-dot.speaking {
  background: var(--accent-bright);
  box-shadow: 0 0 0 3px rgba(63, 127, 232, 0.22);
  animation: pulse 0.85s infinite var(--ease-out);
}

.controls {
  grid-area: controls;
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(6px, 0.85dvh, 10px);
  align-items: stretch;
  align-content: start;
}

/* Buttons: secondary = raised neutral · primary = accent · destructive = red */
.control-btn {
  width: 100%;
  min-width: 0;
  min-height: clamp(46px, 6dvh, 64px);
  padding: clamp(8px, 1.1dvh, 12px);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-md);
  background: var(--button-bg);
  color: var(--text);
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  line-height: 1.2;
  overflow-wrap: anywhere;
  font-size: 14px;
  font-weight: 600;
  letter-spacing: 0.005em;
  cursor: pointer;
  transition: background var(--t-fast) var(--ease-out), border-color var(--t-fast) var(--ease-out), color var(--t-fast) var(--ease-out), transform var(--t-fast) var(--ease-out), box-shadow var(--t-fast) var(--ease-out);
}

.control-btn:hover:not(:disabled) {
  background: var(--button-hover);
  border-color: rgba(151, 168, 192, 0.34);
}

.control-btn:active:not(:disabled) {
  transform: translateY(1px);
}

.control-btn.active {
  background: rgba(47, 190, 134, 0.14);
  border-color: rgba(47, 190, 134, 0.5);
  color: var(--ok-bright);
}

.control-btn.danger,
.control-btn.active.danger,
.control-btn[data-state="stoppable"] {
  background: rgba(214, 69, 69, 0.15);
  border-color: rgba(214, 69, 69, 0.55);
  color: var(--bad-bright);
}

.control-btn.danger:hover:not(:disabled),
.control-btn.active.danger:hover:not(:disabled),
.control-btn[data-state="stoppable"]:hover:not(:disabled) {
  background: rgba(214, 69, 69, 0.24);
}

.control-btn.talk-toggle {
  font-size: 14px;
}

/* Off is a resting state — neutral, not alarming */
.control-btn.talk-toggle:not(.active) {
  background: var(--button-bg);
  color: var(--muted);
}

.control-btn.talk-toggle:not(.active):hover:not(:disabled) {
  color: var(--text);
}

.control-btn.talk-toggle.active {
  background: rgba(47, 190, 134, 0.14);
  border-color: rgba(47, 190, 134, 0.5);
  color: var(--ok-bright);
}

/* Primary action */
.control-btn.conversation {
  grid-column: 1 / -1;
  min-height: clamp(44px, 5.4dvh, 56px);
  background: var(--accent);
  border-color: transparent;
  color: #ffffff;
  box-shadow: 0 1px 0 rgba(255, 255, 255, 0.14) inset, 0 8px 20px -6px rgba(63, 127, 232, 0.45);
}

.control-btn.conversation:hover:not(:disabled) {
  background: #4c8bee;
  border-color: transparent;
}

.control-btn.conversation.active {
  background: rgba(47, 190, 134, 0.16);
  border-color: rgba(47, 190, 134, 0.55);
  color: var(--ok-bright);
  box-shadow: none;
}

.piper-status {
  grid-column: 1 / -1;
  min-height: 18px;
  color: var(--muted);
  font-size: 12px;
  text-align: center;
  font-variant-numeric: tabular-nums;
}

/* When the voice label and the conversation status carry the same message,
   only the conversation status is shown (see dedupeStatusLabels). */
.piper-status.status-duplicate {
  visibility: hidden;
}

.conversation-status {
  grid-column: 1 / -1;
  min-height: 18px;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
  color: var(--muted);
  font-size: 12px;
  text-align: center;
}

.conversation-status.loading::after {
  content: "";
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--ok-bright);
  box-shadow: 10px 0 0 var(--ok-bright), 20px 0 0 var(--ok-bright);
  animation: dotWave 0.9s infinite ease-in-out;
  transform: translateY(0);
}

/* --------------------------------------------------------------------------
   Agent orb
   -------------------------------------------------------------------------- */

.chat-panel {
  flex: 1;
  display: flex;
  flex-direction: column;
  min-height: 0;
  gap: clamp(7px, 1dvh, 12px);
  transition:
    flex 0.55s var(--ease-out),
    min-height 0.55s var(--ease-out),
    opacity 0.35s ease;
}

.agent-orb {
  grid-area: agent;
  position: relative;
  width: 100%;
  min-height: clamp(140px, 24dvh, 220px);
  align-self: stretch;
  flex-shrink: 0;
  display: block;
  border-radius: var(--r-md);
  background: var(--surface);
  border: 1px solid var(--line-strong);
  box-shadow: 0 12px 24px -8px rgba(0, 0, 0, 0.4);
  overflow: hidden;
  transition:
    min-height 0.55s var(--ease-out),
    box-shadow 0.35s ease,
    border-color 0.35s ease;
}

.agent-orb::after {
  display: none;
}

.agent-orb.thinking {
  border-color: rgba(223, 166, 62, 0.75);
  box-shadow: 0 0 0 3px rgba(223, 166, 62, 0.14), 0 14px 30px -8px rgba(0, 0, 0, 0.45);
}

.agent-orb.speaking {
  border-color: rgba(63, 127, 232, 0.8);
  box-shadow: 0 0 0 3px rgba(63, 127, 232, 0.16), 0 14px 30px -8px rgba(0, 0, 0, 0.45);
  animation: orbTalk 1s infinite ease-in-out;
}

.agent-orb-image,
.agent-orb-video {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center top;
  transform: none;
  border-radius: calc(var(--r-md) - 1px);
  background: var(--surface);
}

.agent-orb-image {
  position: relative;
  z-index: 1;
}

.agent-orb-video {
  position: absolute;
  inset: 0;
  opacity: 0;
  pointer-events: none;
  z-index: 2;
  transition: opacity var(--t-med) var(--ease-out);
}

.agent-orb-video.active {
  opacity: 1;
}

/* Chunked avatar speech: between clips the video dims briefly instead of
   snapping to the static photo, then fades back as the next clip starts. */
.agent-orb-video.active.chunk-fade {
  opacity: 0.35;
}

/* Freeze-frame overlay for chunk transitions: holds the last frame of the
   previous clip at full opacity while the next clip loads underneath, so the
   avatar never flashes blank or drops to the static photo mid-answer. */
.agent-orb-freeze {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center top;
  border-radius: calc(var(--r-md) - 1px);
  z-index: 3;
  opacity: 0;
  pointer-events: none;
}

.agent-orb-ring {
  display: none;
}

/* --------------------------------------------------------------------------
   Chat
   -------------------------------------------------------------------------- */

.chat-box {
  flex: 1;
  min-height: clamp(140px, 25dvh, 260px);
  position: relative;
  background: var(--chat-bg);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  padding: clamp(12px, 1.4dvh, 16px);
  overflow-y: auto;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.03);
  transition:
    min-height 0.55s var(--ease-out),
    padding 0.35s ease,
    opacity 0.35s ease;
}

.chat-box > * {
  position: relative;
  z-index: 1;
}

.chat-box::-webkit-scrollbar {
  width: 8px;
}

.chat-box::-webkit-scrollbar-thumb {
  background: rgba(151, 168, 192, 0.22);
  border-radius: 999px;
}

.chat-box::-webkit-scrollbar-thumb:hover {
  background: rgba(151, 168, 192, 0.34);
}

.message-wrapper {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  margin-bottom: 12px;
}

.message-wrapper.user-wrap {
  justify-content: flex-end;
}

.message-wrapper.assistant-wrap {
  justify-content: flex-start;
}

.message {
  max-width: 86%;
  padding: 11px 14px;
  border-radius: 12px;
  line-height: 1.5;
  white-space: pre-wrap;
  word-wrap: break-word;
  font-size: 14.5px;
  border: 1px solid transparent;
  text-wrap: pretty;
}

/* New bubbles rise in gently - state feedback that an exchange landed.
   Runs once per appended wrapper; streaming updates inside the same node
   never retrigger it. */
.message-wrapper {
  animation: msgIn var(--t-med) var(--ease-out) both;
}

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

.user {
  background: var(--user-bubble);
  color: #ffffff;
  border-bottom-right-radius: 5px;
  box-shadow: 0 6px 16px -6px rgba(63, 127, 232, 0.4);
}

.assistant {
  background: var(--assistant-bubble);
  color: var(--text);
  border-color: var(--line);
  border-bottom-left-radius: 5px;
}

/* Rendered Markdown answers (avatar-off mode) --------------------------- */
.message.markdown {
  white-space: normal;
}

.message.markdown > *:first-child { margin-top: 0; }
.message.markdown > *:last-child { margin-bottom: 0; }

.message.markdown p {
  margin: 0 0 10px;
}

.message.markdown h3,
.message.markdown h4,
.message.markdown h5,
.message.markdown h6 {
  margin: 14px 0 8px;
  line-height: 1.25;
  font-weight: 650;
  letter-spacing: -0.005em;
  color: var(--text);
}

.message.markdown h3 { font-size: 16px; }
.message.markdown h4 { font-size: 14.5px; }
.message.markdown h5,
.message.markdown h6 { font-size: 13.5px; color: var(--muted); }

.message.markdown ul,
.message.markdown ol {
  margin: 6px 0 10px;
  padding-left: 22px;
}

.message.markdown li {
  margin: 3px 0;
  line-height: 1.5;
}

.message.markdown li::marker {
  color: var(--accent-bright);
}

.message.markdown strong {
  font-weight: 650;
  color: #f4f7fb;
}

.message.markdown code {
  padding: 1px 5px;
  border-radius: 5px;
  background: rgba(151, 168, 192, 0.16);
  font-family: ui-monospace, "Cascadia Code", "Consolas", monospace;
  font-size: 0.92em;
}

.message.markdown hr {
  margin: 12px 0;
  border: none;
  border-top: 1px solid var(--line-strong);
}

.message.markdown a {
  color: var(--accent-bright);
  text-decoration: underline;
}

/* Tables — clean comparison layout */
.message.markdown table {
  width: 100%;
  margin: 6px 0 10px;
  border-collapse: collapse;
  font-size: 13.5px;
}

.message.markdown thead th {
  padding: 7px 12px 7px 0;
  text-align: left;
  font-weight: 650;
  color: var(--muted);
  border-bottom: 1px solid var(--line-strong);
  white-space: nowrap;
}

.message.markdown tbody td {
  padding: 8px 12px 8px 0;
  border-bottom: 1px solid var(--line);
  vertical-align: top;
}

.message.markdown tbody tr:last-child td {
  border-bottom: none;
}

.message.markdown th:not(:first-child),
.message.markdown td:not(:first-child) {
  padding-left: 12px;
}

.speak-btn {
  border: 1px solid var(--line-strong);
  border-radius: var(--r-sm);
  background: var(--button-bg);
  color: var(--muted);
  padding: 7px 10px;
  cursor: pointer;
  font-size: 12.5px;
  font-weight: 600;
  flex-shrink: 0;
  transition: background var(--t-fast) var(--ease-out), color var(--t-fast) var(--ease-out);
}

.speak-btn:hover:not(:disabled) {
  background: var(--button-hover);
  color: var(--text);
}

.speak-btn:disabled,
.control-btn:disabled {
  background: var(--surface);
  border-color: var(--line);
  color: rgba(154, 167, 184, 0.55);
  cursor: not-allowed;
}

/* --------------------------------------------------------------------------
   Progress
   -------------------------------------------------------------------------- */

.progress-container {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  padding: 10px 12px;
  flex-shrink: 0;
}

.progress-container.hidden {
  display: none;
}

.progress-label {
  display: flex;
  justify-content: space-between;
  gap: 10px;
  font-size: 12.5px;
  color: var(--muted);
  margin-bottom: 8px;
  font-variant-numeric: tabular-nums;
}

.progress-bar {
  width: 100%;
  height: 6px;
  background: rgba(0, 0, 0, 0.35);
  border-radius: 999px;
  overflow: hidden;
}

.progress-fill {
  /* Full-width bar scaled on X so progress animates on the compositor
     (transform) instead of re-laying-out width every frame. script.js
     drives it via style.transform = scaleX(0..1). */
  width: 100%;
  height: 100%;
  background: var(--accent);
  border-radius: 999px;
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform 0.15s linear;
}

/* --------------------------------------------------------------------------
   Input row
   -------------------------------------------------------------------------- */

.input-area {
  display: grid;
  grid-template-columns: 62px 1fr 92px;
  gap: 8px;
  flex-shrink: 0;
}

/* The input and the in-field upload button share one relative wrapper so the
   "+" sits inside the input pill. */
.input-wrap {
  position: relative;
  min-width: 0;
  display: flex;
}

.input-wrap .chat-input {
  padding-left: 42px;
}

/* Clear, unboxed "+" inside the input. Overrides the global button chrome. */
.pdf-upload-btn {
  position: absolute;
  left: 6px;
  top: 50%;
  transform: translateY(-50%);
  display: flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  min-height: 0;
  min-width: 0;
  padding: 0;
  border: none;
  border-radius: 50%;
  background: transparent;
  box-shadow: none;
  color: var(--muted);
  z-index: 2;
  transition: color var(--t-fast) var(--ease-out), background var(--t-fast) var(--ease-out);
}

.pdf-upload-btn:hover:not(:disabled),
.pdf-upload-btn[aria-expanded="true"] {
  background: rgba(151, 168, 192, 0.14);
  color: var(--text);
  box-shadow: none;
}

.pdf-upload-btn:active:not(:disabled) {
  transform: translateY(-50%);
  background: rgba(151, 168, 192, 0.2);
  box-shadow: none;
}

.pdf-upload-icon {
  display: block;
}

.pdf-upload-btn.loaded {
  color: var(--accent-bright);
}

.pdf-upload-btn.uploading {
  color: var(--warn-bright);
}

/* "Upload PDF" popup that opens from the "+". */
.pdf-upload-menu {
  position: absolute;
  left: 0;
  bottom: calc(100% + 8px);
  z-index: var(--z-overlay);
  padding: 5px;
  border: 1px solid var(--line-strong);
  border-radius: var(--r-md);
  background: var(--panel-strong);
  box-shadow: var(--shadow);
}

.pdf-upload-menu.hidden {
  display: none;
}

.pdf-upload-menu-item {
  display: flex;
  align-items: center;
  gap: 9px;
  width: 100%;
  min-height: 0;
  padding: 8px 14px 8px 10px;
  border: none;
  border-radius: var(--r-sm);
  background: transparent;
  box-shadow: none;
  color: var(--text);
  font-size: 13.5px;
  font-weight: 550;
  white-space: nowrap;
}

.pdf-upload-menu-item:hover:not(:disabled) {
  background: var(--accent-soft);
  border-color: transparent;
  box-shadow: none;
}

.pdf-upload-menu-item svg {
  flex-shrink: 0;
  color: var(--accent-bright);
}

/* Uploaded-PDF chip sits just above the input row. */
.pdf-chip {
  display: flex;
  align-items: center;
  gap: 8px;
  max-width: 100%;
  margin-bottom: 8px;
  padding: 6px 10px;
  border-radius: var(--r-md);
  border: 1px solid rgba(63, 127, 232, 0.4);
  background: rgba(63, 127, 232, 0.12);
  color: #d7e4ff;
  font-size: 13px;
}

.pdf-chip.hidden {
  display: none;
}

.pdf-chip.error {
  border-color: rgba(223, 96, 96, 0.5);
  background: rgba(223, 96, 96, 0.12);
  color: #ffd9d9;
}

.pdf-chip-icon {
  flex-shrink: 0;
  padding: 1px 6px;
  border-radius: 5px;
  background: rgba(63, 127, 232, 0.35);
  color: #eaf1ff;
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.04em;
}

.pdf-chip.error .pdf-chip-icon {
  background: rgba(223, 96, 96, 0.4);
  color: #fff0f0;
}

.pdf-chip-name {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.pdf-chip-remove {
  flex-shrink: 0;
  min-height: 0;
  min-width: 0;
  padding: 0 6px;
  line-height: 1;
  font-size: 16px;
  font-weight: 700;
  background: transparent;
  border: none;
  box-shadow: none;
  color: inherit;
  opacity: 0.75;
}

.pdf-chip-remove:hover:not(:disabled) {
  background: transparent;
  opacity: 1;
}

input {
  width: 100%;
  min-width: 0;
  min-height: clamp(42px, 4.6dvh, 48px);
  padding: clamp(9px, 1.1dvh, 12px) 14px;
  border-radius: var(--r-md);
  border: 1px solid var(--line-strong);
  background: var(--input-bg);
  color: var(--input-text);
  font-size: 14.5px;
  font-family: inherit;
  outline: none;
  transition: border-color var(--t-fast) var(--ease-out), box-shadow var(--t-fast) var(--ease-out), background var(--t-fast) var(--ease-out);
}

input::placeholder {
  color: #8b98aa;
}

input:hover {
  border-color: rgba(151, 168, 192, 0.34);
}

input:focus {
  border-color: rgba(63, 127, 232, 0.75);
  box-shadow: 0 0 0 3px rgba(63, 127, 232, 0.18);
  background: #0e131a;
}

/* Obsidian keys — mic + send */
button {
  min-height: clamp(40px, 4.4dvh, 44px);
  border: 1px solid rgba(255, 255, 255, 0.09);
  border-radius: var(--r-md);
  background: linear-gradient(180deg, #242b38, #141821 52%, #0d1015);
  color: #f2f5f9;
  font-size: 14px;
  font-weight: 600;
  font-family: inherit;
  letter-spacing: 0.01em;
  cursor: pointer;
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.1),
    0 8px 16px -8px rgba(0, 0, 0, 0.65);
  transition: background var(--t-fast) var(--ease-out), border-color var(--t-fast) var(--ease-out), transform var(--t-fast) var(--ease-out), box-shadow var(--t-fast) var(--ease-out);
}

button:hover:not(:disabled) {
  background: linear-gradient(180deg, #2c3080, #191e29 52%, #10141a);
  border-color: rgba(255, 255, 255, 0.16);
}

button:active:not(:disabled) {
  transform: translateY(1px);
  background: linear-gradient(180deg, #12161e, #171c26);
  box-shadow: inset 0 2px 6px rgba(0, 0, 0, 0.5);
}

button:disabled {
  background: var(--surface-soft);
  border-color: var(--line);
  color: rgba(154, 167, 184, 0.55);
  box-shadow: none;
  cursor: not-allowed;
}

.mic-btn {
  min-width: 58px;
  color: var(--text);
}

.mic-btn.warming {
  background: rgba(223, 166, 62, 0.18);
  border-color: rgba(223, 166, 62, 0.6);
  color: var(--warn-bright);
}

.mic-btn.listening {
  background: var(--bad);
  border-color: transparent;
  color: #ffffff;
  animation: pulse 1s infinite;
}

.voice-status {
  min-height: clamp(14px, 1.8dvh, 18px);
  margin: 0;
  color: var(--muted);
  font-size: 12.5px;
  flex-shrink: 0;
}

/* --------------------------------------------------------------------------
   Avatar (video) mode
   -------------------------------------------------------------------------- */

.avatar-workspace {
  display: none;
}

.avatar-metric {
  display: grid;
  align-content: center;
  gap: 3px;
  min-width: 0;
  padding: 8px 10px;
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--surface);
}

.metric-label {
  color: var(--muted);
  font-size: 10.5px;
  font-weight: 650;
  letter-spacing: 0.05em;
  text-transform: uppercase;
}

.avatar-metric strong {
  font-size: 13px;
  line-height: 1.15;
  font-variant-numeric: tabular-nums;
}

.avatar-metric span:last-child {
  color: var(--muted);
  font-size: 11px;
  line-height: 1.3;
}

.avatar-shortcuts {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 6px;
  min-width: 0;
}

.avatar-shortcut {
  min-width: 0;
  min-height: 46px;
  padding: 8px 8px 8px 10px;
  display: inline-flex;
  align-items: center;
  justify-content: flex-start;
  gap: 7px;
  border: 1px solid var(--line-strong);
  border-radius: var(--r-md);
  background: var(--button-bg);
  color: var(--text);
  font-size: 12px;
  font-weight: 600;
  line-height: 1.15;
  text-align: left;
  cursor: pointer;
  overflow-wrap: anywhere;
  transition: background var(--t-fast) var(--ease-out), border-color var(--t-fast) var(--ease-out);
}

.avatar-shortcut::before {
  content: "";
  width: 7px;
  height: 7px;
  border-radius: 50%;
  flex-shrink: 0;
  background: var(--accent-bright);
}

.avatar-shortcut:hover:not(:disabled) {
  background: var(--button-hover);
  border-color: rgba(151, 168, 192, 0.36);
  transform: none;
  filter: none;
}

.avatar-shortcut.shortcut-headworks::before {
  background: var(--ok-bright);
}

.avatar-shortcut.shortcut-operations::before {
  background: var(--accent-bright);
}

.avatar-shortcut.shortcut-eq::before {
  background: var(--warn-bright);
}

.avatar-shortcut.shortcut-ait::before {
  background: #b18cff;
}

/* --------------------------------------------------------------------------
   SCADA mini workspace
   -------------------------------------------------------------------------- */

.scada-mini-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  min-width: 0;
}

.scada-mini-title {
  display: grid;
  gap: 2px;
  min-width: 0;
}

.scada-mini-title strong {
  color: var(--text);
  font-size: 14px;
  font-weight: 650;
  line-height: 1.15;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.scada-mini-title span:last-child,
.scada-mini-status {
  color: var(--muted);
  font-size: 11px;
  line-height: 1.25;
}

.scada-mini-tabs {
  display: grid;
  grid-template-columns: repeat(4, 34px);
  gap: 5px;
  flex: 0 0 auto;
}

.scada-mini-tab {
  width: 34px;
  height: 30px;
  border: 1px solid var(--line-strong);
  border-radius: var(--r-sm);
  background: var(--button-bg);
  color: var(--muted);
  font-size: 13px;
  font-weight: 700;
  cursor: pointer;
  transition: background var(--t-fast) var(--ease-out), color var(--t-fast) var(--ease-out), border-color var(--t-fast) var(--ease-out);
}

.scada-mini-tab:hover:not(:disabled) {
  border-color: rgba(63, 127, 232, 0.5);
  color: var(--text);
}

.scada-mini-tab.active {
  background: var(--accent);
  color: #ffffff;
  border-color: transparent;
}

.scada-mini-status {
  min-height: 13px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.scada-mini-canvas {
  width: 100%;
  height: clamp(116px, 16dvh, 176px);
  min-height: 116px;
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: #070b10;
}

.scada-mini-legend {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 4px 7px;
  min-height: 34px;
}

.scada-mini-legend .scada-trend-legend-item {
  font-size: 10px;
}

.scada-mini-legend .scada-trend-legend-item span {
  width: 16px;
}

.scada-trend-legend-item {
  display: flex;
  align-items: center;
  gap: 6px;
  min-width: 0;
  color: var(--muted);
  font-size: 12px;
}

.scada-trend-legend-item span {
  width: 22px;
  height: 3px;
  flex: 0 0 auto;
  border-radius: 2px;
}

.scada-trend-legend-item em {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-style: normal;
}

/* --------------------------------------------------------------------------
   PLC live panel
   -------------------------------------------------------------------------- */

.plc-panel {
  display: grid;
  gap: 10px;
  padding: 12px 14px;
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: var(--panel-strong);
  box-shadow: var(--panel-shadow);
  flex-shrink: 0;
}

.plc-heading {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  min-width: 0;
}

.plc-title {
  display: grid;
  gap: 2px;
  min-width: 0;
}

.plc-title strong {
  font-size: 14px;
  font-weight: 650;
  line-height: 1.15;
  overflow-wrap: anywhere;
}

.plc-badge {
  flex-shrink: 0;
  min-width: 96px;
  padding: 6px 10px;
  border: 1px solid transparent;
  border-radius: 999px;
  text-align: center;
  font-size: 12px;
  font-weight: 650;
  letter-spacing: 0.02em;
  line-height: 1;
}

.plc-badge.live {
  background: rgba(47, 190, 134, 0.14);
  border-color: rgba(47, 190, 134, 0.45);
  color: var(--ok-bright);
}

.plc-badge.checking,
.plc-badge.warning {
  background: rgba(223, 166, 62, 0.14);
  border-color: rgba(223, 166, 62, 0.45);
  color: var(--warn-bright);
}

.plc-badge.bad {
  background: rgba(214, 69, 69, 0.15);
  border-color: rgba(214, 69, 69, 0.5);
  color: var(--bad-bright);
}

.plc-value-row {
  display: flex;
  align-items: baseline;
  gap: 12px;
  min-width: 0;
}

.plc-value {
  min-width: 96px;
  color: var(--text);
  font-size: 30px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.015em;
  line-height: 1;
  overflow-wrap: anywhere;
}

.plc-quality {
  color: var(--muted);
  font-size: 13px;
  line-height: 1.3;
}

.plc-meta {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 6px;
}

.plc-meta span {
  min-width: 0;
  padding: 7px 9px;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: var(--surface);
  color: var(--muted);
  font-size: 12px;
  line-height: 1.25;
  font-variant-numeric: tabular-nums;
  overflow-wrap: anywhere;
}

.plc-message {
  min-height: 18px;
  margin: 0;
  color: var(--muted);
  font-size: 12px;
  line-height: 1.4;
}

/* --------------------------------------------------------------------------
   Motion
   -------------------------------------------------------------------------- */

@keyframes orbTalk {
  0%,
  100% {
    transform: translateY(0) scale(1);
  }

  50% {
    transform: translateY(-2px) scale(1.02);
  }
}

@keyframes pulse {
  0% {
    transform: scale(1);
  }

  50% {
    transform: scale(1.05);
  }

  100% {
    transform: scale(1);
  }
}

@keyframes dotWave {
  0%,
  100% {
    transform: translateY(0);
    opacity: 0.45;
  }

  50% {
    transform: translateY(-5px);
    opacity: 1;
  }
}

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

/* --------------------------------------------------------------------------
   Responsive
   -------------------------------------------------------------------------- */

@media (max-height: 900px) {
  .app-header h1 {
    font-size: 16px;
  }

  .app-header p {
    font-size: 12px;
  }

  .avatar-mode-btn {
    min-height: 32px;
    padding: 6px 10px;
  }

  .agent-panel {
    grid-template-columns: minmax(126px, 160px) minmax(0, 1fr);
    gap: 10px 12px;
  }

  .agent-status {
    min-height: 30px;
    padding: 6px 9px;
    font-size: 12px;
  }

  .agent-orb {
    min-height: clamp(118px, 22dvh, 170px);
  }

  .control-btn {
    font-size: 13px;
  }

  .piper-status,
  .conversation-status {
    font-size: 11px;
  }

  .chat-box {
    min-height: clamp(120px, 24dvh, 200px);
  }

  .message {
    font-size: 13.5px;
  }
}

@media (max-height: 700px) {
  .app {
    padding: 8px;
    gap: 7px;
  }

  .app-header {
    align-items: flex-start;
    flex-direction: column;
    gap: 6px;
  }

  .app-header p,
  .piper-status,
  .conversation-status,
  .voice-status {
    display: none;
  }

  .avatar-mode-controls {
    width: 100%;
    flex-wrap: wrap;
  }

  .avatar-mode-btn {
    flex: 1 1 88px;
  }

  .agent-panel {
    grid-template-columns: minmax(96px, 124px) minmax(0, 1fr);
    gap: 8px;
    padding: 8px;
  }

  .agent-orb {
    min-height: 104px;
  }

  .controls {
    gap: 6px;
  }

  .control-btn,
  .control-btn.conversation {
    min-height: 38px;
    padding: 6px;
    font-size: 12px;
  }

  .chat-box {
    min-height: 110px;
    padding: 10px;
  }

  .message {
    max-width: 94%;
    padding: 10px 11px;
    font-size: 13px;
  }

  .input-area {
    grid-template-columns: 50px 1fr 72px;
    gap: 6px;
  }

  input,
  button {
    min-height: 38px;
    font-size: 13px;
  }
}

@media (max-width: 430px) {
  .app {
    max-width: none;
  }

  .app-header {
    align-items: flex-start;
    flex-direction: column;
    gap: 8px;
  }

  .app-header h1 {
    font-size: 16px;
  }

  .app-header p {
    font-size: 12px;
  }

  .avatar-mode-controls {
    width: 100%;
  }

  .avatar-mode-btn {
    flex: 1;
  }

  .agent-panel {
    grid-template-columns: 1fr;
    grid-template-areas:
      "agent"
      "status"
      "controls";
    padding: 12px;
  }

  .avatar-shortcuts {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .plc-heading,
  .plc-value-row {
    align-items: stretch;
    flex-direction: column;
  }

  .plc-badge {
    width: 100%;
  }

  .plc-meta {
    grid-template-columns: 1fr;
  }

  .controls {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .agent-orb {
    width: 100%;
    min-height: clamp(120px, 22dvh, 180px);
  }

  .input-area {
    grid-template-columns: 48px minmax(0, 1fr) 68px;
    gap: 6px;
  }

  .message {
    max-width: 92%;
    font-size: 14px;
  }

  .mic-btn,
  input,
  button {
    width: 100%;
  }
}

@media (max-width: 430px) and (max-height: 700px) {
  .agent-panel {
    grid-template-columns: minmax(82px, 104px) minmax(0, 1fr);
    grid-template-areas:
      "agent status"
      "agent controls";
    align-items: stretch;
    padding: 8px;
  }

  .agent-orb {
    height: 100%;
    min-height: 96px;
  }

  .agent-status {
    min-width: 0;
    font-size: 11px;
  }

  .control-btn,
  .control-btn.conversation {
    min-height: 34px;
    font-size: 11px;
  }

  .chat-box {
    min-height: 90px;
  }

  .message {
    padding: 9px 10px;
    font-size: 12px;
  }
}

/* ==========================================================================
   MERGED AVATAR UI (2026-08-10)

   Video On and Video Off are collapsed into ONE mode. The animated agent
   always lives in the compact top-left card - the old avatar-OFF geometry -
   with the MuseTalk video overlaying the static photo in place (.agent-orb
   already stacks .agent-orb-image under .agent-orb-video, so no new element
   is needed). The retired fullscreen ".avatar-mode-on" rules were dead code
   (setAvatarLayoutState() never applies that class) and were REMOVED
   2026-08-11 (~4.3 KB). Restore from _ui_backup_20260810/ or git history if
   the fullscreen avatar layout ever comes back.

   The shipped view is the photo card plus the bottom input bar. The answer
   transcript is collapsed behind the header's Transcript toggle so long
   technical answers stay re-readable without occupying the screen.
   ========================================================================== */

/* --- Header transcript toggle (replaces the Video On/Off pair) ----------- */

/* Avatar switcher: same chip language as the transcript toggle, plus a round
   thumbnail of the face you would switch TO (camera-flip style preview). */
.avatar-switch {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  min-height: 34px;
  padding: 4px 12px 4px 5px;
  border: 1px solid var(--line-strong);
  background: var(--button-bg);
  color: var(--muted);
  border-radius: var(--r-sm);
  font-size: 12.5px;
  font-weight: 600;
  letter-spacing: 0.01em;
  cursor: pointer;
  white-space: nowrap;
  transition:
    background var(--t-fast) var(--ease-out),
    color var(--t-fast) var(--ease-out),
    border-color var(--t-fast) var(--ease-out);
}

.avatar-switch:hover {
  background: var(--button-hover);
  color: var(--text);
}

.avatar-switch:hover .avatar-switch-thumb {
  border-color: rgba(63, 127, 232, 0.65);
}

.avatar-switch-thumb {
  width: 26px;
  height: 26px;
  border-radius: 50%;
  object-fit: cover;
  border: 1.5px solid var(--line-strong);
  background: var(--button-bg);
  flex-shrink: 0;
  transition: border-color var(--t-fast) var(--ease-out);
}

.avatar-switch-label {
  line-height: 1;
}

.transcript-toggle {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: 34px;
  padding: 7px 12px;
  border: 1px solid var(--line-strong);
  background: var(--button-bg);
  color: var(--muted);
  border-radius: var(--r-sm);
  font-size: 12.5px;
  font-weight: 600;
  letter-spacing: 0.01em;
  cursor: pointer;
  white-space: nowrap;
  transition:
    background var(--t-fast) var(--ease-out),
    color var(--t-fast) var(--ease-out),
    border-color var(--t-fast) var(--ease-out);
}

.transcript-toggle:hover {
  background: var(--button-hover);
  color: var(--text);
}

.transcript-toggle.active {
  background: var(--accent-soft);
  color: var(--accent-bright);
  border-color: rgba(63, 127, 232, 0.45);
}

.transcript-toggle-icon {
  flex-shrink: 0;
  transition: transform var(--t-med) var(--ease-out);
}

.transcript-toggle.active .transcript-toggle-icon {
  transform: rotate(180deg);
}

.transcript-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 16px;
  height: 16px;
  padding: 0 4px;
  border-radius: 999px;
  background: var(--accent-bright);
  color: #fff;
  font-size: 10px;
  font-weight: 700;
  line-height: 1;
}

.transcript-badge.hidden {
  display: none;
}

/* --- Collapsed transcript: photo card + input bar only ------------------- */

/* Default (collapsed): the transcript is out of flow, the input bar is pinned
   to the bottom of the viewport, and the agent card keeps its corner size. */
.chat-box {
  display: none;
}

.chat-panel {
  flex: 1 1 auto;
  justify-content: flex-end;
}

/* Expanded: restore the scrolling transcript above the input bar. */
.transcript-open .chat-box {
  display: block;
}

.transcript-open .chat-panel {
  justify-content: flex-start;
}

/* --- Reduced motion ------------------------------------------------------ */

@media (prefers-reduced-motion: reduce) {
  .transcript-toggle,
  .transcript-toggle-icon {
    transition: none;
  }
}

/* --- Small screens ------------------------------------------------------- */

@media (max-height: 700px) {
  .transcript-toggle {
    min-height: 30px;
    padding: 5px 9px;
    font-size: 11.5px;
  }
}

@media (max-width: 430px) {
  /* Only two controls live here now (Transcript + SCADA), so the label stays -
     a lone chevron is not a legible affordance. Pinned to content width so it
     cannot stretch across the header the way the old 3-button row did. */
  .transcript-toggle {
    flex: 0 0 auto;
    width: auto;
    min-height: 32px;
    padding: 6px 10px;
    font-size: 11.5px;
  }

  .avatar-mode-controls {
    justify-content: space-between;
  }
}

/* --- Collapsed state: the agent card owns the freed space ---------------- */

/* With the transcript hidden there is a lot of empty height between the card
   and the input bar. Rather than leave a void, the agent card grows and the
   photo/video grows with it - the agent stays anchored top-left, just bigger,
   which is the point of the merged view. Reverts to the compact card the
   moment the transcript is expanded. */
body:not(.transcript-open) .agent-panel {
  flex: 1 1 auto;
  min-height: 0;
  align-content: stretch;
}

body:not(.transcript-open) .agent-orb {
  align-self: stretch;
  height: 100%;
  min-height: clamp(140px, 24dvh, 220px);
  max-height: none;
}

/* Two-column card (photo left, status + controls right). Scoped above the
   mobile breakpoint: below it the panel restacks to a single column via
   grid-template-areas, and forcing a second column there would leave a dead
   half-width gutter beside the photo. */
@media (min-width: 431px) {
  body:not(.transcript-open) .agent-panel {
    grid-template-columns: minmax(190px, 42%) minmax(0, 1fr);
    grid-template-rows: auto auto minmax(0, 1fr);
  }
}

/* Stacked card: the photo takes the slack, status and controls stay compact. */
@media (max-width: 430px) {
  body:not(.transcript-open) .agent-panel {
    grid-template-rows: minmax(0, 1fr) auto auto;
  }
}

body:not(.transcript-open) .agent-orb-image,
body:not(.transcript-open) .agent-orb-video,
body:not(.transcript-open) .agent-orb-freeze {
  object-position: center top;
}

/* The card, not the empty chat panel, absorbs the slack: keeps the input bar
   docked directly beneath the agent instead of leaving a dead band above it. */
body:not(.transcript-open) .chat-panel {
  flex: 0 0 auto;
}

/* ==========================================================================
   OVERLAY DENSITY PASS (2026-08-10b)

   The panel is a fixed-size overlay on the 3DVista tour, so every pixel it
   covers is tour it hides. Three problems measured at 700x480: the header ate
   78px in three stacked rows, 204px of the card's right column (61% of it) was
   empty below the buttons, and the agent photo showed full-height down to the
   hips, which made the face small.
   ========================================================================== */

/* --- 1. Header: left as-is ---------------------------------------------
   Title over the PLC/SCADA/SA dots, tool buttons right. Collapsing this to a
   single line was reverted - the stacked form is the intended look. Only the
   Video On/Video Off pair is gone, replaced by the Transcript toggle.
   ----------------------------------------------------------------------- */

/* --- 2. Controls stretch to fill the card instead of bunching at the top -
   Three full-width buttons stacked down the right column, each taking an equal
   share of the height. Two-across left a 140px slab per button, which just
   swapped one kind of clutter for another; stacked reads as a deliberate
   control strip and gives bigger hit targets for a plant touchscreen.
   Scoped above the mobile breakpoint - below it the card restacks to a single
   column and the buttons must stay compact. */

@media (min-width: 431px) {
  /* Start and Auto Talk share row 1, Conversation spans row 2 - the original
     arrangement, untouched. The two button rows are `1fr` so they grow to
     fill the card instead of leaving a void beneath them; the two status
     lines sit on auto rows underneath and are never stretched. */
  body:not(.transcript-open) .controls {
    align-content: stretch;
    /* Centre each button in its row so the space left by the height cap
       splits evenly above and below, instead of pooling under a top-aligned
       button as one conspicuous gap. */
    align-items: center;
    grid-template-rows: minmax(0, 1fr) minmax(0, 1fr) auto auto;
    height: 100%;
  }

  body:not(.transcript-open) .control-btn {
    height: 100%;
    min-height: 0;
    /* Ceiling so a tall window (a TV at 1280x720 hit 230px) cannot turn the
       buttons back into slabs. Below the cap they still stretch to fill. */
    max-height: clamp(88px, 21dvh, 150px);
  }

  /* Bring the voice/conversation status lines back in the merged view.
     A pre-existing `@media (max-height: 700px)` rule hides them to save
     space, but the overlay is short by design, so they were disappearing
     exactly where they are wanted - and their absence let the two `1fr`
     button rows swallow the freed height and balloon into slabs. Showing
     them both restores the intended look and holds the buttons at a sane
     size. Capped so a long status string cannot push the card around. */
  body:not(.transcript-open) .piper-status,
  body:not(.transcript-open) .conversation-status {
    display: block;
    grid-column: 1 / -1;
    align-self: center;
    max-height: 2.6em;
    overflow: hidden;
  }

  /* piperStatus / conversationStatus are grid children too - pinned to their
     own auto rows so they never get stretched into giant text blocks. */
  body:not(.transcript-open) .piper-status,
  body:not(.transcript-open) .conversation-status {
    grid-column: 1 / -1;
    align-self: center;
  }
}

/* --- 3. Tighter agent crop: head and chest, not down to the hips ---------
   sa-agent.png is 1448x1086 (landscape), so `cover` in the portrait orb was
   showing the FULL height and cropping only the sides. Over-sizing the media
   box vertically and anchoring it to the top clips the lower body instead and
   scales the face up. Applied identically to the photo, the MuseTalk video and
   the freeze-frame canvas so they stay pixel-aligned through every swap - the
   video is rendered from this same photo, so any mismatch would visibly jump.
   Tune with one number. */

.agent-orb {
  /* Crop line. 100% = plain `cover` (whole photo, sides trimmed); raising it
     clips the bottom and scales the face up.

     112.7% lands the cut on the grey reflective stripe across the vest.
     Measured, not guessed: in assets/sa-agent.png (1448x1086) the stripe runs
     y=895-963, so its lower edge sits at 963/1086 = 88.7% of the height, and
     1 / 0.887 = 1.127. The orb is always taller than it is wide relative to
     the 4:3 source, so `cover` binds on height and the source's full height
     maps to the box - which is what makes this arithmetic hold at every
     breakpoint. Re-derive this number if the photo is ever replaced. */
  --agent-crop: 112.7%;
}

body:not(.transcript-open) .agent-orb-image,
body:not(.transcript-open) .agent-orb-video,
body:not(.transcript-open) .agent-orb-freeze {
  height: var(--agent-crop);
  object-fit: cover;
  object-position: center top;
}

/* --- 4. Header: identity left, tools right ------------------------------
   A pre-existing `@media (max-height: 700px)` rule switches .app-header to
   `flex-direction: column`, which on this short overlay pushed Transcript and
   SCADA onto a third row hard against the left edge - burning a whole row of
   header height and leaving the top-right empty. Back to one row: the app
   name and the PLC/SCADA/SA health readout occupy the left, the tool buttons
   sit top-right. Scoped above the mobile breakpoint, where a single narrow
   column is still the right call. */

@media (min-width: 431px) {
  body .app-header {
    flex-direction: row;
    flex-wrap: nowrap;
    align-items: center;
    justify-content: space-between;
    gap: 14px;
  }

  body .app-header > div:first-child {
    min-width: 0;
    flex: 1 1 auto;
  }

  /* The same short-window media query sets `width: 100%` here, which kept the
     tool group spanning the full row even after the header went back to
     `row`. Size it to its contents so `space-between` can push it right. */
  body .avatar-mode-controls {
    width: auto;
    flex: 0 0 auto;
    flex-wrap: nowrap;
    align-self: center;
    justify-content: flex-end;
  }

  /* With the row reclaimed there is room to make the health readout actually
     legible from a few feet away, which is the point of a plant display. */
  body .system-status {
    gap: 14px;
    margin-top: 6px;
  }

  body .system-status-item {
    font-size: 12px;
  }

  body .system-status-dot {
    width: 9px;
    height: 9px;
  }
}

/* ==========================================================================
   MIC FIT + LOADING ZOOM (2026-08-10c)
   ========================================================================== */

/* --- Mic button overflowing into the input ------------------------------
   .input-area is a 3-column grid whose first column narrows to 50px (and 48px
   on phones) on short windows, but .mic-btn carried a hard `min-width: 58px`.
   The button overflowed its own column by 8px and, once the 6px gap was eaten,
   painted 2px over the input pill. Let the grid column decide the width. */

.mic-btn {
  min-width: 0;
  width: 100%;
}

/* --- Loading zoom -------------------------------------------------------
   Reserve zoom for one meaning: "the assistant video is rendering." The photo
   breathes while MuseTalk works and is otherwise perfectly still, so the
   motion is unambiguous. Applied to the photo rather than the orb so the
   frame, border and glow stay put and only the picture moves. */

@keyframes agentLoadingZoom {
  0%,
  100% {
    transform: scale(1);
  }

  50% {
    transform: scale(1.055);
  }
}

/* `avatar-waiting` is the mid-answer twin of `avatar-loading`: the gaps
   between chunk clips, where the avatar holds on a frozen frame (or drops to
   the portrait after a filler) for seconds while the next clip renders. Those
   are the "random pauses" - same cause as the initial wait, so same signal.

   Applied to all three stacked layers, not just the photo: during a chunk gap
   the frozen frame or the dimmed video is what the operator is actually
   looking at, so zooming the photo alone would do nothing. Identical
   animation on each keeps them pixel-aligned. */
.agent-orb.avatar-loading .agent-orb-image,
.agent-orb.avatar-waiting .agent-orb-image,
.agent-orb.avatar-waiting .agent-orb-video,
.agent-orb.avatar-waiting .agent-orb-freeze {
  animation: agentLoadingZoom 2s var(--ease-out) infinite;
  transform-origin: 50% 28%;
  will-change: transform;
}

/* The speaking state used to run `orbTalk`, a translate + scale(1.02) on the
   whole orb. That is a second, competing zoom - and it fired during playback,
   which is exactly when the avatar is NOT loading. Dropped so zoom means one
   thing; speaking still reads clearly from its accent border and glow. */
.agent-orb.speaking {
  animation: none;
}

@media (prefers-reduced-motion: reduce) {
  .agent-orb.avatar-loading .agent-orb-image,
  .agent-orb.avatar-waiting .agent-orb-image,
  .agent-orb.avatar-waiting .agent-orb-video,
  .agent-orb.avatar-waiting .agent-orb-freeze {
    animation: none;
  }

  /* Still distinguish the waiting state without motion. */
  .agent-orb.avatar-loading .agent-orb-image {
    opacity: 0.82;
  }
}

/* --- Yellow outline while the loading zoom runs -------------------------
   The zoom alone says "something is happening"; the amber ring says WHAT.
   It reuses the existing --warn-bright token rather than a raw yellow, so
   the loading signal matches the amber the status dot and the thinking orb
   already use for "working" - one colour language, not a new one.

   Placed after the .thinking / .speaking rules so it wins while a render is
   in flight, and carrying an explicit border-color/box-shadow pair so it
   overrides both of those states cleanly rather than half-inheriting one. */

.agent-orb.avatar-loading,
.agent-orb.avatar-waiting {
  border-color: var(--warn-bright);
  box-shadow:
    0 0 0 3px rgba(240, 193, 105, 0.22),
    0 14px 30px -8px rgba(0, 0, 0, 0.45);
}

/* The ring breathes in step with the zoom - same 2s period - so the two read
   as one signal rather than a static border with a moving picture behind it. */
@keyframes agentLoadingRing {
  0%,
  100% {
    box-shadow:
      0 0 0 2px rgba(240, 193, 105, 0.18),
      0 14px 30px -8px rgba(0, 0, 0, 0.45);
  }

  50% {
    box-shadow:
      0 0 0 5px rgba(240, 193, 105, 0.34),
      0 14px 30px -8px rgba(0, 0, 0, 0.45);
  }
}

.agent-orb.avatar-loading,
.agent-orb.avatar-waiting {
  animation: agentLoadingRing 2s var(--ease-out) infinite;
}

@media (prefers-reduced-motion: reduce) {
  .agent-orb.avatar-loading,
  .agent-orb.avatar-waiting {
    animation: none;
  }
}

/* --------------------------------------------------------------------------
   Wide displays (plant TV / desktop). The 760px column was designed for
   portrait-ish kiosks; on a wide screen it sat letterboxed against the left
   edge with dead space to the right. Widen and center the app so the
   transcript gets real reading width and the layout looks intentional.
   -------------------------------------------------------------------------- */

@media (min-width: 1200px) {
  .app {
    max-width: 1120px;
    margin-inline: auto;
    border-left: 1px solid var(--line);
  }

  /* Give the avatar photo a bit more presence at the larger scale. */
  .agent-panel {
    grid-template-columns: minmax(180px, 250px) minmax(0, 1fr);
  }
}

/* ---------------------------------------------------------------------------
   2026-08-31: the signal-freshness / PLC tag warning strip is hidden on a short
   screen. Operator request - in the 3DVista panel and on a tablet it ate a full
   row above the chat and pushed the input bar down.

   HIDDEN, not removed. The strip is injected from script.js with inline styles,
   but it sets no inline `display`, so this rule wins on its own; !important is
   belt-and-braces against a future inline display. The alert itself is not lost:
   script.js publishes it to `soaFreshnessAlertText` and refreshScadaStatusDot
   ambers the SCADA header dot with the full text in its tooltip, which costs no
   layout. max-height:700px is the breakpoint this file already uses for exactly
   this "the window is small, drop the extras" decision.
   --------------------------------------------------------------------------- */
@media (max-height: 700px) {
  #soaFreshnessStrip {
    display: none !important;
  }
}
