/* ==========================================================================
   beeline.css — the Beeline case: eight agent services, and what wakes them.

   The picture is a tree: the client at the top, a branch down to each of the
   eight services, and every leaf of it a card. A card carries its name, the
   content that service reads, and its agent in a frame — a head and a small
   body, no arms and no legs, because a service is a process in a container
   and anything it could walk on would be a lie about it.

   Division of labour, the same one every other figure here uses:

     this file            the frame and the look
     beeline-timeline.css where everything stands and when it happens, both
                          generated from the one schedule in tools/beeline.py

   Two rules carried over, both learned the hard way on the agent-platforms
   figure:

     · One engine. Every animation is a CSS keyframe on one period, motion
       included — the paths are sampled in Python and emitted as
       `transform: translate()`, which works because a CSS transform inside an
       SVG measures in user units. SMIL would be a second engine, and two
       engines drift apart on a backgrounded tab.
     · A box placed against another box has to stay positioned when the layout
       collapses, or its absolute children escape to the stage.

   Under 1000px it stops being a picture: the tree goes and the eight cards
   become a list in reading order. That state and prefers-reduced-motion share
   one block at the foot of this file — both want every service awake and
   still, because a figure that has stopped moving should show the estate
   running, not the moment before it started.
   ========================================================================== */

.bee {
  --bee-cycle: 22s;          /* beeline-timeline.css sets the real ones */
  --bee-ratio: 1440 / 660;
  margin: var(--s-5) 0 var(--s-6);
}
.bee, .bee figure { margin-inline: 0; }

/* --- the caption ---------------------------------------------------------- */
.bee__cap {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--s-2);
  margin-bottom: var(--s-4);
}
.bee__h {
  font-weight: 640; font-size: var(--fs-md); letter-spacing: -.015em;
  color: var(--ink);
}
.bee__note {
  flex: 1 1 22ch; min-width: 0;
  font-size: var(--fs-3xs); line-height: 1.45; color: var(--ink-3);
}

/* --- the stage ------------------------------------------------------------ */
.bee__body { position: relative; aspect-ratio: var(--bee-ratio); }
.bee__art {
  position: absolute; inset: 0; z-index: 1;
  width: 100%; height: 100%; pointer-events: none; overflow: visible;
}

.bee__tag {
  position: absolute;
  font-family: var(--font-mono); font-size: var(--fs-3xs);
  letter-spacing: .04em; text-transform: lowercase;
  color: var(--ink-3); white-space: nowrap;
}
.bee__tag--client {
  left: var(--x); top: var(--y); transform: translateX(-50%);
  text-align: center;
}

/* --- the client, and the tree from it ------------------------------------- */
.bee-client { fill: var(--c, var(--c-blue)); }
.bee-client__head, .bee-client__body { fill: inherit; }

.bee-stem {
  fill: none; stroke: var(--sc, var(--c, var(--c-blue))); stroke-width: 1.4;
  stroke-linecap: round; opacity: .12;
  vector-effect: non-scaling-stroke;
}

/* The signal that wakes a service. Bright, because it is the only thing on
   this picture that arrives from outside it. */
.bee-wake__dot {
  fill: var(--sc, var(--c-blue));
  filter: drop-shadow(0 0 6px var(--sc, var(--c-blue)));
}
.bee-wake { opacity: 0; }

/* And the answer coming back up it. Smaller and softer than the signal that
   went down: a command is an event, a report is a habit. */
.bee-back { opacity: 0; }
.bee-back__dot {
  fill: var(--sc, var(--c-blue));
  filter: drop-shadow(0 0 4px color-mix(in oklab, var(--sc, var(--c-blue)) 55%, transparent));
}

/* --- a card --------------------------------------------------------------- */
/* Placed rather than laid out, so a card sits under its own branch. The name
   and the source are HTML; everything inside the frame is drawing. */
/* Over the drawing. This layer went *under* it when a card was still an
   opaque box that would have hidden its own contents; now it is nothing but
   two captions with no background of their own, and they have to be over the
   boxes they name — the source's caption is written inside its box, and a
   filled rect drawn on top of it wins. */
.bee__cards { position: absolute; inset: 0; z-index: 2; }
/* The name is a caption over the pair, not a box around it: the two boxes
   below are the objects, and a third outline around both would say they are
   one thing. */
.bee-card {
  position: absolute; left: var(--x); top: var(--y);
  width: var(--w); height: var(--h);
  display: grid; align-content: center; justify-items: center;
  padding: 0 6px;
  opacity: .28;
}
.bee-card__n {
  font-weight: 640; font-size: var(--fs-2xs); letter-spacing: -.01em;
  line-height: 1.2; color: var(--ink); text-align: center;
}
/* What this service reads, named — inside the box that shows it, over the
   picture, so the words and the picture are one object. */
