/* ---------------------------------------------------------------
   Koopman lift figure.

   The SVG is generated by blender/koopman_lift.py and carries no colour
   of its own: every stroke is stroke="currentColor" plus a class, so the
   drawing is lit entirely from here and follows the theme with no
   second palette to maintain. Same arrangement as the valley scene.

   Colour meaning is inherited from the hero figure's legend and must
   stay consistent with it: --signal is the Koopman result, --ink-faint
   is the thing it is being compared against. Here that reads as the
   lifted trajectory against its own nonlinear shadow.

   Loaded only where the page front matter sets `koopman: true`.
   --------------------------------------------------------------- */

.koopman-figure {
  margin: 28px 0 32px;
}

/* .figure-caption is uppercased, which would render a lowercase delta as a
   capital one -- a different symbol entirely, and the wrong one: capital
   delta reads as "change in". Symbols opt out of the transform. */
.koopman-figure .figure-caption .sym {
  text-transform: none;
}

.koopman {
  display: block;
  width: 100%;
  height: auto;
  overflow: visible;
  color: var(--ink);
}

/* --- the ground: state space ------------------------------------ */

.koopman .k-grid {
  stroke: var(--rule);
  stroke-width: 1;
}

.koopman .k-plane {
  stroke: var(--rule);
  stroke-width: 1.3;
}

/* The nonlinear path, as seen from below. Dashed and faint, matching
   how the hero figure draws the baseline it is arguing against. */
.koopman .k-flow-base {
  stroke: var(--ink-faint);
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
  stroke-dasharray: 5 4;
}

/* --- the lift ---------------------------------------------------- */

.koopman .k-tie {
  stroke: var(--ink-faint);
  stroke-width: 1;
  stroke-dasharray: 2.5 4.5;
  opacity: 0.6;
}

.koopman .k-tie-eq {
  stroke: var(--signal);
  stroke-width: 1.3;
  stroke-dasharray: 3 3.5;
  opacity: 0.75;
}

/* --- the sheet: the graph of the observable ---------------------- */

.koopman .k-sheet {
  stroke: var(--ink-faint);
  stroke-width: 1;
  opacity: 0.55;
}

.koopman .k-sheet-edge {
  stroke: var(--ink-faint);
  stroke-width: 1.5;
  opacity: 0.9;
}

/* The whole point of the drawing, so it is the only heavy stroke. */
.koopman .k-flow-lift {
  stroke: var(--signal);
  stroke-width: 2.8;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* --- annotation -------------------------------------------------- */

.koopman .k-axis {
  stroke: var(--ink-faint);
  stroke-width: 1.2;
  opacity: 0.7;
}

.koopman .k-mark-start {
  stroke: var(--ink-faint);
  stroke-width: 1.8;
}

.koopman .k-mark-eq {
  color: var(--signal);
  stroke: none;
}

.koopman .k-text {
  font-family: var(--mono);
}

.koopman .k-label {
  fill: var(--ink-soft);
  font-size: 23px;
}

.koopman .k-note {
  fill: var(--ink-faint);
  font-size: 14px;
  letter-spacing: 0.09em;
}

/* ---------------------------------------------------------------
   Draw-on.

   Keyed to the disclosure opening rather than to load or to scroll:
   the figure lives inside a <details> that starts closed, so an
   on-load animation would play to nobody. Opening the panel is the
   moment the reader has actually asked to see it.

   Only the two trajectories draw; the sheet and the graticule are
   context and should already be there when the paths arrive.
   --------------------------------------------------------------- */
@media (prefers-reduced-motion: no-preference) {
  /* The shadow arrives first, then the lifted path draws along the
     sheet -- which is the order the figure's argument runs in.

     Only the lifted path is drawn by dash animation. .k-flow-base is
     dashed in its resting state, and animating stroke-dasharray on it
     would end at a solid stroke: `both` holds the animation's final
     frame, not the declared style, so the dashes would never come
     back. It fades instead. */
  details[open] .koopman .k-flow-base {
    animation: k-fade 600ms ease-out both;
  }

  /* Exact because the path carries pathLength="1". */
  details[open] .koopman .k-flow-lift {
    animation: k-draw 1500ms ease-out both;
    animation-delay: 320ms;
  }

  details[open] .koopman .k-tie,
  details[open] .koopman .k-tie-eq {
    animation: k-fade 700ms ease-out both;
    animation-delay: 1250ms;
  }

  @keyframes k-draw {
    from {
      stroke-dasharray: 0 1;
    }
    to {
      stroke-dasharray: 1 0;
    }
  }

  @keyframes k-fade {
    from {
      opacity: 0;
    }
  }
}
