/* Animated figures for "Novel Behavior Composition via Skill-Level World Models", drawn by
   /assets/js/robotics-figures.20261014.js. Green, the site's accent, is deliberation: world models,
   imagined states and search. Coral is execution by the skill policies. Ink is the world itself.
   Every colour is a site theme token, so both themes work. */

.rf-art {
  --rf-label: "DM Mono", "SFMono-Regular", Consolas, monospace;
  --rf-sans: "Helvetica Neue", Helvetica, Arial, sans-serif;
  --rf-serif: Georgia, "Times New Roman", serif;
  --rf-sage: rgb(var(--field-glow-rgb));
  --rf-likely: color-mix(in srgb, var(--rf-sage) 64%, var(--ink));
  --rf-wrong: #c45a4a;
  --rf-right: #356aa2;
  --rf-lit: #f39a87;
  max-width: var(--rf-w, none);
  margin: 0 auto;
}
/* As on the homepage, the graphics keep their hues in the dark theme, lightened for a dark ground. */
@media screen {
  html[data-theme="dark"] .rf-art {
    --coral: #eb8b61;
    --accent: #75b68c;
    --accent-text: #94cda7;
    --field-glow-rgb: 181,194,132;
    --rf-likely: var(--rf-sage);
    --rf-wrong: #e88a7a;
    --rf-right: #83b6ef;
    --rf-lit: #ffc8bc;
  }
}
.rf-svg {
  display: block;
  width: 100%;
  height: auto;
  overflow: visible;
  fill: none;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* Labels, drawn in the graphic's own units: a name in mono capitals, a note in the heading face,
   and variables in the serif italic. */
.rf-tag { fill: var(--ink); stroke: none; font: 400 11px/1 var(--rf-label); letter-spacing: 1.1px; }
.rf-tag--sm { font-size: 9.5px; letter-spacing: 1px; }
.rf-tag--muted { fill: var(--muted-text); }
.rf-tag--search { fill: var(--accent-text); }
.rf-tag--policy { fill: var(--coral); }
.rf-tag--zone { fill: color-mix(in srgb, var(--rf-sage) 55%, var(--ink)); }
.rf-note { fill: var(--muted-text); stroke: none; font: 400 12.5px/1 var(--rf-sans); letter-spacing: 0; }
.rf-note--sm { font-size: 11.5px; }
.rf-note--search { fill: var(--accent-text); }
.rf-math { fill: var(--ink); stroke: none; font: italic 400 19px/1 var(--rf-serif); }
.rf-math--sm { font-size: 15px; }
.rf-math--search { fill: var(--accent-text); }
.rf-math--policy { fill: var(--coral); }
.rf-formula { fill: var(--ink); stroke: none; font: 400 14px/1 var(--rf-sans); }
.rf-var { font-family: var(--rf-serif); font-style: italic; font-size: 1.22em; letter-spacing: 0; }
.rf-sub { font-family: var(--rf-sans); font-style: normal; font-size: 0.64em; letter-spacing: 0; }
.rf-sup { font-size: 0.58em; letter-spacing: 0; }
.rf-tag .rf-sub { font-size: 0.8em; }
.rf-tag .rf-var { font-size: 1.3em; }

/* Ticks and crosses: a tick in green; a cross in the ink of the world, for what it would not do. */
.rf-yes circle { fill: var(--paper); stroke: var(--accent); stroke-width: 1.5; }
.rf-yes path { fill: none; stroke: var(--accent); stroke-width: 1.8; }
.rf-no circle { fill: var(--paper); stroke: var(--ink); stroke-opacity: 0.55; stroke-width: 1.3; }
.rf-no path { fill: none; stroke: var(--ink); stroke-opacity: 0.7; stroke-width: 1.5; }

/* Figure 1, the hierarchy: a tree of pills and fans, a timeline, and the boundary between them. */
.rf-edge { stroke: var(--ink); stroke-opacity: 0.45; stroke-width: 1.2; }
.rf-wedge { fill: var(--ink); fill-opacity: 0.05; }
.rf-fan { stroke: var(--ink); stroke-opacity: 0.2; stroke-width: 0.8; }
.rf-hl { stroke: var(--ink); stroke-width: 2.4; }
.rf-hl--search { stroke: var(--accent); }
.rf-hl--policy { stroke: var(--coral); }
.rf-pill { fill: var(--paper); stroke: var(--ink); stroke-opacity: 0.5; stroke-width: 1.1; }
.rf-pill-hl { stroke: var(--ink); stroke-width: 2; }
.rf-pill-hl--search { fill: color-mix(in srgb, var(--accent) 12%, transparent); stroke: var(--accent); }
.rf-axis { stroke: var(--ink); stroke-opacity: 0.5; stroke-width: 1; }
.rf-row { fill: var(--ink); fill-opacity: 0.08; stroke: var(--ink); stroke-opacity: 0.5; stroke-width: 1; }
.rf-row--search { fill: var(--accent); fill-opacity: 0.14; stroke: var(--accent); stroke-opacity: 0.9; }
.rf-trace { stroke: var(--ink); stroke-opacity: 0.7; stroke-width: 1.2; }
.rf-trace--policy { stroke: var(--coral); stroke-opacity: 1; }
.rf-playhead { stroke: var(--ink); stroke-opacity: 0.45; stroke-width: 1; stroke-dasharray: 2 3; }
.rf-boundary { stroke: var(--ink); stroke-opacity: 0.75; stroke-width: 1.4; stroke-dasharray: 7 5; stroke-linecap: butt; }
.rf-tri--search { fill: var(--accent); }
.rf-tri--policy { fill: var(--coral); }
/* Beside the tree, the robot putting a cup in a woven basket, seen from the side. */
.rf-basket { fill: color-mix(in srgb, var(--ink) 7%, var(--paper)); stroke: var(--ink); stroke-opacity: 0.6; stroke-width: 1.3; }
.rf-weave { stroke: var(--ink); stroke-opacity: 0.28; stroke-width: 1; }
.rf-basket-rim { stroke: var(--ink); stroke-opacity: 0.75; stroke-width: 2.4; }

/* Figure 2, the paradigms: each tool's zone on the hierarchy, and what it does there. */
.rf-node { fill: var(--paper); stroke: var(--ink); stroke-opacity: 0.6; stroke-width: 1.3; }
.rf-node--root { fill: var(--ink); stroke: none; }
.rf-node--implicit { fill: none; stroke: var(--coral); stroke-opacity: 0.9; stroke-dasharray: 2 2.4; }
.rf-zone-s { fill: var(--accent); fill-opacity: 0.1; stroke: var(--accent); stroke-opacity: 0.45; stroke-width: 1; }
.rf-zone-p { fill: var(--coral); fill-opacity: 0.1; stroke: var(--coral); stroke-opacity: 0.45; stroke-width: 1; }
.rf-forest { stroke: var(--accent); stroke-opacity: 0.75; }
.rf-probe { stroke: var(--accent); stroke-width: 1.4; stroke-dasharray: 3 3.5; }

/* Figure 3, the tabletop from above. The stick arm's reach covers most of the table and is drawn
   faintly; the gripper's, which decides where a lift can work, more strongly. */
.rf-frame { stroke-width: 1.5; }
.rf-frame--search { stroke: var(--accent); stroke-dasharray: 6 5; stroke-linecap: butt; }
.rf-frame--policy { stroke: var(--coral); }
.rf-table { fill: var(--ink); fill-opacity: 0.04; stroke: var(--ink); stroke-opacity: 0.35; stroke-width: 1.2; }
.rf-zone { fill: var(--rf-sage); fill-opacity: 0.16; stroke: var(--rf-sage); stroke-width: 1.2; stroke-dasharray: 4 4; }
.rf-zone--stick { fill-opacity: 0.06; stroke-opacity: 0.55; }
.rf-obstacle { fill: var(--ink); fill-opacity: 0.55; }
.rf-mount { fill: var(--ink); fill-opacity: 0.7; }
.rf-goal { stroke: var(--ink); stroke-opacity: 0.6; stroke-width: 1.4; stroke-dasharray: 3 3; }
.rf-cube { fill: var(--ink); fill-opacity: 0.9; }
.rf-shadow { fill: var(--ink); fill-opacity: 0.14; }
.rf-ghost { fill: var(--accent); fill-opacity: 0.12; stroke: var(--accent); stroke-width: 1.3; stroke-dasharray: 3 2.2; }
.rf-plan { stroke: var(--ink); stroke-opacity: 0.55; stroke-width: 1.2; stroke-dasharray: 0.1 4.5; }
.rf-subgoal { fill: var(--paper); stroke: var(--ink); stroke-opacity: 0.6; stroke-width: 1.1; }
.rf-pred { stroke: var(--accent); stroke-opacity: 0.6; stroke-width: 1.4; }
.rf-pred--best { stroke-opacity: 1; stroke-width: 2.6; }
.rf-costline { stroke: var(--accent); stroke-opacity: 0.55; stroke-width: 1; stroke-dasharray: 2 3; }
.rf-trail { stroke: var(--coral); stroke-width: 2.6; }
/* An arm: links drawn as outlined capsules, its joints, and the stick or the claw at its end. */
.rf-link-out { stroke: var(--ink); stroke-opacity: 0.32; stroke-width: 15; }
.rf-link-in { stroke: var(--paper); stroke-width: 12; }
.rf-link-out--fore { stroke-width: 12; }
.rf-link-in--fore { stroke-width: 9; }
.rf-joint { fill: var(--paper); stroke: var(--ink); stroke-opacity: 0.5; stroke-width: 1.3; }
.rf-stick { stroke: var(--ink); stroke-width: 4; }
.rf-claw { stroke: var(--ink); stroke-width: 3; }

/* The planner: (skill, subgoal) chips in rows, their costs, and the pair being executed. */
.rf-chip { fill: var(--paper); stroke: var(--ink); stroke-opacity: 0.5; stroke-width: 1; }
.rf-chip--empty { fill: none; stroke-opacity: 0.3; stroke-dasharray: 2.5 2.5; stroke-linecap: butt; }
.rf-chip-rule { stroke: var(--ink); stroke-opacity: 0.18; stroke-width: 1; }
.rf-glyph { stroke: var(--ink); stroke-width: 1.5; }
.rf-glyph--rod { stroke-width: 2.6; }
.rf-chip-on { fill: var(--accent); fill-opacity: 0.12; stroke: var(--accent); stroke-width: 1.8; }
.rf-rowbg { fill: var(--accent); fill-opacity: 0.06; stroke: var(--accent); stroke-opacity: 0.55; stroke-width: 1; }
.rf-rowbg--best { fill-opacity: 0.12; stroke-opacity: 1; stroke-width: 1.5; }
.rf-bar-track { fill: var(--ink); fill-opacity: 0.08; }
.rf-bar { fill: var(--ink); fill-opacity: 0.38; }
.rf-bar--keep { fill: var(--accent); fill-opacity: 1; }
.rf-policy-box { fill: var(--coral); fill-opacity: 0.07; stroke: var(--coral); stroke-opacity: 0.7; stroke-width: 1.2; }
.rf-step-sq { stroke: var(--coral); stroke-opacity: 0.5; stroke-width: 1; }
.rf-step-sq--on { fill: var(--coral); stroke-opacity: 1; }

/* Figure 4, one model per skill: the training cards, and the chain of models at test time. */
.rf-card { fill: var(--cream); fill-opacity: 0.6; stroke: var(--rule); stroke-width: 1; }
.rf-box-p { fill: var(--paper); stroke: var(--coral); stroke-width: 1.3; }
.rf-box-p--on { fill: var(--coral); fill-opacity: 0.14; }
.rf-box-s { fill: var(--paper); stroke: var(--accent); stroke-width: 1.3; }
.rf-progress-track { fill: var(--accent); fill-opacity: 0.16; }
.rf-progress { fill: var(--accent); }
.rf-conn { stroke: var(--ink); stroke-opacity: 0.45; stroke-width: 1.1; }
.rf-data { fill: var(--accent); }
.rf-token { fill: var(--accent); stroke: var(--paper); stroke-width: 1.5; }
.rf-stick--thin { stroke-width: 3; }
.rf-link-thin { stroke: var(--ink); stroke-opacity: 0.3; stroke-width: 5; }
.rf-claw--thin { stroke-width: 2; }
.rf-tabletop { fill: var(--ink); fill-opacity: 0.08; }
.rf-floor { stroke: var(--ink); stroke-opacity: 0.5; stroke-width: 1.2; }
/* The training scenes' obstacles are red, apart from the ink cube, and light up, a paler red with a
   glow, when it strikes one; a rollout that fails leaves a red ghost. */
.rf-hazard { fill: var(--rf-wrong); fill-opacity: 0.85; }
.rf-hazard--lit { fill: var(--rf-lit); fill-opacity: 1; stroke: var(--rf-lit); stroke-width: 7; stroke-opacity: 0.45; }
.rf-ghost--fail { fill: var(--rf-wrong); stroke: var(--rf-wrong); }
/* The thumbnails are the tabletop drawn at a fifth of its size; their lines keep their weight. */
.rf-mini * { vector-effect: non-scaling-stroke; }
.rf-mini .rf-pred { stroke-opacity: 1; stroke-width: 1.6; }
.rf-mini .rf-goal { stroke-dasharray: 2 2; }

/* Figure 5, search over raw actions: each node a sequence of actions, the one it adds in green. */
.rf-act { fill: var(--paper); stroke: var(--ink); stroke-opacity: 0.35; stroke-width: 1; }
.rf-act--new { fill: color-mix(in srgb, var(--accent) 14%, var(--paper)); stroke: var(--accent); stroke-opacity: 1; stroke-width: 1.3; }
.rf-act-label { fill: var(--muted-text); stroke: none; font: italic 400 13px/1 var(--rf-serif); }
.rf-act-label--new { fill: var(--ink); }
.rf-branch { stroke: var(--accent); stroke-opacity: 0.6; stroke-width: 1.2; }
.rf-burst { stroke: var(--accent); stroke-opacity: 0.5; }

/* Figure 6, trying every sequence: the row of actions, emptied and refilled an action at a time,
   and what the search has spent. */
.rf-act--empty { fill: none; stroke-opacity: 0.3; stroke-dasharray: 3 3; stroke-linecap: butt; }
.rf-act-label--lg { fill: var(--ink); font-size: 22px; }
.rf-brace { stroke: var(--ink); stroke-opacity: 0.4; stroke-width: 1; }
.rf-count { fill: var(--ink); stroke: none; font: 300 28px/1 var(--rf-sans); font-variant-numeric: tabular-nums; }
.rf-note--ink { fill: var(--ink); }

/* Figure 9, search over subgoals: figure 5's tree one level up, the subgoals it prunes in red and
   the path to the plan it finds drawn over the rest. */
.rf-act--no { fill: color-mix(in srgb, var(--rf-wrong) 12%, var(--paper)); stroke: var(--rf-wrong); stroke-opacity: 1; stroke-width: 1.3; }
.rf-branch--path { stroke-opacity: 1; stroke-width: 2.4; }

/* Figures 7 and 8, the homepage's search tree as the noisy MoE routing article draws it: a faint
   forest whose branches thin and fade toward the rim, searched by samples that are a deeper sage
   until they end, red on a wrong answer; the right answer is blue. The red and blue are that
   article's (--mr-wrong and --mr-right in moe-router-figures.css); keep them in step. */
.rf-tw { stroke: var(--ink); }
.rf-tw--1 { opacity: 0.7; stroke-width: 1.25; }
.rf-tw--2 { opacity: 0.58; stroke-width: 1.05; }
.rf-tw--3 { opacity: 0.45; stroke-width: 0.85; }
.rf-tw--4 { opacity: 0.34; stroke-width: 0.7; }
.rf-knot { fill: var(--ink); fill-opacity: 0.6; }
.rf-root { fill: var(--ink); }
.rf-ray { stroke: var(--rf-sage); stroke-width: 0.9; stroke-opacity: 0.55; }
.rf-sample { stroke: var(--rf-likely); stroke-width: 1.9; stroke-opacity: 0.8; }
.rf-sample--wrong { stroke: var(--rf-wrong); }
.rf-miss circle { fill: var(--paper); stroke: var(--rf-wrong); stroke-width: 1.5; }
.rf-miss path { stroke: var(--rf-wrong); stroke-width: 1.7; }
.rf-truth { stroke: var(--rf-right); stroke-width: 1.5; stroke-dasharray: 2.6 2.6; }
.rf-tag--panel { font-size: 11.16px; letter-spacing: 1.79px; }
.rf-tag--right { fill: var(--rf-right); }
.rf-note--panel { font-size: 15.12px; }
.rf-key { fill: var(--muted-text); stroke: none; font: 400 10.44px/1 var(--rf-label); letter-spacing: 1.46px; }
/* Figure 7: the paths brute force has tried, and the goal path it never reaches. */
.rf-tried { stroke: var(--rf-wrong); stroke-width: 1.1; stroke-opacity: 0.5; }
.rf-tried--x { stroke-opacity: 0.8; }
.rf-route { stroke: var(--rf-right); stroke-width: 1.8; stroke-dasharray: 3 3.5; }

/* Under each figure, a button to pause, play or replay it, and its steps, each a button that plays
   from there, with a line that fills as the step plays. */
.rf-controls { display: flex; align-items: flex-start; gap: 16px; margin-top: 18px; }
.rf-toggle {
  display: grid;
  flex: none;
  place-items: center;
  width: 30px;
  height: 30px;
  margin: 0;
  padding: 0;
  border: 1px solid var(--rule);
  border-radius: 50%;
  background: var(--paper);
  color: var(--ink);
  cursor: pointer;
}
.rf-toggle:hover { border-color: var(--ink); }
.rf-toggle svg { width: 14px; height: 14px; fill: currentColor; }
.rf-toggle .rf-icon-line { fill: none; stroke: currentColor; stroke-width: 1.6; stroke-linecap: round; }
.rf-steps { display: flex; flex: 1 1 auto; gap: 12px; min-width: 0; margin: 0; padding: 0; list-style: none; }
.rf-steps li { flex: 1 1 0; min-width: 0; margin: 0; padding: 0; }
.rf-steps li::before, .rf-steps li::marker { content: none; }
.rf-step {
  display: grid;
  gap: 8px;
  width: 100%;
  margin: 0;
  padding: 6px 0 4px;
  border: 0;
  background: none;
  color: var(--muted-text);
  font: 400 0.58rem/1.2 var(--rf-label);
  letter-spacing: 0.12em;
  text-align: left;
  text-transform: uppercase;
  cursor: pointer;
}
.rf-step:hover, .rf-step[aria-current="step"] { color: var(--ink); }
.rf-step-name { display: flex; gap: 7px; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
.rf-step-n { color: var(--muted); }
.rf-step-track { display: block; height: 2px; overflow: hidden; background: var(--rule); }
.rf-step-fill { display: block; height: 100%; background: var(--muted); transform: scaleX(0); transform-origin: left center; }
.rf-step[aria-current="step"] .rf-step-fill { background: var(--ink); }
.rf-toggle:focus-visible, .rf-step:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
.rf-noscript { color: var(--muted-text); font: 400 0.85rem/1.5 var(--rf-sans); }

/* The figures are drawn for the 960px column, one unit to a pixel; a narrower figure stays at its
   own width (--rf-w). In a narrower column, each scrolls sideways at a size its labels stay legible
   at, and its steps keep only their numbers; a figure narrower than that size just fits the column. */
@media (max-width: 760px) {
  .rf-stage { overflow-x: auto; overscroll-behavior-x: contain; -webkit-overflow-scrolling: touch; }
  .rf-svg { min-width: 720px; }
  .rf-art--narrow .rf-svg { min-width: 0; }
  .rf-steps { gap: 8px; }
  .rf-step-name { font-size: 0; gap: 0; }
  .rf-step-n { font-size: 0.62rem; }
}
/* As on Evals: say so above a figure that scrolls, since its far side is out of view. The column
   drops below the figures' 720px once the window is narrower than 752px. */
@media (max-width: 751px) {
  .rf-art:has(.rf-svg):not(.rf-art--narrow)::before {
    content: "Scroll sideways to see the whole figure \2192\FE0E";
    display: block;
    margin: 0 0 10px;
    font: 400 12px/1.5 var(--rf-sans);
    color: var(--muted-text);
  }
}

.rf-figure figcaption {
  font-size: 0.82rem;
  line-height: 1.65;
  letter-spacing: 0.04em;
  color: var(--muted-text);
}

.blog-post .article-body figure figcaption {
  font-size: 0.82rem;
}

.blog-post .article-body .blog-video-caption {
  font-size: 0.78rem;
  line-height: 1.6;
}

/* The featured captions sit above their videos; keep each one close to its own video
   (the shared min-height left a gap that, in one column, put it nearer the video above). */
.blog-post .blog-video-table--featured .blog-video-caption {
  min-height: 0;
  margin-bottom: 10px;
}

@media (max-width: 767px) {
  .blog-post .blog-video-table--featured { gap: 32px; }
}

.blog-post .blog-video-table--single {
  grid-template-columns: 1fr;
  max-width: 100%;
  width: 100%;
  margin-left: auto;
  margin-right: auto;
}
@media print {
  .rf-art { break-inside: avoid; }
  .rf-controls { display: none; }
}