.bee-srcname {
  position: absolute; left: var(--x); top: var(--y);
  width: var(--w); height: var(--h);
  display: grid; align-content: center; justify-items: center;
  padding: 0 6px;
  font-family: var(--font-mono); font-size: var(--fs-3xs);
  letter-spacing: -.01em; line-height: 1.3;
  color: var(--ink); text-align: center;
  opacity: 0;
}

/* A clover names its shared content once, under the hub. Each of its three
   members also carries that name for the list below 1000px, where there is no
   middle to write it in; on the drawing that copy is not there at all. */
.bee-srcname--shared { display: none; }
.bee-hubname {
  position: absolute; left: var(--x); top: var(--y);
  width: var(--w); height: var(--h);
  display: grid; align-content: start; justify-items: center;
  padding: 0 4px;
  font-family: var(--font-mono); font-size: var(--fs-3xs);
  letter-spacing: -.01em; line-height: 1.3;
  color: var(--ink); text-align: center;
  opacity: 0;
}

/* --- what a clover reads --------------------------------------------------- */
/* One content, three agents around it. It gets no service colour: it is not
   any one of theirs, and painting it one of the three would say it was. */
.bee-hubbox { opacity: .22; }
.bee-hubbox__box {
  fill: color-mix(in oklab, var(--ink) 5%, var(--surface));
  stroke: color-mix(in oklab, var(--ink) 22%, var(--line));
  stroke-width: 1.2;
}
.bee-hub { opacity: 0; color: var(--ink-2); }
.bee-store__body, .bee-store__lid {
  fill: color-mix(in oklab, var(--bg) 72%, currentColor);
  stroke: currentColor; stroke-width: 1.2;
}
.bee-store__ring {
  fill: none; stroke: currentColor; stroke-width: 1.1; opacity: .55;
}

/* --- the content a service reads ------------------------------------------ */
/* Its own box. What a service reads belongs to somebody else — a competitor's
   site, a customer's call, a colleague's document — so it is drawn as a plain
   panel, and the agent's own box below it is dashed like the container it is. */
.bee-srcbox { opacity: .22; }
.bee-srcbox__box {
  fill: color-mix(in oklab, var(--sc, var(--c-blue)) 7%, var(--surface));
  stroke: color-mix(in oklab, var(--sc, var(--c-blue)) 28%, var(--line));
  stroke-width: 1.2;
}
/* The reading: a short run from the one box to the other. It is the only
   thing joining them, which is the point of their being two. */
.bee-link {
  fill: none; stroke: var(--sc, var(--c-blue)); stroke-width: 1.4;
  stroke-linecap: round; stroke-dasharray: 3 4; opacity: 0;
  vector-effect: non-scaling-stroke;
}

.bee-src { opacity: 0; color: var(--sc, var(--c-blue)); }
.bee-src__box {
  fill: color-mix(in oklab, var(--bg) 66%, currentColor);
  stroke: currentColor; stroke-width: 1.1;
}
.bee-src__ink {
  fill: none; stroke: currentColor; stroke-width: 1.5;
  stroke-linecap: round; stroke-linejoin: round; opacity: .85;
}
.bee-src__wave { stroke-width: 2.2; }
.bee-src__rule { stroke-width: 2.6; }
.bee-src__seal { fill: currentColor; }

/* --- the agent, in its frame ---------------------------------------------- */
/* The frame is the container the case argues about: eight services, eight
   isolated contours, one failure that cannot reach the next. Dashed, because
   it is a boundary rather than a box of ours. */
.bee-frame { opacity: .25; }
.bee-frame__box {
  fill: color-mix(in oklab, var(--sc, var(--c-blue)) 9%, transparent);
  stroke: color-mix(in oklab, var(--sc, var(--c-blue)) 45%, var(--line-strong));
  stroke-width: 1.2; stroke-dasharray: 5 4;
}

