/* /public/css/bot-scene-engine.css — shared styles for the BotScene
   engine (public/js/bot-scene-engine.js). Loaded by every surface
   that mounts a BotScene: Mainframe Workshop today, Activity scene
   after PR 3b.

   PR 3a (2026-05-29): extracted verbatim from the Mainframe Workshop's
   inline <style> block so the visible behaviour stays identical.
   Zoom + pan + leash + pill recolor land in 3c.

   Callers can still add page-local overrides for things like the
   surrounding chrome (heartbeat row, scene wrapper sizing) without
   touching this file. */

/* Scene card — relative-positioned workshop. Bots and target apps
   are absolutely positioned at fixed percent coordinates inside the
   .scene-stage layer that the engine injects on mount. PR 3c added
   the viewport + stage + svg + content layer split so zoom/pan can
   transform the stage without affecting the controls or the outer
   chrome. */
.scene {
  position: relative;
  width: 100%;
  aspect-ratio: 16 / 11;
  max-height: 460px;
  border-radius: 14px;
  border: 1px solid var(--border);
  overflow: hidden;
  /* Solid panel-solid base + subtle overlays. The previous version
     stopped the linear-gradient at var(--panel-hover) (~4% opacity)
     at the top and bottom, letting a user-uploaded appearance scene
     (planet, etc.) bleed through the desk-row and target-row areas.
     Background-color goes opaque + the gradient layers just tint the
     bands so the visual hierarchy stays. */
  background-color: var(--panel-solid);
  background-image:
    radial-gradient(ellipse at top, rgba(245,197,66,.06), transparent 60%),
    linear-gradient(180deg, rgba(255,255,255,.06) 0%, transparent 50%, rgba(255,255,255,.06) 100%);
}
:root[data-theme="light"] .scene {
  background-image:
    radial-gradient(ellipse at top, rgba(245,197,66,.08), transparent 60%),
    linear-gradient(180deg, rgba(0,0,0,.05) 0%, transparent 50%, rgba(0,0,0,.05) 100%);
}

/* Viewport — clips overflow from pan/zoom; captures pointer events. */
.scene-viewport {
  position: absolute;
  inset: 0;
  overflow: hidden;
  touch-action: none;
  cursor: grab;
}

/* Stage — the transform target. translate + scale relative to its
   top-left. Content + leash SVG share its coordinate space. */
.scene-stage {
  position: absolute;
  inset: 0;
  transform-origin: 0 0;
  will-change: transform;
}

/* Leash SVG — sits behind the bots/desks. Each .leash line spans
   from desk to target in percentage coordinates (viewBox 0 0 100 100)
   so a transform on the stage scales the line with everything else. */
.scene-leash {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  z-index: 1;
}
.scene-leash .leash {
  fill: none;
  stroke-width: 0.4;            /* relative to the 0..100 viewBox */
  stroke-linecap: round;
  stroke-dasharray: 2 1.4;
  stroke-opacity: 0.85;
  animation: leash-march 0.6s linear infinite;
}
@keyframes leash-march {
  from { stroke-dashoffset: 0; }
  to   { stroke-dashoffset: -3.4; }
}

/* Content layer — desks / bots / targets live here. z above leash so
   the orbs sit on top of the line. */
.scene-content {
  position: absolute;
  inset: 0;
  z-index: 2;
}

/* Zoom + reset controls — top-right of the scene, outside the stage
   so they don't transform with content. */
.scene-controls {
  position: absolute;
  top: 8px;
  right: 8px;
  display: flex;
  flex-direction: column;
  gap: 4px;
  z-index: 5;
}
.scene-ctrl {
  width: 28px;
  height: 28px;
  border-radius: 8px;
  border: 1px solid var(--border);
  background: rgba(0,0,0,.45);
  color: var(--text);
  font: inherit;
  font-size: 14px;
  font-weight: 700;
  line-height: 1;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: background .15s, border-color .15s;
}
.scene-ctrl:hover { background: rgba(0,0,0,.65); border-color: var(--border-strong); }
.scene-ctrl:active { transform: scale(.94); }
:root[data-theme="light"] .scene-ctrl {
  background: rgba(255,255,255,.85);
  color: var(--text);
}
:root[data-theme="light"] .scene-ctrl:hover {
  background: rgba(255,255,255,.95);
}

