/* SwarmDesk chrome: dialogs, toasts, preloader, progress bar.
 * Plain CSS so it works independent of the Tailwind build. */

/* The "void" scale Tailwind reads. These are the DARK values, and they apply
   when data-theme is absent — light is the default the markup ships with, so
   choosing dark means removing the attribute rather than adding one. Keeping
   it this way round meant not rewriting every derived rule below. */
:root {
  --ov-void-50: 244 244 245;
  --ov-void-100: 228 228 231;
  --ov-void-200: 200 200 205;
  --ov-void-300: 163 163 171;
  /* Measured on the near-black surfaces these actually sit on, the old 400
     came to 3.9:1, the old 500 to 2.5:1 and the old 600 lower still — and
     those three carry nearly all the secondary text. Lifted until the ramp
     reads 7.5 / 5.8 / 4.5 / 3.8, which keeps four distinguishable steps and
     still puts every one of them above what a dim grey used to manage. */
  --ov-void-400: 143 143 153;
  --ov-void-500: 126 126 136;
  --ov-void-600: 112 112 122;
  --ov-void-700: 42 42 48;
  --ov-void-800: 27 27 31;
  --ov-void-850: 20 20 23;
  --ov-void-900: 16 16 19;
  --ov-void-950: 11 11 13;

  --ov-bg: #0b0b0d;
  --ov-panel: #101013;
  --ov-line: rgba(255, 255, 255, 0.07);
  --ov-text: #e4e4e7;
  --ov-dim: #71717a;
  --ov-accent: #2fae87;
  --ov-danger: #d24d59;
  --ov-hairline: rgba(255, 255, 255, 0.06);
  --ov-scroll: #2a2a30;
}

/* Light theme — the default. The scale is flipped, not mechanically inverted:
   the steps are chosen so contrast stays comparable rather than merely
   reversed. */
:root[data-theme="light"] {
  --ov-void-50: 24 24 27;
  --ov-void-100: 39 39 42;
  --ov-void-200: 63 63 70;
  --ov-void-300: 82 82 91;
  --ov-void-400: 99 99 108;
  /* Measured against white, the old 500 (#8a8a93) came to 3.6:1 and the old
     600 (#b0b0b8) to 2.2:1 — and between them they carry most of the secondary
     text in the app. Both are darkened to clear, or nearly clear, 4.5:1. The
     steps sit closer together than in the dark scale because on white there is
     simply no room for four distinct greys that are all still readable;
     hierarchy below this point is carried by size and weight instead. */
  --ov-void-500: 113 113 124;
  --ov-void-600: 124 124 134;
  --ov-void-700: 216 216 220;
  --ov-void-800: 232 232 234;
  --ov-void-850: 240 240 242;
  --ov-void-900: 247 247 248;
  --ov-void-950: 255 255 255;

  --ov-bg: #ffffff;
  --ov-panel: #f7f7f8;
  --ov-line: rgba(0, 0, 0, 0.10);
  --ov-text: #27272a;
  --ov-dim: #6b6b73;
  --ov-hairline: rgba(0, 0, 0, 0.10);
  --ov-scroll: #cfcfd6;
  color-scheme: light;
}

/* Hairlines are written as white alpha throughout the templates, which is
   invisible on a light surface — redirect them rather than editing 26 files. */
:root[data-theme="light"] .border-white\/\[0\.04\],
:root[data-theme="light"] .border-white\/\[0\.06\],
:root[data-theme="light"] .border-white\/\[0\.08\] {
  border-color: var(--ov-hairline);
}
:root[data-theme="light"] .shadow-panel {
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04), 0 12px 32px -20px rgba(0, 0, 0, 0.25);
}

/* The accent scale is fixed hex, so unlike the void scale it does not flip with
   the theme. signal-300 and -400 are mint tones meant to glow on near-black;
   on a white surface — and worse, on the pale signal-600/15 the active nav item
   uses — they wash out to unreadable. Darkened here rather than in 70 templates. */
