/* Qubis OS — the live panel, Qubis (the figure), the Studio, and the per-module panels that
   arrived after them (billing, platform console, roles, boot veil).
   Split out of style.css on 2026-09-10; see the header there for the seam and
   why the <link> order in index.html is load-bearing. */

/* ══════════════════════════════════════════════════════ right-hand "live" panel ══ */
.live-sidebar { display: flex; flex-direction: column; gap: 10px; padding: 16px 14px; overflow-y: auto; border-left: 1px solid var(--color-divider); }
.live-panel { display: flex; flex-direction: column; gap: 8px; }
.live-row {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding: 10px 12px; background: var(--color-surface); border-radius: var(--radius-md); box-shadow: var(--shadow-sm); font-size: 12px;
}
.live-row-label { color: var(--color-neutral-400); }
.live-row-value { font-weight: 600; text-align: right; }
.live-row-value.accent { color: var(--color-accent-300); }
.live-row-value.green { color: var(--signal); }
.live-row-value.red { color: var(--warn); }

/* Social sign-in. Deliberately quieter than the primary submit: for a company
   product, email+password is the path that always works, and the social
   buttons are a shortcut rather than the headline. */
.auth-divider { display: flex; align-items: center; gap: 10px; color: var(--color-neutral-500); font-size: 11px; }
.auth-divider::before, .auth-divider::after { content: ""; flex: 1; height: 1px; background: var(--color-divider); }
/* The SSO button on the sign-in screen (queue item 3), under the social ones. */
.auth-sso { margin-top: 8px; }
.btn-social {
  width: 100%; font: inherit; font-size: 13px; padding: 10px 12px; cursor: pointer;
  border-radius: var(--radius-md); border: 1px solid var(--color-divider);
  background: var(--color-surface); color: inherit;
}
.btn-social:hover { border-color: var(--color-accent); }

/* Wizard layout with Qubis beside the copy. Below 640px the figure moves above
   the text and shrinks — on a phone the words are what matter, and a large
   mascot pushing the buttons below the fold would be charm at the cost of
   the thing people came to do. */
.onboarding-body { display: grid; grid-template-columns: 132px 1fr; gap: 24px; align-items: center; }
.onboarding-lux { display: flex; justify-content: center; }
@media (max-width: 640px) {
  .onboarding-body { grid-template-columns: 1fr; justify-items: center; text-align: center; }
  .onboarding-lux .lux { width: 92px !important; }
}

/* The wizard's choice buttons (chat tool, and anything else that is a pick
   rather than a field). */
.ob-team { display: flex; flex-direction: column; gap: 6px; margin: 4px 0 12px; max-height: 260px; overflow-y: auto; }
.ob-team-row { display: flex; gap: 10px; align-items: flex-start; padding: 7px 9px; border-radius: var(--radius-md); background: var(--color-surface); }
.ob-team-avatar {
  flex: none; width: 24px; height: 24px; border-radius: 50%; display: grid; place-items: center;
  font-size: 11px; font-weight: 700; background: var(--color-surface-2); color: var(--color-neutral-300);
  border: 1px solid var(--color-divider);
}
.ob-team-avatar.qubis { border-color: var(--color-accent); color: var(--color-accent-300); }
.ob-team-name { display: block; font-size: 12.5px; font-weight: 600; }
.ob-team-what { display: block; font-size: 11.5px; color: var(--color-neutral-400); line-height: 1.42; }

.ob-choices { display: flex; flex-direction: column; gap: 8px; margin: 4px 0 12px; }
/* One channel per row: the choice button grows, the primary marker (badge or
   "Make primary") sits beside it — beside, not nested, because a button
   inside a button is invalid HTML and the two taps mean different things. */
.ob-choice-row { display: flex; align-items: center; gap: 10px; }
.ob-choice-row .ob-choice { flex: 1; }
.ob-primary-badge {
  font-size: 11px; font-weight: 600; color: var(--color-accent);
  border: 1px solid var(--color-accent); border-radius: 999px; padding: 2px 8px; white-space: nowrap;
}
.ob-primary-make { font-size: 11.5px; white-space: nowrap; }
.ob-choice {
  display: flex; flex-direction: column; gap: 2px; text-align: left; cursor: pointer;
  padding: 10px 12px; border-radius: var(--radius-md);
  border: 1px solid var(--color-divider); background: var(--color-surface); color: inherit; font: inherit;
}
.ob-choice:hover { border-color: var(--color-accent); }
.ob-choice.selected { border-color: var(--color-accent); background: color-mix(in srgb, var(--color-accent) 12%, var(--color-surface)); }
.ob-choice-label { font-size: 13px; font-weight: 600; }
.ob-choice-note { font-size: 11.5px; color: var(--color-neutral-400); line-height: 1.4; }

/* ═════════════════════════════════════════════════════════════════ Qubis ══ */
/* Jona's figure (2026-09-16: the cube with the floating eyebrows), re-drawn
   in Qubis's palette — the original came in the founders' own house style
   (bone/cream/brick), which is a different company's identity. Every colour
   below is an app token, so the figure follows the theme. */
.lux { display: block; flex: none; }
.lux svg { width: 100%; height: auto; display: block; overflow: visible; }

.lux .lux-fill { fill: var(--color-surface-2); }
.lux .lux-line { fill: none; stroke: var(--color-neutral-400); stroke-width: 3.4; stroke-linecap: round; stroke-linejoin: round; }
.lux .lux-mouth { stroke-width: 3; }
.lux .lux-ink { fill: var(--color-neutral-300); }
.lux .lux-accent { fill: var(--color-accent); }
.lux .lux-warn { fill: var(--warn); }
.lux .lux-shadow { fill: var(--color-divider); }
.lux .lux-check-mark { fill: none; stroke: var(--color-surface); stroke-width: 4; stroke-linecap: round; stroke-linejoin: round; }
.lux .lux-prop-ring { fill: none; stroke: var(--color-accent); stroke-width: 4; stroke-linecap: round; }
/* The arm is drawn twice: a thick stroke in the outline colour with a thinner
   body-coloured stroke on top, which is how it reads as a limb with an
   outline rather than as a line. */
.lux .lux-arm-ink { fill: none; stroke: var(--color-neutral-400); stroke-width: 11; stroke-linecap: round; }
.lux .lux-arm-fill { fill: none; stroke: var(--color-surface-2); stroke-width: 5; stroke-linecap: round; }

