/* =========================================================================
   CRITTER GRAM — "THE DELIVERY"
   The page's signature contraption. One line is typed into the phone, the
   gram flies down the wire, a critter catches it and says it back wrong,
   and what it said leaves as a clip. Three critters take the same sentence
   per loop — same message in, three deliveries out.

   Bolted into the MOTION KIT housing (.stage / .stage-static in styles.css).
   Every selector and keyframe here is scoped/prefixed to this page.

   Loop: 13.5s — three 4.5s beats, one critter each. The message wipes and
   re-types itself under the last critter, so the wrap has nothing to hide.

   Beat, in seconds:
     0.00  picker chip is already lit, SEND flashes
     0.11  the gram leaves the phone and arcs down the wire
     0.35  the critter pops up behind the podium, nameplate with it
     1.06  the gram lands in the critter
     1.12  the bubble pops and the mangled line types itself out
     3.45  the clip pops out of the bubble and launches off frame
     3.90  the critter drops back down, everything clears
   ========================================================================= */

.stage--crittergram {
  --stage-h: clamp(252px, 45vw, 366px);
  --cg-loop: 13.5s;
  --cg-beat: 4.5s;             /* --cg-loop / 3 */
  --cg-fill: var(--field);     /* opaque body fill for the line art */
  --cg-fs-ui: clamp(0.58rem, 1.15vw, 0.9rem);
  --cg-fs-say: clamp(0.82rem, 2.1vw, 1.6rem);
  --cg-fs-name: clamp(0.64rem, 1.35vw, 1rem);

  /* stage geometry — the narrow branch at the bottom re-points all of it */
  --cg-mid: 58%;               /* podium / critter / nameplate centre line   */
  --cg-x0: 30%;                /* where the gram leaves the phone            */
  --cg-run: 16%;               /* how far it travels before the critter eats it */
  --cg-cx: 70%;                /* where the clip pops out of the bubble      */
  --cg-cy: 64%;
  --cg-crun: 22%;              /* how far right the clip flies before it's gone */
  --cg-crise: 40%;             /* and how far up                              */
}

/* ---- the spotlight the critters work under ----------------------------- */
.stage--crittergram .cg-beam {
  position: absolute;
  left: var(--cg-mid); top: 0;
  width: 40%; height: 80%;
  z-index: 1;
  transform: translateX(-50%);
  background: linear-gradient(to bottom, rgba(47, 228, 255, 0.16), rgba(168, 85, 255, 0.02) 78%, transparent);
  clip-path: polygon(40% 0%, 60% 0%, 100% 100%, 0% 100%);
  animation: cg-beam 5.4s ease-in-out infinite;
  pointer-events: none;
}
@keyframes cg-beam {
  0%, 100% { opacity: 0.75; }
  50%      { opacity: 1; }
}

.stage--crittergram .cg-podium {
  position: absolute;
  left: var(--cg-mid); bottom: 10.5%;
  width: 32%; height: 7.5%;
  z-index: 2;
  transform: translateX(-50%);
  border-radius: 50%;
  border: 1px solid rgba(47, 228, 255, 0.45);
  background: radial-gradient(60% 100% at 50% 40%, rgba(47, 228, 255, 0.22), rgba(10, 7, 30, 0.9) 78%);
  box-shadow: 0 0 26px rgba(47, 228, 255, 0.22), inset 0 0 16px rgba(168, 85, 255, 0.3);
}

/* ---- the phone: message in --------------------------------------------- */
.stage--crittergram .cg-phone {
  position: absolute;
  left: 2.5%; top: 50%;
  width: 27%;
  transform: translateY(-50%);
  z-index: 5;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 0.55em;
  padding: 0.85em 0.7em 0.7em;
  border: 1px solid var(--line-2);
  border-radius: 14px;
  background: linear-gradient(180deg, var(--panel-2), var(--panel));
  box-shadow: 0 10px 26px rgba(0, 0, 0, 0.5), inset 0 0 22px rgba(168, 85, 255, 0.1);
  font-family: var(--font-arcade);
  font-size: var(--cg-fs-ui);
}
.stage--crittergram .cg-phone::before {   /* earpiece slit */
  content: "";
  position: absolute; left: 50%; top: 0.5em;
  width: 26%; height: 3px;
  transform: translateX(-50%);
  border-radius: 99px;
  background: rgba(150, 120, 255, 0.35);
}
.stage--crittergram .cg-phone__label {
  color: var(--muted);
  letter-spacing: 0.16em;
  line-height: 1;
}