:root[data-theme="light"] .text-signal-100,
:root[data-theme="light"] .text-signal-300 { color: #186f56; }
:root[data-theme="light"] .text-signal-400 { color: #145a46; }
/* The wordmark is the accent at its brightest and sat at 2.6:1 on the light
   sidebar. It keeps the accent's hue, one step deeper. */
:root[data-theme="light"] .text-signal-500 { color: #1f8d6d; }
:root[data-theme="light"] .ov-brand-word { color: #186f56; }

/* Colours move together when the theme flips, so the switch reads as one
   change of light rather than a dozen independent transitions. */
body, .ov-panel, .ov-input, .ov-chip {
  transition: background-color 0.28s ease, border-color 0.28s ease, color 0.28s ease;
}
@media (prefers-reduced-motion: reduce) {
  body, .ov-panel, .ov-input, .ov-chip { transition: none; }
}

* { scrollbar-width: thin; scrollbar-color: var(--ov-scroll) transparent; }
*::-webkit-scrollbar { width: 8px; height: 8px; }
*::-webkit-scrollbar-thumb { background: var(--ov-scroll); border-radius: 4px; }

/* ---------- top progress bar ---------- */
#ov-progress {
  position: fixed; top: 0; left: 0; height: 2px; width: 0;
  background: linear-gradient(90deg, var(--ov-accent), #7ee0bc);
  z-index: 90; opacity: 0; transition: width 0.25s ease, opacity 0.3s ease;
  pointer-events: none;
}
#ov-progress.active { opacity: 1; }

/* ---------- dialog ---------- */
.ov-dialog__overlay {
  position: fixed; inset: 0; background: rgba(5, 5, 7, 0.7);
  backdrop-filter: blur(4px); z-index: 100;
  animation: ov-fade 0.18s ease;
}
.ov-dialog {
  position: fixed; z-index: 101; left: 50%; top: 50%;
  transform: translate(-50%, -50%);
  width: min(92vw, 26rem);
  background: var(--ov-panel);
  border: 1px solid var(--ov-line);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.06), 0 24px 60px -12px rgba(0, 0, 0, 0.8);
  border-radius: 1rem; padding: 1.25rem;
  color: var(--ov-text); font-size: 0.9rem;
  animation: ov-pop 0.22s cubic-bezier(0.16, 1, 0.3, 1);
}
.ov-dialog__header { display: flex; align-items: center; gap: 0.6rem; }
.ov-dialog__icon { width: 1.35rem; height: 1.35rem; flex: none; }
.ov-dialog__icon--danger { color: var(--ov-danger); }
.ov-dialog__icon--info { color: var(--ov-accent); }
.ov-dialog__title { font-weight: 600; font-size: 1rem; margin: 0; }
.ov-dialog__desc { color: var(--ov-dim); margin: 0.6rem 0 0; line-height: 1.5; }
.ov-dialog__code {
  margin-top: 0.6rem; padding: 0.5rem 0.6rem; border-radius: 0.5rem;
  background: #0b0b0d; border: 1px solid var(--ov-line);
  font-family: "JetBrains Mono", monospace; font-size: 0.78rem;
  white-space: pre-wrap; word-break: break-all; color: #a3a3ab;
  max-height: 10rem; overflow: auto;
}
.ov-dialog__input {
  width: 100%; margin-top: 0.7rem; padding: 0.5rem 0.65rem;
  background: #141417; color: var(--ov-text);
  border: 1px solid #2a2a30; border-radius: 0.5rem;
  font-size: 0.9rem; outline: none;
}
.ov-dialog__input:focus { border-color: var(--ov-accent); }
.ov-dialog__footer {
  display: flex; justify-content: flex-end; gap: 0.5rem; margin-top: 1.1rem;
}
.ov-dialog__btn {
  border-radius: 0.5rem; padding: 0.45rem 0.9rem; font-size: 0.85rem;
  font-weight: 500; cursor: pointer; border: 1px solid transparent;
  transition: all 0.15s ease;
}
.ov-dialog__btn:active { transform: translateY(1px) scale(0.98); }
.ov-dialog__btn--dismiss {
  background: transparent; color: var(--ov-dim); border-color: #2a2a30;
}
.ov-dialog__btn--dismiss:hover { color: var(--ov-text); border-color: #3f3f46; }
.ov-dialog__btn--action { background: var(--ov-accent); color: #06120d; }
.ov-dialog__btn--action:hover { filter: brightness(1.1); }
.ov-dialog__btn--danger { background: var(--ov-danger); color: #fff; }

/* ---------- toasts ---------- */
.ov-toast-stack {
  position: fixed; right: 1rem; bottom: 1rem; z-index: 110;
  display: flex; flex-direction: column; gap: 0.5rem;
  max-width: min(92vw, 22rem);
}
.ov-toast {
  display: flex; align-items: flex-start; gap: 0.6rem;
  background: var(--ov-panel); border: 1px solid var(--ov-line);
  border-radius: 0.75rem; padding: 0.7rem 0.8rem;
  box-shadow: 0 12px 40px -12px rgba(0, 0, 0, 0.7);
  color: var(--ov-text); font-size: 0.85rem;
  animation: ov-slide-in 0.25s cubic-bezier(0.16, 1, 0.3, 1);
}
.ov-toast--leaving { opacity: 0; transform: translateX(0.5rem); transition: all 0.2s ease; }
.ov-toast__icon { width: 1.1rem; height: 1.1rem; flex: none; margin-top: 0.1rem; }
.ov-toast--success .ov-toast__icon { color: var(--ov-accent); }
.ov-toast--error .ov-toast__icon { color: var(--ov-danger); }
.ov-toast--warning .ov-toast__icon { color: #d9a44a; }
.ov-toast--info .ov-toast__icon { color: #8b9dc9; }
.ov-toast__title { font-weight: 600; margin-bottom: 0.1rem; }
.ov-toast__msg { color: #c8c8cd; line-height: 1.4; }
.ov-toast__dismiss {
  margin-left: auto; background: none; border: 0; cursor: pointer;
  color: var(--ov-dim); width: 1rem; height: 1rem; flex: none;
}
.ov-toast__dismiss:hover { color: var(--ov-text); }

/* ---------- preloader (boot sequence) ---------- */
#ov-boot {
  position: fixed; inset: 0; z-index: 200; background: var(--ov-bg);
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 1.4rem;
}
#ov-boot .ov-boot__mark { width: 56px; height: 56px; color: var(--ov-accent); }

#ov-boot .ov-boot__lines {
  font-family: "JetBrains Mono", monospace; font-size: 0.72rem;
  color: var(--ov-dim); min-height: 4.2em; text-align: left; width: 17rem;
}
#ov-boot .ov-boot__lines div { opacity: 0; }
#ov-boot .ov-boot__lines .ok { color: var(--ov-accent); }

/* ---------- content reveal ----------
 * CSS-driven and transform-ONLY, so content is NEVER invisible — even if the
 * tab is unpainted/throttled and the animation is frozen at its first frame,
 * opacity stays 1 and the element is only shifted a few px. A subtle opacity
 * fade rides along via a separate, forwards-filled keyframe that starts near-
 * opaque so a frozen first frame is still legible. */
[data-ov-anim="rise"] {
  animation: ov-rise 0.5s cubic-bezier(0.16, 1, 0.3, 1) both;
}
@keyframes ov-rise {
  from { transform: translateY(12px); }
  to { transform: none; }
}

/* ---------- misc ---------- */
@keyframes ov-fade { from { opacity: 0; } }
@keyframes ov-pop {
  from { opacity: 0; transform: translate(-50%, -48%) scale(0.96); }
}
@keyframes ov-slide-in {
  from { opacity: 0; transform: translateY(0.5rem); }
}
@keyframes ov-pulse-dot {
  0%, 100% { box-shadow: 0 0 0 0 rgba(47, 174, 135, 0.5); }
  60% { box-shadow: 0 0 0 6px rgba(47, 174, 135, 0); }
}
.ov-dot-live {
  width: 8px; height: 8px; border-radius: 999px; background: var(--ov-accent);
  animation: ov-pulse-dot 1.8s ease-out infinite;
}
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; }
}

/* ---------- transcript bubbles ---------- */
/* Transcripts routinely carry diffs, stack traces and pasted code. Collapsing
   their line breaks turns them into an unreadable wall, so whitespace is kept
   and long unbroken tokens (paths, URLs) wrap instead of stretching the page. */
.ov-bubble {
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  tab-size: 2;
}

/* ---------- theme toggle ---------- */
.ov-theme-toggle {
  display: inline-flex; align-items: center; padding: 0.25rem;
  background: none; border: 0; cursor: pointer;
}
.ov-theme-track {
  position: relative; display: block; width: 2.75rem; height: 1.5rem;
  border-radius: 999px; background: rgb(var(--ov-void-800));
  border: 1px solid var(--ov-hairline);
  transition: background-color 0.28s ease;
}
.ov-theme-knob {
  position: absolute; top: 50%; left: 0.1875rem;
  width: 1.125rem; height: 1.125rem; border-radius: 999px;
  display: grid; place-items: center;
  background: rgb(var(--ov-void-950)); color: var(--ov-dim);
  transform: translateY(-50%);
  transition: transform 0.34s cubic-bezier(0.16, 1, 0.3, 1), background-color 0.28s ease;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.35);
}
:root[data-theme="light"] .ov-theme-knob { transform: translate(1.25rem, -50%); }
.ov-theme-knob svg { width: 0.75rem; height: 0.75rem; position: absolute; }
/* The icons cross-fade and rotate through each other rather than swapping. */
.ov-theme-moon, .ov-theme-sun { transition: opacity 0.26s ease, transform 0.34s ease; }
.ov-theme-sun { opacity: 0; transform: rotate(-90deg) scale(0.6); }
:root[data-theme="light"] .ov-theme-moon { opacity: 0; transform: rotate(90deg) scale(0.6); }
:root[data-theme="light"] .ov-theme-sun { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) {
  .ov-theme-knob, .ov-theme-moon, .ov-theme-sun { transition: none; }
}

/* ---------- avatar + account menu ---------- */
.ov-avatar-btn {
  display: grid; place-items: center; padding: 0; border: 0; cursor: pointer;
  background: none; border-radius: 999px;
}
.ov-avatar-btn:focus-visible { outline: 2px solid var(--ov-accent); outline-offset: 2px; }
.ov-avatar {
  display: grid; place-items: center; width: 2rem; height: 2rem;
  border-radius: 999px; background: rgb(var(--ov-void-800));
  border: 1px solid var(--ov-hairline);
  color: rgb(var(--ov-void-200));
  font-size: 0.6875rem; font-weight: 600; letter-spacing: 0.02em;
  text-transform: uppercase;
}
.ov-menu { position: relative; }
.ov-menu-panel {
  position: absolute; right: 0; top: calc(100% + 0.5rem); z-index: 60;
  min-width: 12rem; overflow: hidden;
  background: var(--ov-panel); border: 1px solid var(--ov-line);
  border-radius: 0.75rem;
  box-shadow: 0 20px 44px -20px rgba(0, 0, 0, 0.55);
  animation: ov-menu-in 0.16s cubic-bezier(0.16, 1, 0.3, 1);
}
.ov-menu-panel[hidden] { display: none; }
@keyframes ov-menu-in { from { opacity: 0; transform: translateY(-0.25rem); } }
.ov-menu-item {
  display: block; padding: 0.5rem 0.75rem;
  font-size: 0.8125rem; color: rgb(var(--ov-void-200)); text-decoration: none;
}
.ov-menu-item:hover { background: rgb(var(--ov-void-800)); color: rgb(var(--ov-void-50)); }

/* ---------- avatar dropzone ---------- */
.ov-dropzone {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 0.4rem; padding: 1.25rem; cursor: pointer;
  border: 1px dashed rgb(var(--ov-void-700)); border-radius: 0.75rem;
  background: rgb(var(--ov-void-900));
  transition: border-color 0.18s ease, background-color 0.18s ease;
}
.ov-dropzone:hover { border-color: rgb(var(--ov-void-600)); }
.ov-dropzone--over {
  border-color: var(--ov-accent);
  background: rgba(47, 174, 135, 0.07);
}

/* ---------- search field ---------- */
.ov-search { position: relative; display: flex; align-items: center; min-width: 13rem; }
.ov-search__icon {
  position: absolute; left: 0.625rem; color: rgb(var(--ov-void-500)); pointer-events: none;
}
.ov-search__input {
  width: 100%; padding: 0.4rem 0.7rem 0.4rem 2rem;
  font-size: 0.8125rem; color: rgb(var(--ov-void-100));
  background: rgb(var(--ov-void-900));
  border: 1px solid var(--ov-hairline); border-radius: 0.625rem;
  outline: none; transition: border-color 0.18s ease, background-color 0.18s ease;
}
.ov-search__input::placeholder { color: rgb(var(--ov-void-500)); }
.ov-search__input:focus { border-color: var(--ov-accent); background: rgb(var(--ov-void-850)); }
.ov-search__input::-webkit-search-cancel-button { filter: invert(0.5); cursor: pointer; }

/* Items filtered out by the search collapse rather than merely hide, so the
   grid closes up instead of leaving holes. */
[data-ov-search-item].is-filtered { display: none !important; }

/* ---------- session transcript ---------- */
.ov-session { height: 100%; }
.ov-transcript { transition: opacity 0.25s ease; scroll-behavior: auto; }

.ov-turn { display: flex; margin-bottom: 0.625rem; }
.ov-turn--user { justify-content: flex-end; }
/* Only newly arriving turns animate — replaying this for a whole history on
   open would be a wall of motion. */
.ov-turn--new { animation: ov-turn-in 0.28s cubic-bezier(0.16, 1, 0.3, 1); }
@keyframes ov-turn-in { from { opacity: 0; transform: translateY(0.5rem); } }

.ov-bubble {
  white-space: pre-wrap; overflow-wrap: anywhere; tab-size: 2;
  border-radius: 0.875rem; padding: 0.5rem 0.75rem;
  font-size: 0.8125rem; line-height: 1.55;
}
.ov-bubble--user {
  max-width: 85%; background: rgba(47, 174, 135, 0.14);
  color: rgb(var(--ov-void-50)); border: 1px solid rgba(47, 174, 135, 0.22);
}
.ov-bubble--assistant {
  max-width: 90%; background: rgb(var(--ov-void-850));
  color: rgb(var(--ov-void-200)); border: 1px solid var(--ov-hairline);
}
.ov-bubble--error {
  max-width: 90%; font-size: 0.75rem;
  background: rgba(210, 77, 89, 0.1); color: var(--ov-danger);
  border: 1px solid rgba(210, 77, 89, 0.3);
}

/* Tool calls read as a quiet log line, not as speech. */
.ov-tool {
  display: inline-flex; align-items: center; gap: 0.5rem; min-width: 0;
  max-width: 100%; padding: 0.3rem 0.6rem;
  border: 1px solid var(--ov-hairline); border-radius: 0.5rem;
  background: rgb(var(--ov-void-900));
  font-family: "JetBrains Mono", ui-monospace, monospace; font-size: 0.6875rem;
}
.ov-tool__icon { width: 0.8125rem; height: 0.8125rem; flex: none; color: var(--ov-accent); }
.ov-tool__name { color: rgb(var(--ov-void-200)); flex: none; }
.ov-tool__hint {
  color: rgb(var(--ov-void-500)); min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* Working indicator */
.ov-typing { display: flex; gap: 0.25rem; padding: 0.5rem 0.25rem; }
.ov-typing[hidden] { display: none; }
.ov-typing span {
  width: 0.3rem; height: 0.3rem; border-radius: 999px;
  background: rgb(var(--ov-void-500)); animation: ov-typing 1.2s ease-in-out infinite;
}
.ov-typing span:nth-child(2) { animation-delay: 0.15s; }
.ov-typing span:nth-child(3) { animation-delay: 0.3s; }
@keyframes ov-typing {
  0%, 60%, 100% { opacity: 0.25; transform: translateY(0); }
  30% { opacity: 1; transform: translateY(-0.15rem); }
}

/* Skeleton while the transcript positions itself */
.ov-transcript-loading {
  position: absolute; inset: 0; z-index: 2;
  display: flex; flex-direction: column; gap: 0.75rem;
  padding: 1rem; border-radius: 1rem;
  background: rgb(var(--ov-void-950)); border: 1px solid var(--ov-hairline);
}
.ov-transcript-loading[hidden] { display: none; }
.ov-skel {
  height: 2.5rem; border-radius: 0.875rem;
  background: linear-gradient(90deg,
    rgb(var(--ov-void-850)) 25%, rgb(var(--ov-void-800)) 37%, rgb(var(--ov-void-850)) 63%);
  background-size: 400% 100%;
  animation: ov-shimmer 1.4s ease infinite;
}
.ov-skel--in { width: 62%; }
.ov-skel--out { width: 45%; align-self: flex-end; }
@keyframes ov-shimmer { from { background-position: 100% 50%; } to { background-position: 0 50%; } }

@media (max-width: 640px) {
  .ov-bubble--user, .ov-bubble--assistant, .ov-bubble--error { max-width: 100%; }
  .ov-tool { font-size: 0.625rem; }
}
@media (prefers-reduced-motion: reduce) {
  .ov-turn--new, .ov-typing span, .ov-skel { animation: none; }
}

/* ---------- context menu ---------- */
.ov-context-menu {
  position: fixed; z-index: 120; min-width: 11rem; overflow: hidden;
  background: var(--ov-panel); border: 1px solid var(--ov-line);
  border-radius: 0.75rem;
  box-shadow: 0 20px 44px -20px rgba(0, 0, 0, 0.6);
  animation: ov-menu-in 0.14s cubic-bezier(0.16, 1, 0.3, 1);
}
.ov-context-menu[hidden] { display: none; }

/* ---------- masterchat ---------- */
.ov-chat-mark { display: block; color: var(--ov-accent); opacity: 0.7; }

.ov-composer {
  display: flex; align-items: flex-end; gap: 0.375rem;
  padding: 0.375rem 0.375rem 0.375rem 0.75rem;
  background: rgb(var(--ov-void-900));
  border: 1px solid var(--ov-hairline); border-radius: 1.25rem;
  transition: border-color 0.18s ease, box-shadow 0.18s ease;
}
.ov-composer:focus-within {
  border-color: rgba(47, 174, 135, 0.45);
  box-shadow: 0 0 0 3px rgba(47, 174, 135, 0.08);
}
.ov-composer[hidden] { display: none; }
.ov-composer__input {
  flex: 1; min-height: 2.25rem; max-height: 12.5rem; padding: 0.5rem 0;
  background: none; border: 0; outline: none; resize: none;
  color: rgb(var(--ov-void-100)); font-size: 0.875rem; line-height: 1.5;
  font-family: inherit;
}
.ov-composer__input::placeholder { color: rgb(var(--ov-void-500)); }
.ov-composer__btn {
  display: grid; place-items: center; flex: none;
  width: 2.25rem; height: 2.25rem; border-radius: 999px;
  background: none; border: 0; cursor: pointer;
  color: rgb(var(--ov-void-400));
  transition: background-color 0.15s ease, color 0.15s ease, transform 0.15s ease;
}
.ov-composer__btn svg { width: 1.0625rem; height: 1.0625rem; }
.ov-composer__btn:hover { background: rgb(var(--ov-void-800)); color: rgb(var(--ov-void-100)); }
.ov-composer__btn:active { transform: scale(0.94); }
.ov-composer__btn[hidden] { display: none; }
.ov-composer__btn--send { background: var(--ov-accent); color: #06120d; }
.ov-composer__btn--send:hover { background: var(--ov-accent); filter: brightness(1.1); color: #06120d; }
.ov-composer__btn--send:disabled { opacity: 0.45; cursor: default; }

/* Recording: the meter replaces the composer, since there is nothing to type. */
.ov-voice {
  display: flex; align-items: center; gap: 0.875rem;
  padding: 0.625rem 1rem; border-radius: 1.25rem;
  background: rgb(var(--ov-void-900)); border: 1px solid rgba(47, 174, 135, 0.3);
}
.ov-voice[hidden] { display: none; }
.ov-voice__stop {
  position: relative; display: grid; place-items: center; flex: none;
  width: 2.5rem; height: 2.5rem; border-radius: 999px; border: 0; cursor: pointer;
  background: var(--ov-accent); color: #06120d;
}
.ov-voice__stop svg { width: 1.125rem; height: 1.125rem; position: relative; z-index: 1; }
/* Scaled from the live input level, so it reacts to how loud you speak. */
.ov-voice__pulse {
  position: absolute; inset: 0; border-radius: 999px;
  background: var(--ov-accent); opacity: 0.2;
  transition: transform 0.08s linear, opacity 0.08s linear;
}
.ov-voice__text {
  flex: 1; min-width: 0; font-size: 0.8125rem; line-height: 1.5;
  color: rgb(var(--ov-void-200));
  max-height: 3rem; overflow-y: auto;
}

/* ---------- pending states ---------- */
/* A clicked button should say it is working, not sit there looking ignored. */
.ov-busy { position: relative; color: transparent !important; pointer-events: none; }
.ov-busy > * { visibility: hidden; }
.ov-busy::after {
  content: ""; position: absolute; top: 50%; left: 50%;
  width: 0.875rem; height: 0.875rem; margin: -0.4375rem 0 0 -0.4375rem;
  border: 2px solid currentColor; border-right-color: transparent;
  border-radius: 999px; color: inherit;
  animation: ov-spin 0.6s linear infinite;
  visibility: visible;
}
@keyframes ov-spin { to { transform: rotate(360deg); } }

/* Staggered reveal: siblings arrive one after another rather than all at once. */
[data-ov-anim="rise"]:nth-child(2) { animation-delay: 0.04s; }
[data-ov-anim="rise"]:nth-child(3) { animation-delay: 0.08s; }
[data-ov-anim="rise"]:nth-child(4) { animation-delay: 0.12s; }
[data-ov-anim="rise"]:nth-child(n+5) { animation-delay: 0.16s; }

/* Cards lift a little under the pointer. */
.ov-lift { transition: transform 0.18s cubic-bezier(0.16, 1, 0.3, 1), border-color 0.18s ease; }
.ov-lift:hover { transform: translateY(-2px); }
.ov-lift:active { transform: translateY(0); }

@media (prefers-reduced-motion: reduce) {
  .ov-busy::after { animation-duration: 1.6s; }
  .ov-lift:hover { transform: none; }
  [data-ov-anim="rise"] { animation: none; }
}

/* ---------- brain graph ---------- */
.ov-graph-legend {
  position: absolute; left: 0.875rem; bottom: 0.875rem; z-index: 2;
  padding: 0.625rem 0.75rem; max-width: 13rem;
  background: rgba(16, 16, 19, 0.82); backdrop-filter: blur(8px);
  border: 1px solid var(--ov-hairline); border-radius: 0.75rem;
  animation: ov-rise 0.5s cubic-bezier(0.16, 1, 0.3, 1) both;
  animation-delay: 0.25s;
}
:root[data-theme="light"] .ov-graph-legend { background: rgba(255, 255, 255, 0.86); }
.ov-legend-dot {
  width: 0.5rem; height: 0.5rem; border-radius: 999px; flex: none;
  box-shadow: 0 0 0 3px rgba(255, 255, 255, 0.04);
}
@media (max-width: 640px) { .ov-graph-legend { display: none; } }
@media (prefers-reduced-motion: reduce) { .ov-graph-legend { animation: none; } }

/* ---------- bridge rows ---------- */
.ov-bridge-row { transition: background-color 0.15s ease; }
.ov-bridge-row:hover { background: rgb(var(--ov-void-850)); }

/* ---------- plan comparison ---------- */
.ov-tick { color: var(--ov-accent); flex: none; font-size: 0.75rem; line-height: 1.4; }
.ov-cross { color: rgb(var(--ov-void-600)); flex: none; font-size: 0.75rem; line-height: 1.4; }

/* The entrance animation on the grid creates a stacking context, which was
   letting cards paint over an open dropdown. Lift the toolbar above them. */
.ov-toolbar { position: relative; z-index: 40; }
.ov-menu-panel { z-index: 60; }

/* ---------- modal dialog ---------- */
.ov-modal {
  width: min(92vw, 30rem); padding: 1.25rem; border: 0;
  background: var(--ov-panel); color: var(--ov-text);
  border: 1px solid var(--ov-line); border-radius: 1rem;
  box-shadow: 0 24px 60px -12px rgba(0, 0, 0, 0.7);
}
.ov-modal::backdrop { background: rgba(5, 5, 7, 0.7); backdrop-filter: blur(4px); }
.ov-modal[open] { animation: ov-pop-plain 0.2s cubic-bezier(0.16, 1, 0.3, 1); }
@keyframes ov-pop-plain { from { opacity: 0; transform: scale(0.97); } }
@media (prefers-reduced-motion: reduce) { .ov-modal[open] { animation: none; } }

/* ---------- colour swatches ---------- */
.ov-swatch { cursor: pointer; }
.ov-swatch input { position: absolute; opacity: 0; width: 0; height: 0; }
.ov-swatch span {
  display: block; width: 1.5rem; height: 1.5rem; border-radius: 0.5rem;
  border: 2px solid transparent;
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.08);
  transition: transform 0.15s ease, border-color 0.15s ease;
}
.ov-swatch:hover span { transform: scale(1.08); }
.ov-swatch input:checked + span {
  border-color: rgb(var(--ov-void-50));
  transform: scale(1.08);
}
.ov-swatch input:focus-visible + span { outline: 2px solid var(--ov-accent); outline-offset: 2px; }
@media (prefers-reduced-motion: reduce) { .ov-swatch span { transition: none; } }

/* ---------- multi-pick chips ---------- */
.ov-pick { cursor: pointer; }
.ov-pick input { position: absolute; opacity: 0; width: 0; height: 0; }
.ov-pick span {
  display: inline-block; padding: 0.25rem 0.625rem; border-radius: 999px;
  font-size: 0.6875rem; color: rgb(var(--ov-void-400));
  border: 1px solid var(--ov-hairline); background: rgb(var(--ov-void-900));
  transition: all 0.15s ease;
}
.ov-pick:hover span { color: rgb(var(--ov-void-200)); }
.ov-pick input:checked + span {
  color: var(--ov-accent); border-color: rgba(47, 174, 135, 0.45);
  background: rgba(47, 174, 135, 0.12);
}

/* ---------- sidebar: brand mark + desktop collapse ---------- */
/* Collapsing is a desktop affordance. On small screens the sidebar is an
   overlay that is either open or gone, so a half-width version would be a
   third state nobody asked for. */
.ov-brand { color: var(--ov-accent); }

/* ---------- the mark ----------
   A terminal prompt with three agents inside it. At rest it only breathes:
   the dots take turns being the bright one, left to right, so work appears to
   move through the brackets. In the sidebar it sits beside the navigation all
   day, which is why nothing here spins.

   `--live` is for the two places where the mark is the subject rather than a
   label — the preloader and the empty Masterchat. */
.ov-mark { flex: none; }
.ov-mark__dot { transform-origin: 50% 50%; }
.ov-mark--live .ov-mark__dot {
  animation: ov-mark-step 2.4s ease-in-out infinite;
}
.ov-mark--live .ov-mark__dot:nth-of-type(1) { animation-delay: 0s; }
.ov-mark--live .ov-mark__dot:nth-of-type(2) { animation-delay: 0.28s; }
.ov-mark--live .ov-mark__dot:nth-of-type(3) { animation-delay: 0.56s; }
/* The brackets draw themselves once, then stay put; a border that keeps
   redrawing reads as a page that never finished loading. */
.ov-mark--live .ov-mark__bracket {
  stroke-dasharray: 120;
  animation: ov-mark-draw 0.9s cubic-bezier(0.16, 1, 0.3, 1) both;
}
.ov-mark--live .ov-mark__bracket:nth-of-type(2) { animation-delay: 0.12s; }

/* Hovering the sidebar brand runs the same wave once, so the logo answers
   the pointer without moving anything around it. */
.ov-brand:hover .ov-mark__dot {
  animation: ov-mark-step 1.4s ease-in-out infinite;
}
.ov-brand:hover .ov-mark__dot:nth-of-type(2) { animation-delay: 0.16s; }
.ov-brand:hover .ov-mark__dot:nth-of-type(3) { animation-delay: 0.32s; }

@keyframes ov-mark-step {
  0%, 55%, 100% { opacity: 0.42; transform: scale(0.9); }
  22%           { opacity: 1;    transform: scale(1.1); }
}
@keyframes ov-mark-draw {
  from { stroke-dashoffset: 120; }
  to   { stroke-dashoffset: 0; }
}

#ov-sidebar { transition: transform 0.3s cubic-bezier(0.16, 1, 0.3, 1), width 0.3s cubic-bezier(0.16, 1, 0.3, 1); }
.ov-nav-label {
  overflow: hidden;
  white-space: nowrap;
  transition: opacity 0.2s ease, max-width 0.3s cubic-bezier(0.16, 1, 0.3, 1);
  max-width: 12rem;
  opacity: 1;
}

@media (min-width: 1024px) {
  /* id + attribute beats the w-60 utility class, so no !important needed. */
  :root[data-ov-nav="collapsed"] #ov-sidebar { width: 4.25rem; }
  :root[data-ov-nav="collapsed"] #ov-sidebar .ov-nav-label {
    max-width: 0;
    opacity: 0;
  }
  /* gap: 0 as well — the gap next to a zero-width label still takes space and
     would push every icon off-centre by half of it. */
  :root[data-ov-nav="collapsed"] #ov-sidebar .ov-brand,
  :root[data-ov-nav="collapsed"] #ov-sidebar .ov-nav-item {
    padding-left: 0;
    padding-right: 0;
    gap: 0;
    justify-content: center;
  }
  :root[data-ov-nav="collapsed"] #ov-sidebar nav { padding-left: 0.75rem; padding-right: 0.75rem; }
}

@media (prefers-reduced-motion: reduce) {
  .ov-mark__dot, .ov-mark__bracket,
  #ov-sidebar, .ov-nav-label {
    transition: none;
    animation: none;
  }
}

/* ---------- segmented view switch ----------
   Two views of the same data, so the control shows both at once with one of
   them raised — a pair of separate buttons would read as two destinations. */
.ov-seg {
  display: inline-flex; gap: 0.125rem; padding: 0.1875rem;
  background: rgb(var(--ov-void-900)); border: 1px solid var(--ov-hairline);
  border-radius: 0.625rem;
}
.ov-seg__item {
  display: inline-flex; align-items: center; gap: 0.375rem;
  padding: 0.3125rem 0.6875rem; border-radius: 0.4375rem;
  font-size: 0.75rem; line-height: 1; color: rgb(var(--ov-void-400));
  transition: color 0.15s ease, background-color 0.18s ease;
}
.ov-seg__item:hover { color: rgb(var(--ov-void-100)); }
.ov-seg__item--on {
  color: rgb(var(--ov-void-50));
  background: rgb(var(--ov-void-950));
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.18), inset 0 1px 0 rgba(255, 255, 255, 0.05);
}
.ov-seg__item svg { flex: none; }

/* The plus turns as the button is approached — a small promise that something
   is about to open. */
.ov-add__icon { transition: transform 0.35s cubic-bezier(0.16, 1, 0.3, 1); }
.ov-add:hover .ov-add__icon { transform: rotate(90deg); }
@media (prefers-reduced-motion: reduce) { .ov-add__icon { transition: none; } }

/* ---------- data table ----------
   Replaces the card grid. Cards of unequal height left holes and nothing lined
   up, so two notes could not be compared; a table gives every row the same
   four columns in the same places. */
.ov-table-wrap {
  border: 1px solid var(--ov-hairline); border-radius: 0.875rem;
  /* Rows sit on the page tone and the header on the panel tone, so the header
     reads as a header. The other way round they were the same colour. */
  background: rgb(var(--ov-void-950)); overflow: hidden;
}
/* Narrow screens scroll the table, never the page. */
@media (max-width: 900px) { .ov-table-wrap { overflow-x: auto; } }


/* fixed, so the column widths in the markup are the widths on screen — with
   auto layout the longest title decided them and the columns moved with the
   content. */
.ov-table { width: 100%; border-collapse: collapse; table-layout: fixed; min-width: 44rem; }
/* Two tables side by side get half the width, and the floor above would push
   their last column out of the clipped wrapper. These carry short numbers
   rather than prose, so they can simply be narrower. */
.ov-table--compact { min-width: 0; }
.ov-table--compact td, .ov-table--compact th { padding-left: 0.625rem; padding-right: 0.625rem; }
/* On a phone a four-column table is either unreadably narrow or half
   off-screen with nothing saying so. The columns that carry the least — a
   label, a count — step aside, and the ones that answer "which row is this"
   and "how recent" stay. Marked in the markup rather than by position,
   because which column is expendable differs per table. */
@media (max-width: 700px) {
  .ov-table { min-width: 0; }
  .ov-col-optional { display: none; }
}

/* Not sticky: the wrapper clips its overflow, so a sticky header would have
   nothing to stick to and would only cost a paint layer. */
.ov-table thead th {
  padding: 0.625rem 0.875rem; text-align: left;
  font-size: 0.625rem; font-weight: 500; letter-spacing: 0.08em;
  text-transform: uppercase; color: rgb(var(--ov-void-500));
  background: rgb(var(--ov-void-900));
  border-bottom: 1px solid var(--ov-hairline);
}
.ov-table td { padding: 0.6875rem 0.875rem; vertical-align: top; }
.ov-table tbody tr + tr { border-top: 1px solid var(--ov-hairline); }

.ov-table__row {
  cursor: pointer;
  transition: background-color 0.15s ease;
  animation: ov-row-in 0.42s cubic-bezier(0.16, 1, 0.3, 1) both;
  /* Rows arrive in sequence rather than all at once; capped so a long list
     does not keep the reader waiting on the last row. */
  animation-delay: calc(min(var(--i, 0), 14) * 22ms);
}
.ov-table__row:hover { background: rgb(var(--ov-void-850)); }
.ov-table__row:hover .ov-table__title { color: var(--ov-accent); }
@keyframes ov-row-in { from { opacity: 0; transform: translateY(5px); } }
@media (prefers-reduced-motion: reduce) {
  .ov-table__row { animation: none; }
}

.ov-table__title {
  display: block; font-size: 0.8125rem; font-weight: 500; line-height: 1.35;
  color: rgb(var(--ov-void-100));
  transition: color 0.15s ease;
}
.ov-table__excerpt {
  display: block; margin-top: 0.1875rem;
  font-size: 0.6875rem; line-height: 1.4; color: rgb(var(--ov-void-500));
  /* One line: the excerpt is a hint at the content, and a wrapping one would
     bring back the ragged row heights the cards had. */
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* ---------- figure row ----------
   Four numbers read across as one figure. Boxing each one would say they are
   four unrelated things; a hairline between them says they belong together. */
.ov-figures {
  display: grid; gap: 0;
  grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
  border: 1px solid var(--ov-hairline); border-radius: 0.875rem;
  background: rgb(var(--ov-void-950)); overflow: hidden;
}
.ov-figure { padding: 0.9rem 1.1rem; border-left: 1px solid var(--ov-hairline); }
.ov-figure:first-child { border-left: 0; }
.ov-figure__label {
  font-size: 0.625rem; letter-spacing: 0.08em; text-transform: uppercase;
  color: rgb(var(--ov-void-500));
}
.ov-figure__value {
  margin-top: 0.3rem; font-family: "JetBrains Mono", monospace;
  font-size: 1.35rem; font-weight: 600; font-variant-numeric: tabular-nums;
  color: rgb(var(--ov-void-50)); line-height: 1.1;
}
.ov-figure__unit {
  margin-left: 0.4rem; font-size: 0.75rem; font-weight: 400;
  color: rgb(var(--ov-void-500));
}

/* ---------- day bars ----------
   A chart library for one bar per day would be more code than the chart. */
.ov-bars {
  display: grid; gap: 2px; align-items: end; height: 7rem;
  grid-template-columns: repeat(var(--count, 30), minmax(0, 1fr));
}
.ov-bar { display: flex; height: 100%; align-items: flex-end; }
.ov-bar__fill {
  display: block; width: 100%; min-height: 2px; border-radius: 2px 2px 0 0;
  background: rgba(47, 174, 135, 0.55);
  height: var(--h, 0%);
  transform-origin: bottom;
  animation: ov-bar-grow 0.5s cubic-bezier(0.16, 1, 0.3, 1) both;
  animation-delay: calc(min(var(--i, 0), 30) * 12ms);
  transition: background-color 0.15s ease;
}
.ov-bar:hover .ov-bar__fill { background: var(--ov-accent); }
/* A day with nothing spent still gets its 2px, so the gap is visible as a gap
   rather than as a missing column. */
.ov-bar__fill[style*="--h: 0%"] { background: rgb(var(--ov-void-700)); }
@keyframes ov-bar-grow { from { transform: scaleY(0); opacity: 0; } }
@media (prefers-reduced-motion: reduce) { .ov-bar__fill { animation: none; } }

/* ---------- session timeline ----------
   Sits above the transcript rather than beside it: the column is already
   narrow, and a side rail would take width from the thing being read. */
.ov-timeline {
  flex: none; margin-top: 0.75rem;
  border: 1px solid var(--ov-hairline); border-radius: 0.75rem;
  background: var(--ov-panel);
}
.ov-timeline__summary {
  display: flex; align-items: center; gap: 0.5rem; cursor: pointer;
  padding: 0.5rem 0.75rem; font-size: 0.75rem; color: rgb(var(--ov-void-200));
  list-style: none;
}
.ov-timeline__summary::-webkit-details-marker { display: none; }
.ov-timeline__caret { flex: none; transition: transform 0.2s ease; color: rgb(var(--ov-void-500)); }
.ov-timeline[open] .ov-timeline__caret { transform: rotate(90deg); }
.ov-timeline__count {
  padding: 0.05rem 0.4rem; border-radius: 999px; font-size: 0.625rem;
  font-family: "JetBrains Mono", monospace;
  background: rgb(var(--ov-void-850)); color: rgb(var(--ov-void-300));
}
.ov-timeline__hint {
  font-size: 0.6875rem; color: rgb(var(--ov-void-500));
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.ov-timeline__body {
  padding: 0.25rem 0.75rem 0.75rem;
  border-top: 1px solid var(--ov-hairline);
}
.ov-timeline__compare {
  display: flex; align-items: center; justify-content: flex-end; gap: 0.5rem;
  margin-top: 0.5rem; padding-top: 0.5rem;
  border-top: 1px solid var(--ov-hairline);
}
/* A class rule beats the browser's [hidden] { display: none }, so the bar
   would sit there offering a comparison of nothing. */
.ov-timeline__compare[hidden] { display: none; }

/* The checkpoints themselves, on a rail so they read as one sequence. */
.ov-cps { position: relative; margin-top: 0.5rem; max-height: 15rem; overflow-y: auto; }
.ov-cps::before {
  content: ""; position: absolute; left: 0.3125rem; top: 0.6rem; bottom: 0.6rem;
  width: 1px; background: var(--ov-hairline);
}
.ov-cp {
  position: relative; display: flex; align-items: flex-start; gap: 0.625rem;
  padding: 0.4rem 0 0.4rem 0;
  animation: ov-row-in 0.4s cubic-bezier(0.16, 1, 0.3, 1) both;
  animation-delay: calc(min(var(--i, 0), 10) * 25ms);
}
.ov-cp__mark {
  flex: none; width: 0.6875rem; height: 0.6875rem; margin-top: 0.15rem;
  border-radius: 999px; background: var(--ov-panel);
  box-shadow: inset 0 0 0 2px rgb(var(--ov-void-600));
}
.ov-cp__mark--ready   { box-shadow: inset 0 0 0 2px var(--ov-accent); }
.ov-cp__mark--pending { box-shadow: inset 0 0 0 2px rgb(var(--ov-void-500)); animation: ov-cp-wait 1.6s ease-in-out infinite; }
.ov-cp__mark--failed  { box-shadow: inset 0 0 0 2px var(--ov-danger); }
@keyframes ov-cp-wait { 50% { opacity: 0.35; } }

.ov-cp__label {
  font-size: 0.75rem; color: rgb(var(--ov-void-100)); line-height: 1.3;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.ov-cp__meta { margin-top: 0.1rem; font-size: 0.625rem; color: rgb(var(--ov-void-500)); }
.ov-cp__note { margin-top: 0.2rem; font-size: 0.625rem; line-height: 1.4; color: rgb(var(--ov-void-400)); }

.ov-cp__actions { display: flex; flex: none; align-items: center; gap: 0.3rem; }
.ov-cp__pick { display: inline-flex; align-items: center; gap: 0.25rem; cursor: pointer; }
.ov-cp__pick input { accent-color: var(--ov-accent); width: 0.8rem; height: 0.8rem; }
.ov-cp__pick span { font-size: 0.625rem; color: rgb(var(--ov-void-500)); }
.ov-cp__pick:hover span { color: rgb(var(--ov-void-300)); }
.ov-cp__del {
  display: inline-flex; padding: 0.2rem; border-radius: 0.375rem;
  color: rgb(var(--ov-void-500)); transition: color 0.15s ease, background-color 0.15s ease;
}
.ov-cp__del:hover { color: var(--ov-danger); background: rgb(var(--ov-void-850)); }
@media (prefers-reduced-motion: reduce) {
  .ov-cp { animation: none; }
  .ov-cp__mark--pending { animation: none; }
}

/* A note in the transcript that is neither the agent nor the user. */
.ov-marker {
  display: inline-flex; align-items: center; gap: 0.4rem; margin: 0 auto;
  padding: 0.25rem 0.625rem; border-radius: 999px;
  border: 1px dashed var(--ov-hairline);
  font-size: 0.6875rem; color: rgb(var(--ov-void-400));
}
.ov-marker svg { flex: none; color: rgb(var(--ov-void-500)); }
.ov-marker__link { color: var(--ov-accent); }
.ov-marker__link:hover { text-decoration: underline; }

/* ---------- changed-path list ---------- */
.ov-paths { padding: 0.25rem 0; max-height: 26rem; overflow-y: auto; }
.ov-path {
  display: flex; align-items: baseline; gap: 0.5rem;
  padding: 0.3rem 0.875rem; font-family: "JetBrains Mono", monospace;
  font-size: 0.6875rem;
  animation: ov-row-in 0.35s cubic-bezier(0.16, 1, 0.3, 1) both;
  animation-delay: calc(min(var(--i, 0), 20) * 15ms);
}
.ov-path + .ov-path { border-top: 1px solid var(--ov-hairline); }
.ov-path__sign { flex: none; width: 0.75rem; text-align: center; }
.ov-path__name { color: rgb(var(--ov-void-200)); word-break: break-all; }
.ov-path--added .ov-path__sign { color: var(--ov-accent); }
.ov-path--removed .ov-path__sign { color: var(--ov-danger); }
.ov-path--changed .ov-path__sign { color: #d9a44a; }
@media (prefers-reduced-motion: reduce) { .ov-path { animation: none; } }

/* ---------- CLAUDE.md editor ----------
   A plain textarea, because the file is plain text and a code editor would be
   300 kB of dependency to colour four kinds of markdown. */
.ov-md-editor {
  width: 100%; min-height: 26rem; padding: 1rem 1.1rem;
  border: 1px solid var(--ov-hairline); border-radius: 0.875rem;
  background: rgb(var(--ov-void-950)); color: rgb(var(--ov-void-100));
  font-family: "JetBrains Mono", monospace; font-size: 0.75rem; line-height: 1.7;
  resize: vertical; outline: none; tab-size: 2;
  transition: border-color 0.15s ease;
}
.ov-md-editor:focus { border-color: rgba(47, 174, 135, 0.45); }

.ov-md-preview {
  min-height: 26rem; padding: 1.25rem 1.5rem;
  font-size: 0.8125rem; line-height: 1.65; color: rgb(var(--ov-void-200));
}
.ov-md-preview h1, .ov-md-preview h2, .ov-md-preview h3,
.ov-md-preview h4, .ov-md-preview h5, .ov-md-preview h6 {
  margin: 1.4em 0 0.5em; font-weight: 600; color: rgb(var(--ov-void-50));
  line-height: 1.3;
}
.ov-md-preview h1 { font-size: 1.25rem; }
.ov-md-preview h2 { font-size: 1.05rem; }
.ov-md-preview h3 { font-size: 0.9rem; }
.ov-md-preview > :first-child { margin-top: 0; }
.ov-md-preview p { margin: 0.75em 0; }
.ov-md-preview ul { margin: 0.75em 0; padding-left: 1.2em; list-style: disc; }
.ov-md-preview li { margin: 0.25em 0; }
.ov-md-preview code {
  padding: 0.1em 0.35em; border-radius: 0.3rem;
  background: rgb(var(--ov-void-850)); color: var(--ov-accent);
  font-family: "JetBrains Mono", monospace; font-size: 0.9em;
}
.ov-md-preview pre {
  margin: 0.9em 0; padding: 0.8rem 1rem; border-radius: 0.6rem; overflow-x: auto;
  background: rgb(var(--ov-void-950)); border: 1px solid var(--ov-hairline);
}
.ov-md-preview pre code { padding: 0; background: none; color: rgb(var(--ov-void-200)); }
.ov-md-preview a { color: var(--ov-accent); }
.ov-md-preview a:hover { text-decoration: underline; }
.ov-md-preview strong { color: rgb(var(--ov-void-50)); font-weight: 600; }

/* A button that cannot do anything should not look like the main action. */
.ov-btn-primary:disabled, .ov-btn-ghost:disabled, .ov-btn-danger:disabled {
  opacity: 0.45; cursor: not-allowed; filter: grayscale(0.4);
}
.ov-btn-primary:disabled:hover, .ov-btn-ghost:disabled:hover,
.ov-btn-danger:disabled:hover { transform: none; }

/* ---------- header layout ----------
   Two equal side tracks with the search between them, so it is centred on the
   header rather than on whatever the page title left over. Both sides may
   shrink; only the title truncates, because the controls on the right cannot. */
.ov-head__side { flex: 1 1 0; min-width: 0; }
/* The header carries a z-index and therefore a stacking context of its own, so
   the search results are stacked *inside* it — under the page's own toolbar,
   which sits higher. Raised only while searching, so the mobile drawer keeps
   the top layer the rest of the time. */
body.ov-searching .ov-head { z-index: 60; }
.ov-head__side--end { justify-content: flex-end; flex-shrink: 0; }

/* ---------- global search ----------
   In the header because it belongs to the application, not to a page. Wide
   enough that a result line is readable inside the dropdown it opens. */
.ov-gs { position: relative; flex: 0 1 34rem; min-width: 0; }
.ov-gs__field {
  display: flex; align-items: center; gap: 0.5rem;
  width: 100%; padding: 0.45rem 0.6rem 0.45rem 0.75rem;
  border: 1px solid var(--ov-hairline); border-radius: 0.7rem;
  background: rgb(var(--ov-void-900));
  transition: border-color 0.15s ease, background-color 0.15s ease;
}
.ov-gs__field:focus-within {
  border-color: rgba(47, 174, 135, 0.45);
  background: rgb(var(--ov-void-950));
}
.ov-gs__icon { flex: none; color: rgb(var(--ov-void-500)); }
.ov-gs__input {
  flex: 1; min-width: 0; background: none; border: 0; outline: none;
  font-size: 0.8125rem; color: rgb(var(--ov-void-100));
}
.ov-gs__input::placeholder { color: rgb(var(--ov-void-500)); }
.ov-gs__input::-webkit-search-cancel-button { display: none; }
.ov-gs__kbd {
  flex: none; padding: 0.1rem 0.35rem; border-radius: 0.3rem;
  border: 1px solid var(--ov-hairline);
  font-family: "JetBrains Mono", monospace; font-size: 0.625rem;
  color: rgb(var(--ov-void-500));
}
.ov-gs__mic {
  flex: none; display: inline-flex; padding: 0.25rem; border-radius: 0.4rem;
  color: rgb(var(--ov-void-500)); transition: color 0.15s ease, background-color 0.15s ease;
}
.ov-gs__mic:hover { color: rgb(var(--ov-void-200)); background: rgb(var(--ov-void-850)); }
/* Listening has to be unmistakable — the microphone is on. */
.ov-gs__mic--on { color: var(--ov-accent); animation: ov-gs-listen 1.3s ease-in-out infinite; }
@keyframes ov-gs-listen { 50% { opacity: 0.4; } }

/* Below this the three tracks do not fit: the field becomes an icon that
   opens the search as a full-width overlay, so it is still one tap away
   without squeezing the title off the header. */
@media (max-width: 860px) {
  .ov-head__side { flex: 0 1 auto; }
  .ov-gs { position: static; flex: none; }
  .ov-gs__field {
    width: 2.5rem; padding-left: 0.6rem; padding-right: 0.6rem;
    cursor: pointer;
  }
  .ov-gs__input, .ov-gs__kbd, .ov-gs__mic { display: none; }
  .ov-gs--open .ov-gs__field {
    position: absolute; left: 0.75rem; right: 0.75rem; top: 0.6rem; z-index: 75;
    width: auto; padding-right: 0.5rem;
  }
  .ov-gs--open .ov-gs__input { display: block; }
  .ov-gs--open .ov-gs__mic { display: inline-flex; }
  .ov-gs__panel { left: 0.75rem; right: 0.75rem; width: auto; top: 3.5rem; }
}
@media (prefers-reduced-motion: reduce) {
  .ov-gs__mic--on { animation: none; }
}

.ov-gs__panel {
  position: absolute; left: 0; right: 0; top: calc(100% + 0.45rem); z-index: 70;
  max-height: min(26rem, 60vh); overflow-y: auto;
  padding: 0.35rem; border-radius: 0.8rem;
  border: 1px solid var(--ov-line); background: var(--ov-panel);
  box-shadow: 0 18px 50px -16px rgba(0, 0, 0, 0.7);
  animation: ov-gs-drop 0.16s cubic-bezier(0.16, 1, 0.3, 1);
}
.ov-gs__panel[hidden] { display: none; }
@keyframes ov-gs-drop { from { opacity: 0; transform: translateY(-4px); } }

.ov-gs__group {
  padding: 0.5rem 0.65rem 0.25rem;
  font-size: 0.5625rem; letter-spacing: 0.09em; text-transform: uppercase;
  color: rgb(var(--ov-void-500));
}
.ov-gs__item {
  display: block; padding: 0.4rem 0.65rem; border-radius: 0.5rem;
  transition: background-color 0.12s ease;
}
.ov-gs__item:hover, .ov-gs__item--on { background: rgb(var(--ov-void-850)); }
.ov-gs__title {
  display: block; font-size: 0.8125rem; color: rgb(var(--ov-void-100));
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.ov-gs__sub {
  display: block; margin-top: 0.1rem; font-size: 0.6875rem;
  color: rgb(var(--ov-void-500));
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.ov-gs__all {
  display: block; margin-top: 0.25rem; padding: 0.5rem 0.65rem;
  border-top: 1px solid var(--ov-hairline); border-radius: 0 0 0.5rem 0.5rem;
  font-size: 0.75rem; color: var(--ov-accent);
}
.ov-gs__all:hover { background: rgb(var(--ov-void-850)); }
.ov-gs__empty { padding: 0.9rem 0.7rem; font-size: 0.8125rem; color: rgb(var(--ov-void-500)); }

/* ---------- search results page ---------- */
.ov-hit {
  animation: ov-row-in 0.4s cubic-bezier(0.16, 1, 0.3, 1) both;
  animation-delay: calc(min(var(--i, 0), 12) * 20ms);
}
.ov-hit + .ov-hit { border-top: 1px solid var(--ov-hairline); }
.ov-hit__link {
  display: block; padding: 0.6rem 0.875rem;
  transition: background-color 0.15s ease;
}
.ov-hit__link:hover { background: rgb(var(--ov-void-850)); }
.ov-hit__link:hover .ov-hit__title { color: var(--ov-accent); }
.ov-hit__title {
  display: block; font-size: 0.8125rem; font-weight: 500;
  color: rgb(var(--ov-void-100)); transition: color 0.15s ease;
}
.ov-hit__sub {
  display: block; margin-top: 0.15rem; font-size: 0.6875rem; line-height: 1.4;
  color: rgb(var(--ov-void-500));
}
@media (prefers-reduced-motion: reduce) { .ov-hit { animation: none; } }

/* A quoted line from a transcript, shown under an observation as its
   evidence. Indented rather than boxed: it is a citation, not a panel. */
.ov-quote {
  padding-left: 0.7rem; border-left: 2px solid var(--ov-hairline);
  font-size: 0.6875rem; line-height: 1.5; color: rgb(var(--ov-void-400));
  font-style: italic;
}

/* Detail that earns its space on a desktop and costs a line wrap on a phone. */
@media (max-width: 700px) { .ov-only-wide { display: none; } }

/* A command or a file shown on a card, where getting a character wrong
   matters. Wraps rather than scrolls sideways: a half-visible shell command
   is worse than a wrapped one. */
.ov-code {
  padding: 0.7rem 0.85rem; border-radius: 0.6rem;
  border: 1px solid var(--ov-hairline); background: rgb(var(--ov-void-950));
  font-family: "JetBrains Mono", monospace; font-size: 0.6875rem;
  line-height: 1.6; color: rgb(var(--ov-void-200));
  white-space: pre-wrap; word-break: break-word;
}
