/* ============================================================
   Ctrl Digital — figures

   The host box is the frame; the runtime drops one <svg> inside it,
   absolutely filling it. The four custom properties below are the entire
   interface between the page and the generators — set them differently and
   the same maths comes out in a different key.

   The defaults reach for the site's own tokens first (--ink, --green,
   --purple, …) and fall back to the brand values, so the figures look right
   on a page that has none of them.
   ============================================================ */
ctrl-figure{ display:block; aspect-ratio:var(--fig-ratio, 4 / 3); }
/* an exported still: the same frame, holding an <img> */
.fig--still{ aspect-ratio:var(--fig-ratio, 4 / 3); }

.fig{
  position:relative; overflow:hidden;
  background:var(--fig-bg);
  --fig-line: var(--ink, #0d0b1c);
  --fig-accent: var(--green, #28f7a8);
  --fig-accent-2: var(--purple, #5620db);
  --fig-bg: #f0eee7;
}
.fig--dark{
  --fig-line: #c9d6ff;        /* pale periwinkle: reads as a lit hairline, not as chalk */
  --fig-accent: var(--green, #28f7a8);
  --fig-accent-2: var(--purple-lift, #7a4dff);
  --fig-bg: var(--dark, #08060f);
}
/* the heroes' key. Green at full brightness sits at 1.4:1 on white, so the
   form takes --green-deep. The seam between the two accents is ink rather
   than periwinkle: on a light ground it reads as the darkest edge of the
   form rather than its lit one. */
.fig--hero{
  --fig-line: var(--ink, #0d0b1c);
  --fig-accent: var(--green-deep, #0aa46f);
  --fig-accent-2: var(--purple, #5620db);
  --fig-bg: var(--hero-ground, #fbfbf9);
}
/* drawn inline by the runtime (or shipped as an exported SVG), a few with a
   canvas over the svg for their moving parts. Either way the host has its size and
   background from CSS, so the layout never waits on them. */
.fig img,
.fig svg,
.fig canvas{ position:absolute; inset:0; width:100%; height:100%; display:block; }
.fig canvas{ pointer-events:none; }

/* --- figure motion ---
   Only for figures drawn in the page: an exported SVG has its motion frozen. All of it is CSS transform or
   stroke offset, parked until the figure is on screen (.is-live, .is-entered,
   set by the runtime) and slow enough to be noticed only on a second look.
   .is-paused on a host holds all of it, the rAF-driven figures included. */
.fig-spin{ animation-play-state:paused; }
.fig.is-live .fig-spin{ animation-play-state:running; }

@keyframes fig-spin{ to{ transform:rotate(360deg); } }
.fig-spin{ animation:fig-spin 300s linear infinite; }

/* the measured route draws itself once, when you first reach it */
.fig-draw{ stroke-dasharray:100; stroke-dashoffset:100; }
.fig.is-entered .fig-draw{ animation:fig-draw 2.4s cubic-bezier(.35,0,.15,1) forwards; }
@keyframes fig-draw{ to{ stroke-dashoffset:0; } }

/* the tracked route on a loop: drawn (0–28%), held, taken back from its start
   (60–88%), a beat of nothing, again. Each observation shows from when the
   head reaches it until the tail does: the same keyframes for every mark, its
   delay set in builders/site/trace.js. Linear throughout, or the marks lose time. */
.fig-route{ stroke-dasharray:100; stroke-dashoffset:100; }
.fig-mark{ opacity:0; }
.fig.is-entered .fig-route{ animation:fig-route 10s linear infinite; }
.fig.is-entered .fig-mark{ animation:fig-mark 10s linear infinite both; }
@keyframes fig-route{ 28%, 60%{ stroke-dashoffset:0; } 88%, 100%{ stroke-dashoffset:-100; } }
@keyframes fig-mark{ 0%{ opacity:0; } 1.5%, 60%{ opacity:1; } 61.5%, 100%{ opacity:0; } }
.fig.is-entered:not(.is-live) .fig-route,
.fig.is-entered:not(.is-live) .fig-mark{ animation-play-state:paused; }

.fig.is-paused .fig-spin,
.fig.is-paused .fig-route,
.fig.is-paused .fig-mark{ animation-play-state:paused; }
.fig.is-paused .fig-draw{ animation:none; stroke-dashoffset:0; }

@media (prefers-reduced-motion:reduce){
  .fig-draw{ stroke-dashoffset:0; }
  .fig-route{ animation:none !important; stroke-dashoffset:0; }
  .fig-mark{ animation:none !important; opacity:1; }
}