.lux .lux-float, .lux .lux-body, .lux .lux-eyes, .lux .lux-arm, .lux .lux-brow, .lux .lux-prop, .lux .lux-shadow { transform-box: view-box; }
.lux .lux-arm-l { transform-origin: 35px 84px; }
.lux .lux-arm-r { transform-origin: 105px 84px; }
.lux .lux-body { transform-origin: 70px 112px; }
.lux .lux-eyes { transform-origin: 70px 74px; }
.lux .lux-brow-l { transform-origin: 55px 31px; }
.lux .lux-brow-r { transform-origin: 85px 31px; }
.lux .lux-shadow { transform-origin: 70px 130px; }
.lux .lux-float { transform-origin: 70px 112px; }
.lux .lux-prop { opacity: 0; }
.lux .lux-prop-ring { transform-origin: 70px 77px; }
.lux .lux-prop-check { transform-origin: 112px 104px; }
.lux .lux-prop-alert { transform-origin: 114px 26px; }

/* Breathing, blinking and a drifting brow happen in every state — it is
   what makes the figure read as present rather than as a diagram. */
.lux .lux-float { animation: lux-bob 3.4s ease-in-out infinite; }
.lux .lux-eyes { animation: lux-blink 5.2s ease-in-out infinite; }
.lux .lux-shadow { animation: lux-shadow 3.4s ease-in-out infinite; }
.lux .lux-brow-l { animation: lux-brow-drift 3.4s ease-in-out infinite; }
.lux .lux-brow-r { animation: lux-brow-drift 3.4s ease-in-out .15s infinite; }
.lux .lux-dot { animation: lux-glow 3.4s ease-in-out infinite; }

/* wave: brows up, arm waves */
.lux[data-state="wave"] .lux-arm-r { animation: lux-wave 1.5s ease-in-out infinite; }
.lux[data-state="wave"] .lux-brow-l, .lux[data-state="wave"] .lux-brow-r { animation: lux-brow-up 1.5s ease-in-out infinite; }
/* point: one brow raised */
.lux[data-state="point"] .lux-arm-r { animation: lux-point 2.4s ease-in-out infinite; }
.lux[data-state="point"] .lux-brow-r { animation: lux-brow-raise 2.4s ease-in-out infinite; }
/* think: brows slanted, a glance up, the dots */
.lux[data-state="think"] .lux-brow-l { animation: lux-brow-think-l 3s ease-in-out infinite; }
.lux[data-state="think"] .lux-brow-r { animation: lux-brow-think-r 3s ease-in-out infinite; }
.lux[data-state="think"] .lux-eyes { animation: lux-blink 5.2s ease-in-out infinite, lux-lookup .3s ease-out forwards; }
.lux[data-state="think"] .lux-body { animation: lux-lean 4s ease-in-out infinite; }
.lux[data-state="think"] .lux-prop-think { opacity: 1; }
.lux[data-state="think"] .lux-prop-think circle:nth-child(1) { animation: lux-dots 1.6s ease-in-out infinite; }
.lux[data-state="think"] .lux-prop-think circle:nth-child(2) { animation: lux-dots 1.6s ease-in-out .2s infinite; }
.lux[data-state="think"] .lux-prop-think circle:nth-child(3) { animation: lux-dots 1.6s ease-in-out .4s infinite; }
/* busy: the ring turns, the tempo goes up */
.lux[data-state="busy"] .lux-prop-ring { opacity: 1; animation: lux-spin 1.6s linear infinite; }
.lux[data-state="busy"] .lux-float { animation: lux-bob 1.2s ease-in-out infinite; }
.lux[data-state="busy"] .lux-dot { animation: lux-pulse 1.2s ease-in-out infinite; }
.lux[data-state="busy"] .lux-brow-l { animation: lux-brow-busy 1.2s ease-in-out infinite; }
.lux[data-state="busy"] .lux-brow-r { animation: lux-brow-busy 1.2s ease-in-out .1s infinite; }
/* done: a hop, arms up, the check */
.lux[data-state="done"] .lux-float { animation: lux-hop 1.6s ease-in-out infinite; }
.lux[data-state="done"] .lux-arm-r { animation: lux-cheer-r 1.6s ease-in-out infinite; }
.lux[data-state="done"] .lux-arm-l { animation: lux-cheer-l 1.6s ease-in-out infinite; }
.lux[data-state="done"] .lux-brow-l, .lux[data-state="done"] .lux-brow-r { animation: lux-brow-high 1.6s ease-in-out infinite; }
.lux[data-state="done"] .lux-prop-check { opacity: 1; animation: lux-pop .5s cubic-bezier(.2,1.5,.4,1) backwards; }
/* alert: brows down and in, a tilt */
.lux[data-state="alert"] .lux-body { animation: lux-tilt 1.4s ease-in-out infinite; }
.lux[data-state="alert"] .lux-brow-l { animation: lux-brow-frown-l .3s ease-out forwards; }
.lux[data-state="alert"] .lux-brow-r { animation: lux-brow-frown-r .3s ease-out forwards; }
.lux[data-state="alert"] .lux-prop-alert { opacity: 1; animation: lux-nudge 1.4s ease-in-out infinite; }