.stage--crittergram .cg-field {
  display: flex;
  align-items: center;
  gap: 0.15em;
  padding: 0.4em 0.45em;
  border: 1px solid var(--line-2);
  border-radius: 6px;
  background: var(--field);
  box-shadow: inset 0 0 10px rgba(47, 228, 255, 0.08);
  overflow: hidden;
}
.stage--crittergram .cg-field__txt {
  --cg-fw: 15ch;
  display: block;
  width: var(--cg-fw);
  overflow: hidden;
  white-space: nowrap;
  letter-spacing: 0;
  font-family: var(--font-body);
  font-size: 0.86em;
  color: var(--ink);
  animation: cg-field-type var(--cg-loop) steps(15, end) infinite;
}
.stage--crittergram .cg-field__caret {
  flex: none;
  width: 0.42em; height: 1em;
  background: var(--lime);
  box-shadow: var(--glow-l);
  animation: cg-caret 1.06s steps(2, end) infinite;
}
@keyframes cg-caret { 0%, 100% { opacity: 1; } 50% { opacity: 0.05; } }

/* Full for most of the loop; wiped and re-typed under the last critter, so
   the loop point lands on a field that is already full again. */
@keyframes cg-field-type {
  0%   { width: var(--cg-fw); animation-timing-function: steps(1, end); }
  85%  { width: var(--cg-fw); animation-timing-function: steps(1, end); }
  87%  { width: 0ch; }                 /* re-typed with the element's steps() */
  98%  { width: var(--cg-fw); animation-timing-function: steps(1, end); }
  100% { width: var(--cg-fw); }
}

/* the critter picker + the send key */
.stage--crittergram .cg-row {
  display: flex;
  align-items: center;
  gap: 0.4em;
}
.stage--crittergram .cg-chip {
  flex: none;
  width: 1.35em; height: 1.35em;
  border: 1px solid currentColor;
  border-radius: 4px;
  background: rgba(255, 255, 255, 0.05);
  box-shadow: 0 0 10px currentColor, inset 0 0 6px currentColor;
  opacity: 0.28;
  animation: cg-chip var(--cg-loop) var(--ease) infinite;
  animation-delay: var(--cg-d, 0s);
}
/* the chip is picked a beat-fraction early — you choose, then you send */
.stage--crittergram .cg-chip--chip { color: var(--cyan-t);    --cg-d: -0.7s; }
.stage--crittergram .cg-chip--duck { color: var(--magenta-t); --cg-d: -9.7s; }
.stage--crittergram .cg-chip--dodo { color: var(--lime-t);    --cg-d: -5.2s; }
/* Lit for its whole beat, and 0.35s of the beat before it: you pick, then
   you send. Handover between two chips is the only moment two are up. */
@keyframes cg-chip {
  0%, 1%      { opacity: 0.28; background: rgba(255, 255, 255, 0.05); transform: scale(1); }
  2.6%        { opacity: 1;    background: currentColor;              transform: scale(1.22); }
  4.4%, 35%   { opacity: 1;    background: currentColor;              transform: scale(1.06); }
  36.5%, 100% { opacity: 0.28; background: rgba(255, 255, 255, 0.05); transform: scale(1); }
}

.stage--crittergram .cg-send {
  margin-left: auto;
  padding: 0.2em 0.55em;
  border: 1px solid var(--lime);
  border-radius: 5px;
  color: var(--lime-t);
  letter-spacing: 0.12em;
  line-height: 1.25;
  white-space: nowrap;
  animation: cg-send var(--cg-beat) var(--ease) infinite;
}
@keyframes cg-send {
  0%, 8%   { background: rgba(182, 255, 58, 0.92); color: var(--bg); box-shadow: var(--glow-l); }
  20%,100% { background: transparent;              color: var(--lime-t); box-shadow: none; }
}