.bee-bots { opacity: 0; color: var(--sc, var(--c-blue)); }
.bee-bot__head {
  fill: color-mix(in oklab, #000 16%, currentColor);
  stroke: currentColor; stroke-width: 1.2;
}
.bee-bot__body {
  fill: color-mix(in oklab, #000 34%, currentColor);
  stroke: currentColor; stroke-width: 1.1;
}
.bee-bot__eye { fill: var(--bg); }
.bee-bot__ant, .bee-bot__slot {
  fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round;
}
.bee-bot__slot { opacity: .5; }
.bee-bot__tip, .bee-bot__tipsq { fill: currentColor; }

/* The parts that tell one agent from another: what it looks with, what it
   listens with, what it carries. */
.bee-bot__visor {
  fill: var(--bg); stroke: currentColor; stroke-width: 1.1;
}
.bee-bot__lens {
  fill: color-mix(in oklab, var(--bg) 55%, currentColor);
  stroke: currentColor; stroke-width: 1.4;
}
.bee-bot__dish, .bee-bot__band, .bee-bot__link {
  fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round;
}
.bee-bot__link { stroke-width: 1.6; opacity: .6; }
.bee-bot__ear, .bee-bot__sub {
  fill: color-mix(in oklab, #000 26%, currentColor);
  stroke: currentColor; stroke-width: 1.2;
}
.bee-bot__sub { opacity: .75; }
.bee-bot__slotbox {
  fill: var(--bg); stroke: currentColor; stroke-width: 1.1; opacity: .8;
}
/* The monitoring agent's glass, worn on the chest. */
.bee-bot__glass {
  fill: color-mix(in oklab, var(--bg) 45%, currentColor);
  stroke: currentColor; stroke-width: 1.6;
}
.bee-bot__grip {
  fill: none; stroke: currentColor; stroke-width: 2.4; stroke-linecap: round;
}

/* What a working service looks like: one packet at a time out of its own
   content and into its agent. */
.bee-feed { opacity: 0; }
.bee-feed__dot {
  fill: var(--sc, var(--c-blue));
  filter: drop-shadow(0 0 5px color-mix(in oklab, var(--sc, var(--c-blue)) 60%, transparent));
}

/* --- one clock ------------------------------------------------------------ */
/* Every animation on the figure runs the same pass, motion included. */
.bee-stem, .bee-wake, .bee-back, .bee-card, .bee-src, .bee-srcname,
.bee-srcbox, .bee-link, .bee-frame, .bee-bots, .bee-feed, .bee-bot__eye,
.bee-hubbox, .bee-hub, .bee-hubname {
  animation-duration: var(--bee-cycle);
  animation-timing-function: var(--ease-out);
  animation-iteration-count: infinite;
  animation-fill-mode: both;
}
.bee-wake, .bee-back, .bee-feed { animation-timing-function: linear; }

/* --------------------------------------------------------------------------
   Below 1000px the picture is a list.

   Eight cards side by side need eight columns; there is room for two, and a
   tree drawn over a stack says nothing. So the drawing goes and the cards
   become what they always were — eight named services, each with the thing it
   reads written under it.
   -------------------------------------------------------------------------- */
@media (max-width: 1000px) {
  .bee__body {
    aspect-ratio: auto;
    display: grid; gap: var(--s-2);
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
  .bee__art { display: none; }
  .bee__tag--client { position: static; transform: none; grid-column: 1 / -1; }
  .bee__cards {
    position: static; grid-column: 1 / -1;
    display: grid; gap: var(--s-2);
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
  /* `relative`, not `static`: a card is a positioning context for nothing
     today, and will be for something tomorrow — and the block that learned
     this the hard way is one figure over. */
  .bee-card {
    position: relative; left: auto; top: auto;
    width: auto; height: auto; min-height: 0;
    align-content: center; justify-items: start;
    padding: var(--s-3);
  }
  .bee-card__n { text-align: left; }
  .bee-srcname {
    position: static; width: auto; height: auto; opacity: 1;
    justify-items: start; padding: 0; text-align: left;
  }
  .bee-srcname--shared { display: grid; }
  .bee-hubname { display: none; }
}

@media (max-width: 620px) {
  .bee__body, .bee__cards { grid-template-columns: minmax(0, 1fr); }
}

/* --------------------------------------------------------------------------
   The estate, awake and still — for the two contexts that want a picture that
   has stopped. Not the moment before the signal: a dark grid of eight boxes
   is the one frame of this story that says nothing.
   -------------------------------------------------------------------------- */
@media (max-width: 1000px), (prefers-reduced-motion: reduce) {
  .bee-wake, .bee-back, .bee-feed { display: none; }
  .bee-stem, .bee-card, .bee-src, .bee-srcname, .bee-srcbox, .bee-link,
  .bee-frame, .bee-bots, .bee-bot__eye, .bee-hubbox, .bee-hub,
  .bee-hubname {
    animation: none;
  }
  .bee-hub, .bee-hubname { opacity: 1; }
  .bee-hubbox { opacity: .7; }
  .bee-card, .bee-src, .bee-srcname, .bee-bots, .bee-bot__eye { opacity: 1; }
  .bee-srcbox { opacity: .7; }
  .bee-link { opacity: .5; }
  .bee-frame { opacity: .8; }
  .bee-stem { opacity: .3; }
}