@keyframes lux-bob { 0%,100% { transform: translateY(0); } 50% { transform: translateY(-4px); } }
@keyframes lux-shadow { 0%,100% { transform: scale(1); } 50% { transform: scale(.9); } }
@keyframes lux-blink { 0%,93%,100% { transform: scaleY(1); } 96% { transform: scaleY(.1); } }
@keyframes lux-lookup { to { transform: translate(2px,-4px); } }
@keyframes lux-glow { 0%,100% { opacity: 1; } 50% { opacity: .5; } }
@keyframes lux-pulse { 0%,100% { opacity: 1; } 50% { opacity: .3; } }
@keyframes lux-brow-drift { 0%,100% { transform: translateY(0); } 50% { transform: translateY(-1.5px); } }
@keyframes lux-brow-up { 0%,100% { transform: translateY(-2px); } 50% { transform: translateY(-6px); } }
@keyframes lux-brow-raise { 0%,100% { transform: translateY(-3px) rotate(-8deg); } 50% { transform: translateY(-8px) rotate(-12deg); } }
@keyframes lux-brow-busy { 0%,100% { transform: translateY(-1px); } 50% { transform: translateY(-5px); } }
@keyframes lux-brow-high { 0%,100% { transform: translateY(-4px); } 45% { transform: translateY(-9px); } }
@keyframes lux-brow-think-l { 0%,100% { transform: translateY(-4px) rotate(-12deg); } 50% { transform: translateY(-6px) rotate(-14deg); } }
@keyframes lux-brow-think-r { 0%,100% { transform: translateY(-5px) rotate(10deg); } 50% { transform: translateY(-7px) rotate(12deg); } }
@keyframes lux-brow-frown-l { to { transform: translateY(3px) rotate(16deg); } }
@keyframes lux-brow-frown-r { to { transform: translateY(3px) rotate(-16deg); } }
@keyframes lux-wave { 0%,100% { transform: rotate(-58deg); } 50% { transform: rotate(-96deg); } }
@keyframes lux-point { 0%,100% { transform: rotate(-40deg); } 50% { transform: rotate(-50deg); } }
@keyframes lux-lean { 0%,100% { transform: rotate(-3deg); } 50% { transform: rotate(1deg); } }
@keyframes lux-tilt { 0%,100% { transform: rotate(-4deg); } 50% { transform: rotate(4deg); } }
@keyframes lux-dots { 0%,100% { opacity: .2; } 45% { opacity: 1; } }
@keyframes lux-spin { to { transform: rotate(360deg); } }
@keyframes lux-hop { 0%,100% { transform: translateY(0); } 25% { transform: translateY(-13px); } 45% { transform: translateY(0); } 62% { transform: translateY(-5px); } 78% { transform: translateY(0); } }
@keyframes lux-cheer-r { 0%,100% { transform: rotate(-92deg); } 45% { transform: rotate(-118deg); } }
@keyframes lux-cheer-l { 0%,100% { transform: rotate(92deg); } 45% { transform: rotate(118deg); } }
@keyframes lux-pop { from { transform: scale(0); } 70% { transform: scale(1.18); } to { transform: scale(1); } }
@keyframes lux-nudge { 0%,100% { transform: translateY(0); } 50% { transform: translateY(-5px); } }

/* Motion off, meaning kept: the props that carry information (the check, the
   alert, the thinking dots) stay visible, and the poses freeze where they
   read correctly instead of collapsing to neutral. */
@media (prefers-reduced-motion: reduce) {
  .lux * { animation: none !important; }
  .lux[data-state="think"] .lux-prop-think,
  .lux[data-state="busy"] .lux-prop-ring,
  .lux[data-state="done"] .lux-prop-check,
  .lux[data-state="alert"] .lux-prop-alert { opacity: 1; }
  .lux[data-state="wave"] .lux-arm-r { transform: rotate(-80deg); }
  .lux[data-state="point"] .lux-arm-r { transform: rotate(-45deg); }
  .lux[data-state="done"] .lux-arm-r { transform: rotate(-104deg); }
  .lux[data-state="done"] .lux-arm-l { transform: rotate(104deg); }
  .lux[data-state="alert"] .lux-brow-l { transform: translateY(3px) rotate(16deg); }
  .lux[data-state="alert"] .lux-brow-r { transform: translateY(3px) rotate(-16deg); }
  .lux[data-state="think"] .lux-brow-l { transform: translateY(-4px) rotate(-12deg); }
  .lux[data-state="think"] .lux-brow-r { transform: translateY(-5px) rotate(10deg); }
}

/* Qubis at the top, agents in a row beneath, curved lines between — the
   marketing site's diagram, drawn in the app's own tokens. Beams are dashed
   paths whose dash offset is driven from studio.js; everything else is CSS. */
.studio-svg { width: 100%; height: 100%; overflow: hidden; touch-action: none; cursor: grab; user-select: none; }
.studio-svg.panning { cursor: grabbing; }
/* The pannable scene inside the SVG: the viewBox is the window, this group is the world. */
.st-world { transform-box: fill-box; }
#studioStage { position: relative; }
.studio-zoom { position: absolute; right: 10px; bottom: 10px; display: flex; flex-direction: column; gap: 4px; }
.studio-zoom button { width: 30px; height: 30px; border-radius: 8px; border: 1px solid var(--color-divider); background: var(--color-surface); color: var(--color-text); font: inherit; font-size: 15px; cursor: pointer; }
.studio-zoom button:hover { border-color: var(--color-accent); }
.st-path { fill: none; stroke: var(--color-divider); stroke-width: 1.25; }
.st-beam { fill: none; stroke: var(--color-accent); stroke-width: 1.8; stroke-linecap: round; }
.st-beam.st-back { stroke: var(--signal); }
.st-node { cursor: pointer; }
.st-circle { fill: var(--color-surface); stroke: var(--color-divider); stroke-width: 1.4; transition: stroke .4s, fill .4s; }
.st-circle.qubis { stroke: var(--color-accent); stroke-width: 1.6; }
.st-core { fill: var(--color-accent); transition: r .4s; }
.st-qubis.busy .st-core { animation: st-core 1.4s ease-in-out infinite; }
@keyframes st-core { 50% { r: 9; } }
/* The ring that leaves Qubis and fades: the system is on. */
.st-ring { fill: none; stroke: var(--color-accent); stroke-width: 1; opacity: 0; animation: st-ring 2.6s ease-out infinite; }
@keyframes st-ring { 0% { r: 34; opacity: .35; } 100% { r: 60; opacity: 0; } }
.st-node.busy .st-circle { stroke: var(--color-accent); fill: color-mix(in srgb, var(--color-accent) 10%, var(--color-surface)); }
.st-node.waiting .st-circle { stroke: var(--warn); }
.st-node.returned .st-circle { stroke: var(--signal); fill: color-mix(in srgb, var(--signal) 10%, var(--color-surface)); }
.st-hold { fill: none; stroke: var(--warn); stroke-width: 1.2; stroke-dasharray: 3 3; opacity: 0; transition: opacity .4s; }
.st-node.waiting .st-hold { opacity: 1; animation: st-hold 2.2s ease-in-out infinite; }
@keyframes st-hold { 50% { r: 34; } }
.st-initials { fill: var(--color-neutral-300); font-size: 11px; font-weight: 700; text-anchor: middle; dominant-baseline: middle; }
.st-name { fill: var(--color-neutral-400); font-size: 11px; text-anchor: middle; }
.st-name.qubis { fill: var(--color-text); font-weight: 600; font-size: 13px; }
.st-doing { fill: var(--color-accent-300); font-size: 10px; text-anchor: middle; }
.st-tag { fill: var(--warn); font-size: 10px; text-anchor: middle; opacity: 0; transition: opacity .4s; }
.st-node.waiting .st-tag { opacity: 1; }
.st-done { fill: var(--signal); font-size: 10.5px; opacity: 0; transition: opacity .5s; }
.st-done.show { opacity: 1; }
.st-amb { fill: var(--color-accent); opacity: 0; }
@media (prefers-reduced-motion: reduce) {
  .st-ring, .st-qubis.busy .st-core, .st-node.waiting .st-hold { animation: none; }
  .st-ring { opacity: 0; }
}