/* ---- the wire the gram travels ----------------------------------------- */
.stage--crittergram .cg-wire {
  position: absolute;
  left: var(--cg-x0);
  width: var(--cg-run);
  top: 49%;
  height: 2px;
  z-index: 1;
  background: repeating-linear-gradient(90deg,
    rgba(47, 228, 255, 0.55) 0 7px,
    rgba(47, 228, 255, 0) 7px 14px);
  mask-image: linear-gradient(90deg, transparent, #000 10%, #000 78%, transparent);
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 10%, #000 78%, transparent);
  animation: cg-wire 0.55s linear infinite;
}
@keyframes cg-wire { to { background-position: 14px 0; } }

/* The gram itself. It travels on `margin-left` (a layout property, so the
   flight stays on the main thread) and arcs with `transform`. */
.stage--crittergram .cg-pip {
  position: absolute;
  left: var(--cg-x0); top: 49%;
  width: clamp(17px, 2.4vw, 26px);
  z-index: 5;
  color: var(--lime-t);
  filter: drop-shadow(0 0 8px rgba(182, 255, 58, 0.8));
  animation: cg-pip var(--cg-beat) linear infinite;
}
.stage--crittergram .cg-pip svg,
.stage--crittergram .cg-card svg { display: block; width: 100%; height: auto; }
.stage--crittergram .cg-fill { fill: var(--cg-fill); }
@keyframes cg-pip {
  0%    { margin-left: 0;                            opacity: 0; transform: translate(-50%, -50%) scale(0.3); }
  2.5%  { margin-left: calc(var(--cg-run) * 0.05);   opacity: 1; transform: translate(-50%, -62%) scale(1); }
  8%    { margin-left: calc(var(--cg-run) * 0.26);   opacity: 1; transform: translate(-50%, -150%) scale(1.06) rotate(-8deg); }
  14%   { margin-left: calc(var(--cg-run) * 0.55);   opacity: 1; transform: translate(-50%, -172%) scale(1.02) rotate(4deg); }
  20%   { margin-left: calc(var(--cg-run) * 0.86);   opacity: 1; transform: translate(-50%, -86%) scale(0.94) rotate(10deg); }
  23.5% { margin-left: var(--cg-run);                opacity: 0; transform: translate(-50%, -46%) scale(0.34) rotate(14deg);
          animation-timing-function: steps(1, end); }
  100%  { margin-left: var(--cg-run);                opacity: 0; transform: translate(-50%, -46%) scale(0.34) rotate(14deg); }
}

/* ---- the critters ------------------------------------------------------ */
.stage--crittergram .cg-critter {
  position: absolute;
  left: var(--cg-mid); bottom: 13.5%;
  height: 48%;
  aspect-ratio: 1;
  z-index: 4;
  opacity: 0;
  animation: cg-critter var(--cg-loop) var(--ease) infinite;
  animation-delay: var(--cg-d, 0s);
}
.stage--crittergram .cg-critter__bob {
  width: 100%; height: 100%;
  animation: cg-bob 1.7s ease-in-out infinite;
}
.stage--crittergram .cg-art {
  display: block;
  width: 100%; height: 100%;
  fill: var(--cg-fill);
  stroke: currentColor;
  filter: drop-shadow(0 0 9px currentColor);
}
.stage--crittergram .cg-solid { fill: currentColor; stroke: none; }
.stage--crittergram .cg-teeth { fill: var(--lime-t); stroke: none; }
.stage--crittergram .cg-dark  { fill: var(--panel-2); }

/* pops up from behind the podium to catch the gram, holds, drops back */
@keyframes cg-critter {
  0%, 2.6%    { bottom: -3%;   opacity: 0; transform: translateX(-50%) scale(0.86); }
  4.1%        { bottom: 17.5%; opacity: 1; transform: translateX(-50%) scale(1.06); }
  5.6%, 28.9% { bottom: 13.5%; opacity: 1; transform: translateX(-50%) scale(1); }
  30.4%       { bottom: 16%;   opacity: 1; transform: translateX(-50%) scale(1.02); }
  32.2%       { bottom: -2%;   opacity: 0; transform: translateX(-50%) scale(0.9); }
  100%        { bottom: -3%;   opacity: 0; transform: translateX(-50%) scale(0.86); }
}
@keyframes cg-bob {
  0%, 100% { transform: translateY(1.5%) rotate(-1.2deg); }
  50%      { transform: translateY(-1.5%) rotate(1.2deg); }
}

/* mouths. Each animal runs its own jaw at its own rate — the tape-speed
   trick, drawn. */
.stage--crittergram .cg-mouth {
  transform-box: fill-box;
  transform-origin: 50% 0%;
  animation: cg-gape 0.16s ease-in-out infinite alternate;
}
.stage--crittergram .cg-jaw {
  transform-box: fill-box;
  transform-origin: 2% 4%;
  animation: cg-jaw 0.26s ease-in-out infinite alternate;
}
@keyframes cg-gape { from { transform: scaleY(0.16); } to { transform: scaleY(1); } }
@keyframes cg-jaw  { from { transform: rotate(0deg); } to { transform: rotate(var(--cg-open, 17deg)); } }

.stage--crittergram .cg-critter--chip { color: var(--cyan-t);    --cg-d: 0s; }
.stage--crittergram .cg-critter--duck { color: var(--magenta-t); --cg-d: -9s; }
.stage--crittergram .cg-critter--dodo { color: var(--lime-t);    --cg-d: -4.5s; }
.stage--crittergram .cg-critter--duck .cg-jaw { animation-duration: 0.22s; --cg-open: 12deg; }
.stage--crittergram .cg-critter--dodo .cg-jaw { animation-duration: 0.34s; --cg-open: 19deg; }

/* ---- what comes out ---------------------------------------------------- */
.stage--crittergram .cg-say {
  position: absolute;
  left: 62%; bottom: var(--cg-y, 60%);
  --cg-y: 60%;
  z-index: 5;
  opacity: 0;
  transform-origin: 0% 100%;
  animation: cg-say var(--cg-loop) var(--ease) infinite;
  animation-delay: var(--cg-d, 0s);
}
.stage--crittergram .cg-say__box {
  position: relative;
  min-width: 1.4em;
  padding: 0.3em 0.55em 0.34em;
  border: 2px solid currentColor;
  border-radius: 10px;
  background: var(--cg-fill);
  box-shadow: 0 0 16px currentColor, inset 0 0 12px rgba(255, 255, 255, 0.06);
  animation: cg-wobble 1.15s ease-in-out infinite;
}
.stage--crittergram .cg-say__box::after {   /* tail, aimed back at the mouth */
  content: "";
  position: absolute;
  left: 0.55em; bottom: -0.42em;
  width: 0.8em; height: 0.8em;
  background: var(--cg-fill);
  border-left: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  border-bottom-left-radius: 3px;
  transform: rotate(45deg);
}
.stage--crittergram .cg-say__txt {
  display: block;
  width: var(--cg-w, 15ch);
  overflow: hidden;
  white-space: nowrap;
  letter-spacing: 0;
  font-family: var(--font-arcade);
  font-size: var(--cg-fs-say);
  line-height: 1.15;
  color: currentColor;
  text-shadow: 0 0 12px currentColor;
  animation: cg-say-type var(--cg-loop) linear infinite;
  animation-delay: var(--cg-d, 0s);
}
@keyframes cg-say {
  0%, 8.15%  { bottom: calc(var(--cg-y) - 6%);   opacity: 0; transform: scale(0.4); }
  9.6%       { bottom: calc(var(--cg-y) + 1.5%); opacity: 1; transform: scale(1.06); }
  11%, 28.5% { bottom: var(--cg-y);              opacity: 1; transform: scale(1); }
  30.4%      { bottom: calc(var(--cg-y) + 4%);   opacity: 0; transform: scale(0.9); }
  100%       { bottom: calc(var(--cg-y) - 6%);   opacity: 0; transform: scale(0.4); }
}
@keyframes cg-wobble {
  0%, 100% { transform: rotate(-1deg) translateY(0); }
  50%      { transform: rotate(1.1deg) translateY(-3%); }
}
/* The words arrive one at a time, like they are being said. The first letter
   lands while the bubble is still popping open, so it is never seen empty. */
@keyframes cg-say-type {
  0%    { width: 0ch; animation-timing-function: steps(1, end); }
  8.3%  { width: 0ch; }                /* typed with the element's steps() */
  23.3% { width: var(--cg-w); animation-timing-function: steps(1, end); }
  100%  { width: var(--cg-w); }
}
.stage--crittergram .cg-say--chip { color: var(--cyan-t);    --cg-d: 0s;    --cg-w: 15ch; }
.stage--crittergram .cg-say--duck { color: var(--magenta-t); --cg-d: -9s;   --cg-w: 18ch; }
.stage--crittergram .cg-say--dodo { color: var(--lime-t);    --cg-d: -4.5s; --cg-w: 16ch; }
.stage--crittergram .cg-say--chip .cg-say__txt { animation-timing-function: steps(15, end); }
.stage--crittergram .cg-say--duck .cg-say__txt { animation-timing-function: steps(18, end); }
.stage--crittergram .cg-say--dodo .cg-say__txt { animation-timing-function: steps(16, end); }

/* ---- and out it goes as a clip ----------------------------------------- */
/* Same gesture the factory conveyor ends on: the finished thing launches
   out of frame. Travels on margin-left/bottom so it stays photographable. */
.stage--crittergram .cg-card {
  position: absolute;
  left: var(--cg-cx); bottom: var(--cg-cy);
  width: clamp(20px, 3vw, 34px);
  z-index: 6;
  opacity: 0;
  color: var(--lime-t);
  filter: drop-shadow(0 0 9px rgba(182, 255, 58, 0.8));
  animation: cg-card var(--cg-beat) var(--ease) infinite;
}
/* On the beat clock, so every critter's turn ends with a clip going out. */
@keyframes cg-card {
  0%, 75% { margin-left: 0;                           bottom: var(--cg-cy);
            opacity: 0; transform: translate(-50%, 50%) scale(0.2) rotate(-10deg); }
  78.5%   { margin-left: calc(var(--cg-crun) * 0.05); bottom: calc(var(--cg-cy) + 3%);
            opacity: 1; transform: translate(-50%, 50%) scale(1.18) rotate(-5deg); }
  82%     { margin-left: calc(var(--cg-crun) * 0.14); bottom: calc(var(--cg-cy) + 7%);
            opacity: 1; transform: translate(-50%, 50%) scale(1) rotate(0deg); }
  90%     { margin-left: calc(var(--cg-crun) * 0.62); bottom: calc(var(--cg-cy) + var(--cg-crise) * 0.6);
            opacity: 0.9; transform: translate(-50%, 50%) scale(0.86) rotate(11deg); }
  97%     { margin-left: var(--cg-crun);              bottom: calc(var(--cg-cy) + var(--cg-crise));
            opacity: 0; transform: translate(-50%, 50%) scale(0.55) rotate(18deg);
            animation-timing-function: steps(1, end); }
  100%    { margin-left: 0;                           bottom: var(--cg-cy);
            opacity: 0; transform: translate(-50%, 50%) scale(0.2) rotate(-10deg); }
}

/* ---- who is currently on the podium ------------------------------------ */
.stage--crittergram .cg-name {
  position: absolute;
  left: var(--cg-mid); bottom: 2.6%;
  z-index: 5;
  opacity: 0;
  white-space: nowrap;
  transform: translateX(-50%);
  font-family: var(--font-arcade);
  font-size: var(--cg-fs-name);
  letter-spacing: 0.14em;
  line-height: 1;
  color: currentColor;
  text-shadow: 0 0 12px currentColor;
  animation: cg-name var(--cg-loop) var(--ease) infinite;
  animation-delay: var(--cg-d, 0s);
}
@keyframes cg-name {
  0%, 3.4%    { bottom: 0.2%; opacity: 0; }
  5.4%, 29%   { bottom: 2.6%; opacity: 1; }
  31.5%, 100% { bottom: 4.8%; opacity: 0; }
}
.stage--crittergram .cg-name--chip { color: var(--cyan-t);    --cg-d: 0s; }
.stage--crittergram .cg-name--duck { color: var(--magenta-t); --cg-d: -9s; }
.stage--crittergram .cg-name--dodo { color: var(--lime-t);    --cg-d: -4.5s; }

/* ---- narrow frames: shorter housing, tighter phone, bubble pulled in ---- */
@media (max-width: 620px) {
  .stage--crittergram {
    --stage-h: clamp(268px, 68vw, 320px);
    --cg-mid: 60%;
    --cg-x0: 37%;
    --cg-run: 12%;
    --cg-cx: 70%;
    --cg-cy: 70%;
    --cg-crun: 30%;
    --cg-crise: 26%;
  }
  .stage--crittergram .cg-phone { width: 35%; left: 2%; padding: 0.8em 0.5em 0.6em; gap: 0.45em; }
  .stage--crittergram .cg-critter { height: 40%; }
  .stage--crittergram .cg-podium { width: 34%; }
  /* Pulled well left of the wall: the longest line still clears the housing
     if the arcade webfont has not landed yet and the wider fallback is up. */
  .stage--crittergram .cg-say { left: 50%; --cg-y: 62%; }
  .stage--crittergram .cg-name { font-size: 0.62rem; letter-spacing: 0.08em; }
  /* A 358px housing has room for one placard, the nameplate and the clip's
     exit — not for all four. The second placard stands down: the bottom rail
     goes to the nameplate (which the fallback font makes a good deal wider)
     and the top-right corner to the clip on its way out. */
  .stage--crittergram .stage__tag { font-size: 0.8rem; }
  .stage--crittergram .stage__tag--end { display: none; }
}

/* =========================================================================
   The still twin (reduced motion). The kit paints the frame; this only
   colours the steps in.
   ========================================================================= */
.stage-static--crittergram .cg-step--a { color: var(--ink); }
.stage-static--crittergram .cg-step--b { color: var(--cyan-t); }
.stage-static--crittergram .cg-step--c { color: var(--magenta-t); }
.stage-static--crittergram .cg-step--d { color: var(--lime-t); }
.stage-static--crittergram .stage-static__step svg {
  fill: var(--field);
  stroke: currentColor;
  filter: drop-shadow(0 0 7px currentColor);
}
.stage-static--crittergram .cg-solid { fill: currentColor; stroke: none; }
.stage-static--crittergram .cg-teeth { fill: var(--lime-t); stroke: none; }
