/* core/donna-companion.css — app-wide Donna companion.
 *
 * Everything Donna-as-a-character wears, in one file:
 *   1. her docked right-edge peek, tour and thinking states
 *   2. the flyout mini-chat (.dcx-*) and the composer mounted into it
 *   3. UNO herself — the character SVG, roam scenes and props (bottom of this file)
 *
 * Section 3 came from dashboard.css. Splitting her appearance across a route
 * stylesheet and this one meant the character that core/donna/moods.js drives on every
 * turn, app-wide, was styled by the dashboard — see core/donna/mascot.js.
 */

/* ── Docked right-edge peek ──────────────────────────────────────────────────
   In dock mode she is tucked against the right edge of the viewport. The figure is
   the drag handle (grab cursor); a plain click opens the chatbox. A soft shadow +
   hover nudge make her feel like a tab you can pull. */
.uno-actor[data-mode="dock"] { z-index: 61; }
.uno-actor[data-mode="dock"] .uno-figure {
  cursor: grab;
  filter: drop-shadow(-6px 8px 14px rgba(0, 0, 0, .28));
  touch-action: none;                 /* let the pointer drag own vertical movement */
}
.uno-actor[data-mode="dock"] .uno-figure:active { cursor: grabbing; }

/* A faint "grip" halo so she reads as interactive against busy routes. */
.uno-actor[data-mode="dock"]::after {
  content: "";
  position: absolute; inset: -6px -2px -6px -10px;
  border-radius: 18px;
  background: radial-gradient(60% 60% at 30% 50%, color-mix(in srgb, var(--uno-primary, #7c5cff) 22%, transparent), transparent 72%);
  opacity: 0; transition: opacity .2s ease; pointer-events: none;
}
.uno-actor[data-mode="dock"]:hover::after { opacity: 1; }

/* Roam and perched modes never show the dock chrome. */
.uno-actor[data-mode="roam"]::after,
.uno-actor[data-mode="perched"]::after { content: none; }

.uno-actor[data-mode="perched"] {
  position: absolute;
  top: 0;
  left: 0;
  z-index: 21;
  pointer-events: auto;
  cursor: pointer;
}

/* During a guided tour she must sit ABOVE the tutorial dim mask (z 99990+) so the
   user can see her pointing at the highlighted target. */
.uno-actor.uno-tour { z-index: 100000; }

/* "Thinking" — a soft pulse while a turn streams so she visibly works. */
.uno-actor.uno-thinking .uno-figure { animation: unoThink 1.1s ease-in-out infinite; }
@keyframes unoThink {
  0%, 100% { filter: brightness(1) drop-shadow(0 0 0 transparent); }
  50% { filter: brightness(1.12) drop-shadow(0 0 10px color-mix(in srgb, var(--uno-primary, #7c5cff) 60%, transparent)); }
}
@media (prefers-reduced-motion: reduce) {
  .uno-actor.uno-thinking .uno-figure { animation: none; }
}

@media (prefers-reduced-motion: reduce) {
  .uno-actor[data-mode="dock"]::after { transition: none; }
}

/* ── Floating mini-chat (donna/companion/chatbox.js) ─────────────────────────── */
.dcx {
  position: fixed;
  right: 78px;
  bottom: 24px;
  z-index: 62;
  /* Width lives in a variable so a future "expand" state is a class + one value,
     not a layout refactor. A single column is genuinely enough for the conversation,
     history, files and settings views — only code and tables need to scroll (below). */
  --dcx-w: 370px;
  width: min(var(--dcx-w), calc(100vw - 32px));
  max-height: min(72vh, 640px);
  display: flex;
  flex-direction: column;
  background: var(--surface), var(--btrans);
  backdrop-filter: blur(10px) brightness(0.5);
  border: var(--line);
  border-radius: 16px;
  box-shadow: 0 24px 60px rgba(0, 0, 0, .42), 0 2px 8px rgba(0, 0, 0, .3);
  overflow: hidden;
  transform: translateY(12px) scale(.98);
  opacity: 0;
  transition: transform .2s cubic-bezier(.2, .8, .2, 1), opacity .2s ease;
}
.dcx.is-open { transform: none; opacity: 1; }

.dcx-head { display: flex; align-items: center; gap: 10px; padding: 12px 14px; border-bottom: 1px solid var(--line, rgba(255, 255, 255, .08)); }
.dcx-avatar {
  display: grid; place-items: center; width: 30px; height: 30px; border-radius: 999px; flex: 0 0 auto;
  background: linear-gradient(135deg, var(--uno-light, #a999ff), var(--uno-primary, #7c5cff));
  color: #fff; font-size: 15px;
}
.dcx-head-text { display: flex; flex-direction: column; line-height: 1.15; min-width: 0; }
.dcx-title { font-weight: 600; font-size: 14px; }
.dcx-sub { font-size: 11px; opacity: .6; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* While she works the subtitle carries the live step instead of the route, so what she is
   doing stays legible after the newest bubble has been scrolled past. */
.dcx-sub.is-working {
  opacity: 1;
  color: var(--primary, #7c5cff);
  font-weight: 600;
}
.dcx-sub.is-working::before {
  content: "";
  display: inline-block;
  width: 5px; height: 5px;
  margin-right: 5px;
  vertical-align: 1px;
  border-radius: 50%;
  background: currentColor;
  animation: donnaPulse 1.25s ease-in-out infinite;
}
@media (prefers-reduced-motion: reduce) { .dcx-sub.is-working::before { animation: none; } }
.dcx-close { margin-left: auto; background: none; border: 0; color: inherit; font-size: 22px; line-height: 1; opacity: .6; cursor: pointer; padding: 0 4px; }
.dcx-close:hover { opacity: 1; }

.dcx-body { flex: 1 1 auto; overflow-y: auto; padding: 14px; display: flex; flex-direction: column; gap: 14px; }
.dcx-row { display: flex; }
.dcx-row--user { justify-content: flex-end; }
.dcx-row--donna { justify-content: flex-start; }
.dcx-bubble { max-width: 88%; padding: 9px 12px; border-radius: 14px; font-size: 13.5px; line-height: 1.5; word-wrap: break-word; }
/* pre-wrap lives on .dcx-att-caption, not here: the bubble also holds attachment
   chips, and the markup's own indentation would print as blank lines. */
.dcx-bubble--user { background: var(--primary, #7c5cff); color: #fff; border-bottom-right-radius: 5px; }

/* Her side of the conversation.
   It used to take the same 9px/14px pill treatment as the user's, which does not survive
   the contents: a Donna bubble holds a step log, a reasoning pill, markdown and sometimes
   a confirmation card, and a faint 8%-tint pill around all of that read as a big grey slab
   with a second bordered box floating inside it. So her turn gets a leading rail instead
   of a fill — the eye follows the rail down the whole answer, however tall it grows, and
   the inner rows no longer look like cards inside a card. */
.dcx-bubble--donna {
  max-width: 94%;
  background: transparent;
  border-left: 2px solid color-mix(in srgb, var(--primary, #7c5cff) 55%, transparent);
  border-radius: 0 12px 12px 0;
  padding: 2px 4px 2px 11px;
}
.dcx-bubble--donna p:first-child { margin-top: 0; }
.dcx-bubble--donna p:last-child { margin-bottom: 0; }

/* The rail carries the turn's state, so a glance down the thread shows which reply is
   still running, which stopped and which is finished — without reading a word. */
.dcx-bubble--donna:has(.donna-turn[data-phase="error"]) { border-left-color: var(--danger, #ff6b6b); }
.dcx-bubble--donna:has(.donna-turn[data-phase="waiting"]) { border-left-color: #f59e0b; }
.dcx-bubble--donna:has(.donna-turn[data-phase="done"]) {
  border-left-color: color-mix(in srgb, var(--text, #e6e9ef) 22%, transparent);
}

.dcx-muted { opacity: .6; }
.dcx-error { color: var(--danger, #ff6b6b); }

/* The steps / thoughts / prompt rules that used to sit here styled `.dcx-step*`,
   `.dcx-thoughts*`, `.dcx-prompt*` and `.dcx-pbtn` — the class names of the flyout's OWN
   simplified renderer, which was deleted when core/donna/render.js became the one bubble
   renderer for every surface. Nothing has emitted those classes since, so the rules styled
   markup that no longer exists while the markup that DOES exist (.donna-turn and its
   children) went unstyled here. Their replacements are in the "shared turn renderer"
   section below. */

/* ══════════════════════════════════════════════════════════════════════════════
   SHARED TURN RENDERER (core/donna/render.js)

   Donna left the chat route, but her bubble's styles did not: `.donna-turn` and its
   children are still defined in chat.css, which index.html happens to load app-wide.
   That works, so those rules are left where they are rather than moved in a way that
   would risk the chat thread. What lives HERE is what chat.css never had:

     • `.donna-activity` — the headline row, new in this renderer.
     • `.donna-answer-wait` / `.donna-answer-error` — emitted since the renderer was
       written, styled by nothing, so "Donna is thinking…" rendered as plain body text
       and a failed turn's reason was not even red.
     • the flyout's narrow-column treatment of the terminal.
   ══════════════════════════════════════════════════════════════════════════════ */

/* ── The activity line ───────────────────────────────────────────────────────
   One sentence saying what she is doing, and the only thing in the turn that animates.
   It is also the terminal's disclosure control, so the transcript is one click from the
   summary and there is no second affordance to hunt for. */
.donna-activity { display: flex; margin: 0 0 6px; }

.donna-activity .da-head {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  max-width: 100%;
  padding: 3px 9px 3px 7px;
  border: 1px solid transparent;
  border-radius: 999px;
  background: color-mix(in srgb, var(--text, #e6e9ef) 7%, transparent);
  color: var(--text-muted, #9aa3b2);
  font: inherit;
  font-size: 11.5px;
  font-weight: 600;
  line-height: 1.4;
  cursor: pointer;
  transition: background .18s ease, color .18s ease, border-color .18s ease;
}
/* No steps means nothing to expand — it stays a label, not a dead button. */
.donna-activity .da-head[disabled] { cursor: default; }
.donna-activity.has-steps .da-head:hover {
  background: color-mix(in srgb, var(--primary, #7c5cff) 16%, transparent);
  border-color: color-mix(in srgb, var(--primary, #7c5cff) 34%, transparent);
  color: var(--text, #e6e9ef);
}

.donna-activity .da-ico { font-size: 12px; flex: none; opacity: .9; }
.donna-activity .da-text { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.donna-activity .da-meta { flex: none; opacity: .6; font-weight: 500; font-variant-numeric: tabular-nums; }
.donna-activity .da-meta:empty { display: none; }
.donna-activity .da-caret { font-size: 10px; flex: none; opacity: .55; transition: transform .28s cubic-bezier(.4,0,.2,1); }
.donna-activity .da-head[aria-expanded="true"] .da-caret { transform: rotate(180deg); }
/* The caret promises detail; without steps there is none to promise. */
.donna-activity:not(.has-steps) .da-caret { display: none; }

/* The live dot. Present in every state so the row never changes width when she finishes —
   it just stops breathing and goes quiet. */
.donna-activity .da-dot {
  width: 6px; height: 6px; flex: none;
  border-radius: 50%;
  background: currentColor;
  opacity: .35;
}
.donna-activity.is-live .da-head {
  background: color-mix(in srgb, var(--primary, #7c5cff) 15%, transparent);
  border-color: color-mix(in srgb, var(--primary, #7c5cff) 32%, transparent);
  color: var(--text, #e6e9ef);
}
.donna-activity.is-live .da-dot {
  background: var(--primary, #7c5cff);
  opacity: 1;
  animation: donnaPulse 1.25s ease-in-out infinite;
}
.donna-activity.is-live .da-ico { opacity: 1; color: var(--primary, #7c5cff); }
@keyframes donnaPulse {
  0%, 100% { transform: scale(1);   box-shadow: 0 0 0 0 color-mix(in srgb, var(--primary, #7c5cff) 55%, transparent); }
  50%      { transform: scale(1.25); box-shadow: 0 0 0 4px transparent; }
}

/* Terminal states carry through to the headline so a failure or a pause is obvious
   without opening anything. */
.donna-turn[data-phase="error"] .da-head {
  background: color-mix(in srgb, var(--danger, #ff6b6b) 15%, transparent);
  border-color: color-mix(in srgb, var(--danger, #ff6b6b) 34%, transparent);
  color: #ffb4b4;
}
.donna-turn[data-phase="waiting"] .da-head {
  background: color-mix(in srgb, #f59e0b 16%, transparent);
  border-color: color-mix(in srgb, #f59e0b 36%, transparent);
  color: #ffd79a;
}
.donna-turn[data-phase="waiting"] .da-dot { background: #f59e0b; opacity: 1; }

@media (prefers-reduced-motion: reduce) {
  .donna-activity .da-dot { animation: none !important; }
  .donna-activity .da-caret, .donna-activity .da-head { transition: none; }
}

/* ── The two states the renderer emitted but nothing styled ─────────────────── */
.donna-answer-wait {
  display: inline-block;
  color: var(--text-muted, #9aa3b2);
  font-size: 13px;
  font-style: italic;
  opacity: .75;
}
.donna-answer-error {
  display: flex;
  align-items: flex-start;
  gap: 7px;
  margin: 2px 0 0;
  padding: 8px 10px;
  border-radius: 9px;
  border: 1px solid color-mix(in srgb, var(--danger, #ff6b6b) 30%, transparent);
  background: color-mix(in srgb, var(--danger, #ff6b6b) 11%, transparent);
  color: #ffb4b4;
  font-size: 12.5px;
  line-height: 1.45;
}
.donna-answer-error .bi { flex: none; margin-top: 1px; opacity: .9; }

/* ── The terminal, inside a turn ─────────────────────────────────────────────
   Scoped to `.donna-turn`, not to the flyout: inside a turn the activity line IS the
   terminal's head, on every surface, so a second header would be two controls for one
   disclosure. A `buildTerminal()` used on its own keeps the head chat.css gives it —
   nothing does that today, but the rule should not be the thing that stops it. */
.donna-turn .donna-terminal-head { display: none; }

/* With the head hidden, a CLOSED terminal is chat.css's bordered card wrapped around a
   zero-height reveal — an empty box under every finished reply. The card belongs to the
   transcript, so it only exists while the transcript does. */
.donna-turn .donna-terminal:not(.open) {
  border: 0;
  padding: 0;
  background: transparent;
}

/* The rest is the narrow column. chat.css gives the terminal a full card (border + fill +
   inner scroller), which is right in a wide thread and wrong in a 430px flyout where it
   sits nested inside her own turn: the result read as a box inside a box, and the step log
   outweighed the answer it belonged to. Here it drops the card and becomes an indented
   transcript hanging off the rail. */
#dcxBody .donna-terminal {
  border: 0;
  background: transparent;
  margin: 0 0 6px;
}
#dcxBody .donna-terminal.open .donna-terminal-scroll {
  padding: 2px 0 6px 4px;
  border-left: 1px dashed color-mix(in srgb, var(--text, #e6e9ef) 16%, transparent);
  margin-left: 3px;
}
#dcxBody .donna-terminal .term-line { font-size: 11px; }
#dcxBody .donna-answer { font-size: 13.5px; line-height: 1.55; }
#dcxBody .donna-thoughts { margin: 0 0 6px; }

/* ── Composer ─────────────────────────────────────────────────────────────────
   The input is core/composer.js, mounted by donna/companion/chatbox.js into the
   flyout footer. There is no bespoke bar/textarea/send button here any more —
   that copy is exactly why the flyout had no attachments and no recorder. These
   rules only fit the shared widget into a 370px column. */
/* .ifly-foot is styled as a caption row (11.5px, muted). The composer's input and Send
   button both take `font: inherit`, so the type context is reset once here rather than
   patched on each descendant. */
.dcx-composer-host {
  flex: 1 1 auto;
  min-width: 0;
  font-size: 13.5px;
  color: var(--text);
}
.dcx-composer-host .ucx-composer { border-radius: 16px; }

/* One attach + one record + Send fit the column comfortably; the gap is what the
   default 12px would waste. */
.dcx-composer-host .ucx-bottom { gap: 6px; }
.dcx-composer-host .ucx-input { max-height: 132px; }

/* The tray is a single scrolling row here rather than a wrapping grid: two chips
   side by side would otherwise push the panel's own width. */
.dcx-composer-host .ucx-attach-tray {
  flex-wrap: nowrap;
  overflow-x: auto;
  scrollbar-width: thin;
}

/* Attachment chips on a sent message. */
.dcx-atts { display: flex; flex-direction: column; gap: 5px; margin-bottom: 6px; }
.dcx-att {
  display: flex; align-items: center; gap: 7px;
  padding: 5px 8px; border-radius: 9px;
  background: color-mix(in srgb, #000 18%, transparent);
  font-size: 11.5px; min-width: 0;
}
.dcx-att img { width: 26px; height: 26px; object-fit: cover; border-radius: 6px; flex: 0 0 auto; }
.dcx-att i { flex: 0 0 auto; opacity: .8; }
.dcx-att-name { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dcx-att-size { flex: 0 0 auto; opacity: .65; }
.dcx-att-caption { display: block; white-space: pre-wrap; }

/* The footer is a container for the composer, not a row of its own controls. */
.ifly-foot:has(.dcx-composer-host) { padding: 8px 10px; display: block; }

/* ── Inline notices ──────────────────────────────────────────────────────────
   Feedback ABOUT the composer belongs next to the composer. "2 attachments could not be
   uploaded", "that file is too large", "nothing to export yet" were all going to the
   app-wide toast at bottom-right — which is exactly where this panel is, so the message
   landed on top of the input that caused it and covered the Send button while it sat there.

   This is not a second toast system (see the app-wide one in core/alerts.js, which stays
   the only floating notifier): it is a static strip inside the panel that pushes nothing
   around and disappears on the next action. Genuinely app-wide events still toast. */
.dcx-notice {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  margin: 0 0 6px;
  padding: 7px 9px;
  border-radius: 9px;
  border: 1px solid color-mix(in srgb, var(--text, #e6e9ef) 14%, transparent);
  background: color-mix(in srgb, var(--text, #e6e9ef) 7%, transparent);
  color: var(--text-muted, #9aa3b2);
  font-size: 11.5px;
  line-height: 1.45;
}
.dcx-notice[hidden] { display: none; }
.dcx-notice .dcx-notice-ico { flex: none; margin-top: 1px; }
.dcx-notice .dcx-notice-text { flex: 1 1 auto; min-width: 0; }
.dcx-notice .dcx-notice-close {
  flex: none; border: 0; background: none; color: inherit;
  font-size: 15px; line-height: 1; padding: 0 2px; cursor: pointer; opacity: .6;
}
.dcx-notice .dcx-notice-close:hover { opacity: 1; }
.dcx-notice.is-warn {
  border-color: color-mix(in srgb, #f59e0b 34%, transparent);
  background: color-mix(in srgb, #f59e0b 12%, transparent);
  color: #ffd79a;
}
.dcx-notice.is-error {
  border-color: color-mix(in srgb, var(--danger, #ff6b6b) 32%, transparent);
  background: color-mix(in srgb, var(--danger, #ff6b6b) 11%, transparent);
  color: #ffb4b4;
}

/* App-wide toasts default to the bottom-right corner, which the open flyout occupies.
   Slide the stack clear of it so a real app-wide toast stays readable instead of being
   half-hidden behind Donna's panel — and so it stops covering her composer.
   Only for the right-hand positions; a user who moved their toasts to the left already
   has no collision. */
body:has(.ifly[data-open="1"]) .toast-bar:not(.pos-top-left):not(.pos-bottom-left),
body:has(.ifly[data-open="1"]) .toasts:not(.pos-top-left):not(.pos-bottom-left) {
  right: calc(min(430px, 100vw) + 16px);
  transition: right .28s cubic-bezier(.22, .8, .28, 1);
}
@media (max-width: 560px) {
  /* The panel is full-width here, so there is nowhere to slide to; let them overlap. */
  body:has(.ifly[data-open="1"]) .toast-bar,
  body:has(.ifly[data-open="1"]) .toasts { right: 16px; }
}
@media (prefers-reduced-motion: reduce) {
  .toast-bar, .toasts { transition: none; }
}

@media (max-width: 560px) { .dcx { right: 12px; left: 12px; bottom: 12px; width: auto; } }
@media (prefers-reduced-motion: reduce) { .dcx { transition: opacity .15s ease; transform: none; } }

/* ── Narrow-column safety ─────────────────────────────────────────────────────
   Donna's flyout is one readable column. Everything she renders fits that except
   two things, which must scroll INSIDE themselves rather than widen the panel or
   spill: fenced code blocks (they carry a line-number gutter) and markdown tables.
   This is what makes the peek width sufficient without an expand mode. */
.dcx-body pre,
.dcx-body .code-block-container,
.dcx-body .m-code-grid,
#dcxBody pre,
#dcxBody .code-block-container,
#dcxBody .m-code-grid {
  max-width: 100%;
  overflow-x: auto;
}

.dcx-body table,
#dcxBody table {
  display: block;
  max-width: 100%;
  overflow-x: auto;
  border-collapse: collapse;
}

/* ── Markdown tables (assets/js/chat/chat-markdown.js) ───────────────────────
   Donna answers most data questions with a table — query_data returns rows, rows become a
   table — so this is the shape of her most valuable replies, and until the renderer grew
   table support they printed as raw `| Stage | Count |` pipes. Styled app-wide, not just
   in the flyout: the same renderer draws Home and chat. */
.md-table-wrap {
  max-width: 100%;
  overflow-x: auto;
  margin: 8px 0;
  border: 1px solid color-mix(in srgb, var(--text, #e6e9ef) 13%, transparent);
  border-radius: 9px;
}
.md-table {
  border-collapse: collapse;
  width: 100%;
  font-size: 12px;
  line-height: 1.45;
}
.md-table th,
.md-table td {
  padding: 6px 10px;
  text-align: left;
  border-bottom: 1px solid color-mix(in srgb, var(--text, #e6e9ef) 9%, transparent);
  white-space: nowrap;         /* a data cell that wraps is harder to scan than one that scrolls */
}
.md-table thead th {
  background: color-mix(in srgb, var(--text, #e6e9ef) 7%, transparent);
  font-weight: 700;
  font-size: 11px;
  letter-spacing: .02em;
  text-transform: uppercase;
  color: var(--text-muted, #9aa3b2);
}
.md-table tbody tr:last-child td { border-bottom: 0; }
.md-table tbody tr:hover td { background: color-mix(in srgb, var(--text, #e6e9ef) 4%, transparent); }
/* Numbers line up only if the digits do. */
.md-table td[style*="right"], .md-table th[style*="right"] { font-variant-numeric: tabular-nums; }

/* The wrapper is the scroller; the inner table must not also be one (the narrow-column
   rule above turns every `table` into a block-level scroller). */
.dcx-body .md-table-wrap table,
#dcxBody .md-table-wrap table { display: table; overflow-x: visible; }

/* Long unbroken strings (URLs, ids, tokens) must never push the column wider. */
.dcx-body,
#dcxBody { overflow-wrap: anywhere; }

/* ── One scroller, not two ───────────────────────────────────────────────────
   `.ifly-body` is the panel's scroll container and already carries its padding. The
   conversation used to add `height:100%; overflow-y:auto; padding:12px` inline on top of
   that, so the thread scrolled inside a box that itself scrolled — two scrollbars, doubled
   padding, and a "scroll to the newest message" that moved whichever one it reached first.
   The chat tab now flags the panel body and the conversation is a plain flow column. */
.ifly-body.is-dcx { padding: 12px 12px 6px; }
.ifly-body.is-dcx > .dcx-body {
  overflow: visible;                 /* the panel body is the scroller */
  padding: 0;
  min-height: 100%;
  justify-content: flex-end;         /* a short thread sits on the composer, not adrift */
}

/* The flyout's tab row reuses the peek engine's chip styling; this only marks the
   active tab so one view at a time reads clearly. */
.ifly-chip[data-donna-tab][aria-pressed="true"] {
  background: color-mix(in srgb, var(--primary, #60a5fa) 22%, transparent);
  border-color: color-mix(in srgb, var(--primary, #60a5fa) 45%, transparent);
}

/* ══════════════════════════════════════════════════════════════════════════════
   UNO — the character herself (core/donna/mascot.js).

   Moved here from dashboard.css. It styled an app-wide module from a route
   stylesheet, and the two halves of her appearance (the dock/tour/thinking states
   above, the character + roam scenes below) sat in different files.

   Order is preserved exactly as the browser saw it: donna-companion.css loads before
   dashboard.css in index.html, so the dock/tour rules above still come first. The two
   sets share no selector.
   ══════════════════════════════════════════════════════════════════════════════ */
/* ============================================================================
   UNO MASCOT — "Uno", a small character who roams the Home #content area and
   acts out (wordlessly, with props) what Unobits can do. position:fixed but
   confined by JS to #content so she never crosses the sidebar.
   ========================================================================== */
.uno-actor, .uno-props, .uno-front {
  --uno-primary: var(--primary, #7c5cff);
  --uno-light: color-mix(in srgb, var(--uno-primary) 42%, #ffffff);
  --uno-deep:  color-mix(in srgb, var(--uno-primary) 72%, #190f33);
  --uno-dark:  color-mix(in srgb, var(--uno-primary) 42%, #000);
  /* solid panel fill for props — NB: theme --surface is a noise image, not a colour */
  --uno-surface: color-mix(in srgb, var(--uno-primary) 16%, #11141c);
}
.uno-props { position: fixed; inset: 0; z-index: 57; pointer-events: none; }  /* props BEHIND her */
.uno-front { position: fixed; inset: 0; z-index: 60; pointer-events: none; }  /* labels/bubbles IN FRONT */
.uno-actor {
  position: fixed; left: 0; top: 0; width: 56px; z-index: 58; pointer-events: none;
  transition: none;          /* moves are animated by JS (moveTo); plain setPos is instant */
  will-change: left, top;
}
.uno-actor[hidden], .uno-props[hidden], .uno-front[hidden] { display: none; }
.uno-actor-flip { transform-origin: 50% 70%;transform: scaleX(-1); transition: transform .2s ease; }
.uno-actor[data-dir="left"] .uno-actor-flip { transform: scaleX(1);  }
.uno-figure { animation: unoBob 3.2s ease-in-out infinite; pointer-events: auto; cursor: pointer; }
.uno-figure:hover { filter: brightness(1.07); }
.uno-char {display: block;overflow: visible;}
@keyframes unoBob { 0%,100% { transform: translateY(0); } 50% { transform: translateY(-4px); } }
.uno-actor.uno-pop .uno-actor-flip { animation: unoPop .6s cubic-bezier(.2,.9,.25,1); }
@keyframes unoPop { 0% { transform: translateY(34px) scale(.5); opacity: 0; } 100% { transform: translateY(0) scale(1); opacity: 1; } }

/* ── Character parts (3D read: extruded side, rim light, gloss) ──────────────── */
.uno-shadow { fill: var(--text); transform-box: fill-box; transform-origin: center; animation: unoShadow 3.2s ease-in-out infinite; }
@keyframes unoShadow { 0%,100% { transform: scale(1); opacity: .34; } 50% { transform: scale(.84); opacity: .22; } }
.uno-actor.floating .uno-shadow { animation: none; opacity: .12; transform: scale(.7); }   /* faint when she's up in a corner */
.uno-antenna { stroke: var(--uno-light); stroke-width: 3; stroke-linecap: round; }
.uno-bit { fill: var(--uno-light); transform-box: fill-box; transform-origin: center; animation: unoBit 2s ease-in-out infinite; }
@keyframes unoBit { 0%,100% { opacity: .7; } 50% { opacity: 1; filter: drop-shadow(0 0 6px var(--uno-primary)); } }
.uno-body {stroke: color-mix(in srgb, var(--uno-primary) 60%, #000);stroke-width: 1.4;}
.uno-body-side {opacity: .96;}
.uno-rim {fill: none;stroke: #ffffff;stroke-width: 10; stroke-linecap: round; opacity: .5;}
.uno-sheen { fill: #ffffff; opacity: .18; }
.uno-face {stroke: rgba(0,0,0,.35);stroke-width: 1;}
.uno-eyes { transform-box: fill-box; transform-origin: center; animation: unoBlink 5s infinite; }
@keyframes unoBlink { 0%,5%,100% { transform: scaleY(1); } 2.5% { transform: scaleY(.08); } }
.uno-eye { filter: drop-shadow(0 0 4px rgba(103,232,255,.55)); }
.uno-glint { fill: #ffffff; opacity: .9; }
.uno-lash { stroke: #9fe3ff; stroke-width: 2; stroke-linecap: round; fill: none; opacity: .9; }  /* "she" */
.uno-cheek { fill: color-mix(in srgb, var(--uno-primary) 40%, #ff7eb6); opacity: .5; }
.uno-smile {fill: none;stroke: #67e8ff;stroke-width: 2.8;stroke-linecap: round;opacity: .65;}
.uno-limb {fill: url(#unoLimb);}
.uno-hand { fill: var(--uno-light); }
/* carried props — hidden until the matching act shows them */
.uno-cbox, .uno-cphone, .uno-cjotter, .uno-cplane, .uno-brows { display: none; }
.uno-cplane .uno-plane { filter: drop-shadow(0 2px 3px rgba(0,0,0,.4)); }
.uno-brows { stroke: #ff8585; stroke-width: 4; stroke-linecap: round; fill: none; }

/* limbs pivot at the shoulder / hip (top of their bbox) */
.uno-arm, .uno-leg { transform-box: fill-box; transform-origin: 50% 0%; }

/* idle: gentle arm sway */
.uno-actor[data-act="idle"] .uno-arm-l { animation: unoSwayA 3.2s ease-in-out infinite; }
.uno-actor[data-act="idle"] .uno-arm-r { animation: unoSwayB 3.2s ease-in-out infinite; }
@keyframes unoSwayA { 0%,100% { transform: rotate(5deg); } 50% { transform: rotate(-3deg); } }
@keyframes unoSwayB { 0%,100% { transform: rotate(-5deg); } 50% { transform: rotate(3deg); } }

/* walk */
.uno-actor[data-act="walk"] .uno-figure { animation: unoWalkBob .46s linear infinite; }
.uno-actor[data-act="walk"] .uno-leg-l { animation: unoStep .46s linear infinite; }
.uno-actor[data-act="walk"] .uno-leg-r { animation: unoStep .46s linear infinite; animation-delay: -.23s; }
.uno-actor[data-act="walk"] .uno-arm-l { animation: unoStepArm .46s linear infinite; animation-delay: -.23s; }
.uno-actor[data-act="walk"] .uno-arm-r { animation: unoStepArm .46s linear infinite; }
@keyframes unoWalkBob { 0%,100% { transform: translateY(0); } 50% { transform: translateY(-3px); } }
@keyframes unoStep { 0% { transform: rotate(22deg); } 50% { transform: rotate(-22deg); } 100% { transform: rotate(22deg); } }
@keyframes unoStepArm { 0% { transform: rotate(-18deg); } 50% { transform: rotate(18deg); } 100% { transform: rotate(-18deg); } }

/* wave */
.uno-actor[data-act="wave"] .uno-arm-r { animation: unoWave 1.9s ease-in-out infinite; }
@keyframes unoWave {
  0% { transform: rotate(8deg); } 18% { transform: rotate(135deg); }
  34% { transform: rotate(112deg); } 50% { transform: rotate(160deg); }
  66% { transform: rotate(112deg); } 82% { transform: rotate(135deg); }
  100% { transform: rotate(8deg); }
}

/* present — both arms out (showing something off) */
.uno-actor[data-act="present"] .uno-arm-l { transform: rotate(38deg); }
.uno-actor[data-act="present"] .uno-arm-r { transform: rotate(-38deg); }

/* point — right arm extended toward a target */
.uno-actor[data-act="point"] .uno-arm-l { transform: rotate(-58deg); }
.uno-actor[data-act="point"] .uno-arm-r { transform: rotate(8deg); }

/* press / tap — quick repeated press (send button, dial pad, tools) */
.uno-actor[data-act="press"] .uno-arm-r { animation: unoTap .4s ease-in-out infinite; }
@keyframes unoTap { 0%,100% { transform: rotate(-2deg); } 45% { transform: rotate(44deg); } }

/* carry — both arms forward holding the box */
.uno-actor[data-act="carry"] .uno-arm-l { transform: rotate(62deg); }
.uno-actor[data-act="carry"] .uno-arm-r { transform: rotate(-62deg); }
.uno-actor[data-act="carry"] .uno-cbox { display: block; }

/* post — reach up into the mailbox slot */
.uno-actor[data-act="post"] .uno-arm-r { transform: rotate(-10deg); }
.uno-actor[data-act="post"] .uno-arm-l { transform: rotate(80deg); }

/* push — shove docs/sheets/slides onto the board */
.uno-actor[data-act="push"] .uno-arm-l { transform: rotate(-74deg); }
.uno-actor[data-act="push"] .uno-arm-r { transform: rotate(74deg); }

/* look — lean in and study the board */
.uno-actor[data-act="look"] .uno-body { transform: rotate(2deg) translateY(-1px); }
.uno-actor[data-act="look"] .uno-eyes { transform: scale(.08); }
.uno-actor[data-act="look"] .uno-arm-r { transform: rotate(-26deg); }

/* jot — hold the clipboard up, tick it with the other hand */
.uno-actor[data-act="jot"] .uno-arm-l { transform: rotate(34deg); }
.uno-actor[data-act="jot"] .uno-arm-r { animation: unoTick .5s ease-in-out 3; }
.uno-actor[data-act="jot"] .uno-cjotter { display: block; }
@keyframes unoTick { 0%,100% { transform: rotate(30deg); } 50% { transform: rotate(58deg); } }

/* call — right hand up to the jaw, phone (drawn on top) at the ear + sound waves */
.uno-actor[data-act="call"] .uno-arm-r { transform: rotate(30deg); }
.uno-actor[data-act="call"] .uno-cphone { display: block; }
.uno-cwave { animation: unoWave2 1.1s ease-in-out infinite; }
@keyframes unoWave2 { 0%,100% { opacity: .3; } 50% { opacity: 1; } }

/* jump — it works! a couple of hops, arms up */
.uno-actor[data-act="jump"] .uno-arm-l { transform: rotate(150deg); }
.uno-actor[data-act="jump"] .uno-arm-r { transform: rotate(-150deg); }
.uno-actor[data-act="jump"] .uno-figure { animation: unoJump .5s ease-in-out 2; }
@keyframes unoJump { 0%,100% { transform: translateY(0); } 36% { transform: translateY(-15px); } }

/* throw — one big sweep (paper-plane send-off) */
.uno-actor[data-act="throw"] .uno-arm-r { animation: unoThrow .65s ease; }
@keyframes unoThrow { 0% { transform: rotate(-150deg); } 100% { transform: rotate(42deg); } }

/* ride — grips the underside of the plane, dangling, legs swinging */
.uno-actor[data-act="ride"] .uno-figure { animation: none; }
.uno-actor[data-act="ride"] .uno-arm-l { transform: rotate(158deg); }
.uno-actor[data-act="ride"] .uno-arm-r { transform: rotate(-158deg); }
.uno-actor[data-act="ride"] .uno-leg-l { transform: rotate(-16deg); }
.uno-actor[data-act="ride"] .uno-leg-r { transform: rotate(14deg); }
.uno-actor[data-act="ride"] .uno-cplane { display: block; }
.uno-actor[data-act="ride"] .uno-shadow { opacity: 0; }

/* hang — pull-up grip on the Donna bar, body dips + swings */
.uno-actor[data-act="hang"] .uno-figure { transform-origin: -10% 5%; animation: unoHang 2.6s ease-in-out infinite; }
.uno-actor[data-act="hang"] .uno-arm-l { transform: rotate(26deg); }
.uno-actor[data-act="hang"] .uno-arm-r { transform: rotate(-26deg); }
.uno-actor[data-act="hang"] .uno-leg-l { animation: unoDangleA 2.4s ease-in-out infinite; }
.uno-actor[data-act="hang"] .uno-leg-r { animation: unoDangleB 2.4s ease-in-out infinite; }
.uno-actor[data-act="hang"] .uno-shadow { opacity: 0; }
@keyframes unoHang { 0%,100% { transform: rotate(-3deg); } 50% { transform: rotate(3deg); } }
@keyframes unoDangleA { 0%,100% { transform: rotate(20deg); } 50% { transform: rotate(-20deg); } }
@keyframes unoDangleB { 0%,100% { transform: rotate(-20deg); } 50% { transform: rotate(20deg); } }

/* angry — frown + brows, fists in (double-tap) */
.uno-actor[data-act="angry"] .uno-brows { display: block; }
.uno-actor[data-act="angry"] .uno-smile { transform: rotate(180deg) translateY(-1px); transform-box: fill-box; transform-origin: center; stroke: #ff8585; }
.uno-actor[data-act="angry"] .uno-arm-l { transform: rotate(26deg); }
.uno-actor[data-act="angry"] .uno-arm-r { transform: rotate(-26deg); }
.uno-actor[data-act="angry"] .uno-eyes { transform: scaleY(.8); }

/* ── Roaming scene props (position:fixed in #content viewport coords) ────────── */
.uno-pr { position: fixed; pointer-events: none; will-change: transform, opacity; }
.uno-pr.in { animation: unoPrPop .42s cubic-bezier(.2,1.4,.4,1); }
@keyframes unoPrPop { 0% { transform: scale(.4); opacity: 0; } 60% { transform: scale(1.08); } 100% { transform: scale(1); opacity: 1; } }
.uno-pr.out { opacity: 0; transition: opacity .45s ease; }

/* automation nodes + wires */
.uno-node {
  position: fixed; width: 40px; height: 40px; transform: translate(-50%,-50%) scale(0);
  border-radius: 13px; display: grid; place-items: center; color: #fff; font-size: 16px; z-index: 2;
  background: linear-gradient(155deg, var(--n-light, color-mix(in srgb, var(--uno-primary) 45%, #fff)), var(--n, var(--uno-primary)) 55%, var(--n-deep, var(--uno-deep)));
  box-shadow: 0 8px 16px rgba(0,0,0,.42), inset 0 1.5px 0 rgba(255,255,255,.4), inset 0 -5px 9px rgba(0,0,0,.28);
}
.uno-node.is-in { animation: unoNodePop .5s cubic-bezier(.18,1.5,.4,1) forwards; }
@keyframes unoNodePop {
  0% { transform: translate(-50%,-50%) scale(0) rotate(-12deg); }
  70% { transform: translate(-50%,-50%) scale(1.14) rotate(5deg); }
  100% { transform: translate(-50%,-50%) scale(1) rotate(0); }
}
.uno-wires { position: fixed; inset: 0; z-index: 1; overflow: visible; }
.uno-wire { fill: none; stroke: color-mix(in srgb, var(--uno-primary) 72%, #fff); stroke-width: 3; stroke-linecap: round; filter: drop-shadow(0 1px 2px rgba(0,0,0,.4)); transition: stroke .3s ease; }
.uno-wire-pulse { fill: none; stroke: #fff; stroke-width: 3.4; stroke-linecap: round; stroke-dasharray: 10 240; opacity: .9; animation: unoFlow 1.1s linear infinite; }
.uno-wires.ok .uno-wire { stroke: #34d399; }
.uno-wires.ok .uno-wire-pulse { stroke: #d1fae5; }
@keyframes unoFlow { to { stroke-dashoffset: -250; } }

/* mailbox + letter */
.uno-pr.mailbox { z-index: 1; }
.uno-pr.mailbox.is-left > svg { transform: scaleX(-1); }
.mb-flag { transition: transform .3s ease; transform-box: fill-box; transform-origin: bottom center; }
.mb-flag.up { transform: rotate(-32deg); }
.uno-pr.letter { width: 18px; height: 13px; border-radius: 2px; background: #fff; box-shadow: 0 3px 8px rgba(0,0,0,.3); z-index: 3; }
.uno-pr.letter::after { content: ''; position: absolute; inset: 0; clip-path: polygon(0 0, 100% 0, 50% 62%); background: rgba(0,0,0,.12); }

/* logistics — truck + goods box */
.uno-pr.truck { z-index: 1; filter: drop-shadow(0 5px 8px rgba(0,0,0,.32)); }
.uno-pr.truck.is-left > svg { transform: scaleX(-1); }
.tk-bed { fill: color-mix(in srgb, var(--uno-primary) 28%, #1b2030); }
.tk-body { fill: var(--uno-primary); }
.tk-cab { fill: color-mix(in srgb, var(--uno-primary) 65%, #000); }
.tk-wheel { fill: #10131a; stroke: #4b5563; stroke-width: 2; }
.tk-wheel.spin { transform-box: fill-box; transform-origin: center; animation: unoWheel .4s linear infinite; }
@keyframes unoWheel { to { transform: rotate(360deg); } }
.uno-pr.goodsbox { width: 22px; height: 18px; border-radius: 3px; background: #f5a524; z-index: 2; box-shadow: inset 0 -4px rgba(0,0,0,.18), 0 4px 10px rgba(0,0,0,.3); }

/* messaging — composer, message bubble, delivered receipt */
.uno-pr.composer { display: flex; align-items: center; gap: 6px; padding: 5px 6px 5px 10px; border-radius: 16px; z-index: 2; width:70px;
  background: var(--uno-surface); border: 1px solid rgba(255,255,255,.12); box-shadow: 0 6px 14px rgba(0,0,0,.3); }
.uno-pr.composer.is-left { flex-direction: row-reverse; }
.cmp-line { width: 40px; height: 6px; border-radius: 4px; background: rgba(255,255,255,.2); }
.uno-pr.composer.typing .cmp-line { animation: cmpType 1s ease-in-out infinite; }
@keyframes cmpType { 0%,100% { width: 18px; } 50% { width: 46px; } }
.cmp-send { width: 24px; height: 24px; border: 0; border-radius: 50%; display: grid; place-items: center; color: #fff; font-size: 13px;
  background: linear-gradient(160deg, var(--uno-light), var(--uno-primary)); box-shadow: inset 0 1px 0 rgba(255,255,255,.4); }
.cmp-send.press { animation: prPress .26s ease; }
@keyframes prPress { 50% { transform: scale(.78); } }
.uno-pr.msgbub { z-index: 2; }
.uno-pr.msgbub span { display: block; width: 34px; height: 8px; border-radius: 5px; background: linear-gradient(180deg, var(--uno-light), var(--uno-primary)); box-shadow: 0 4px 10px rgba(0,0,0,.3); }
.uno-pr.delivered, .uno-pr.greenchk { display: inline-flex; align-items: center; gap: 4px; padding: 3px 7px; border-radius: 8px; z-index: 2;
  font-size: 10px; font-weight: 700; white-space: nowrap; background: var(--uno-surface); box-shadow: 0 4px 10px rgba(0,0,0,.3); }
.uno-pr.delivered { color: #86f0bb; } .uno-pr.delivered i { color: #34d399; }
.uno-pr.greenchk { color: #86f0bb; } .uno-pr.greenchk i { color: #10b981; }

/* docs — board + doc/sheet/slide cards */
.uno-pr.board { z-index: 1; filter: drop-shadow(0 6px 10px rgba(0,0,0,.3)); }
.uno-pr.doccard { width: 26px; height: 32px; border-radius: 5px; display: grid; place-items: center; color: #fff; font-size: 14px; z-index: 2;
  background: linear-gradient(160deg, color-mix(in srgb, var(--c, #60a5fa) 65%, #fff), var(--c, #60a5fa));
  box-shadow: 0 6px 14px rgba(0,0,0,.34), inset 0 1px 0 rgba(255,255,255,.45); }

/* calls — floating call UI */
.uno-pr.callui { display: flex; flex-direction: column; align-items: center; gap: 1px; padding: 5px 10px; border-radius: 10px; z-index: 2;
  background: var(--uno-surface); border: 1px solid rgba(255,255,255,.12); box-shadow: 0 6px 14px rgba(0,0,0,.3); }
.cl-state { font-size: 10px; font-weight: 800; color: #86f0bb; }
.cl-time { font-size: 10px; color: var(--text-muted, #9aa3b2); font-variant-numeric: tabular-nums; }
.uno-pr.callui.ended .cl-state { color: #ff8585; }

/* site — laptop + tools */
.uno-pr.laptop { z-index: 1; filter: drop-shadow(0 5px 8px rgba(0,0,0,.3)); }
.uno-pr.laptop.is-left > svg { transform: scaleX(-1); }
.lp-screen { fill: #0b0e16; stroke: #374151; }
.lp-green { transition: opacity .4s ease; }
.lp-green.on { opacity: 1 !important; }
.lp-base { fill: #374151; }
.uno-pr.tool { font-size: 18px; color: #cbd5e1; z-index: 3; filter: drop-shadow(0 2px 3px rgba(0,0,0,.5)); }

/* the paper plane she throws on delivery */
.uno-pr.flyplane { z-index: 3; filter: drop-shadow(0 2px 4px rgba(0,0,0,.4)); }
.uno-pr.flyplane.is-left > svg { transform: scaleX(-1); }

/* speech bubbles (double-tap) */
.uno-pr.say { transform: translate(-50%,-100%); opacity: 0; transition: opacity .25s ease; z-index: 4;
  background: #fff; color: #11141c; font-weight: 800; font-size: 12px; padding: 7px 11px; border-radius: 12px;
  white-space: nowrap; box-shadow: 0 8px 18px rgba(0,0,0,.35); }
.uno-pr.say.in { opacity: 1; }
.uno-pr.say.out { opacity: 0; }
.uno-pr.say::after { content: ''; position: absolute; bottom: -5px; left: 50%; transform: translateX(-50%); border: 6px solid transparent; border-top-color: #fff; }
.uno-pr.say.angry { background: #ffe3e3; color: #b42318; }
.uno-pr.say.angry::after { border-top-color: #ffe3e3; }

/* messaging — chat thread (≥3 bubbles flowing in/out, in order) */
.uno-pr.thread { display: flex; flex-direction: column; gap: 6px; width: 140px; transform: translateY(-100%); }
.uno-bubble { min-width: 46px; max-width: 120px; padding: 7px 9px; border-radius: 11px; display: flex; flex-direction: column; gap: 4px;
  box-shadow: 0 6px 13px rgba(0,0,0,.3); animation: unoBubbleIn .4s cubic-bezier(.2,1.3,.4,1) forwards; }
.uno-bubble.in { align-self: flex-start; border-bottom-left-radius: 4px;
  background: linear-gradient(180deg, color-mix(in srgb, #fff 14%, var(--uno-surface)), var(--uno-surface)); }
.uno-bubble.out { align-self: flex-end; border-bottom-right-radius: 4px;
  background: linear-gradient(180deg, var(--uno-light), var(--uno-primary)); }
.uno-bubble-line { height: 5px; border-radius: 3px; }
.uno-bubble.in .uno-bubble-line { background: color-mix(in srgb, #fff 18%, transparent); }
.uno-bubble.out .uno-bubble-line { background: rgba(255,255,255,.6); }
.uno-bubble.is-out { animation: unoBubbleOut .4s ease forwards; }
@keyframes unoBubbleIn { 0% { opacity: 0; transform: translateY(8px) scale(.7); } 100% { opacity: 1; transform: translateY(0) scale(1); } }
@keyframes unoBubbleOut { to { opacity: 0; transform: translateY(-8px) scale(.85); } }

/* finance — card reader, tapped card, coins, paid label */
.uno-pr.terminal { z-index: 1; filter: drop-shadow(0 5px 8px rgba(0,0,0,.3)); }
.uno-pr.terminal.is-left > svg { transform: scaleX(-1); }
.tm-scr { transition: fill .3s ease; }
.tm-scr.ok { fill: #10b981; }
.uno-pr.paycard { width: 26px; height: 16px; border-radius: 3px; z-index: 3;
  background: linear-gradient(135deg, #ffd98a, #f5a524 60%, #c87f12); box-shadow: 0 4px 10px rgba(0,0,0,.34); }
.uno-pr.paycard::after { content: ''; position: absolute; left: 4px; top: 5px; width: 6px; height: 5px; border-radius: 1px; background: rgba(0,0,0,.35); }
.uno-pr.coin { color: #fcd34d; font-size: 15px; z-index: 3; filter: drop-shadow(0 2px 3px rgba(0,0,0,.4)); }
.uno-pr.paylbl { display: inline-flex; align-items: center; gap: 4px; padding: 3px 8px; border-radius: 8px;
  font-size: 10px; font-weight: 800; white-space: nowrap; color: #86f0bb; background: var(--uno-surface); box-shadow: 0 5px 12px rgba(0,0,0,.34); }
.uno-pr.paylbl i { color: #34d399; }

@media (prefers-reduced-motion: reduce) {
  .uno-actor *, .uno-props *, .uno-front * { animation: none !important; transition: none !important; }
}
@media (max-width: 640px) { .uno-actor, .uno-props, .uno-front { display: none; } }

/* ══════════════════════════════════════════════════════════════════════════════
   BYOK dialog (core/donna/byok.js).

   Everything is scoped under .donna-byok-modal on purpose. The version this replaced
   used inline styles to survive being opened on any route; the same requirement holds
   now that it is core, and route stylesheets already define their own .provider-card
   and .provider-grid (files.css, cal.css, email.css) that would otherwise bleed in.
   ══════════════════════════════════════════════════════════════════════════════ */
.donna-byok-modal .u-card { width: min(520px, 94vw); }

.donna-byok-modal .byok-modal-body .section + .section,
.donna-byok-modal .byok-modal-body hr + .section { margin-top: 18px; }

.donna-byok-modal .u-label {
  display: block;
  font-size: 12px;
  font-weight: 600;
  color: var(--text-muted);
}

.donna-byok-modal .provider-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 10px;
  margin-top: 8px;
}

.donna-byok-modal .provider-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  padding: 15px;
  border: var(--line);
  border-radius: 10px;
  background: transparent;
  color: var(--text);
  font: inherit;
  font-size: 12px;
  cursor: pointer;
  transition: border-color .15s ease, background .15s ease;
}
.donna-byok-modal .provider-card i { font-size: 20px; }
.donna-byok-modal .provider-card:hover { background: var(--strans); }
/* Selection is a class, not an inline style the click handler has to paint on every
   card — that loop was the only thing keeping the old modal's selected state correct. */
.donna-byok-modal .provider-card.active {
  border-color: var(--primary);
  background: var(--primary-faded, color-mix(in srgb, var(--primary) 14%, transparent));
}

.donna-byok-modal #byokApiKey,
.donna-byok-modal #byokCustomPrompt { width: 100%; margin-top: 8px; }
.donna-byok-modal #byokCustomPrompt { resize: vertical; }

.donna-byok-modal .byok-sep { margin: 20px 0; border: 0; border-top: 1px solid var(--line); }
.donna-byok-modal .section-h h3 { font-size: 14px; margin: 0 0 10px; }

.donna-byok-modal .setting-toggle-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  font-size: 13px;
}
.donna-byok-modal .setting-toggle-row + .setting-toggle-row { margin-top: 10px; }

.donna-byok-modal .byok-foot { display: flex; gap: 10px; width: 100%; align-items: center; }
.donna-byok-modal .byok-foot-spacer { flex: 1; }
