/**
 * The flyingRobot's whole look, and every animation it owns.
 *
 * Contract with the JS: the frame loop writes exactly one transform on .fr
 * and the custom properties --flap, --bx, --by. Everything else that moves —
 * thruster pulse, moods, the trail, the highlight — is CSS animation keyed off
 * data- attributes, so the compositor carries the load and the script stays
 * out of the style system.
 *
 * Colours defer to the host page's tokens (--accent etc.) with fallbacks
 * matching this site's palette, so on a foreign page it degrades to sensible
 * greens instead of breaking.
 */

.fr-layer{
  position:fixed;
  inset:0;
  z-index:70;              /* under the site header (80) and any modal */
  pointer-events:none;      /* the layer is glass; only the pieces opt in */
}

/* ---- the flyingRobot ------------------------------------------------------- */
.fr{
  position:fixed;
  left:0; top:0;
  width:120px; height:100px;
  margin-left:-60px; margin-top:-50px;   /* transform origin = its centre */
  pointer-events:auto;
  cursor:pointer;
  will-change:transform;
  --flap:620ms;
  -webkit-tap-highlight-color:transparent;
}
.fr:focus-visible{outline:2px solid var(--accent,#2ee6a8);outline-offset:6px;border-radius:50%}

.fr-svg{width:100%;height:100%;display:block;overflow:visible}

/* ---- purpose-built hovering robot --------------------------------------- */
.fr-shell rect{fill:#0d1724;stroke:var(--accent,#2ee6a8);stroke-width:1.8}
.fr-shell path{fill:none;stroke:rgba(106,169,255,.7);stroke-width:1.2;stroke-linecap:round}
.fr-face rect{fill:#050a11;stroke:rgba(106,169,255,.75);stroke-width:1.2}
.fr-eye{fill:var(--accent,#2ee6a8);filter:drop-shadow(0 0 3px rgba(46,230,168,.75));transform-box:fill-box;transform-origin:center}
.fr-mouth{fill:none;stroke:#7c91aa;stroke-width:1.2;stroke-linecap:round}
.fr-arms path,.fr-arms circle{fill:#0d1724;stroke:var(--accent-2,#6aa9ff);stroke-width:1.5;stroke-linecap:round}
.fr-antenna path{fill:none;stroke:var(--accent-2,#6aa9ff);stroke-width:1.4;stroke-linecap:round}
.fr-antenna circle,.fr-stabilizer{fill:var(--accent,#2ee6a8);stroke:#071018;stroke-width:1.2}
.fr-thrust{fill:rgba(46,230,168,.7);filter:drop-shadow(0 0 5px rgba(46,230,168,.7));transform-box:fill-box;transform-origin:50% 0;animation:frThruster var(--flap) ease-in-out infinite alternate}
.fr-thrust--r{animation-delay:-180ms}
.fr-scan{fill:var(--accent,#2ee6a8);opacity:0;filter:drop-shadow(0 0 3px var(--accent,#2ee6a8))}
@keyframes frThruster{from{transform:scaleY(.45);opacity:.45}to{transform:scaleY(1);opacity:1}}
.fr.is-still .fr-thrust{animation-play-state:paused}
.fr[data-mood="SCANNING"] .fr-scan,.fr[data-mood="THINKING"] .fr-scan{opacity:1;animation:frScan .65s ease-in-out infinite alternate}
@keyframes frScan{from{transform:translateY(0)}to{transform:translateY(22px)}}
.fr[data-mood="HAPPY"] .fr-mouth{d:path("M54 50 Q60 56 66 50")}
.fr[data-mood="ANNOYED"] .fr-eye,.fr[data-mood="ANGRY"] .fr-eye{fill:#ff766f}
.fr[data-mood="SURPRISED"] .fr-eye{transform:scale(1.55)}

/* ---- moods ---------------------------------------------------------------- */
/* Moods repaint the face and tint the thrusters; they never move the body — that
   is the flight's job, and doubling up made every mood look like turbulence. */
.fr[data-mood="EXCITED"]{--flap:150ms}
.fr[data-mood="ANGRY"] .fr-glyph{fill:#ff5f57}
.fr[data-mood="ANGRY"] .fr-eye{fill:#ff5f57}
.fr[data-mood="CONFUSED"] .fr-eye--l{transform:translateY(-1px)}
.fr[data-mood="CONFUSED"] .fr-eye--r{transform:translateY(1px)}
.fr[data-mood="CURIOUS"] .fr-eye,
.fr[data-mood="FOLLOWING"] .fr-eye{transform:scale(1.35)}
.fr[data-mood="THINKING"] .fr-eye{transform:scaleY(.5)}
.fr[data-mood="SLEEPING"] .fr-eye{transform:scaleY(.12)}
.fr[data-mood="SLEEPING"] .fr-zzz{opacity:1;animation:frZzz 2.4s ease-in-out infinite}
.fr[data-mood="PLAYFUL"] .fr-eye--r{transform:scaleY(.2)}   /* a wink */
/* Mid-scene with another character: attentive — wide eyes, brighter thrusters,
   and a slower, calmer thruster pulse than plain hovering. */
.fr[data-mood="INTERACTING"] .fr-eye{transform:scale(1.25)}
.fr[data-mood="INTERACTING"]{--flap:420ms}
.fr-zzz{font:700 9px/1 "JetBrains Mono",Consolas,monospace;fill:var(--muted,#93a3b8);opacity:0}

@keyframes frZzz{
  0%,100%{opacity:.25;transform:translateY(0)}
  50%{opacity:1;transform:translateY(-3px)}
}

/* ---- the code-particle trail ---------------------------------------------- */
.fr-trail{opacity:0}
.fr.has-trail .fr-trail{opacity:1}
.fr-trail__p{
  font:600 8px/1 "JetBrains Mono",Consolas,monospace;
  fill:var(--accent,#2ee6a8);
  animation:frTrail 900ms ease-out infinite;
}
.fr-trail__p--b{animation-delay:300ms;fill:var(--accent-2,#6aa9ff)}
.fr-trail__p--c{animation-delay:600ms}

@keyframes frTrail{
  from{opacity:.9;transform:translate(0,0)}
  to{opacity:0;transform:translate(-14px,10px)}
}

/* ---- cargo ---------------------------------------------------------------- */
.fr-cargo{
  position:absolute;
  left:50%;top:78%;
  width:22px;height:22px;
  margin-left:-11px;
  opacity:0;
  transition:opacity .3s;
}
.fr-cargo g{display:none;stroke:var(--accent,#2ee6a8);stroke-width:1.6;fill:#0e1725}
.fr-cargo path{stroke-linecap:round}
.fr[data-cargo="coffee"] .fr-cargo{opacity:1}
.fr[data-cargo="coffee"] .fr-cargo__coffee{display:block}

/* The Hero developer receives, drinks and sets down the same cup the Flying
   Robot carries. It is a separate prop, so the existing SVG stays untouched. */
.developer-coffee{
  position:absolute;z-index:7;left:58%;top:57%;width:22px;height:17px;
  border:2px solid #2ee6a8;border-radius:3px 3px 7px 7px;
  background:linear-gradient(180deg,#162637,#09131f);opacity:0;
  transform:translate(-50%,-50%) scale(.7);transform-origin:center;
  box-shadow:0 0 14px rgba(46,230,168,.28);pointer-events:none;
  transition:opacity .2s ease,transform .45s ease,left .45s ease,top .45s ease;
}
.developer-coffee::after{
  content:"";position:absolute;right:-8px;top:3px;width:7px;height:8px;
  border:2px solid #2ee6a8;border-left:0;border-radius:0 7px 7px 0;
}
.developer-coffee i,.developer-coffee b{
  position:absolute;bottom:17px;width:2px;height:8px;border-radius:999px;
  background:rgba(157,255,224,.75);filter:blur(.2px);
  animation:developerCoffeeSteam 1.2s ease-in-out infinite;
}
.developer-coffee i{left:6px}.developer-coffee b{left:13px;animation-delay:.45s}
.avatar-stage[data-coffee="receive"] .developer-coffee{opacity:1;left:62%;top:49%;transform:translate(-50%,-50%) scale(1)}
.avatar-stage[data-coffee="drink"] .developer-coffee{opacity:1;left:55%;top:30%;transform:translate(-50%,-50%) rotate(-12deg) scale(1)}
.avatar-stage[data-coffee="desk"] .developer-coffee{opacity:1;left:70%;top:70%;transform:translate(-50%,-50%) scale(.95)}
@keyframes developerCoffeeSteam{0%,100%{opacity:0;transform:translateY(2px)}50%{opacity:1;transform:translateY(-4px)}}
.fr[data-cargo="chocolate"] .fr-cargo{opacity:1}
.fr[data-cargo="chocolate"] .fr-cargo__choc{display:block;stroke:#c98a4b}
.fr-cargo__steam{stroke:var(--muted,#93a3b8);stroke-width:1;opacity:.8}

/* ---- the speech bubble ---------------------------------------------------- */
/* Anchored off the same coordinates as the body via --bx/--by, so following
   the flyingRobot costs two custom-property writes and zero rect reads. */
/* Centred above the flyingRobot's head, per the drag-to-explain spec, dropping
   below only when the flyingRobot is too near the top. The rise on open is what
   makes it feel spoken rather than switched on. */
.fr-say{
  position:fixed;
  left:var(--bx,50vw); top:var(--by,50vh);
  transform:translate(-50%,calc(-100% - 46px)) translateY(6px);
  width:max-content;
  max-width:min(310px,76vw);
  padding:.65rem .8rem;
  background:rgba(10,15,24,.96);
  border:1px solid var(--line-strong,rgba(255,255,255,.16));
  border-radius:14px;
  box-shadow:0 18px 44px -18px rgba(0,0,0,.85);
  font:500 .82rem/1.45 Inter,system-ui,sans-serif;
  color:var(--text,#f2f6fb);
  opacity:0;
  visibility:hidden;
  transition:opacity .25s,visibility .25s,transform .25s;
  pointer-events:none;
  z-index:71;
}
.fr-say::after{           /* the little tail pointing down at the flyingRobot */
  content:"";
  position:absolute;
  left:50%; bottom:-6px;
  width:12px; height:12px;
  transform:translateX(-50%) rotate(45deg);
  background:inherit;
  border-right:1px solid var(--line-strong,rgba(255,255,255,.16));
  border-bottom:1px solid var(--line-strong,rgba(255,255,255,.16));
}
.fr-say.is-open{opacity:1;visibility:visible;pointer-events:auto;transform:translate(-50%,calc(-100% - 46px))}
.fr-say.is-below{transform:translate(-50%,52px) translateY(-6px)}
.fr-say.is-below.is-open{transform:translate(-50%,52px)}
.fr-say.is-below::after{bottom:auto;top:-6px;border:none;border-left:1px solid var(--line-strong,rgba(255,255,255,.16));border-top:1px solid var(--line-strong,rgba(255,255,255,.16))}
.fr-say p{margin:0}
.fr-say__title{
  display:block;
  margin-bottom:.3rem;
  font:700 .84rem/1.3 Sora,Inter,system-ui,sans-serif;
  color:var(--accent,#2ee6a8);
}
.fr-say__title[hidden]{display:none}

.fr-say__opts{display:none;flex-wrap:wrap;gap:.35rem;margin-top:.55rem}
.fr-say.has-opts .fr-say__opts{display:flex}
.fr-say__opts button{
  font:600 .72rem/1 Inter,system-ui,sans-serif;
  color:var(--accent,#2ee6a8);
  background:var(--accent-soft,rgba(46,230,168,.12));
  border:1px solid rgba(46,230,168,.35);
  border-radius:999px;
  padding:.42rem .7rem;
  cursor:pointer;
  transition:background .2s,transform .2s;
}
.fr-say__opts button:hover{background:rgba(46,230,168,.22);transform:translateY(-1px)}
.fr-say__opts button:focus-visible{outline:2px solid var(--accent,#2ee6a8);outline-offset:2px}

/* ---- the instruction line ------------------------------------------------- */
/* The bubble's little sibling: same anchoring, quieter voice. Never up at the
   same time as the bubble — the JS enforces that. */
.fr-hint{
  position:fixed;
  left:var(--bx,50vw); top:var(--by,50vh);
  transform:translate(-50%,calc(-100% - 40px)) translateY(5px);
  width:max-content;
  max-width:min(250px,68vw);
  padding:.4rem .65rem;
  background:rgba(10,15,24,.92);
  border:1px solid rgba(46,230,168,.3);
  border-radius:999px;
  font:600 .72rem/1.35 Inter,system-ui,sans-serif;
  color:var(--accent,#2ee6a8);
  text-align:center;
  opacity:0;
  visibility:hidden;
  transition:opacity .3s,visibility .3s,transform .3s;
  pointer-events:none;
  z-index:71;
}
.fr-hint.is-open{opacity:1;visibility:visible;transform:translate(-50%,calc(-100% - 40px))}
.fr-hint.is-below{transform:translate(-50%,46px) translateY(-5px)}
.fr-hint.is-below.is-open{transform:translate(-50%,46px)}

/* ---- dragging ------------------------------------------------------------- */
/* The grip: enables the pointer gesture (touch-action stops the page from
   scrolling under a touch drag) and the held look. */
.fr{touch-action:none}
.fr.is-grabbed{cursor:grabbing}
.fr.is-grabbed .fr-svg{
  filter:drop-shadow(0 14px 18px rgba(0,0,0,.55));
  transform:scale(1.12);
  transition:transform .2s,filter .2s;
}
.fr[data-mood="DRAGGING"]{--flap:240ms}
.fr[data-mood="TARGET_DETECTED"]{--flap:190ms}
.fr[data-mood="TARGET_DETECTED"] .fr-eye{transform:scale(1.4)}
.fr[data-mood="SCANNING"] .fr-eye{transform:scale(1.35)}
.fr[data-mood="SCANNING"]{--flap:520ms}
.fr[data-mood="EXPLAINING"] .fr-eye{transform:scale(1.1)}
.fr[data-mood="RETURNING"]{--flap:300ms}

/* What the flyingRobot is being held over, when it is something explainable. */
.fr-drop-target{
  box-shadow:0 0 0 2px rgba(46,230,168,.6),0 6px 34px -6px rgba(46,230,168,.3) !important;
  border-radius:var(--radius-sm,12px);
  transition:box-shadow .2s;
}

/* ---- what "highlight" looks like on the host page ------------------------- */
.fr-highlight{
  animation:frHighlight 2.8s ease;
}
@keyframes frHighlight{
  0%,100%{box-shadow:0 0 0 0 rgba(46,230,168,0)}
  18%,60%{box-shadow:0 0 0 2px rgba(46,230,168,.55),0 8px 40px -8px rgba(46,230,168,.35)}
}

/* ---- reduced motion ------------------------------------------------------- */
/* The engine already parks the flyingRobot; this kills anything periodic that
   is left, keeps the bubble transitions instant, and that is the whole
   simplified experience — present, legible, still. */
@media (prefers-reduced-motion:reduce){
  .fr-thrust,.fr-scan{animation:none}
  .fr-trail__p{animation:none}
  .fr-zzz{animation:none}
  .fr-say{transition:none}
  .fr-hint{transition:none}
  .fr.is-grabbed .fr-svg{transition:none}
  .fr-highlight{animation:none;box-shadow:0 0 0 2px rgba(46,230,168,.55)}
}

/* ---- small screens -------------------------------------------------------- */
@media (max-width:700px){
  .fr-say{
    max-width:min(260px,78vw);
    font-size:.78rem;
  }
  .fr-say__opts button{padding:.5rem .75rem}   /* finger-sized */
}