/* Floor seam between the desk row (top half) and the targets row. */
.scene::before {
  content: "";
  position: absolute;
  left: 6%;
  right: 6%;
  top: 53%;
  border-bottom: 1px dashed var(--border);
  pointer-events: none;
}

.row-tag {
  position: absolute;
  font-size: 9px;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--text-dim);
  pointer-events: none;
}
.row-tag.desks { left: 8px; top: 8px; }
.row-tag.floor { left: 8px; bottom: 8px; }

/* Desk — platform under each bot. Idle bots stand on top of their
   desk; working bots leave the desk visible behind. */
.desk {
  position: absolute;
  transform: translate(-50%, -50%);
  width: 76px;
  text-align: center;
  pointer-events: none;
}
.desk-plate {
  width: 64px;
  height: 8px;
  margin: 0 auto;
  background: var(--panel-solid);
  border: 1px solid var(--border);
  border-radius: 3px;
  box-shadow: 0 2px 4px rgba(0,0,0,.25);
}
.desk-name {
  /* PR 3c: pill background is set inline to the orb's color; CSS shapes
     it. The previous static text-on-panel style stays as a fallback
     when no color is provided. */
  display: inline-block;
  margin-top: 6px;
  padding: 1px 8px;
  border-radius: 999px;
  font-size: 10px;
  font-weight: 700;
  color: var(--text);
  line-height: 1.3;
  letter-spacing: .01em;
  box-shadow: 0 1px 2px rgba(0,0,0,.25);
}
.desk-meta {
  font-size: 9px;
  color: var(--text-dim);
  font-family: 'Consolas','SF Mono',monospace;
  line-height: 1.3;
  margin-top: 2px;
}
.desk-meta .ok      { color: #5cb85c; }
.desk-meta .err     { color: #d9534f; }
.desk-meta .running { color: #f5c542; }

/* Target app icon — sits on the floor row. Highlights when a bot is
   at work on it. */
.target {
  position: absolute;
  transform: translate(-50%, -50%);
  width: 64px;
  text-align: center;
  pointer-events: none;
}
.target-icon {
  width: 40px;
  height: 40px;
  margin: 0 auto;
  border-radius: 10px;
  background: var(--panel-solid);
  border: 1px solid var(--border);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 18px;
  color: var(--text-soft);
  transition: border-color .3s, background .3s, box-shadow .3s;
}
.target.hot .target-icon {
  border-color: rgba(245,197,66,.55);
  background: rgba(245,197,66,.10);
  box-shadow: 0 0 12px rgba(245,197,66,.30);
}
.target-name {
  margin-top: 4px;
  font-size: 10px;
  color: var(--text-dim);
  line-height: 1.2;
}
.target.hot .target-name { color: #f5c542; }

/* Bot avatar — the walking thing. Idle = its desk; working = its
   target. CSS transition handles the walk. */
.bot {
  position: absolute;
  width: 30px;
  height: 30px;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 11px;
  font-weight: 700;
  color: #0a0a0a;
  background: var(--bot-color, #7fc4d4);
  box-shadow: 0 3px 8px rgba(0,0,0,.35);
  transition: left 1.2s cubic-bezier(.4,0,.2,1), top 1.2s cubic-bezier(.4,0,.2,1);
  z-index: 2;
}
.bot[data-busy="1"]::after {
  content: "";
  position: absolute;
  top: -14px;
  left: 50%;
  transform: translateX(-50%);
  width: 20px;
  height: 6px;
  background-image: radial-gradient(circle, #f5c542 38%, transparent 40%);
  background-size: 6px 6px;
  background-repeat: repeat-x;
  animation: bot-scene-dots 1.2s steps(3) infinite;
}
@keyframes bot-scene-dots {
  0%   { opacity: .3; }
  50%  { opacity: 1; }
  100% { opacity: .3; }
}

/* prefers-reduced-motion — locked decision (Activity-Conceptual PR 3).
   Bots jump-cut to their app and back; three-dot indicator stops
   animating but stays visible so the "working" signal doesn't vanish.
   Marching-dash leash also goes static (dashes still visible, just not
   moving). */
@media (prefers-reduced-motion: reduce) {
  .bot { transition: none; }
  .bot[data-busy="1"]::after { animation: none; opacity: 1; }
  .scene-leash .leash { animation: none; }
}
