/* =========================================================================
   CoLateral - Beamy canvas buddy
   Tiny pixel-art I-beam mascot. Palette + motion live here so the SVG stays
   pure structure. Every tone is mixed from --blue, the theme accent the
   CoLateral wordmark uses, so Beamy repaints with whatever theme is active
   (Office Blue, Dracula, Nord, ...) instead of carrying a palette of his own.
   The literal fallback is Office Blue, for pages that never load themes.css.
   All movement is gated behind prefers-reduced-motion.
   ========================================================================= */

.beamy {
  /* palette */
  --beamy-tone: var(--blue, #0078d4);
  --beamy-body: var(--beamy-tone);
  --beamy-hi: color-mix(in srgb, var(--beamy-tone) 62%, #ffffff);
  --beamy-shadow: color-mix(in srgb, var(--beamy-tone) 72%, #001427);
  --beamy-ink: color-mix(in srgb, var(--beamy-tone) 22%, #0a1119);
  --beamy-hat: #ffffff;
  --beamy-hatsh: color-mix(in srgb, var(--beamy-tone) 28%, #d8e1ea);
  --beamy-accent: color-mix(in srgb, var(--beamy-tone) 86%, #001427);
  --beamy-glow: color-mix(in srgb, var(--beamy-tone) 34%, transparent);

  /* size token, overridden by [data-size] */
  --beamy-w: 78px;

  position: absolute;
  right: 16px;
  bottom: 16px;
  z-index: 45; /* above canvas cards (z 3-5), below toast (50) / modal (80) */
  margin: 0;
  /* wrapper never eats canvas interactions; only the button below opts in */
  pointer-events: none;
  -webkit-user-select: none;
  user-select: none;
}

.beamy[hidden] {
  display: none;
}
.beamy.beamy--home {
  /* perched right beside the hero CoLateral logo. beamyHome.js measures the
     logo and sets left/top so he tracks it on resize instead of floating off
     in the top-right corner. */
  position: absolute;
  left: 0;
  top: 0;
  right: auto;
  bottom: auto;
  z-index: 18;
  will-change: left, top;
}
/* Beamy IS the logo on the entrance pages, so on those he sits in the normal
   flow inside the hero's logo slot rather than being measured and pinned
   beside it. Everything else about the home perch - palette, hover, bubble -
   still applies, so this only undoes the absolute positioning. */
.beamy.beamy--home.beamy--hero {
  position: relative;
  left: auto;
  top: auto;
  z-index: auto;
  will-change: auto;
}
/* Larger on the homepage perch so he stands eye to eye with the 88px hero
   logo tile - the sprite only fills the middle 20 of its 32 design columns,
   so the box has to be wider than the logo to read the same size. 128px is
   also an exact 4x of the 32-column grid, which keeps every pixel edge sharp.
   Higher specificity than the [data-size] tokens so this wins. */
.beamy.beamy--home[data-size] {
  --beamy-w: 128px;
}
/* A short laptop screen has to fit the whole hero above the fold, and Beamy is
   the first thing in it. Comes before the width rules so a window that is both
   short and narrow still takes the smaller of the two. */
@media (max-height: 900px) {
  .beamy.beamy--home[data-size] {
    --beamy-w: 104px;
  }
}
/* Below ~1080px the hero logo drops off its 88px clamp, so step Beamy down to
   the next exact 3x multiple of the sprite grid to keep the pair matched. */
@media (max-width: 1080px) {
  .beamy.beamy--home[data-size] {
    --beamy-w: 96px;
  }
}
/* Phones: the hero logo bottoms out at its 64px clamp and the stage is only a
   few hundred px wide, so the next step down keeps the pair matched and leaves
   room for him beside the centred logo rather than jammed against the edge. */
@media (max-width: 560px) {
  .beamy.beamy--home[data-size] {
    --beamy-w: 72px;
  }
}
/* No mood sparkles on the perch: a lone "+" floating beside him next to the
   hero logo reads as a stray artifact rather than as charm. */
.beamy.beamy--home[data-mood] .b-moodfx {
  display: none;
}
.beamy.beamy--home .beamy__btn {
  animation: beamy-hover 6.5s ease-in-out infinite;
}
@keyframes beamy-hover {
  0%,
  100% {
    transform: translate3d(0, 0, 0) rotate(-2deg);
  }
  50% {
    transform: translate3d(0, -8px, 0) rotate(2deg);
  }
}
/* On the home hero the bubble sits beside Beamy, to his right. That only works
   while there is room to his right: the hero logo he tracks is centred, so on a
   phone "Beamy's right edge + a wide bubble" lands past the viewport and
   scrolls the landing page sideways. Below 560px the override is dropped and the
   base rules apply, which hang the bubble above him and extend it leftwards. */
@media (min-width: 561px) {
  /* The home perch is a narrow absolutely positioned box (one sprite wide), so
     a bubble that only sets `left` would shrink-to-fit inside it and wrap one
     word per line.
     `width: max-content` sizes the bubble from the line itself and the
     max-width caps it at a comfortable reading measure. */
  .beamy.beamy--home .beamy__bubble {
    right: auto;
    /* The sprite only fills columns 6-26 of its 32-column grid, so offsets are
       taken from the drawn edge (0.8 of the box) rather than the box edge. */
    left: calc(var(--beamy-w) * 0.8 + 10px);
    bottom: calc(var(--beamy-w) * 0.55);
    width: max-content;
    /* He is bigger on the hero perch and his lines are longer, so give the box
       room to run horizontally. The second term is the hard limit: Beamy is
       centred, so the space to his right is half the viewport less his own box
       and the page margin. Without it a long pun sized itself from the text and
       ran off the window edge. */
    max-width: min(350px, calc(50vw - var(--beamy-w) - 32px));
    padding: 9px 12px;
    border-radius: 12px;
    font-size: 13px;
    line-height: 1.45;
    text-wrap: balance;
    transform-origin: bottom left;
  }
  .beamy.beamy--home .beamy__bubble::after {
    right: auto;
    left: -5px;
    bottom: 10px;
    border-right: 0;
    border-bottom: 1px solid color-mix(in srgb, var(--indigo, #b98bfa) 50%, transparent);
    border-left: 1px solid color-mix(in srgb, var(--indigo, #b98bfa) 50%, transparent);
  }
}
/* Mirrored side, used when the bubble would otherwise run off the viewport.
   beamyHome.js measures the bubble and toggles this class. */
.beamy.beamy--home .beamy__bubble.is-left {
  left: auto;
  right: calc(var(--beamy-w) * 0.8 + 10px);
  transform-origin: bottom right;
}
.beamy.beamy--home .beamy__bubble.is-left::after {
  left: auto;
  right: -5px;
  border-left: 0;
  border-right: 1px solid color-mix(in srgb, var(--indigo, #b98bfa) 50%, transparent);
  border-bottom: 1px solid color-mix(in srgb, var(--indigo, #b98bfa) 50%, transparent);
}
/* Laptop widths and below: neither side of a centred Beamy is wide enough for
   a full pun, so the bubble hangs above him and centres on his head. It has the
   whole hero width to run into there, and nothing to collide with.
   This is a media query rather than a measured class because the measurement
   can only happen once the bubble has been painted - so the bubble opened at
   his side and jumped overhead a frame later, in view. */
@media (max-width: 1180px) {
  .beamy.beamy--home .beamy__bubble,
  .beamy.beamy--home .beamy__bubble.is-left {
    left: 50%;
    right: auto;
    /* His hat tops out around 0.81 of the box, and the tail hangs 5px below the
       box, so this leaves the tail clear of the brim rather than behind it. */
    bottom: calc(var(--beamy-w) * 0.95);
    translate: -50% 0;
    max-width: min(340px, calc(100vw - 48px));
    transform-origin: bottom center;
  }
  .beamy.beamy--home .beamy__bubble::after,
  .beamy.beamy--home .beamy__bubble.is-left::after {
    left: 50%;
    right: auto;
    bottom: -5px;
    margin-left: -4px;
    border-left: 0;
    border-right: 1px solid color-mix(in srgb, var(--indigo, #b98bfa) 50%, transparent);
    border-bottom: 1px solid color-mix(in srgb, var(--indigo, #b98bfa) 50%, transparent);
  }
}
/* Same reason as the sprite shadow below: the canvas bubble shadow is tuned
   for a dark surface and reads as a smudge on the pale hero. */
.beamy.beamy--home .beamy__bubble {
  box-shadow: 0 8px 20px rgba(16, 42, 67, 0.14);
}
/* Pixel art on a pale hero reads better with a soft brand-tinted shadow than
   with the dark canvas drop shadow. */
.beamy.beamy--home .beamy__btn {
  filter: drop-shadow(0 6px 10px rgba(16, 42, 67, 0.16));
}
.beamy.beamy--home .beamy__btn:hover {
  filter: drop-shadow(0 8px 14px rgba(16, 42, 67, 0.22))
    drop-shadow(0 0 8px var(--beamy-glow));
}

/* ---- sizes -------------------------------------------------------------- */
.beamy[data-size="sm"] {
  --beamy-w: 56px;
}
.beamy[data-size="md"] {
  --beamy-w: 78px;
}
.beamy[data-size="lg"] {
  --beamy-w: 108px;
}

/* ---- the clickable buddy ------------------------------------------------ */
.beamy__btn {
  pointer-events: auto;
  display: block;
  padding: 0;
  border: 0;
  background: transparent;
  cursor: pointer;
  line-height: 0;
  filter: drop-shadow(0 4px 6px rgba(0, 0, 0, 0.45));
  transition: transform 0.15s var(--ease-ios, cubic-bezier(0.32, 0.72, 0, 1)),
    filter 0.15s var(--ease-ios, cubic-bezier(0.32, 0.72, 0, 1));
  /* no blue tap flash on touch, no text selection, no double-tap zoom delay */
  -webkit-tap-highlight-color: transparent;
  -webkit-user-select: none;
  user-select: none;
  touch-action: manipulation;
}
.beamy__btn:hover {
  transform: translateY(-2px);
  filter: drop-shadow(0 6px 9px rgba(0, 0, 0, 0.5))
    drop-shadow(0 0 6px var(--beamy-glow));
}
/* Clicking or tapping Beamy must never draw a selection rectangle around him.
   Beamy.js also blocks the focus move on mouse press; this is the belt to that
   set of braces. Keyboard users still get the ring below. */
.beamy__btn:focus {
  outline: none;
}
.beamy__btn:focus-visible {
  outline: 2px solid color-mix(in srgb, var(--beamy-hi) 70%, transparent);
  outline-offset: 4px;
  border-radius: 16px;
}

.beamy__svg {
  display: block;
  width: var(--beamy-w);
  height: auto;
  image-rendering: pixelated;
  image-rendering: crisp-edges;
  overflow: visible;
}

/* ---- palette mapping ---------------------------------------------------- */
.beamy .b-body {
  fill: var(--beamy-body);
}
.beamy .b-hi {
  fill: var(--beamy-hi);
}
.beamy .b-shadow {
  fill: var(--beamy-shadow);
}
.beamy .b-ink {
  fill: var(--beamy-ink);
}
.beamy .b-hat {
  fill: var(--beamy-hat);
}
.beamy .b-hatsh {
  fill: var(--beamy-hatsh);
}
.beamy .b-accent {
  fill: var(--beamy-accent);
}
.beamy .b-glass {
  fill: color-mix(in srgb, var(--beamy-tone) 20%, transparent);
  stroke: var(--beamy-ink);
  stroke-width: 1.4;
}
.beamy .b-glass-handle {
  stroke: var(--beamy-ink);
  stroke-width: 1.6;
  stroke-linecap: round;
}

/* ---- expression: reveal only the active eyes + mouth + decoration ------- */
.beamy .b-mouth,
.beamy .b-eyes,
.beamy .b-moodfx,
.beamy .b-acc {
  display: none;
}
/* mouth, one per expression */
.beamy[data-mood="happy"] .b-mouth--happy,
.beamy[data-mood="grin"] .b-mouth--grin,
.beamy[data-mood="wink"] .b-mouth--wink,
.beamy[data-mood="cool"] .b-mouth--cool {
  display: block;
}
/* eyes - happy and grin share the open pair; wink and cool get their own */
.beamy[data-mood="happy"] .b-eyes--open,
.beamy[data-mood="grin"] .b-eyes--open,
.beamy[data-mood="wink"] .b-eyes--wink,
.beamy[data-mood="cool"] .b-eyes--cool {
  display: block;
}
/* sparkle decoration on the cheerful expressions */
.beamy[data-mood="happy"] .b-moodfx--happy,
.beamy[data-mood="grin"] .b-moodfx--grin {
  display: block;
}

/* ---- action: reveal only the active accessory -------------------------- */
.beamy[data-action="think"] .b-acc--think,
.beamy[data-action="inspect"] .b-acc--inspect,
.beamy[data-action="approve"] .b-acc--approve,
.beamy[data-action="celebrate"] .b-acc--celebrate,
.beamy[data-action="nap"] .b-acc--nap,
.beamy[data-action="draft"] .b-acc--draft,
.beamy[data-action="type"] .b-acc--type,
.beamy[data-action="build"] .b-acc--build {
  display: block;
}

/* ---- speech bubble ------------------------------------------------------
   iOS-flavoured motion: a soft spring on the way in, a quicker ease on the way
   out, and a measured width/height morph when one line swaps for the next so
   the box glides between sizes instead of snapping. The durations below are
   mirrored by the timers in Beamy.js - keep the two in step.
   The box is deliberately wide: these lines are sentences, not labels, and a
   narrow column of three-word rows read badly. */
.beamy__bubble {
  pointer-events: none;
  position: absolute;
  right: 0;
  bottom: calc(var(--beamy-w) + 12px);
  box-sizing: border-box;
  width: max-content;
  max-width: clamp(210px, 26vw, 320px);
  padding: 9px 13px;
  border-radius: 13px;
  background: var(--s1, #181226);
  border: 1px solid color-mix(in srgb, var(--indigo, #b98bfa) 50%, transparent);
  box-shadow: 0 10px 26px rgba(0, 0, 0, 0.45);
  color: var(--tx, #e7defb);
  font-family: "Inter", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  font-size: 12.5px;
  line-height: 1.42;
  text-wrap: pretty;
  overflow-wrap: break-word;
  transform-origin: bottom right;
  transform: translateY(6px) scale(0.9);
  opacity: 0;
  transition:
    opacity 0.19s cubic-bezier(0.4, 0, 1, 1),
    transform 0.22s cubic-bezier(0.4, 0, 1, 1);
}
.beamy__bubble::after {
  /* little tail pointing down toward Beamy */
  content: "";
  position: absolute;
  right: 18px;
  bottom: -5px;
  width: 8px;
  height: 8px;
  background: var(--s1, #181226);
  border-right: 1px solid color-mix(in srgb, var(--indigo, #b98bfa) 50%, transparent);
  border-bottom: 1px solid color-mix(in srgb, var(--indigo, #b98bfa) 50%, transparent);
  border-bottom-right-radius: 3px;
  transform: rotate(45deg);
}
.beamy__bubble.is-on {
  opacity: 1;
  transform: translateY(0) scale(1);
  transition:
    opacity 0.26s var(--ease-ios, cubic-bezier(0.32, 0.72, 0, 1)),
    transform 0.44s cubic-bezier(0.2, 1.24, 0.38, 1);
}
/* while the box travels between two line sizes (Beamy.js pins the start and
   end geometry inline, this rule supplies the easing) */
.beamy__bubble.is-morphing {
  transition:
    opacity 0.26s var(--ease-ios, cubic-bezier(0.32, 0.72, 0, 1)),
    transform 0.44s cubic-bezier(0.2, 1.24, 0.38, 1),
    width 0.3s var(--ease-ios, cubic-bezier(0.32, 0.72, 0, 1)),
    height 0.3s var(--ease-ios, cubic-bezier(0.32, 0.72, 0, 1));
}
.beamy__bubble-text {
  display: block;
  opacity: 1;
  transform: translateY(0);
  transition:
    opacity 0.2s var(--ease-ios, cubic-bezier(0.32, 0.72, 0, 1)),
    transform 0.2s var(--ease-ios, cubic-bezier(0.32, 0.72, 0, 1));
}
.beamy__bubble.is-swapping .beamy__bubble-text {
  opacity: 0;
  transform: translateY(-3px);
  transition: opacity 0.13s ease-in, transform 0.13s ease-in;
}

/* ---- motion (idle breathing + per-action flourishes) ------------------- */
.beamy__btn {
  animation: beamy-bob 4.2s ease-in-out infinite;
}
@keyframes beamy-bob {
  0%,
  100% {
    transform: translateY(0);
  }
  50% {
    transform: translateY(-2px);
  }
}

/* walk: a gentle lean + alternating feet, plus motion ticks */
.beamy[data-action="walk"] .beamy__foot--l {
  animation: beamy-step 0.6s steps(2) infinite;
}
.beamy[data-action="walk"] .beamy__foot--r {
  animation: beamy-step 0.6s steps(2) infinite 0.3s;
}
@keyframes beamy-fly {
  0%,
  100% {
    transform: translate3d(0, 0, 0) rotate(-3deg);
  }
  50% {
    transform: translate3d(0, -10px, 0) rotate(4deg);
  }
}
@keyframes beamy-step {
  50% {
    transform: translateX(1px);
  }
}

/* celebrate: arms up + sparkle twinkle */
.beamy[data-action="celebrate"] .beamy__arm--l {
  transform: translate(2px, -7px) rotate(-12deg);
}
.beamy[data-action="celebrate"] .beamy__arm--r {
  transform: translate(-2px, -7px) rotate(12deg);
}
.beamy[data-action="celebrate"] .b-acc--celebrate {
  animation: beamy-twinkle 0.8s steps(2) infinite;
}
@keyframes beamy-twinkle {
  50% {
    opacity: 0.35;
  }
}

/* think / nap: drift the floaty bits upward */
.beamy[data-action="think"] .b-acc--think,
.beamy[data-action="nap"] .b-acc--nap {
  animation: beamy-drift 2.4s ease-in-out infinite;
}
@keyframes beamy-drift {
  0%,
  100% {
    transform: translateY(0);
    opacity: 0.85;
  }
  50% {
    transform: translateY(-2px);
    opacity: 1;
  }
}

/* nap: sink down a touch so he reads as resting */
.beamy[data-action="nap"] .beamy__body,
.beamy[data-action="nap"] .beamy__hat,
.beamy[data-action="nap"] .beamy__eyes,
.beamy[data-action="nap"] .beamy__mouth {
  transform: translateY(2px);
}

/* approve / inspect: a tiny nod toward the prop */
.beamy[data-action="approve"] .b-acc--approve {
  animation: beamy-bob 1.6s ease-in-out infinite;
}

.beamy__arm,
.beamy__foot {
  transition: transform 0.2s var(--ease-ios, cubic-bezier(0.32, 0.72, 0, 1));
  transform-origin: center;
}

/* poke reaction: a short squash-and-stretch so a click reads as a reply. This
   sits on the SVG rather than the button, which already owns the idle bob. */
.beamy__svg {
  transform-origin: 50% 100%;
}
.beamy.is-reacting .beamy__svg {
  animation: beamy-pop 0.44s cubic-bezier(0.2, 1.16, 0.36, 1);
}
@keyframes beamy-pop {
  0% {
    transform: scale(1, 1);
  }
  32% {
    transform: scale(1.09, 0.92);
  }
  64% {
    transform: scale(0.97, 1.05);
  }
  100% {
    transform: scale(1, 1);
  }
}

/* =========================================================================
   Beam Buddy chat dock (Project Canvas)
   Beamy sits at the bottom-centre paired with a chat bar: the text box in the
   middle, Beamy right next to it. The wrapper is click-through so the canvas
   keeps panning underneath; only the input and Beamy himself are interactive.
   ========================================================================= */
.beamy-dock {
  position: absolute;
  left: 50%;
  bottom: 16px;
  transform: translateX(-50%);
  z-index: 46; /* above cards, below toast (50) and modal (80) */
  display: flex;
  align-items: flex-end;
  gap: 10px;
  /* A FIXED width, not a max. The dock is centred with translateX(-50%),
     which is measured off its own box - so while it shrank to fit the composer,
     every width change re-centred the whole row and slid Beamy sideways in the
     middle of a sentence. Given a box of its own the pill grows inside it and
     nothing around it moves. The basis is the composer's widest state (560px)
     plus Beamy and the gap, so he keeps a fixed berth at the bar's right-hand
     end rather than tracking its edge. */
  width: min(94vw, 622px);
  pointer-events: none; /* only children opt back in */
}
.beamy-dock[hidden] {
  display: none;
}

/* Beamy, when docked, sits in the flex row instead of the viewport corner. */
.beamy.beamy--dock {
  position: relative;
  right: auto;
  bottom: auto;
  z-index: auto;
  flex: 0 0 auto;
  /* Docked next to the chat bar he reads best kept close to the bar's own
     height, so shrink him here regardless of the saved size preference.
     Two classes out-specify the [data-size] tokens above, and this rule
     comes later in source order, so it wins. */
  --beamy-w: 52px;
}
.beamy.beamy--dock[data-size="sm"] {
  --beamy-w: 44px;
}
.beamy.beamy--dock[data-size="lg"] {
  --beamy-w: 60px;
}

/* ---- the chat form ------------------------------------------------------ */
.beamy-chat {
  pointer-events: auto;
  position: relative;
  display: flex;
  flex-direction: column;
  /* Fills the dock's fixed width, so the column - and the lifted panel layer
     that centres on it - has a width of its own instead of the composer's. */
  flex: 1 1 auto;
  min-width: 0;
  /* Centre, never stretch. With `stretch` the relay line above the field
     dictated the pill's width, so one long status message left an empty bar
     stretched across the canvas until the next keystroke. Each row now sizes
     itself and stays centred over Beamy. */
  align-items: center;
}
.beamy-chat-field {
  box-sizing: border-box;
  /* Centred while the composer is one line - bottom-aligning a single line
     pushed the text to the floor of the pill. Only once it grows into a
     paragraph do the buttons drop to the last line. */
  align-items: center;
  /* autoGrow writes the width inline (two steps, animated). The cap is a
     backstop for a very narrow window. */
  max-width: min(92vw, 760px);
  display: flex;
  gap: 6px;
  padding: 7px 8px 7px 16px;
  background: color-mix(in srgb, var(--s1, #181226) 94%, transparent);
  border: 1px solid color-mix(in srgb, var(--indigo, #b98bfa) 50%, transparent);
  border-radius: 999px;
  box-shadow: 0 8px 22px rgba(0, 0, 0, 0.5);
  -webkit-backdrop-filter: blur(4px);
  backdrop-filter: blur(4px);
  transition: border-color 0.15s var(--ease-ios, cubic-bezier(0.32, 0.72, 0, 1)),
    box-shadow 0.15s var(--ease-ios, cubic-bezier(0.32, 0.72, 0, 1)),
    width 0.26s var(--ease-ios, cubic-bezier(0.32, 0.72, 0, 1)),
    border-radius 0.28s var(--ease-ios, cubic-bezier(0.32, 0.72, 0, 1));
}
/* Once the box is a paragraph a stadium radius reads as a lozenge, so it
   squares off as it grows and rounds back as it empties. */
.beamy-chat-field.is-multiline {
  border-radius: 20px;
  align-items: flex-end;
}
.beamy-chat:focus-within .beamy-chat-field {
  border-color: var(--indigo, var(--beamy-hi, #b18ee6));
  box-shadow: 0 8px 22px rgba(0, 0, 0, 0.5),
    0 0 0 3px color-mix(in srgb, var(--indigo, #b98bfa) 18%, transparent);
}
.beamy-chat-input {
  flex: 1 1 auto;
  min-width: 0;
  /* The pill owns the width (set by autoGrow); the textarea just fills it. */
  width: 100%;
  margin: 0;
  border: 0;
  padding: 5px 0;
  background: transparent;
  color: var(--tx, #e7defb);
  font: 14px/1.45 "Inter", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  outline: none;
  /* Textarea specifics: it grows by script, never by drag handle, and it must
     not carry the UA's default scrollbar gutter while it is one line tall. */
  resize: none;
  overflow-y: hidden;
  display: block;
  transition: width 0.26s var(--ease-ios, cubic-bezier(0.32, 0.72, 0, 1)),
    height 0.26s var(--ease-ios, cubic-bezier(0.32, 0.72, 0, 1));
}
/* The twin fitHeight measures instead of the box being typed into. Same class
   as the real textarea, so it has the same font, padding and line box; parked
   off screen, out of the flow, and explicitly never animated. Measuring the
   real box meant writing "auto" and reading scrollHeight on every keystroke -
   a forced layout per key, taken against a height that CSS was still
   animating, so the number came back describing where the box had got to
   rather than where it needed to be. */
.beamy-chat-measure {
  position: absolute;
  top: 0;
  left: -9999px;
  visibility: hidden;
  height: auto;
  max-height: none;
  overflow: hidden;
  pointer-events: none;
  transition: none;
}

/* The scrollbar only appears once the box has hit its ceiling, and it should
   not shove the text sideways when it does. */
.beamy-chat-input::-webkit-scrollbar {
  width: 6px;
}
.beamy-chat-input::-webkit-scrollbar-thumb {
  border-radius: 3px;
  background: color-mix(in srgb, var(--indigo, #b98bfa) 34%, transparent);
}
.beamy-chat-input::placeholder {
  color: var(--tx3, rgba(231, 222, 251, 0.5));
}
.beamy-chat-send {
  flex: 0 0 auto;
  width: 30px;
  height: 30px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: var(--indigo, var(--beamy-body, #8a5cc9));
  color: #fff;
  cursor: pointer;
  transition: background 0.15s var(--ease-ios, cubic-bezier(0.32, 0.72, 0, 1)),
    transform 0.1s var(--ease-ios, cubic-bezier(0.32, 0.72, 0, 1));
}
.beamy-chat-send:hover {
  background: color-mix(in srgb, var(--indigo, #b18ee6) 80%, #fff);
}
.beamy-chat-send:active {
  transform: scale(0.92);
}
.beamy-chat-send:focus-visible {
  outline: 2px solid var(--indigo, var(--beamy-hi, #b18ee6));
  outline-offset: 2px;
}

/* ---- orchestrator: dictation ------------------------------------------
   The flat mic, deliberately not the round one. Dictation types into the box
   and costs nothing to run; voice mode opens a paid conversation. Two
   controls that looked alike would be read as two ways into the same thing,
   so this one wears the clip's treatment - a glyph in the row - and the
   conversation keeps the ring. */
.beamy-chat-dictate {
  flex: 0 0 auto;
  width: 28px;
  height: 28px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: var(--tx3, rgba(231, 222, 251, 0.55));
  cursor: pointer;
  transition: color 0.15s var(--ease-ios, cubic-bezier(0.32, 0.72, 0, 1)),
    background 0.15s var(--ease-ios, cubic-bezier(0.32, 0.72, 0, 1)),
    box-shadow 0.12s var(--ease-ios, cubic-bezier(0.32, 0.72, 0, 1));
}
/* Same trap as the mic and the clip: the UA's [hidden] rule is a bare element
   selector and loses to the display above. */
.beamy-chat-dictate[hidden] {
  display: none;
}
.beamy-chat-dictate:hover {
  color: var(--tx, #e7defb);
  background: color-mix(in srgb, var(--indigo, #b98bfa) 16%, transparent);
}
.beamy-chat-dictate:focus-visible {
  outline: 2px solid var(--indigo, #b18ee6);
  outline-offset: 2px;
}
/* Recording. The halo is driven by --beamy-mic-level off the peak of each
   captured block, so an open microphone that is hearing nothing looks
   different from one that is hearing you - which is the difference between
   "still going" and "your input is muted". */
.beamy-chat-dictate.is-live {
  color: #fff;
  background: var(--red, #f05050);
  box-shadow: 0 0 0 calc(2px + 6px * var(--beamy-mic-level, 0))
    color-mix(in srgb, var(--red, #f05050) 26%, transparent);
}
.beamy-chat-dictate.is-thinking {
  color: var(--indigo, #b98bfa);
  background: color-mix(in srgb, var(--indigo, #b98bfa) 18%, transparent);
}
/* Nothing installed to decode with. Dimmed rather than hidden: a control that
   is not there cannot explain how to get it, and the click says where to go. */
.beamy-chat-dictate.is-needs-setup {
  color: color-mix(in srgb, var(--tx3, #536b82) 70%, transparent);
}
.beamy-chat-dictate.is-needs-setup:hover {
  color: var(--tx2, #b9aed4);
}
/* The pill itself while dictating, so the box reads as busy from across the
   desk rather than only at the 28px control. */
.beamy-chat-field.is-dictating {
  border-color: color-mix(in srgb, var(--red, #f05050) 55%, transparent);
}

/* ---- orchestrator: microphone + relay ----------------------------------
   The mic sits inside the pill next to Send. While it is live the ring
   around it is driven by --beamy-mic-level, which the capture loop sets from
   the peak of each audio frame, so the engineer can see they are being heard
   before the model has said anything back. */
.beamy-chat-mic {
  flex: 0 0 auto;
  width: 30px;
  height: 30px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: 1px solid color-mix(in srgb, var(--indigo, #b98bfa) 45%, transparent);
  border-radius: 50%;
  background: transparent;
  color: var(--tx2, #b9aed4);
  cursor: pointer;
  transition: background 0.15s var(--ease-ios, cubic-bezier(0.32, 0.72, 0, 1)),
    color 0.15s var(--ease-ios, cubic-bezier(0.32, 0.72, 0, 1)),
    border-color 0.15s var(--ease-ios, cubic-bezier(0.32, 0.72, 0, 1)),
    box-shadow 0.12s var(--ease-ios, cubic-bezier(0.32, 0.72, 0, 1));
}
/* The UA's [hidden] rule is a bare element selector, so the display above
   outranks it and hiding the mic would silently do nothing - the pill would
   keep the extra 36px it reserves. Same trap as the settings overlay. */
.beamy-chat-mic[hidden] {
  display: none;
}
.beamy-chat-mic:hover {
  color: var(--tx, #e7defb);
  border-color: var(--indigo, #b98bfa);
}
.beamy-chat-mic:focus-visible {
  outline: 2px solid var(--indigo, var(--beamy-hi, #b18ee6));
  outline-offset: 2px;
}
.beamy-chat-mic.is-live {
  background: var(--red, #f05050);
  border-color: var(--red, #f05050);
  color: #fff;
  box-shadow: 0 0 0 calc(2px + 6px * var(--beamy-mic-level, 0))
    color-mix(in srgb, var(--red, #f05050) 26%, transparent);
}
/* Visible but quieter when voice still needs SuperGrok OAuth or an API key -
   the control stays in the pill so the search bar matches ChatGPT / Claude. */
.beamy-chat-mic.is-needs-setup {
  color: var(--tx3, rgba(231, 222, 251, 0.55));
  border-color: color-mix(in srgb, var(--tx3, #536b82) 40%, transparent);
  border-style: dashed;
}
.beamy-chat-mic.is-needs-setup:hover {
  color: var(--tx2, #b9aed4);
  border-color: var(--indigo, #b98bfa);
  border-style: solid;
}
/* SuperGrok OAuth is live: solid indigo so the dashed setup state is clearly gone. */
.beamy-chat-mic.is-oauth-ready:not(.is-live) {
  color: var(--indigo, #b98bfa);
  border-color: var(--indigo, #b98bfa);
  border-style: solid;
}
.beamy-chat-mic.is-muted {
  background: color-mix(in srgb, var(--amber, #e0a300) 22%, transparent);
  border-color: var(--amber, #e0a300);
  color: var(--amber, #e0a300);
  box-shadow: none;
}

/* ---- voice mode: viewport frame + docked vertical HUD --------------------
   Frame is pointer-free and full-screen. Controls live in a vertical rail
   beside the search bar (left of Beamy), never over the relay subtitles. */
.beamy-chat-shell {
  pointer-events: none;
  display: flex;
  flex-direction: row;
  align-items: flex-end;
  gap: 8px;
  max-width: min(94vw, 820px);
}
.beamy-chat-shell > .beamy-chat {
  pointer-events: auto;
  min-width: 0;
  flex: 1 1 auto;
}
/* The chat history's off switch. It folds the transcript away and leaves the
   composer exactly where it is: this chevron used to take the whole bar down
   with it, which put the one control the engineer always wants behind the one
   they sometimes want. */
.beamy-chat-history-toggle {
  pointer-events: auto;
  align-self: flex-end;
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  margin-bottom: 5px;
  border-radius: 999px;
  border: 1px solid var(--bd2, rgba(255, 255, 255, 0.16));
  background: var(--s1, #181226);
  color: var(--tx2, #b9aed4);
  cursor: pointer;
  transition: transform 0.16s var(--ease-ios, cubic-bezier(0.32, 0.72, 0, 1)),
    color 0.16s var(--ease-ios, cubic-bezier(0.32, 0.72, 0, 1));
}
.beamy-chat-history-toggle:hover {
  color: var(--tx, #e7defb);
}
.beamy-chat-history-toggle:focus-visible {
  outline: 2px solid var(--indigo, #b18ee6);
  outline-offset: 2px;
}
/* Pointing down while the history is up, and up while it is away: the chevron
   says which way the panel will move, not which state it is in. */
.beamy-chat-shell.is-thread-hidden .beamy-chat-history-toggle {
  transform: rotate(180deg);
}
.beamy-voice-frame {
  position: fixed;
  inset: 0;
  z-index: 40;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.22s var(--ease-ios, cubic-bezier(0.32, 0.72, 0, 1)),
    box-shadow 0.22s var(--ease-ios, cubic-bezier(0.32, 0.72, 0, 1));
  box-shadow:
    inset 0 0 0 2px color-mix(in srgb, var(--indigo, #4da6ff) 55%, transparent),
    inset 0 0 72px color-mix(in srgb, var(--indigo, #4da6ff) 14%, transparent);
}
.beamy-voice-frame.is-on {
  opacity: 1;
}
.beamy-voice-frame.is-speaking {
  box-shadow:
    inset 0 0 0 2px color-mix(in srgb, var(--green, #1ed96b) 60%, transparent),
    inset 0 0 80px color-mix(in srgb, var(--green, #1ed96b) 12%, transparent);
}
.beamy-voice-frame.is-muted {
  box-shadow:
    inset 0 0 0 2px color-mix(in srgb, var(--amber, #e0a300) 55%, transparent),
    inset 0 0 64px color-mix(in srgb, var(--amber, #e0a300) 12%, transparent);
}
.beamy-voice-hud {
  pointer-events: auto;
  position: relative;
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 6px;
  padding: 8px;
  border-radius: 16px;
  border: 1px solid color-mix(in srgb, var(--indigo, #4da6ff) 40%, transparent);
  background: color-mix(in srgb, var(--s1, #111922) 94%, transparent);
  color: var(--tx, #dce7f0);
  box-shadow: 0 8px 22px rgba(0, 0, 0, 0.35);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
}
.beamy-voice-hud[hidden] {
  display: none;
}
.beamy-voice-hud-status {
  font: 700 10px Inter, ui-sans-serif, system-ui, sans-serif;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--tx2, #9bb0c5);
  text-align: center;
  padding: 2px 2px 4px;
  max-width: 4.5rem;
  line-height: 1.2;
}
.beamy-voice-hud.is-muted .beamy-voice-hud-status {
  color: var(--amber, #e0a300);
}
.beamy-voice-hud.is-speaking .beamy-voice-hud-status {
  color: var(--green, #1ed96b);
}
.beamy-voice-hud-btn {
  flex: 0 0 auto;
  min-width: 36px;
  height: 36px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0 8px;
  border-radius: 12px;
  border: 1px solid color-mix(in srgb, var(--bd, rgba(255, 255, 255, 0.12)) 100%, transparent);
  background: transparent;
  color: var(--tx, #dce7f0);
  font: 700 11px Inter, ui-sans-serif, system-ui, sans-serif;
  cursor: pointer;
}
.beamy-voice-hud-btn:hover {
  border-color: var(--indigo, #4da6ff);
  color: var(--tx, #fff);
}
.beamy-voice-hud-btn:disabled {
  opacity: 0.4;
  cursor: default;
}
.beamy-voice-hud-btn.is-active {
  background: color-mix(in srgb, var(--amber, #e0a300) 22%, transparent);
  border-color: var(--amber, #e0a300);
  color: var(--amber, #e0a300);
}
.beamy-voice-hud-end {
  background: color-mix(in srgb, var(--red, #f05050) 88%, #000);
  border-color: var(--red, #f05050);
  color: #fff;
}
.beamy-voice-hud-end:hover {
  background: var(--red, #f05050);
  border-color: var(--red, #f05050);
  color: #fff;
}
/* Relay stays readable above the field; never compete with the side rail. */
.beamy-chat.is-listening .beamy-chat-relay {
  max-width: min(92vw, 560px);
  z-index: 1;
}
/* Live ASR caption in the search box while the engineer is speaking. */
.beamy-chat.is-live-transcript .beamy-chat-input {
  color: var(--tx, #e7defb);
}
.beamy-chat.is-live-transcript .beamy-chat-field {
  border-color: color-mix(in srgb, var(--indigo, #4da6ff) 70%, transparent);
}
.beamy-chat.is-speaking .beamy-chat-field {
  border-color: var(--green, #1ed96b);
}

/* ---- attachments: the clip, the drop target, the chips ------------------
   The clip is the mic's twin without the ring: same size, same resting
   treatment, so the pill reads as one row of controls rather than a toolbar
   that grew. */
.beamy-chat-clip {
  flex: 0 0 auto;
  width: 28px;
  height: 28px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: var(--tx3, rgba(231, 222, 251, 0.55));
  cursor: pointer;
  transition: color 0.15s var(--ease-ios, cubic-bezier(0.32, 0.72, 0, 1)),
    background 0.15s var(--ease-ios, cubic-bezier(0.32, 0.72, 0, 1));
}
/* Same trap as the mic: the UA's [hidden] rule is a bare element selector and
   loses to the display above, so hiding the clip would do nothing. */
.beamy-chat-clip[hidden] {
  display: none;
}
.beamy-chat-clip:hover {
  color: var(--tx, #e7defb);
  background: color-mix(in srgb, var(--indigo, #b98bfa) 16%, transparent);
}
.beamy-chat-clip:focus-visible {
  outline: 2px solid var(--indigo, #b18ee6);
  outline-offset: 2px;
}
.beamy-chat-file {
  display: none;
}

/* Dragging a file over the bar lights the pill, so it is obvious the drop
   will land here and not on the canvas behind it. */
.beamy-chat-field.is-dropping {
  border-color: var(--indigo, #b98bfa);
  border-style: dashed;
  background: color-mix(in srgb, var(--indigo, #b98bfa) 14%, var(--s1, #181226));
}

.beamy-chat-attach-row {
  align-self: center;
  max-width: min(92vw, 620px);
  margin: 0 0 6px;
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  justify-content: center;
}
.beamy-chat-attach-row[hidden] {
  display: none;
}
.beamy-chat-attach {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  max-width: 220px;
  padding: 4px 6px 4px 10px;
  border-radius: 999px;
  border: 1px solid color-mix(in srgb, var(--indigo, #b98bfa) 45%, transparent);
  background: color-mix(in srgb, var(--s1, #181226) 92%, transparent);
  color: var(--tx, #e7defb);
  font: 600 var(--beamy-orch-text, 12px) / 1.3 "Inter", system-ui, sans-serif;
}
/* Kind is carried on the chip so an image reads differently from a drawing at
   a glance, without an icon set for file types nobody has drawn yet. */
.beamy-chat-attach[data-kind="image"] {
  border-color: color-mix(in srgb, var(--green, #1ed96b) 45%, transparent);
}
.beamy-chat-attach[data-kind="pdf"] {
  border-color: color-mix(in srgb, var(--amber, #e0a300) 55%, transparent);
}
.beamy-chat-attach-name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.beamy-chat-attach-drop {
  flex: 0 0 auto;
  width: 18px;
  height: 18px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: var(--tx3, rgba(231, 222, 251, 0.6));
  font: 700 11px "Inter", system-ui, sans-serif;
  cursor: pointer;
}
.beamy-chat-attach-drop:hover {
  background: color-mix(in srgb, var(--red, #f05050) 22%, transparent);
  color: var(--tx, #e7defb);
}
.beamy-chat-attach-drop:focus-visible {
  outline: 2px solid var(--indigo, #b18ee6);
  outline-offset: 1px;
}

/* ---- history, queue, and scalable orchestrator text ---------------------
   --beamy-orch-text is written by beamyChat from the Settings step, and every
   surface the orchestrator speaks through reads it. */
.beamy-chat-history-btn {
  flex: 0 0 auto;
  width: 26px;
  height: 26px;
  margin-left: -6px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: 0;
  border-radius: 8px;
  background: transparent;
  color: var(--tx3, rgba(231, 222, 251, 0.55));
  cursor: pointer;
  transition: color 0.15s var(--ease-ios, cubic-bezier(0.32, 0.72, 0, 1)),
    background 0.15s var(--ease-ios, cubic-bezier(0.32, 0.72, 0, 1));
}
.beamy-chat-history-btn:hover,
.beamy-chat-history-btn.is-on {
  color: var(--tx, #e7defb);
  background: color-mix(in srgb, var(--indigo, #b98bfa) 16%, transparent);
}
.beamy-chat-history-btn:focus-visible {
  outline: 2px solid var(--indigo, #b18ee6);
  outline-offset: 2px;
}

.beamy-chat-queue {
  flex: 0 0 auto;
  align-self: center;
  padding: 3px 8px;
  border-radius: 999px;
  background: color-mix(in srgb, var(--amber, #e0a300) 18%, transparent);
  border: 1px solid color-mix(in srgb, var(--amber, #e0a300) 45%, transparent);
  color: var(--amber, #e0a300);
  font: 700 10.5px "Inter", system-ui, sans-serif;
  white-space: nowrap;
}
.beamy-chat-queue[hidden] {
  display: none;
}

/* The stack of waiting prompts, above the bar. It grows upward as prompts are
   added, then scrolls rather than climbing over the cards it is about. */
.beamy-chat-queue-stack {
  align-self: center;
  width: min(92vw, 620px);
  margin: 0 0 8px;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  border-radius: 14px;
  background: color-mix(in srgb, var(--s1, #181226) 94%, transparent);
  border: 1px solid color-mix(in srgb, var(--amber, #e0a300) 40%, transparent);
  box-shadow: 0 10px 26px rgba(0, 0, 0, 0.45);
  -webkit-backdrop-filter: blur(4px);
  backdrop-filter: blur(4px);
  animation: beamyRelayIn 0.24s var(--ease-ios, cubic-bezier(0.32, 0.72, 0, 1));
}
.beamy-chat-queue-stack[hidden] {
  display: none;
}
.beamy-chat-queue-head {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 7px 12px;
  border-bottom: 1px solid color-mix(in srgb, var(--amber, #e0a300) 20%, transparent);
  font: 800 10.5px "Inter", system-ui, sans-serif;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--amber, #e0a300);
}
/* Collapsed, the stack is one line: what is running and how much is behind
   it. That is all a queue is normally worth, and it leaves the cards under it
   visible. */
.beamy-chat-queue-toggle {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  border: 0;
  background: none;
  padding: 2px 0;
  color: inherit;
  font: inherit;
  letter-spacing: inherit;
  text-transform: inherit;
  cursor: pointer;
}
.beamy-chat-queue-caret {
  width: 0;
  height: 0;
  border-left: 5px solid currentColor;
  border-top: 4px solid transparent;
  border-bottom: 4px solid transparent;
  transform: rotate(90deg);
  transition: transform 0.16s var(--ease-ios, cubic-bezier(0.32, 0.72, 0, 1));
}
.beamy-chat-queue-stack.is-collapsed .beamy-chat-queue-caret {
  transform: rotate(0deg);
}
.beamy-chat-queue-stack.is-collapsed .beamy-chat-queue-body {
  display: none;
}
.beamy-chat-queue-stack.is-collapsed .beamy-chat-queue-head {
  border-bottom: 0;
}
.beamy-chat-queue-body {
  display: flex;
  flex-direction: column;
  min-height: 0;
  overflow: hidden;
}
.beamy-chat-queue-toggle:focus-visible {
  outline: 2px solid var(--amber, #e0a300);
  outline-offset: 2px;
  border-radius: 4px;
}
.beamy-chat-queue-clear {
  border: 0;
  background: none;
  padding: 2px 4px;
  color: var(--tx3, rgba(231, 222, 251, 0.55));
  font: 700 10.5px "Inter", system-ui, sans-serif;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  cursor: pointer;
}
.beamy-chat-queue-clear:hover {
  color: var(--red, #f05050);
}
.beamy-chat-queue-list {
  overflow-y: auto;
  /* Ten rows read as a stack; past that it scrolls, so a long queue never
     pushes Beam Buddy and the bar off the bottom of the canvas. */
  max-height: 20vh;
  padding: 6px;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.beamy-chat-queue-row {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 8px;
  border-radius: 9px;
  border: 1px solid transparent;
  background: color-mix(in srgb, var(--s2, #221a38) 70%, transparent);
  cursor: pointer;
  transition: background 0.14s var(--ease-ios, cubic-bezier(0.32, 0.72, 0, 1)),
    border-color 0.14s var(--ease-ios, cubic-bezier(0.32, 0.72, 0, 1)),
    opacity 0.14s var(--ease-ios, cubic-bezier(0.32, 0.72, 0, 1));
}
.beamy-chat-queue-row:hover,
.beamy-chat-queue-row:focus-visible {
  background: color-mix(in srgb, var(--indigo, #b98bfa) 14%, transparent);
  border-color: color-mix(in srgb, var(--indigo, #b98bfa) 34%, transparent);
  outline: none;
}
/* Fading out, and already gone as far as the queue is concerned: only the
   picture of the row is still there, so it must not answer a click. */
.beamy-chat-queue-row.is-leaving {
  pointer-events: none;
}
.beamy-chat-queue-row.is-next {
  border-color: color-mix(in srgb, var(--amber, #e0a300) 40%, transparent);
}
.beamy-chat-queue-row.is-dragging {
  opacity: 0.45;
  border-style: dashed;
  border-color: color-mix(in srgb, var(--indigo, #b98bfa) 60%, transparent);
}
.beamy-chat-queue-grip {
  flex: 0 0 auto;
  display: flex;
  color: var(--tx3, rgba(231, 222, 251, 0.4));
  cursor: grab;
}
.beamy-chat-queue-row.is-dragging .beamy-chat-queue-grip {
  cursor: grabbing;
}
.beamy-chat-queue-pos {
  flex: 0 0 auto;
  min-width: 15px;
  text-align: center;
  color: var(--tx3, rgba(231, 222, 251, 0.5));
  font: 800 10px "Inter", system-ui, sans-serif;
}
.beamy-chat-queue-row.is-next .beamy-chat-queue-pos {
  color: var(--amber, #e0a300);
}
.beamy-chat-queue-main {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
/* Dots, activity, clock - one caption under the prompt they describe. */
.beamy-chat-queue-live {
  display: flex;
  align-items: center;
  gap: 7px;
  color: var(--tx3, rgba(231, 222, 251, 0.55));
}
.beamy-chat-queue-live .beamy-chat-clock {
  margin-left: 0;
  padding-left: 0;
}
.beamy-chat-queue-activity {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font: 600 11px "Inter", system-ui, sans-serif;
}
.beamy-chat-queue-text {
  flex: 1 1 auto;
  min-width: 0;
  color: var(--tx2, #b9aed4);
  font: 500 var(--beamy-orch-text, 14px) / 1.4 "Inter", system-ui, sans-serif;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* Clicking a row opens it: the whole prompt, and the controls that act on it. */
.beamy-chat-queue-row.is-open {
  align-items: flex-start;
  background: color-mix(in srgb, var(--indigo, #b98bfa) 16%, transparent);
  border-color: color-mix(in srgb, var(--indigo, #b98bfa) 40%, transparent);
}
.beamy-chat-queue-row.is-open .beamy-chat-queue-text {
  color: var(--tx, #e7defb);
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}
.beamy-chat-queue-actions {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: 4px;
  opacity: 0;
  transition: opacity 0.14s var(--ease-ios, cubic-bezier(0.32, 0.72, 0, 1));
}
.beamy-chat-queue-row:hover .beamy-chat-queue-actions,
.beamy-chat-queue-row:focus-within .beamy-chat-queue-actions,
.beamy-chat-queue-row.is-open .beamy-chat-queue-actions {
  opacity: 1;
}
/* Touch has no hover, so the controls are simply always there. */
@media (hover: none) {
  .beamy-chat-queue-actions {
    opacity: 1;
  }
}
.beamy-chat-queue-steer,
.beamy-chat-queue-drop {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 3px 7px;
  border-radius: 999px;
  border: 1px solid color-mix(in srgb, var(--indigo, #b98bfa) 34%, transparent);
  background: none;
  color: var(--tx2, #b9aed4);
  font: 800 9.5px "Inter", system-ui, sans-serif;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  cursor: pointer;
}
.beamy-chat-queue-steer:hover {
  color: var(--amber, #e0a300);
  border-color: color-mix(in srgb, var(--amber, #e0a300) 55%, transparent);
  background: color-mix(in srgb, var(--amber, #e0a300) 14%, transparent);
}
.beamy-chat-queue-drop:hover {
  color: var(--red, #f05050);
  border-color: color-mix(in srgb, var(--red, #f05050) 55%, transparent);
  background: color-mix(in srgb, var(--red, #f05050) 14%, transparent);
}
.beamy-chat-queue-steer:focus-visible,
.beamy-chat-queue-drop:focus-visible {
  outline: 2px solid var(--indigo, #b18ee6);
  outline-offset: 2px;
}
/* The front of the queue is already next; there is nothing to steer it past. */
.beamy-chat-queue-row.is-next .beamy-chat-queue-steer {
  display: none;
}
@media (prefers-reduced-motion: reduce) {
  .beamy-chat-queue-stack,
  .beamy-chat-queue-row,
  .beamy-chat-queue-caret,
  .beamy-chat-queue-actions {
    animation: none;
    transition: none;
  }
}

/* The conversation, above the bar. Closed it shows the turn in hand and
   nothing else, so it costs a couple of lines of canvas; open it is the whole
   transcript and the engineer asked for it. It used to be a rail docked to the
   right edge, standing there whether or not there was anything to read. */
.beamy-chat-history {
  align-self: center;
  width: min(92vw, 620px);
  max-height: 190px;
  margin: 0 0 8px;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  border-radius: 14px;
  background: var(--s1, #181226);
  border: 1px solid color-mix(in srgb, var(--indigo, #b98bfa) 42%, transparent);
  box-shadow: 0 14px 34px rgba(0, 0, 0, 0.5);
  animation: beamyRelayIn 0.24s var(--ease-ios, cubic-bezier(0.32, 0.72, 0, 1));
  /* Opening the transcript is a height change - 190px to 40vh - so max-height
     has to be in the transition or the panel snaps to its new size under a
     composer that is still easing. Every property the idle state below also
     touches is listed here, on one duration and one curve, so the two states
     always animate the same set. */
  transition: max-height 0.32s var(--ease-ios, cubic-bezier(0.32, 0.72, 0, 1)),
    opacity 0.32s var(--ease-ios, cubic-bezier(0.32, 0.72, 0, 1)),
    transform 0.32s var(--ease-ios, cubic-bezier(0.32, 0.72, 0, 1)),
    margin-bottom 0.32s var(--ease-ios, cubic-bezier(0.32, 0.72, 0, 1)),
    padding 0.32s var(--ease-ios, cubic-bezier(0.32, 0.72, 0, 1)),
    border-color 0.32s var(--ease-ios, cubic-bezier(0.32, 0.72, 0, 1));
}
.beamy-chat-history[hidden] {
  display: none;
}
.beamy-chat-history.is-open {
  max-height: 40vh;
}
/* Closed and finished with, the thread gives the canvas back rather than
   parking over the cards for the rest of the session. It is faded, not
   emptied: the next word from either side brings it straight back, and the
   history button still opens the whole transcript.

   One block, one transition. This used to be two rules on the same element,
   and between them the transition named only opacity and transform - so the
   height, the margin and the border collapsed instantly at t=0 while the ghost
   took most of a second to fade, jumping the composer and everything above it
   the moment the countdown fired. The box now goes at the same rate as the
   ink; only the duration changes on the way out. */
.beamy-chat-history.is-idle {
  opacity: 0;
  transform: translateY(8px);
  pointer-events: none;
  max-height: 0;
  margin-bottom: 0;
  padding: 0;
  border-color: transparent;
  /* Leaving is the slow direction here on purpose: the panel is giving the
     canvas back rather than getting out of the way of anything. */
  transition-duration: 0.8s;
}
/* Focusing a card is asking to look at that card. The panel is centred over
   the canvas directly above the composer, which is exactly where a focused
   card is framed, so the transcript ends up sitting on the thing it is talking
   about - and it stayed there through the whole focus, because nothing in the
   idle machinery knows about focus mode.

   Keyed off the body class canvasWorkspace.enterCardFocus already sets, which
   means no state to thread through and no way for the two to disagree: leaving
   focus removes the class and the panel eases back to whatever it was doing.
   It also outranks .is-open and .is-idle, so a transcript the engineer left
   open, and a new line arriving mid-focus, both stay tucked.

   Collapsed rather than `display: none` so it animates - and so it uses the
   same property set and the same curve declared on .beamy-chat-history above,
   which is what makes this read as the panel getting out of the way rather
   than blinking off. Precedent for keying a canvas body class from here is
   `body.cv-canvas-fullscreen` further down. */
body.cv-card-focus-mode .beamy-chat-history,
body.cv-card-focus-mode .beamy-chat-history.is-open {
  opacity: 0;
  transform: translateY(8px) scale(0.98);
  pointer-events: none;
  max-height: 0;
  margin-bottom: 0;
  padding: 0;
  border-color: transparent;
}
/* Closed, the panel is the turn in hand rather than a transcript: no head to
   act on, and only the rows since the newest prompt. The rest stay in the list
   so opening is a class change rather than a rebuild, which mid-turn would
   throw away the step chips and the half-streamed answer. */
.beamy-chat-history:not(.is-open) .beamy-chat-history-head,
.beamy-chat-history:not(.is-open) .beamy-chat-history-empty,
.beamy-chat-history:not(.is-open) .beamy-chat-history-row:not(.is-live-turn) {
  display: none;
}
.beamy-chat-history-head {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 9px 12px;
  border-bottom: 1px solid color-mix(in srgb, var(--indigo, #b98bfa) 20%, transparent);
  font: 800 11px "Inter", system-ui, sans-serif;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--tx3, rgba(231, 222, 251, 0.55));
}
/* The lifted layer over the composer: the queue and the transcript.
   Out of the column's flow on purpose. Beam Buddy stands beside that column in
   the dock, so its widest child is what decides where he stands - a 620px
   transcript over a 350px pill put him 130px out into open canvas, attached to
   nothing. Absolutely placed, the column is the pill again and he stays with
   it however much is being said. */
.beamy-chat-above {
  position: absolute;
  left: 50%;
  bottom: 100%;
  transform: translateX(-50%);
  width: min(92vw, 620px);
  display: flex;
  flex-direction: column;
  align-items: center;
  pointer-events: auto;
}
/* The column is a place to put panels, not a surface. Left hit-testable it
   was a 620px transparent rectangle over the cards, and after the thread faded
   it was still swallowing every click and drag aimed underneath it. */
.beamy-chat-above {
  pointer-events: none;
  max-height: 60vh;
  overflow: hidden;
}
.beamy-chat-above > * {
  pointer-events: auto;
}
.beamy-chat-history-clear {
  border: 0;
  background: none;
  padding: 2px 4px;
  color: var(--tx3, rgba(231, 222, 251, 0.55));
  font: 700 10.5px "Inter", system-ui, sans-serif;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  cursor: pointer;
}
.beamy-chat-history-clear:hover {
  color: var(--red, #f05050);
}
.beamy-chat-history-list {
  overflow-y: auto;
  padding: 8px 12px 10px;
  display: flex;
  flex-direction: column;
  gap: 9px;
}
/* One bubble per line, the engineer's on the right and the agent's on the
   left. Two speakers are told apart by side and colour here, which is what
   every messaging app the engineer already uses does - the stamped name over
   every line was the single biggest thing making this read as a build log
   rather than a conversation. */
.beamy-chat-history-row {
  display: flex;
  flex-direction: column;
  max-width: 82%;
  align-self: flex-start;
  /* The corner the bubble grows out of. A sent line swells from the composer
     side and a reply from the agent's, so each one appears to come from where
     it came from rather than inflating in place. */
  transform-origin: bottom left;
}
.beamy-chat-history-row[data-role="user"] {
  align-self: flex-end;
  align-items: flex-end;
  transform-origin: bottom right;
}
/* A line that was just said. `is-new` is set only on a live append (see
   markThreadRowNew) and dropped on animationend, so a reload does not replay
   the whole conversation and no row ever arrives twice.

   The curve is the one the rest of the dock uses. It overshoots slightly and
   settles, which is what makes a sent message read as launched rather than
   drawn - the same reason iMessage does not simply fade its bubbles in. */
@keyframes beamyBubbleIn {
  from {
    opacity: 0;
    transform: translateY(12px) scale(0.72);
  }
  to {
    opacity: 1;
    transform: translateY(0) scale(1);
  }
}
.beamy-chat-history-row.is-new {
  animation: beamyBubbleIn 0.34s var(--ease-ios, cubic-bezier(0.32, 0.72, 0, 1)) both;
}
/* What a new line comes in on when the engineer has asked for less motion. */
@keyframes beamyFadeIn {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}
/* The agent's side is the calmer of the two: an answer lands, it is not sent.
   Same curve, less travel, so the pair still read as one system. */
@keyframes beamyBubbleArrive {
  from {
    opacity: 0;
    transform: translateY(7px) scale(0.9);
  }
  to {
    opacity: 1;
    transform: translateY(0) scale(1);
  }
}
.beamy-chat-history-row.is-new[data-role="assistant"],
.beamy-chat-history-row.is-new[data-role="error"] {
  animation: beamyBubbleArrive 0.28s var(--ease-ios, cubic-bezier(0.32, 0.72, 0, 1)) both;
}
/* Announced, not drawn. Screen readers still hear who is speaking. */
.beamy-chat-history-who {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}
.beamy-chat-history-text {
  padding: 7px 12px;
  border-radius: 16px;
  background: var(--s2, #221a38);
  color: var(--tx, #e7defb);
  font: 500 var(--beamy-orch-text, 14px) / 1.45 "Inter", system-ui, sans-serif;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}
.beamy-chat-history-row[data-role="assistant"] .beamy-chat-history-text,
.beamy-chat-history-row[data-role="error"] .beamy-chat-history-text {
  border-bottom-left-radius: 5px;
}
/* The agent answers in markdown, and the bubble draws it - same painter and
   same parser as the Terminal Card, under this sheet's own class prefix
   because canvas.css is not loaded on the entrance page. `pre-wrap` moves off
   the bubble and onto the blocks inside it: the blocks carry their own
   newlines, and left on the container it put a blank line between every one of
   them. */
.beamy-chat-history-text.is-md {
  display: flex;
  flex-direction: column;
  gap: 6px;
  white-space: normal;
}
.beamy-chat-md-p {
  margin: 0;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}
.beamy-chat-md-h {
  margin: 0;
  font-weight: 700;
  line-height: 1.35;
}
/* No `flex` in here: a flex parent makes each item a flex item, which silently
   removes the marker that is the whole reason the list is a list. */
.beamy-chat-md-list {
  margin: 0;
  padding-left: 1.35em;
  overflow-wrap: anywhere;
}
.beamy-chat-md-li {
  margin: 0;
  padding-left: 2px;
  white-space: pre-wrap;
}
.beamy-chat-md-li::marker {
  color: var(--tx3, #8b82a6);
}
.beamy-chat-md-quote {
  margin: 0;
  padding: 1px 0 1px 9px;
  border-left: 2px solid color-mix(in srgb, currentColor 24%, transparent);
  color: var(--tx2, #b9b0d4);
  font-style: italic;
}
.beamy-chat-md-strong {
  font-weight: 700;
}
.beamy-chat-md-em {
  font-style: italic;
}
/* Sized in em rather than px so it sits on whatever line it is in - a fixed
   size looked right in a paragraph and wrong in a heading. */
.beamy-chat-md-code-inline {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.92em;
  padding: 0.5px 4px;
  border-radius: 5px;
  background: color-mix(in srgb, currentColor 12%, transparent);
  overflow-wrap: anywhere;
}
/* Wrapped, not clipped. The bar is narrow, and a fence scrolled sideways hides
   the end of every line behind a scrollbar nobody drags. */
.beamy-chat-md-code {
  margin: 0;
  padding: 8px 10px;
  border-radius: 8px;
  background: color-mix(in srgb, currentColor 10%, transparent);
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.88em;
  line-height: 1.5;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}
/* Underlined rather than coloured alone, which is the only version of it that
   works for a reader who cannot pick the accent out of the text around it. */
.beamy-chat-md-link {
  color: inherit;
  text-decoration: underline;
  text-underline-offset: 2px;
  overflow-wrap: anywhere;
}
.beamy-chat-history-row[data-role="user"] .beamy-chat-history-text {
  border-bottom-right-radius: 5px;
  background: var(--blue, #0b84ff);
  color: var(--on-blue, #fff);
}
.beamy-chat-history-row[data-role="error"] .beamy-chat-history-text {
  background: color-mix(in srgb, var(--red, #f05050) 18%, var(--s2, #221a38));
  color: var(--red, #f05050);
}
/* Answered by the canvas itself, with no agent and no round trip. A tint says
   it without spending a line on it. */
.beamy-chat-history-row.is-local .beamy-chat-history-text {
  background: color-mix(in srgb, var(--green, #1ed96b) 14%, var(--s2, #221a38));
}
/* Consecutive lines from the same speaker read as one turn, so only the last
   of a run keeps its tail. */
.beamy-chat-history-row[data-role="assistant"] + .beamy-chat-history-row[data-role="assistant"] {
  margin-top: -4px;
}
.beamy-chat-history-row[data-role="user"] + .beamy-chat-history-row[data-role="user"] {
  margin-top: -4px;
}
/* The prompt in flight is already on screen in the running row; drawing it
   again here is the double text. The line stays in the thread - it is the
   transcript - and comes back the moment the turn ends. */
.beamy-chat-history-row.is-inflight {
  display: none;
}
.beamy-chat-history-empty {
  color: var(--tx3, rgba(231, 222, 251, 0.5));
  font: 500 var(--beamy-orch-text, 14px) / 1.5 "Inter", system-ui, sans-serif;
}

.beamy-chat-relay {
  display: flex;
  align-items: center;
  gap: 8px;
  /* Sizes to its own text and stays centred. It must never dictate the width
     of the field below it. */
  align-self: center;
  max-width: min(92vw, 620px);
  animation: beamyRelayIn 0.24s var(--ease-ios, cubic-bezier(0.32, 0.72, 0, 1));
  margin: 0 10px 6px;
  padding: 6px 12px;
  border-radius: 12px;
  background: color-mix(in srgb, var(--s1, #181226) 94%, transparent);
  border: 1px solid color-mix(in srgb, var(--indigo, #b98bfa) 38%, transparent);
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.42);
  -webkit-backdrop-filter: blur(4px);
  backdrop-filter: blur(4px);
  color: var(--tx2, #b9aed4);
  font: 500 var(--beamy-orch-text, 14px) / 1.45 "Inter", system-ui, -apple-system,
    "Segoe UI", Roboto, sans-serif;
  cursor: default;
}
.beamy-chat-relay[hidden] {
  display: none;
}
.beamy-chat-relay-text {
  min-width: 0;
  /* An orchestrator answer can run long; three lines then clip, so the bar
     never grows tall enough to cover the cards it is talking about. */
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.beamy-chat-relay-dot {
  flex: 0 0 auto;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--tx3, rgba(231, 222, 251, 0.5));
}
.beamy-chat-relay[data-tone="busy"] .beamy-chat-relay-dot {
  background: var(--amber, #e0a300);
  animation: beamyRelayPulse 1.1s ease-in-out infinite;
}
.beamy-chat-relay[data-tone="heard"] .beamy-chat-relay-dot {
  background: var(--blue, #4da6ff);
}
.beamy-chat-relay[data-tone="reply"] .beamy-chat-relay-dot {
  background: var(--green, #1ed96b);
}
.beamy-chat-relay[data-tone="reply"] .beamy-chat-relay-text {
  color: var(--tx, #e7defb);
}
.beamy-chat-relay[data-tone="error"] {
  border-color: color-mix(in srgb, var(--red, #f05050) 50%, transparent);
}
.beamy-chat-relay[data-tone="error"] .beamy-chat-relay-dot {
  background: var(--red, #f05050);
}
.beamy-chat-relay-close {
  flex: 0 0 auto;
  width: 20px;
  height: 20px;
  margin: -2px -6px -2px 2px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: var(--tx3, rgba(231, 222, 251, 0.55));
  cursor: pointer;
  transition: color 0.15s ease, background 0.15s ease;
}
.beamy-chat-relay-close[hidden] {
  display: none;
}
.beamy-chat-relay-close:hover {
  color: var(--tx, #e7defb);
  background: color-mix(in srgb, var(--tx, #e7defb) 14%, transparent);
}
.beamy-chat-relay-close:focus-visible {
  outline: 2px solid var(--indigo, #b18ee6);
  outline-offset: 1px;
}
.beamy-chat-relay[data-tone="error"] .beamy-chat-relay-close:hover {
  color: var(--red, #f05050);
  background: color-mix(in srgb, var(--red, #f05050) 18%, transparent);
}
@keyframes beamyRelayPulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.25; }
}
@keyframes beamyRelayIn {
  from { opacity: 0; transform: translateY(6px) scale(0.98); }
  to { opacity: 1; transform: none; }
}
/* Every motion added for the composer is decorative. Reduced-motion users get
   the same states with none of the travel. */
@media (prefers-reduced-motion: reduce) {
  .beamy-chat-relay[data-tone="busy"] .beamy-chat-relay-dot {
    animation: none;
  }
  .beamy-chat-relay {
    animation: none;
  }
  /* A new line still has to appear, so the send keeps its fade and loses the
     travel and the scale - the part that actually moves. */
  .beamy-chat-history-row.is-new,
  .beamy-chat-history-row.is-new[data-role="assistant"],
  .beamy-chat-history-row.is-new[data-role="error"] {
    animation: beamyFadeIn 0.2s linear both;
  }
  /* Tucking for a focused card becomes a cut rather than a slide. */
  body.cv-card-focus-mode .beamy-chat-history,
  body.cv-card-focus-mode .beamy-chat-history.is-open {
    transition: none;
    transform: none;
  }
  .beamy-chat-suggest,
  .beamy-chat-input,
  .beamy-chat-field {
    transition: none;
  }
  .beamy-chat-suggest {
    opacity: 1;
    transform: translateX(-50%);
  }
}

/* ---- suggestions / live tutorial popover (opens upward) ----------------- */
.beamy-chat-suggest {
  position: absolute;
  /* Centred over the pill rather than pinned to its left edge: the pill now
     changes width, and a left-anchored popover slides out from under it. */
  left: 50%;
  bottom: calc(100% + 8px);
  min-width: 320px;
  max-width: 460px;
  max-height: 340px;
  overflow-y: auto;
  padding: 6px;
  background: var(--s1, #181226);
  border: 1px solid color-mix(in srgb, var(--indigo, #b98bfa) 50%, transparent);
  border-radius: 12px;
  box-shadow: 0 12px 30px rgba(0, 0, 0, 0.55);
  /* Rises out of the field and settles, rather than blinking into existence.
     Anchored to the bottom edge so it reads as growing from the bar. */
  transform-origin: bottom center;
  opacity: 0;
  transform: translateX(-50%) translateY(10px) scale(0.97);
  transition: opacity 0.19s var(--ease-ios, cubic-bezier(0.32, 0.72, 0, 1)),
    transform 0.19s var(--ease-ios, cubic-bezier(0.32, 0.72, 0, 1));
  will-change: opacity, transform;
}
.beamy-chat-suggest.is-open {
  opacity: 1;
  transform: translateX(-50%) translateY(0) scale(1);
}
.beamy-chat-suggest[hidden] {
  display: none;
}
.beamy-chat-suggest-head {
  padding: 5px 9px 7px;
  font: 11px/1.3 "Inter", system-ui, sans-serif;
  color: var(--tx2, rgba(231, 222, 251, 0.6));
}
.beamy-chat-suggest-foot {
  margin-top: 4px;
  padding: 7px 9px 3px;
  border-top: 1px solid color-mix(in srgb, var(--indigo, #b98bfa) 18%, transparent);
  font: 10.5px/1.3 "Inter", system-ui, sans-serif;
  color: var(--tx3, rgba(231, 222, 251, 0.42));
}
.beamy-chat-opt {
  display: flex;
  align-items: center;
  gap: 9px;
  width: 100%;
  padding: 7px 9px;
  border: 0;
  border-radius: 8px;
  background: transparent;
  color: var(--tx, #e7defb);
  font: 12.5px/1.2 "Inter", system-ui, sans-serif;
  text-align: left;
  cursor: pointer;
}
.beamy-chat-opt.is-active {
  background: color-mix(in srgb, var(--indigo, #b98bfa) 16%, transparent);
}
.beamy-chat-opt-ic {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 18px;
  /* The only live use of the old lavender in this file - every other one sits
     behind a var(--tx3, ...) fallback that a themed page never reaches, so this
     icon was the one thing here still painting itself purple. */
  color: var(--tx3, rgba(231, 222, 251, 0.72));
}
.beamy-chat-opt.is-active .beamy-chat-opt-ic,
.beamy-chat-opt.is-auto .beamy-chat-opt-ic {
  color: #d3b5fd;
}
.beamy-chat-opt-ic svg {
  width: 16px;
  height: 16px;
}
.beamy-chat-opt-body {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 1px;
}
.beamy-chat-opt-label {
  font-weight: 600;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.beamy-chat-opt-hint {
  font-size: 11px;
  color: var(--tx2, rgba(231, 222, 251, 0.55));
}
.beamy-chat-opt-badge {
  flex: 0 0 auto;
  padding: 2px 6px;
  border-radius: 999px;
  font-size: 9.5px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  background: color-mix(in srgb, var(--green, #7ac48d) 22%, transparent);
  color: var(--green, #8fe0a4);
}
.beamy-chat-opt-badge--warn {
  background: color-mix(in srgb, var(--amber, #f0b429) 20%, transparent);
  color: var(--amber, #f0c66b);
}

/* ---- light theme: darken the dropdown so icons + text stay legible ------
   The default palette is tuned for the dark canvas (pale lavender icons on a
   near-black popover). On the light theme the popover turns light, so those
   pale icons wash out - swap in darker tones that read against a light card. */
body.light .beamy-chat-opt-ic {
  color: rgba(24, 33, 48, 0.66);
}
body.light .beamy-chat-opt.is-active .beamy-chat-opt-ic,
body.light .beamy-chat-opt.is-auto .beamy-chat-opt-ic {
  color: var(--blue, #0078d4);
}

/* narrow screens: let the dock span the width and Beamy ride the end */
@media (max-width: 640px) {
  .beamy-dock {
    left: 12px;
    right: 12px;
    transform: none;
    width: auto;
  }
  .beamy-chat {
    flex: 1 1 auto;
  }
  .beamy-chat-input {
    max-width: none;
  }
  .beamy-chat-suggest {
    max-width: none;
    right: 0;
  }
}

/* ---- reduced motion: stand still, keep every pose readable -------------- */
@media (prefers-reduced-motion: reduce) {
  .beamy__btn,
  .beamy *,
  .beamy__bubble,
  .beamy.beamy--home,
  .beamy-chat-field,
  .beamy-chat-input,
  .beamy-chat-send {
    animation: none !important;
    transition: none !important;
  }
  .beamy[data-action="celebrate"] .beamy__arm--l {
    transform: translate(2px, -7px) rotate(-12deg);
  }
  .beamy[data-action="celebrate"] .beamy__arm--r {
    transform: translate(-2px, -7px) rotate(12deg);
  }
}


/* ---- working poses ------------------------------------------------------
   Played while the orchestrator is mid-turn. A CLI turn can run for a minute
   with nothing on screen moving, which reads as a hang; Beamy doing the job
   is the progress indicator. Three poses cycle so it never looks like a
   single looping GIF. */
.beamy[data-action="draft"] .b-draft-pencil {
  animation: beamyDraft 1.6s ease-in-out infinite;
}
@keyframes beamyDraft {
  0%, 100% { transform: translate(0, 0); }
  50% { transform: translate(6px, 3px); }
}
.beamy[data-action="type"] .b-type-hand--l {
  animation: beamyTap 0.42s steps(2, end) infinite;
}
.beamy[data-action="type"] .b-type-hand--r {
  animation: beamyTap 0.42s steps(2, end) infinite 0.21s;
}
@keyframes beamyTap {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(-1.4px); }
}
.beamy[data-action="build"] .b-build-girder {
  animation: beamyCrane 2.1s ease-in-out infinite;
}
@keyframes beamyCrane {
  0%, 100% { transform: translateY(-3px); }
  45%, 60% { transform: translateY(3px); }
}
.beamy[data-action="build"] .b-build-spark {
  animation: beamySpark 2.1s ease-out infinite;
  opacity: 0;
}
@keyframes beamySpark {
  0%, 44% { opacity: 0; transform: scale(0.6); }
  52% { opacity: 1; transform: scale(1); }
  70%, 100% { opacity: 0; transform: scale(1.3); }
}
/* Every working pose is decoration over a status the relay already states in
   words, so reduced motion simply holds the first frame. */
@media (prefers-reduced-motion: reduce) {
  .beamy[data-action="draft"] .b-draft-pencil,
  .beamy[data-action="type"] .b-type-hand--l,
  .beamy[data-action="type"] .b-type-hand--r,
  .beamy[data-action="build"] .b-build-girder,
  .beamy[data-action="build"] .b-build-spark {
    animation: none;
  }
  .beamy[data-action="build"] .b-build-spark {
    opacity: 1;
  }
}


/* ---- the working indicator ----------------------------------------------
   Three bouncing dots and an elapsed clock. A single pulsing dot read as a
   static status light, and a CLI turn runs long enough that "is this even
   running" is a fair question. Elapsed time rather than a progress bar,
   because the agent decides how many rounds it needs - a percentage here
   would be invented. */
.beamy-chat-dots {
  display: inline-flex;
  align-items: flex-end;
  gap: 3px;
  flex: 0 0 auto;
  height: 1em;
}
.beamy-chat-dots[hidden] {
  display: none;
}
.beamy-chat-dots i {
  width: 4px;
  height: 4px;
  border-radius: 50%;
  background: currentColor;
  opacity: 0.45;
  animation: beamyDots 1.1s ease-in-out infinite;
}
.beamy-chat-dots i:nth-child(2) {
  animation-delay: 0.16s;
}
.beamy-chat-dots i:nth-child(3) {
  animation-delay: 0.32s;
}
@keyframes beamyDots {
  0%, 70%, 100% { transform: translateY(0); opacity: 0.35; }
  35% { transform: translateY(-4px); opacity: 1; }
}
.beamy-chat-clock {
  flex: 0 0 auto;
  margin-left: auto;
  padding-left: 10px;
  font-variant-numeric: tabular-nums;
  font-size: 0.85em;
  color: var(--tx3, rgba(231, 222, 251, 0.5));
}
.beamy-chat-clock[hidden] {
  display: none;
}
/* While work is in flight the bubble breathes, so the whole strip reads as
   live rather than as a message that happens to be sitting there. */
.beamy-chat-relay[data-tone="busy"] {
  animation: beamyRelayIn 0.24s var(--ease-ios, cubic-bezier(0.32, 0.72, 0, 1)),
    beamyRelayBreathe 2.4s ease-in-out 0.24s infinite;
}
@keyframes beamyRelayBreathe {
  0%, 100% { border-color: color-mix(in srgb, var(--indigo, #b98bfa) 38%, transparent); }
  50% { border-color: color-mix(in srgb, var(--indigo, #b98bfa) 78%, transparent); }
}
@media (prefers-reduced-motion: reduce) {
  .beamy-chat-dots i,
  .beamy-chat-relay[data-tone="busy"] {
    animation: none;
  }
  .beamy-chat-dots i {
    opacity: 0.8;
  }
}


/* =========================================================================
   The conversation panel's head, and the chips inside it
   -------------------------------------------------------------------------
   Answers used to live for six seconds in a one-line strip clamped to three
   lines, and the engineer's own prompt was never drawn at all, so a turn that
   ran for a minute and dropped nine cards left nothing behind to read. Then
   the fix over-corrected into a rail docked to the right edge of the canvas,
   standing there whether or not there was a conversation. The panel above the
   bar is the middle: the turn in hand while it runs, the transcript when the
   toggle in the pill asks for it.
   ========================================================================= */
.beamy-chat-history-title {
  font: 800 11px "Inter", system-ui, sans-serif;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--tx3, rgba(231, 222, 251, 0.55));
  margin-right: auto;
}
.beamy-chat-history-close {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  padding: 0;
  border: 0;
  border-radius: 7px;
  background: none;
  color: var(--tx3, rgba(231, 222, 251, 0.55));
  cursor: pointer;
  transition: color 0.16s var(--ease-ios, cubic-bezier(0.32, 0.72, 0, 1));
}
.beamy-chat-history-close:hover {
  color: var(--tx, #e7defb);
}
.beamy-chat-history-close:focus-visible {
  outline: 2px solid var(--indigo, #b18ee6);
  outline-offset: 2px;
}
/* ---- the typing indicator ------------------------------------------------
   An answer still being written is three dots in an empty assistant bubble at
   the foot of the thread - same side, same radius, same tail as anything else
   the agent says. It replaces a pill that read "Working on it ... 10s" and sat
   between the last bubble and the composer, outside the conversation: the one
   place no messaging app has ever put it.

   Each dot runs the same 1.3s loop 0.18s behind the one before it, so the
   three of them read as a wave rather than a blink - the iOS cadence. */
.beamy-chat-history-row.is-pending .beamy-chat-history-text {
  min-height: 0;
}
.beamy-chat-typing {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 1px 2px;
  vertical-align: middle;
}
.beamy-chat-typing i {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--tx3, rgba(231, 222, 251, 0.55));
  opacity: 0.34;
  transform: scale(0.86);
  animation: beamyTyping 1.3s ease-in-out infinite;
}
.beamy-chat-typing i:nth-child(2) {
  animation-delay: 0.18s;
}
.beamy-chat-typing i:nth-child(3) {
  animation-delay: 0.36s;
}
@keyframes beamyTyping {
  0%, 58%, 100% { opacity: 0.34; transform: scale(0.86); }
  29% { opacity: 1; transform: scale(1.06); }
}
/* Elapsed time survives the pill, but only where a timestamp lives: under the
   bubble, dimmed, tabular so it does not twitch as the seconds roll. */
.beamy-chat-typing-clock {
  margin: 3px 0 0 6px;
  font: 500 11px / 1 "Inter", system-ui, sans-serif;
  font-variant-numeric: tabular-nums;
  color: var(--tx3, rgba(231, 222, 251, 0.42));
  opacity: 0.75;
}

/* ---- the prompt actually running ----------------------------------------
   Bound to the count of WAITING prompts, the stack hid the one prompt worth
   seeing for the whole minute it took. It sits at the head of the queue now,
   in its own container so a reorder in the list below still commits at the
   index the row was dropped at. */
.beamy-chat-queue-running {
  display: flex;
  flex-direction: column;
  padding: 6px 6px 0;
}
.beamy-chat-queue-running[hidden] {
  display: none;
}
.beamy-chat-queue-row.is-running {
  cursor: default;
  border-color: color-mix(in srgb, var(--blue, #4da6ff) 45%, transparent);
  background: color-mix(in srgb, var(--blue, #4da6ff) 12%, transparent);
}
.beamy-chat-queue-row.is-running .beamy-chat-queue-actions {
  opacity: 1;
}
.beamy-chat-queue-now {
  flex: 0 0 auto;
  font: 800 9.5px "Inter", system-ui, sans-serif;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--blue, #4da6ff);
}
/* The running row points at the thread instead of reprinting the prompt the
   thread is already showing one line below it. Quieter than a real prompt, so
   the eye goes to the words rather than to the pointer at them. */
.beamy-chat-queue-text.is-echo {
  font-style: italic;
  color: var(--tx3, rgba(231, 222, 251, 0.55));
}

/* ---- Stop takes Send's place while a turn runs -------------------------- */
.beamy-chat-stop {
  flex: 0 0 auto;
  width: 30px;
  height: 30px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: var(--red, #f05050);
  color: #fff;
  cursor: pointer;
  transition: background 0.15s var(--ease-ios, cubic-bezier(0.32, 0.72, 0, 1)),
    transform 0.1s var(--ease-ios, cubic-bezier(0.32, 0.72, 0, 1));
}
.beamy-chat-stop:hover {
  background: color-mix(in srgb, var(--red, #f05050) 82%, #fff);
}
.beamy-chat-stop:active {
  transform: scale(0.92);
}
.beamy-chat-stop:focus-visible {
  outline: 2px solid var(--red, #f05050);
  outline-offset: 2px;
}
.beamy-chat-stop[hidden],
.beamy-chat-send[hidden] {
  display: none;
}

/* Office Blue is the default appearance, and these panels are lifted straight
   off a white board - the heavy black shadow the dark palette needs reads as
   grime there. Same geometry, softer ink. */
body.light .beamy-chat-history,
html.light .beamy-chat-history {
  border-color: color-mix(in srgb, var(--blue, #0078d4) 26%, transparent);
  box-shadow: 0 12px 30px rgba(16, 44, 77, 0.16);
}
body.light .beamy-chat-history-row[data-role="assistant"] .beamy-chat-history-text,
html.light .beamy-chat-history-row[data-role="assistant"] .beamy-chat-history-text {
  background: var(--s2, #eaf1f8);
  color: var(--tx, #16273c);
}

/* A short window has less canvas to spend, so the open transcript gives some
   of its height back rather than climbing over the cards. */
@media (max-height: 720px) {
  .beamy-chat-history.is-open {
    max-height: 32vh;
  }
}
@media (prefers-reduced-motion: reduce) {
  .beamy-chat-history,
  .beamy-chat-history-close,
  .beamy-chat-stop {
    animation: none;
    transition: none;
  }
  .beamy-chat-typing i {
    animation: none;
    opacity: 0.7;
    transform: scale(1);
  }
}