.msg-cached-note { font-size: 10.5px; color: var(--color-neutral-500); margin-top: 3px; }

/* Guided key setup (onboarding). One card per vendor: the button that opens
   its console, the shape the key will have, the paste field. */
.byok-card {
  border: 1px solid var(--color-divider); border-radius: var(--radius-md);
  padding: 12px; margin-bottom: 10px; background: var(--color-surface);
}
.byok-card-head { display: flex; justify-content: space-between; align-items: baseline; margin-bottom: 8px; }
.byok-card-name { font-size: 13px; font-weight: 600; }
.byok-card-row { display: flex; gap: 8px; }
.byok-card-row .field-input { flex: 1; min-width: 0; }
.byok-card-state { font-size: 11.5px; color: var(--color-neutral-400); margin-top: 6px; line-height: 1.4; min-height: 1em; }

/* Work page (Section 3, 2026-09-11): one list of root tasks, each row a
   button that opens the drawer; the same row renders the tree inside it. */
.task-list { display: flex; flex-direction: column; gap: 6px; }
.task-row {
  display: flex; align-items: center; justify-content: space-between; gap: 12px; width: 100%;
  padding: 10px 12px; border-radius: var(--radius-md); border: 1px solid transparent;
  background: transparent; color: var(--color-text); text-align: left; font: inherit; cursor: pointer;
  transition: background 0.15s, border-color 0.15s;
}
.task-row:hover, .task-row:focus-visible { background: color-mix(in srgb, var(--color-text) 5%, transparent); border-color: var(--color-divider); outline: none; }
.task-row-main { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.task-row-title { font-size: 14px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.task-row-detail { font-size: 12px; color: var(--color-neutral-400); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* One task in full, inside the drawer. */
.task-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.task-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 12px; }
.task-section-label { font-size: 12px; color: var(--color-neutral-400); margin: 16px 0 6px; }
.task-block {
  font-size: 13px; line-height: 1.55; white-space: pre-wrap; word-break: break-word;
  padding: 10px 12px; border-radius: var(--radius-md);
  background: var(--color-surface-2); border: 1px solid var(--color-divider);
}
.task-block-bad { border-color: color-mix(in srgb, var(--warn) 45%, transparent); }
.drawer .execution { max-width: none; }
.execution-at { margin-left: auto; flex: none; font-size: 11px; color: var(--color-neutral-500); font-variant-numeric: tabular-nums; }
.task-feedback { display: flex; gap: 8px; flex-wrap: wrap; }
.task-feedback input { flex: 1; min-width: 200px; }

/* Approvals. Deliberately the loudest thing in the sidebar — an accent border
   and a warm ground — because it is the only item there that is blocked on the
   person reading it, and one that scrolls past unnoticed is an agent that
   silently never finished. */
.approval-row {
  display: flex; flex-direction: column; gap: 6px;
  padding: 10px 12px; border-radius: var(--radius-md);
  background: color-mix(in srgb, var(--warn) 10%, var(--color-surface));
  border: 1px solid color-mix(in srgb, var(--warn) 45%, transparent);
}
.approval-what { font-size: 12px; font-weight: 600; line-height: 1.4; }
.approval-meta { font-size: 11px; color: var(--color-neutral-400); line-height: 1.35; }
.approval-actions { display: flex; gap: 6px; margin-top: 2px; }
.approval-yes, .approval-no {
  flex: 1; font-size: 11px; font-weight: 600; padding: 5px 8px; cursor: pointer;
  border-radius: var(--radius-sm); border: 1px solid var(--color-divider); background: transparent;
  color: var(--color-neutral-400);
}
/* Approve carries the accent; reject stays quiet. Rejecting is already the
   safe half — an approval expires on its own if nobody acts — so it does not
   need to compete for attention. */
.approval-yes { background: var(--color-accent); border-color: var(--color-accent); color: white; }
.approval-yes:hover:not(:disabled) { filter: brightness(1.08); }
.approval-no:hover:not(:disabled) { color: var(--warn); border-color: var(--warn); }
.approval-yes:disabled, .approval-no:disabled { cursor: default; opacity: 0.6; }
/* Edit sits between them and stays quiet like Reject: it is a door to a form,
   not a decision, and must not compete with Approve. */
.approval-edit {
  flex: 1; font-size: 11px; font-weight: 600; padding: 5px 8px; cursor: pointer;
  border-radius: var(--radius-sm); border: 1px solid var(--color-divider); background: transparent;
  color: var(--color-neutral-400);
}
.approval-edit:hover:not(:disabled) { color: var(--color-accent); border-color: var(--color-accent); }
.approval-edit:disabled { cursor: default; opacity: 0.6; }
.approval-form { display: flex; flex-direction: column; gap: 6px; width: 100%; }
.approval-field { display: flex; flex-direction: column; gap: 3px; font-size: 10.5px; color: var(--color-neutral-400); }
.approval-field textarea {
  font: inherit; font-size: 12px; line-height: 1.4; color: var(--color-text);
  background: var(--color-surface); border: 1px solid var(--color-divider); border-radius: var(--radius-sm);
  padding: 6px 8px; resize: vertical; min-height: 28px;
}
.approval-field textarea:focus { outline: none; border-color: var(--color-accent); }

/* Active tasks. Same card as .live-row so the sidebar reads as one column,
   but stacked rather than label-and-value: a task has a name, a state and a
   bar, and squeezing those onto one line is how you get a row nobody reads. */
.task-row {
  display: flex; flex-direction: column; gap: 6px;
  padding: 10px 12px; background: var(--color-surface); border-radius: var(--radius-md); box-shadow: var(--shadow-sm);
}
.task-title { font-size: 12px; font-weight: 600; line-height: 1.35; }
.task-note { font-size: 11px; color: var(--color-neutral-400); line-height: 1.35; }
.task-bar { height: 4px; border-radius: 2px; background: var(--color-divider); overflow: hidden; }
/* Transitions the width so a jump from 15% to 45% reads as progress rather
   than as a redraw — and honours a reader who has asked for less motion. */
.task-bar-fill { height: 100%; background: var(--color-accent); border-radius: 2px; transition: width 400ms ease; }
@media (prefers-reduced-motion: reduce) { .task-bar-fill { transition: none; } }
/* No bar at all when the system is waiting on a person: a progress bar that
   cannot move is a promise it is not keeping. */
.task-waiting { font-size: 11px; font-weight: 600; color: var(--warn); }
.task-stop {
  align-self: flex-start; font-size: 11px; padding: 3px 9px; cursor: pointer;
  background: transparent; color: var(--color-neutral-400);
  border: 1px solid var(--color-divider); border-radius: var(--radius-sm);
}
.task-stop:hover:not(:disabled) { color: var(--warn); border-color: var(--warn); }
.task-stop:disabled { cursor: default; opacity: 0.6; }

@media (max-width: 1180px) {
  .app-shell { grid-template-columns: var(--left-col, 248px) 1fr; }
  .live-sidebar { display: none; }
  .side-rail-right { display: none !important; }
}

/* ═══════════════════════════════════════════════════════════ degraded notice ══ */
/* Rendered by core.js only when optional() could not fetch a module, which is
   why there is no markup for it in index.html — most sessions never have one.
   Bottom-left: the chat input owns the bottom centre and the live panel the
   right. Below the auth and onboarding overlays (z-index 100) deliberately — a
   person who cannot sign in has a more pressing problem than a missing panel —
   and pointer-events: none, so it can never end up on top of a control. */
.degraded-notice {
  position: fixed; left: 16px; bottom: 16px; z-index: 90; pointer-events: none;
  max-width: 340px; padding: 10px 14px; font-size: 12px; line-height: 1.45;
  background: color-mix(in srgb, var(--warn) 12%, var(--color-surface));
  color: color-mix(in srgb, var(--warn) 70%, var(--color-text));
  border: 1px solid color-mix(in srgb, var(--warn) 30%, transparent);
  border-radius: var(--radius-md); box-shadow: var(--shadow-md);
  animation: fadeUp 0.5s ease both;
}

/* ---------- Agent nav expander (shell.js) ---------- */
/* This list used to carry `max-height: min(38vh, 460px); overflow-y: auto`, to
   keep the user controls from being pushed out of a short viewport. It bought
   that by making itself a scroll container — which gives a flex item an
   automatic minimum size of 0 — so under a long nav it silently squashed
   towards nothing instead of pushing the sidebar past its height. The sidebar
   therefore never overflowed, never grew a scrollbar, and the bottom of the
   agent list was unreachable. The controls stay in view by .sidebar-bottom
   being sticky (style.css) instead, which costs the column nothing. */
.agent-nav-toggle { color: var(--color-neutral-500); }
.agent-nav-toggle .agent-avatar { background: transparent; border: 1px dashed var(--color-divider); }
.agent-nav-toggle:hover { color: var(--color-text); }

/* ---------- Overview spend & caps (dashboard.js) ---------- */
.spend-rows { display: flex; flex-direction: column; gap: 14px; }
.spend-row { display: flex; align-items: center; gap: 14px; }
.cap-ring {
  width: 46px; height: 46px; border-radius: 50%; flex: none;
  display: flex; align-items: center; justify-content: center;
}
.cap-ring-hole {
  width: 34px; height: 34px; border-radius: 50%;
  background: var(--color-surface);
  display: flex; align-items: center; justify-content: center;
  font-size: 10.5px; font-family: var(--font-mono); color: var(--color-neutral-300);
}
.cap-ring-none { border: 1px dashed var(--color-divider); }
.spend-main { flex: 1; min-width: 0; }
.spend-provider { font-size: 13.5px; font-weight: 500; }
.spend-amounts { font-size: 13px; margin-top: 2px; }
.spend-sub { color: var(--color-neutral-400); font-size: 12px; }
.spend-byo { font-size: 12px; color: var(--color-neutral-400); margin-top: 3px; }

/* ---------- Billing meter (billing.js) ---------- */
.meter-headline { display: flex; flex-direction: column; gap: 2px; margin-bottom: 12px; }
.meter-amount { font-size: 26px; font-weight: 500; font-family: var(--font-heading); letter-spacing: -0.01em; }
.meter-caption { font-size: 12px; color: var(--color-neutral-400); }
.meter-caps { margin: 12px 0; }
.routing-bar-fill.over { background: linear-gradient(90deg, var(--warn), var(--warn)); }

/* ---------- Platform console (platform.js) ---------- */
.platform-org-row { cursor: pointer; }
.platform-org-row:hover { background: color-mix(in srgb, var(--color-text) 5%, transparent); border-radius: var(--radius-sm); }
.platform-cap-form { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 12px; }
/* A feature row on the platform package card (queue item 8): the checkbox,
   the label and the source read as one line. */
.platform-feature-row { cursor: pointer; }
.platform-feature-row input[type="checkbox"] { margin-right: 8px; }
.platform-cap-form input {
  flex: 1; min-width: 130px; background: var(--color-surface-2); color: var(--color-text);
  border: 1px solid var(--color-divider); border-radius: var(--radius-sm); padding: 8px 10px; font-size: 13px;
}
.platform-cap-form button {
  background: var(--color-accent); color: var(--color-bg); border: none;
  border-radius: var(--radius-sm); padding: 8px 14px; font-size: 13px; cursor: pointer;
}
.platform-modal {
  position: fixed; inset: 0; z-index: 50;
  background: color-mix(in srgb, var(--color-bg) 70%, transparent);
  display: flex; align-items: center; justify-content: center;
}
.platform-modal-card { max-width: 460px; width: calc(100% - 40px); }
.platform-modal-actions { display: flex; justify-content: flex-end; gap: 8px; margin-top: 14px; }
.platform-modal-actions button {
  border: 1px solid var(--color-divider); border-radius: var(--radius-sm);
  padding: 7px 14px; font-size: 13px; background: transparent; color: var(--color-text); cursor: pointer;
}
.platform-modal-actions #platformModalConfirm { background: var(--color-accent); color: var(--color-bg); border-color: transparent; }

/* ---------- Settings tabs (settings.js) ---------- */
/* Tabs (tabs.js): pills over a panel stack, on Settings, Connections and
   Automations. .tab-off is the tabs' own hiding class, separate from .hidden —
   a panel a permission removed stays removed whichever tab is on. */
.tabs { display: flex; gap: 4px; flex-wrap: wrap; }
.tab {
  background: transparent; border: none; color: var(--color-neutral-400); font-size: 13px;
  padding: 7px 12px; border-radius: 8px; cursor: pointer; transition: background 0.15s, color 0.15s;
}
.tab:hover { background: color-mix(in srgb, var(--color-text) 7%, transparent); color: var(--color-text); }
.tab.active { background: var(--color-surface-2); color: var(--color-text); font-weight: 500; box-shadow: var(--shadow-sm); }
.tab:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 2px; }
/* !important for the reason .hidden has it: a tabbed panel may carry a display
   of its own — Automations' schedule stack is a flex column, and its rule sits
   later in this file — and a hiding utility that a panel's own styling can
   out-rank is not a hiding utility. */
.tab-off { display: none !important; }

/* The provider order editor (providers.js): numbered chips with move/remove
   icon buttons — they were native, unstyled ↑ ↓ × buttons before 2026-09-11. */
.order-editor { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
.order-chip {
  display: inline-flex; align-items: center; gap: 2px; padding: 2px 4px 2px 8px; font-size: 12.5px;
  border: 1px solid var(--color-divider); border-radius: 8px;
}
.order-btn { width: 22px; height: 22px; padding: 0; font-size: 13px; border-radius: 5px; }
.order-btn:disabled { opacity: 0.35; cursor: default; }
.provider-failure { color: color-mix(in srgb, var(--warn) 75%, var(--color-text)); }

/* ---------- Boot veil (index.html / app.js) ---------- */
.boot-veil {
  position: fixed; inset: 0; z-index: 100;
  background: var(--color-bg);
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 18px;
  transition: opacity 0.25s ease;
}
.boot-veil.lifting { opacity: 0; pointer-events: none; }
.boot-veil-text { font-size: 13px; color: var(--color-neutral-500); font-family: var(--font-mono); letter-spacing: 0.06em; }

/* ═══════════════════════════════════════════════════════════════ Roles ══ */
.roles-table { width: 100%; border-collapse: collapse; font-size: 12.5px; }
.roles-table th { text-align: center; font-size: 11px; font-weight: 600; color: var(--color-neutral-400); padding: 6px 4px; text-transform: capitalize; }
.roles-table th:first-child { text-align: left; }
.roles-group td { padding: 12px 4px 4px; font-size: 10.5px; font-weight: 600; letter-spacing: .06em; text-transform: uppercase; color: var(--color-neutral-400); border-bottom: 1px solid var(--color-divider); }
.roles-label { padding: 6px 4px; color: var(--color-text); }
.roles-lock { margin-left: 6px; font-size: 10px; color: var(--color-neutral-400); border: 1px solid var(--color-divider); border-radius: 999px; padding: 1px 6px; }
.roles-cell { text-align: center; padding: 4px; white-space: nowrap; }
/* A cell nobody may edit is a dot, never a dead switch: a greyed toggle
   invites a click that 403s. */
.roles-dot { display: inline-block; width: 8px; height: 8px; border-radius: 50%; background: var(--color-divider); }
.roles-dot.on { background: var(--color-neutral-400); }
.roles-switch { width: 30px; height: 17px; border-radius: 999px; border: 1px solid var(--color-divider); background: var(--color-surface); position: relative; cursor: pointer; padding: 0; vertical-align: middle; transition: background .15s, border-color .15s; }
.roles-switch::after { content: ""; position: absolute; top: 2px; left: 2px; width: 11px; height: 11px; border-radius: 50%; background: var(--color-neutral-400); transition: transform .15s, background .15s; }
.roles-switch.on { background: var(--color-accent); border-color: var(--color-accent); }
.roles-switch.on::after { transform: translateX(13px); background: white; }
/* The ring marks a cell that differs from the shipped default — the one thing
   a person scanning this grid actually wants to find. */
.roles-switch.overridden { box-shadow: 0 0 0 2px color-mix(in srgb, var(--color-accent) 35%, transparent); }
.roles-switch:disabled { opacity: .5; cursor: default; }
.roles-reset { margin-left: 4px; font-size: 12px; line-height: 1; background: transparent; border: 0; color: var(--color-neutral-400); cursor: pointer; vertical-align: middle; }
.roles-reset:hover { color: var(--color-accent); }

/* ═════════════════════════════════════════════ the sign-in starfield ═══ */
/* Sits between the page ground and the colour blobs' own layer: soft wash
   underneath, points of light over it. pointer-events:none because a canvas
   across the whole overlay would otherwise eat every click on the form. */
/* width/height are not redundant next to inset:0. A <canvas> is a replaced
   element, so `width:auto` resolves to its intrinsic 300x150 rather than
   stretching to the insets — without these two the field renders as a small
   rectangle in the top-left corner. */
.auth-stars { position: absolute; inset: 0; width: 100%; height: 100%; z-index: 0; pointer-events: none; }
/* Everything the person is actually here for stays above it. Without this the
   canvas paints over the card, since both are children of the same overlay. */
.auth-overlay > .nv-ambient { z-index: 0; }
.auth-overlay > .auth-hero,
.auth-overlay > .auth-card,
.auth-overlay > .auth-footnote { position: relative; z-index: 1; }
/* Light theme: fewer, fainter. The same field that reads as depth on a dark
   ground reads as dust on a white one. */
[data-theme="light"] .auth-stars { opacity: 0.55; }
/* The blobs are Qubis's own and stay, but at full strength they wash the stars
   out — two backgrounds competing and neither winning. Dimmed here only; every
   other surface that uses .nv-ambient is untouched. */
.auth-overlay > .nv-ambient .nv-blob { opacity: 0.34; }
[data-theme="light"] .auth-overlay > .nv-ambient .nv-blob { opacity: 0.24; }

/* The audit row's detail carries margin-top, which says it was meant to sit on
   its own line — but it is a <span> inside a <span>, so it renders inline and
   the margin does nothing: "Own AI key savedu1 · anthropic". Scoped to this
   list rather than fixing .connector-card-detail globally, where the class
   already sits in a flex column and behaves. */
#orgAuditList .connector-card-detail { display: block; }

/* A renameable role header. The company's own word on top, ours underneath
   when they differ — so somebody who called "member" a Foreman can still tell
   which row the documentation means. */
.roles-name.editable { cursor: pointer; border-bottom: 1px dashed var(--color-divider); }
/* A permission the role cannot change; the title says why. */
.roles-fixed { opacity: 0.55; cursor: help; }
.roles-name.editable:hover, .roles-name.editable:focus-visible { color: var(--color-accent); border-bottom-color: var(--color-accent); }
.roles-alias { display: block; font-family: var(--font-mono); font-size: 9.5px; font-weight: 400; color: var(--color-neutral-500); text-transform: none; margin-top: 1px; }

/* ── setup assistant ─────────────────────────────────────────────────────
   Onboarding's second half: the guided road from "tenant exists" to "Qubis is
   actually running the company". Same visual family as the wizard (card,
   Qubis, one accent), because it is the same guide finishing the same
   conversation. */
.assistant-btn {
  display: flex; align-items: center; gap: 8px; width: 100%;
  margin: 10px 0 4px; padding: 9px 10px; border-radius: var(--radius-md);
  border: 1px solid var(--color-accent-700); background: color-mix(in oklab, var(--color-accent-500) 12%, transparent);
  color: var(--color-text); font-size: 12.5px; font-weight: 500; text-align: left;
}
.assistant-btn:hover { background: color-mix(in oklab, var(--color-accent-500) 20%, transparent); }
.assistant-btn i { color: var(--color-accent-300); font-size: 15px; }
.assistant-btn em {
  margin-left: auto; font-style: normal; font-family: var(--font-mono); font-size: 10.5px;
  color: var(--color-accent-300); border: 1px solid var(--color-accent-700); border-radius: 999px; padding: 1px 7px;
}

.assistant-overlay {
  position: fixed; inset: 0; z-index: 90; display: flex; align-items: center; justify-content: center;
  background: rgba(8, 10, 16, 0.55); backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);
  padding: 20px;
}
.assistant-card {
  position: relative; width: min(640px, 100%); max-height: min(84vh, 760px); overflow-y: auto;
  background: var(--color-surface); border-radius: var(--radius-lg); box-shadow: var(--shadow-lg);
  padding: 26px 26px 18px;
}
.assistant-close { position: absolute; top: 12px; right: 12px; }
.assistant-head { display: flex; gap: 18px; align-items: center; padding-right: 30px; }
.assistant-head h2 { font-size: 20px; letter-spacing: -0.015em; }
.assistant-subline { margin-top: 6px; font-size: 13px; line-height: 1.55; color: var(--color-neutral-500); max-width: 44ch; }
.assistant-lux { flex: 0 0 auto; }

.assistant-meter {
  height: 4px; border-radius: 999px; background: var(--color-surface-2); margin: 18px 0 6px; overflow: hidden;
}
.assistant-meter i {
  display: block; height: 100%; width: 0; border-radius: 999px;
  background: var(--color-accent-400); transition: width 0.5s ease;
}

.assistant-steps { display: grid; gap: 0; }
.assistant-step {
  display: grid; grid-template-columns: 30px minmax(0, 1fr); gap: 14px;
  padding: 16px 0; border-bottom: 1px solid var(--color-divider); align-items: start;
}
.assistant-step:last-child { border-bottom: 0; }
.assistant-mark {
  width: 24px; height: 24px; border-radius: 50%; display: flex; align-items: center; justify-content: center;
  font-size: 12px; font-weight: 600; color: var(--color-accent-300);
  border: 1px solid var(--color-accent-700); background: color-mix(in oklab, var(--color-accent-500) 10%, transparent);
  margin-top: 1px;
}
.assistant-step.done .assistant-mark {
  border-color: transparent; background: var(--signal); color: #04150D;
}
.assistant-step.done .assistant-mark::after { content: "\2713"; font-size: 12px; }
.assistant-step-body h3 { font-size: 14.5px; font-weight: 600; letter-spacing: -0.01em; }
.assistant-step.done .assistant-step-body h3 { color: var(--color-neutral-500); text-decoration: line-through; text-decoration-color: var(--color-neutral-700); }
.assistant-step-body p { margin-top: 5px; font-size: 13px; line-height: 1.6; color: var(--color-neutral-500); max-width: 60ch; }
.assistant-step.done .assistant-step-body p { display: none; }
.assistant-act { margin-top: 10px; font-size: 12.5px; padding: 7px 14px; }
.assistant-note { color: var(--warn); font-size: 12px; margin-top: 6px; min-height: 0; }
.assistant-foot { display: flex; justify-content: flex-end; padding-top: 12px; }
.assistant-foot .btn-text { font-size: 12px; color: var(--color-neutral-500); }
.assistant-foot .btn-text:hover { color: var(--color-neutral-300); }

/* ---------- Automations (Section 4, 2026-09-11) ---------- */
/* One row shape for an objective, a schedule and a follow-up: what it is, a
   few quiet lines about it, and its controls on the right. The editor slot
   opens inline under the row's own lines. */
.automation-list { display: flex; flex-direction: column; gap: 6px; }
.automation-row {
  display: flex; align-items: flex-start; justify-content: space-between; gap: 14px;
  padding: 12px 12px; border-radius: var(--radius-md); border: 1px solid var(--color-divider);
}
.automation-main { display: flex; flex-direction: column; gap: 3px; min-width: 0; flex: 1; }
.automation-title { font-size: 14px; display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.automation-detail { font-size: 12.5px; color: var(--color-neutral-400); line-height: 1.5; overflow-wrap: anywhere; }
.automation-words { color: var(--color-text); }
.automation-error { color: color-mix(in srgb, var(--warn) 70%, var(--color-text)); }
.automation-actions { display: flex; flex-wrap: wrap; gap: 6px; justify-content: flex-end; flex: none; max-width: 46%; }
.automation-editor:not(:empty) { margin-top: 8px; }
.automation-add { margin-top: 10px; }
.automation-add > summary { cursor: pointer; font-size: 13px; color: var(--color-accent-300); }

/* Forms: one column of labelled fields, the base control look from
   style.css, and a compact two-column grid for the segment. */
/* One form vocabulary, page-independent (Section 6, 2026-09-12): a stack of
   labelled fields, an inline row for a checkbox and its label, an actions
   row. .automation-form keeps its name as the alias Automations already uses. */
.form-stack, .automation-form { display: flex; flex-direction: column; gap: 10px; max-width: 640px; margin-top: 8px; }
.field { display: flex; flex-direction: column; gap: 4px; font-size: 13px; }
.field > span { color: var(--color-neutral-400); font-size: 12px; }
.field-inline { display: flex; flex-direction: row; align-items: center; gap: 8px; font-size: 13px; }
.form-stack textarea, .automation-form textarea { resize: vertical; }
.form-actions, .automation-form-actions { display: flex; gap: 8px; }
.automation-when { display: flex; gap: 12px; }
.automation-when .field { flex: 1; }
.automation-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.automation-count { font-size: 12.5px; color: var(--color-accent-300); min-height: 1.2em; }

/* Template cards: a radio group that reads as choices, not a <select> with
   sentence-long options. The checked card takes the accent border. */
.template-cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 8px; }
.template-card { display: flex; gap: 10px; padding: 10px 12px; border: 1px solid var(--color-divider); border-radius: var(--radius-md); cursor: pointer; align-items: flex-start; }
.template-card:has(input:checked) { border-color: var(--color-accent); background: color-mix(in srgb, var(--color-accent) 8%, transparent); }
.template-card:has(input:focus-visible) { outline: 2px solid var(--color-accent); outline-offset: 2px; }
.template-card input { margin-top: 3px; accent-color: var(--color-accent); }
.template-card-body { display: flex; flex-direction: column; gap: 3px; }
.template-card-title { font-size: 14px; }
.template-card-detail { font-size: 12px; color: var(--color-neutral-400); line-height: 1.45; }

/* ---------- Contacts (crm.js, crmContact.js) ---------- */
.crm-toolbar { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.crm-toolbar > input[type="search"] { flex: 1 1 260px; }
.crm-toolbar-actions { display: flex; gap: 6px; flex-wrap: wrap; margin-left: auto; }
/* The segment: four fields and a consent switch, the same keys the approval
   card and the agent tool read. Quieter than the search — it narrows, the
   search finds. */
.crm-filters { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; margin-top: 8px; }
.crm-filters input:not([type="checkbox"]) { flex: 1 1 140px; max-width: 200px; font-size: 13px; padding: 6px 9px; }
.crm-table .crm-row { cursor: pointer; }
.crm-table .crm-row:hover td, .crm-table .crm-row:focus-visible td { background: color-mix(in srgb, var(--color-text) 4%, transparent); }
.crm-table .crm-row:focus-visible { outline: none; }
.pager { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-top: 10px; font-size: 12.5px; }
.pager-buttons { display: flex; gap: 6px; }
.crm-field { display: flex; gap: 12px; padding: 6px 0; font-size: 13.5px; border-bottom: 1px solid color-mix(in srgb, var(--color-divider) 60%, transparent); }
.crm-field > span:first-child { color: var(--color-neutral-400); flex: 0 0 110px; font-size: 12.5px; }
.crm-field > span:last-child { overflow-wrap: anywhere; }
.crm-list { display: flex; flex-direction: column; gap: 6px; }
.crm-deal { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 8px 10px; border: 1px solid var(--color-divider); border-radius: var(--radius-md); font-size: 13.5px; }
.crm-deal-main { min-width: 0; overflow-wrap: anywhere; }
.crm-deal select { font-size: 12.5px; padding: 4px 8px; }
.crm-deal-form { margin-top: 8px; }
.crm-note { padding: 8px 10px; border-radius: var(--radius-md); background: var(--color-surface-2); font-size: 13.5px; line-height: 1.5; white-space: pre-wrap; }
.crm-note .text-muted { font-size: 11.5px; margin-top: 3px; }
.crm-outreach-count { font-size: 13.5px; color: var(--color-accent-300); margin: 0 0 8px; }

/* ---------- What your agents may do (settings.js) ---------- */
/* A tool is a switch, and reads as one: a check when the agent has it, a
   prohibit sign when it was withdrawn. The inline strike-through it used to
   carry left a person guessing whether the chip was a tag or a control. */
/* One <details> per agent, so this panel is as long as the number of agents
   rather than the number of tools — every grid used to be on screen at once and
   a person who came to withdraw one capability scrolled past all of them. The
   summary is the row it always was, and keeps everything worth reading without
   opening anything: the name, the counts and why a tool was withdrawn.
   inline-block on the head leaves the disclosure triangle its own space on the
   first line, and the width it gives back stops a long reason line from
   wrapping underneath the marker. */
.tool-agent > summary { cursor: pointer; font-size: 14px; }
.tool-agent-head { display: inline-block; vertical-align: top; max-width: calc(100% - 22px); }
.tool-agent-head label { display: block; }
.tool-chips { margin: 8px 0 2px 22px; }
.tool-chip { display: inline-flex; align-items: center; gap: 5px; }
.tool-chip i { font-size: 13px; }
.tool-chip.withdrawn { opacity: 0.6; text-decoration: line-through; border-style: dashed; }
.tool-chip.withdrawn i { color: var(--warn); }
.tool-chip:disabled { cursor: default; }
.tool-withdrawn-reasons { display: block; margin-top: 4px; color: color-mix(in srgb, var(--warn) 70%, var(--color-text)); }

/* "Watch Qubis work" (2026-09-17): the header button that opens the Studio
   on the current turn, and the Studio's focus — the turn's agents lit, the
   rest dimmed, the question in a caption above the stage. */
.watch-btn { display: inline-flex; align-items: center; gap: 6px; margin-left: auto; margin-right: 10px; padding: 7px 12px; border-radius: 999px;
  border: 1px solid var(--color-accent); background: color-mix(in srgb, var(--color-accent) 12%, var(--color-surface)); color: var(--color-text);
  font: inherit; font-size: 12.5px; cursor: pointer; }
.watch-btn i { color: var(--color-accent); }
.watch-btn:hover { background: color-mix(in srgb, var(--color-accent) 22%, var(--color-surface)); }
.studio-focus { display: flex; align-items: center; gap: 12px; padding: 8px 12px; margin-bottom: 8px; border-radius: var(--radius-md);
  border: 1px solid var(--color-divider); background: var(--color-surface); font-size: 12.5px; color: var(--color-neutral-300); }
.studio-focus button { margin-left: auto; }
.studio-svg.focused .st-node:not(.focus) { opacity: .28; transition: opacity .4s; }
.studio-svg.focused .st-node.focus .st-circle { stroke: var(--color-accent); stroke-width: 2; }
.studio-svg.focused .st-node.focus .st-name { fill: var(--color-text); }

/* The credential form's status line (2026-09-17): checking, connected, or
   the vendor's refusal — inline, under the Save button, never a modal. */
.connector-credential-msg { min-height: 1.2em; margin-top: 6px; font-size: 12px; color: var(--color-neutral-300); }
