/* ==========================================================================
   CONTINUOUS INTELLIGENCE ENGINE
   Loaded only on the pages that show the factory. Everything here is scoped
   under .engine and prefixed engine-, so it cannot reach the hero motif, the
   product mockups, or any shared component.
   ========================================================================== */

/* ---------- scene wrapper ---------- */
.engine { margin: 0; }
.engine-scene {
  position: relative;
  background: var(--bg-soft);
  border: 1px solid var(--border);
  border-radius: 24px;
  padding: 32px;
  /* One static wash behind the machine, and the card texture in the four
     corners. No outer shadow — the machine carries its own. */
  isolation: isolate;
  --engine-corner: 190px;
}
.engine-scene::before {
  content: ""; position: absolute; inset: 0; z-index: 0; pointer-events: none;
  border-radius: inherit;
  background: radial-gradient(58% 62% at 50% 46%,
              rgba(110, 76, 224, 0.08) 0%, rgba(110, 76, 224, 0) 72%);
}
/* The same 68px tile as .panel-tr/.panel-br, but four masks instead of one:
   mask layers composite additively, so the corners union.

   A fixed 190px radius, not the cards' 78%. A percentage of a box this wide and
   short (1116x550) draws a flat 379x187 ellipse that reaches most of the way in;
   an absolute radius stays a corner on any aspect ratio. The scene's corners are
   not literally empty at desktop — the two lists reach the left and right edges
   — so the flourish is kept faint and its worst-case contrast is measured rather
   than assumed. */
.engine-scene::after {
  content: ""; position: absolute; inset: 0; z-index: 0; pointer-events: none;
  border-radius: inherit;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='68' height='68'%3E%3Crect x='17' y='17' width='34' height='34' rx='10' fill='none' stroke='%236E4CE0' stroke-width='1'/%3E%3C/svg%3E");
  opacity: 0.5;
  mask-image:
    radial-gradient(var(--engine-corner) var(--engine-corner) at 0% 0%,     rgba(0,0,0,0.55) 0%, rgba(0,0,0,0.22) 42%, transparent 72%),
    radial-gradient(var(--engine-corner) var(--engine-corner) at 100% 0%,   rgba(0,0,0,0.55) 0%, rgba(0,0,0,0.22) 42%, transparent 72%),
    radial-gradient(var(--engine-corner) var(--engine-corner) at 0% 100%,   rgba(0,0,0,0.55) 0%, rgba(0,0,0,0.22) 42%, transparent 72%),
    radial-gradient(var(--engine-corner) var(--engine-corner) at 100% 100%, rgba(0,0,0,0.55) 0%, rgba(0,0,0,0.22) 42%, transparent 72%);
  -webkit-mask-image:
    radial-gradient(var(--engine-corner) var(--engine-corner) at 0% 0%,     rgba(0,0,0,0.55) 0%, rgba(0,0,0,0.22) 42%, transparent 72%),
    radial-gradient(var(--engine-corner) var(--engine-corner) at 100% 0%,   rgba(0,0,0,0.55) 0%, rgba(0,0,0,0.22) 42%, transparent 72%),
    radial-gradient(var(--engine-corner) var(--engine-corner) at 0% 100%,   rgba(0,0,0,0.55) 0%, rgba(0,0,0,0.22) 42%, transparent 72%),
    radial-gradient(var(--engine-corner) var(--engine-corner) at 100% 100%, rgba(0,0,0,0.55) 0%, rgba(0,0,0,0.22) 42%, transparent 72%);
}
.engine-scene > * { position: relative; z-index: 1; }

/* ---------- desktop: inputs -> machine -> docks ---------- */
@media (min-width: 1100px) {
  .engine-scene {
    display: grid;
    /* The right column no longer carries two cards, so the machine takes the
       width back. */
    grid-template-columns: minmax(0, 0.78fr) minmax(0, 2.1fr) minmax(0, 0.92fr);
    gap: 24px 24px; align-items: center;
  }
  /* The labels are their own row, so the two lists centre on the machine
     rather than on the machine plus its captions — which is what made them
     read low. Every child is placed explicitly. */
  /* The child combinator is required, not decorative: the base rule
     `.engine-head { margin-bottom: 20px }` sits AFTER this block at equal
     specificity, so a plain `.engine-head` margin here loses the cascade — a
     `margin-bottom: 4px` sat here and never applied to anything. Measured
     before this: 33px above the mark, 44px from the kicker to the columns. */
  .engine-head           { grid-column: 1 / -1; grid-row: 1; }
  .engine-scene > .engine-head { margin-top: 6px; margin-bottom: 14px; }
  .engine-scene > .engine-col:not(.engine-out) { grid-column: 1; grid-row: 2; }
  .engine-machine        { grid-column: 2; grid-row: 2; }
  /* Was padding-inline: 13.75%, which matched the chambers at viewBox x 88-552
     but left each block 110px at 1100 and 123px at 1440 — against the 144px the
     widest description needs for two lines, so every one of them ran to three.
     Negative margins widen the row past the machine column while keeping it
     centred on it, so the middle block stays on chamber 2 and the outer two
     spread symmetrically. Percentage margins resolve against the grid area, so
     this scales with the machine: ~193px a block at 1100, ~215px at 1440. */
  .engine-chamber-labels {
    grid-column: 2; grid-row: 3; margin-top: 0; margin-inline: -11%;
  }
  .engine-out            { grid-column: 3; grid-row: 2; }
}

/* ---------- the engine's name ----------
   Inside the box, centered, spanning every column. */
.engine-head { margin-bottom: 20px; text-align: center; }
/* The product mark, centred over the name it belongs to. */
.engine-mark {
  display: block; width: 30px; height: 30px; margin: 0 auto 6px;
  color: var(--primary-deep);
}
.engine-name {
  font-family: var(--font-display); font-size: var(--fs-h3);
  font-weight: 700; color: var(--ink); line-height: 1.2;
}
.engine-kicker {
  font-size: var(--fs-meta); line-height: 1.5; color: var(--ink-soft);
  margin-top: 2px;
}

/* ---------- column headings ----------
   Both columns are the same kind of list, so they get the same heading. */
.engine-col-h {
  font-size: var(--fs-meta); line-height: 1.5; font-weight: 600;
  color: var(--ink-soft); margin-bottom: 10px;
}
.engine-col { min-width: 0; }

/* ---------- input labels ---------- */
.engine-inputs { list-style: none; margin: 0; padding: 0; min-width: 0; }
.engine-inputs li {
  display: grid; grid-template-columns: 20px 1fr; gap: 10px;
  align-items: start; font-size: var(--fs-meta); line-height: 1.5;
  color: var(--ink-soft); min-width: 0;
}
.engine-inputs li + li { margin-top: 12px; }
/* Both halves of the reflow fix, per the enlarged-text trap: min-width releases
   the track's min-content floor, and break-word handles a word that cannot fit
   at any track width. At a 200% text setting "encounters" alone overran the
   column by 9px without them. */
.engine-inputs li > span { min-width: 0; overflow-wrap: break-word; }
.engine-ico { width: 20px; height: 20px; color: var(--primary-deep); margin-top: 1px; }

/* ---------- machine ---------- */
.engine-machine { min-width: 0; }
.engine-svg {
  display: block; width: 100%; height: auto;
  filter: drop-shadow(0 8px 20px rgba(13, 28, 50, 0.28));
}
.engine-svg-portrait { display: none; }
@media (max-width: 1099px) {
  .engine-svg-landscape { display: none; }
  .engine-svg-portrait { display: block; margin: 0 auto; max-width: 440px; }
}

/* Palette. Front faces white, tops soft, sides tinted — a shallow three-quarter
   read with no gradients on the machinery itself. */
.engine-body       { fill: var(--ground); stroke: rgba(13, 28, 50, 0.7); stroke-width: 2; vector-effect: non-scaling-stroke; }
.engine-face-top   { fill: var(--bg-soft); stroke: rgba(13, 28, 50, 0.7); stroke-width: 2; stroke-linejoin: round; vector-effect: non-scaling-stroke; }
.engine-face-side  { fill: var(--tint);    stroke: rgba(13, 28, 50, 0.7); stroke-width: 2; stroke-linejoin: round; vector-effect: non-scaling-stroke; }
.engine-foot       { fill: var(--tint);    stroke: rgba(13, 28, 50, 0.35); stroke-width: 1.5; }
.engine-chamber    { fill: var(--bg-soft); stroke: rgba(13, 28, 50, 0.35); stroke-width: 1.5; }

.engine-inner path        { fill: none; stroke: var(--primary); stroke-width: 2; stroke-linecap: round; }
/* The node absorbs each arriving input, so it scales about its own centre. */
/* Chamber 1: pressure and plumbing. A rail the feed lines land on, a vessel
   that fills and strains, a needle gauge reading the load, and a two-leaf
   hatch that lets the charge go. */
.engine-rail   { fill: var(--primary); opacity: 0.5; }
.engine-vessel { fill: var(--ground); stroke: var(--primary); stroke-width: 2; }
.engine-fill   { fill: var(--primary); opacity: 0.22; }

/* The halo is the critical-mass tell. Painted, not a filter: a drop-shadow on
   an SVG child rasterises at rendered size and the machine is drawn at eight
   different widths. */
.engine-halo {
  fill: var(--primary); opacity: 0;
  transition: opacity 220ms var(--ease);
}
.engine-vessel-g.is-charged .engine-halo  { opacity: 0.14; }
.engine-vessel-g.is-charged .engine-vessel,
.engine-vessel-g.is-charged .engine-node  { stroke: var(--primary-deep); stroke-width: 2.5; }

/* The dial. These selectors carry two classes so they outrank
   `.engine-inner path`, which would otherwise paint the whole dial in the
   full-strength primary the flow lines use. */
.engine-inner .engine-dial {
  fill: none; stroke: var(--primary); stroke-width: 2;
  opacity: 0.45; stroke-linecap: round;
}
.engine-inner .engine-dial-stem {
  fill: none; stroke: rgba(13, 28, 50, 0.35); stroke-width: 1.5;
}
.engine-inner .engine-needle path {
  fill: none; stroke: var(--primary-deep); stroke-width: 2; stroke-linecap: round;
}
.engine-needle {
  transform-box: view-box; transform-origin: var(--engine-gx) var(--engine-gy);
}
.engine-dial-hub { fill: var(--ink); }

/* The discharge. One ring, scaled and faded from the outlet — no keyframe, and
   clipped to chamber 1 so it cannot bloom into the mill. */
.engine-ring {
  fill: none; stroke: var(--primary); stroke-width: 2;
  opacity: 0; transform-box: view-box;
  transform-origin: var(--engine-gx) var(--engine-gy);
}

/* Chamber 2: force and material. The load stands on a sprung deck, the teeth
   break the bundle apart, and a press compresses the pieces back into one.

   A balance beam was the right metaphor and the wrong drawing: at roughly five
   pixels per SVG unit a fulcrum, a counterweight and a hanging pan read as a
   bent bracket. A deck that visibly goes down under the load does not. */
.engine-scale-base { fill: var(--tint); stroke: rgba(13, 28, 50, 0.5); stroke-width: 1.5; }
.engine-bezel { fill: var(--ground); stroke: rgba(13, 28, 50, 0.5); stroke-width: 1.5; }
.engine-deck {
  fill: var(--tint); stroke: rgba(13, 28, 50, 0.7); stroke-width: 1.5;
  transform-box: view-box;
}
/* Scaled about its own base, so the coil shortens downward-to-upward and its
   top stays welded to the descending deck. */
.engine-spring {
  fill: none; stroke: var(--primary); stroke-width: 2;
  stroke-linejoin: round; stroke-linecap: round;
  transform-box: view-box; transform-origin: 273px 222px;
}
/* Portrait has no scale, so its readout is mounted rather than floating. */
.engine-plate   { fill: var(--bg-soft); stroke: rgba(13, 28, 50, 0.35); stroke-width: 1.5; }
.engine-bracket { fill: none; stroke: rgba(13, 28, 50, 0.5); stroke-width: 2; stroke-linecap: round; }

/* The pieces the mill makes. Small, blank, and clipped to the chambers like
   the tile they came from. */
.engine-frag {
  fill: var(--tint); stroke: var(--primary); stroke-width: 1.5;
  transform-box: view-box; transform-origin: 0 0;
}

/* The press. Posts and bed are structure — kept light, because at full weight
   they read as chamber pillars rather than a frame. The platen is the only
   part that moves. */
.engine-press-post { fill: none; stroke: rgba(13, 28, 50, 0.3); stroke-width: 1.5; stroke-linecap: round; }
.engine-anvil {
  fill: var(--tint); stroke: rgba(13, 28, 50, 0.45); stroke-width: 1.5;
  transform-box: view-box;
}
.engine-platen {
  fill: var(--tint); stroke: rgba(13, 28, 50, 0.7); stroke-width: 2;
  transform-box: view-box;
}

/* The vessel outline is the node now, so it carries the absorb settle. */
.engine-node {
  fill: none; stroke: var(--primary); stroke-width: 2;
  transform-box: fill-box; transform-origin: center;
}

/* Assessment, shown as segments filling. Never a number: a score on the face
   of the machine would assert something the engine does not claim. */
.engine-seg { fill: var(--border); }
.engine-seg.is-on { fill: var(--primary); }

/* A slow tick passing over the working chamber. Ambient, clipped to the
   chamber, and faint enough to read as machinery rather than a highlight. */
.engine-scan {
  fill: var(--primary); opacity: 0.12;
  transform-box: view-box;
}

/* Chamber 3: track and signal. Both roads are always drawn; the one the result
   takes brightens, the tongue swings to bridge it, and the signal clears ahead
   of the packet the way a real one does. The controller dims the whole road —
   rail and sleepers together — so opacity lives on the group. */
.engine-road { opacity: 0.3; }
.engine-lane {
  fill: none; stroke: var(--primary); stroke-width: 2;
  stroke-linecap: round; stroke-linejoin: round;
}
.engine-inner .engine-sleepers path {
  fill: none; stroke: var(--primary); stroke-width: 1.5;
  stroke-linecap: round; opacity: 0.7;
}
.engine-tongue {
  transform-box: view-box; transform-origin: var(--engine-gx) var(--engine-gy);
}
.engine-tongue path { fill: var(--primary); stroke: none; }

.engine-signal-post { fill: none; stroke: rgba(13, 28, 50, 0.5); stroke-width: 2; stroke-linecap: round; }
.engine-signal-head { fill: var(--tint); stroke: rgba(13, 28, 50, 0.7); stroke-width: 1.5; }

/* The applicator that marks a result for where it is going. */
.engine-stamp-stem { fill: none; stroke: rgba(13, 28, 50, 0.5); stroke-width: 2; stroke-linecap: round; }
.engine-stamp-head {
  fill: var(--tint); stroke: rgba(13, 28, 50, 0.7); stroke-width: 1.5;
  transform-box: view-box;
}

/* Jaws, not a collar. They open for the result and latch behind it — the same
   two-leaf vocabulary as the two hatches, deliberately at a different tempo. */
.engine-jaw {
  fill: var(--tint); stroke: rgba(13, 28, 50, 0.6); stroke-width: 1.5;
  transform-box: view-box;
}

/* The return channel's two ends. Same drawing at both, so the pipe reads as one
   object with a mouth and an outlet rather than a line that stops. */
.engine-collar { fill: var(--tint); stroke: rgba(13, 28, 50, 0.55); stroke-width: 2; }
/* The tap itself is a real bore, wide enough for something to fall down it —
   one path drawn twice, walls then bore, so the two can never disagree about
   where the pipe is. */
/* Two classes deep, because the tee sits inside .engine-inner and
   `.engine-inner path` outranks a single class — the same trap the dial hit.
   Without this the whole tube renders as a 2px primary stroke, which looks like
   a line rather than a pipe and is invisible to any check that measures
   positions instead of paint. */
.engine-inner .engine-tube-wall,
.engine-tube-wall { fill: none; stroke: rgba(13, 28, 50, 0.32); stroke-width: 16; stroke-linecap: butt; }
.engine-inner .engine-tube-bore,
.engine-tube-bore { fill: none; stroke: var(--bg-soft); stroke-width: 12; stroke-linecap: butt; }

/* Two leaves in the body wall, closed at rest. */
.engine-leaf {
  fill: var(--tint); stroke: rgba(13, 28, 50, 0.7); stroke-width: 1.5;
  transform-box: view-box;
}
/* Signal aspects. Dark at rest so the lit one reads as a cleared road rather
   than a decoration. */
.engine-lamp {
  fill: var(--ground); stroke: var(--primary); stroke-width: 2;
  opacity: 0.32;
}
/* Solid, not dashed. A dashed line reads as a conceptual arrow; what this
   carries is material, and it has a mouth at each end to prove it. */
.engine-feedback {
  fill: none; stroke: rgba(13, 28, 50, 0.28);
  stroke-width: 2; stroke-linecap: round; stroke-linejoin: round;
}
.engine-slot { fill: var(--tint); stroke: rgba(13, 28, 50, 0.35); stroke-width: 1.5; }

/* Gears: pale lavender faces, violet outlines, navy hubs. */
/* The controller rotates these about their own centres. transform-box makes
   the origin resolve in viewBox units, which is what the emitted custom
   properties are expressed in. */
.engine-gear, .engine-roller-g { transform-box: view-box; }
.engine-gear   { transform-origin: var(--engine-gear-cx) var(--engine-gear-cy); }
.engine-roller-g { transform-origin: var(--engine-rx) var(--engine-ry); }
.engine-roller-tick { stroke: rgba(13, 28, 50, 0.35); stroke-width: 1.2; stroke-linecap: round; }

.engine-gear-body  { fill: #EDE8FD; stroke: var(--primary); stroke-width: 2; stroke-linejoin: round; }
.engine-gear-spoke { stroke: var(--primary); stroke-width: 1.5; stroke-linecap: round; opacity: 0.55; }
.engine-gear-hub   { fill: var(--ink); }

/* Conveyors */
.engine-belt-face   { fill: var(--ground); stroke: rgba(13, 28, 50, 0.7); stroke-width: 2; }
.engine-belt-return { stroke: rgba(13, 28, 50, 0.35); stroke-width: 2; }
.engine-tread       { stroke: rgba(13, 28, 50, 0.2); stroke-width: 1.5; stroke-linecap: round; }
.engine-roller      { fill: var(--tint); stroke: rgba(13, 28, 50, 0.35); stroke-width: 1.5; }

/* Chamber emphasis. A tinted overlay the controller fades, rather than a
   restyle of the chamber itself — nothing underneath has to be recomputed. */
.engine-lit {
  fill: var(--tint); stroke: var(--primary); stroke-width: 2;
  opacity: 0; pointer-events: none;
}

/* Packets carry a shape, never a value. */
.engine-packet rect { fill: var(--ground); stroke: var(--primary); stroke-width: 2; }
/* Out of the press: a violet solid carrying a white glyph. The tinted version
   this replaces was a near-white box becoming a slightly-less-white box, which
   left the transformation with nothing to see. --primary is 5.57:1, so the
   white glyph clears AA on it.

   The glyph is a <use> in light DOM with its stroke set on the element itself,
   so this descendant selector reaches it and the symbol's paths inherit —
   the same mechanism .engine-packet-glyph already relies on. */
.engine-packet.is-result rect { fill: var(--primary); stroke: var(--primary-deep); }
.engine-packet.is-result .engine-packet-glyph { stroke: var(--ground); }
/* The destination mark. Which edge carries it is which road was taken, so the
   split has a consequence that survives the recombine. */
.engine-tab { fill: var(--primary-deep); opacity: 0; }
.engine-packet.is-atlas   .engine-tab-a { opacity: 1; }
.engine-packet.is-compass .engine-tab-b { opacity: 1; }
.engine-packet-live { transform-box: view-box; transform-origin: 0 0; }
/* The glyph is a <use> of a symbol, so its contents live in shadow DOM and a
   descendant selector cannot reach them. Stroke and fill are inherited, so
   they are set on the <use> itself and the symbol's paths pick them up. */
.engine-packet-glyph {
  fill: none; stroke: var(--primary); stroke-width: 2;
  stroke-linecap: round; stroke-linejoin: round;
}
.engine-defs { position: absolute; width: 0; height: 0; overflow: hidden; }

/* ---------- chamber labels, in HTML under the machine ----------
   The chambers span viewBox x 88-552 of 640, so a grid across the full column
   starts well left of the machine and reads misaligned. Inset to match, and
   centre each block under its own chamber. */
.engine-chamber-labels {
  display: grid; grid-template-columns: repeat(3, 1fr);
  gap: 16px; margin-top: 20px;
}
.engine-cl { min-width: 0; overflow-wrap: break-word; text-align: center; }
.engine-cl-t { display: block; font-size: 1rem; font-weight: 600; color: var(--ink); }
.engine-cl-d { display: block; font-size: var(--fs-meta); line-height: 1.5; color: var(--ink-soft); margin-top: 2px; }

/* ---------- output side ----------
   No spine. It existed to tie the dispatch belt to the dock cards; with those
   gone and the list static, a rule down this column only made it look unlike
   the inputs column it is meant to match. */
.engine-out { min-width: 0; }
/* What comes out is the same kind of list as what goes in, so it is set the
   same way and nothing in it lights. */
.engine-outputs { list-style: none; margin: 0; padding: 0; }
.engine-outputs li {
  display: grid; grid-template-columns: 20px 1fr; gap: 10px; align-items: start;
  font-size: var(--fs-meta); line-height: 1.5; color: var(--ink-soft);
  min-width: 0;
}
.engine-outputs li + li { margin-top: 12px; }
.engine-outputs li > span { min-width: 0; overflow-wrap: break-word; }


/* ---------- accessible equivalent ----------
   Carried by .visually-hidden (css/theme.css) rather than a disclosure, so it
   is out of sight but still in the accessibility tree — and still the one place
   the schedule lives in the DOM. */
.engine-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 12px; }
.engine-list li {
  border-left: 3px solid var(--border); padding-left: 14px;
  font-size: var(--fs-meta); line-height: 1.5;
}
.engine-li-in   { display: block; color: var(--ink-faint); }
.engine-li-t    { display: block; font-weight: 600; color: var(--ink); }
.engine-li-d    { display: block; color: var(--ink-soft); }
.engine-li-dock { display: block; color: var(--primary-deep); font-weight: 600; margin-top: 2px; }

/* ---------- tablet and phone ---------- */
/* Stacked, the section reads name -> what the chambers do -> the machine ->
   the two lists side by side. Every child is placed, the same discipline the
   >=1100 block uses: with any of them auto-placed a later addition would land
   in whatever cell was free rather than the one it belongs in. */
@media (max-width: 1099px) {
  .engine-scene {
    padding: 24px;
    display: grid; grid-template-columns: 1fr 1fr; gap: 0 20px;
  }
  .engine-head           { grid-column: 1 / -1; grid-row: 1; }
  .engine-chamber-labels { grid-column: 1 / -1; grid-row: 2; margin-top: 0; }
  .engine-machine        { grid-column: 1 / -1; grid-row: 3; margin-top: 20px; }
  .engine-scene > .engine-col:not(.engine-out) { grid-column: 1; grid-row: 4; margin-top: 20px; }
  .engine-out            { grid-column: 2; grid-row: 4; margin-top: 20px; }
  /* A single stack again: it spread across two columns only because it had the
     full width to itself, and it now has half of it. */
  .engine-inputs li + li { margin-top: 12px; }
}
@media (max-width: 599px) {
  /* The box is ~300px wide here, so a 190px corner reaches the title. */
  .engine-scene { padding: 16px; gap: 0 16px; --engine-corner: 110px; }
  .engine-svg-portrait { max-width: none; }
  /* Two up and one across the pair, rather than three stacked. */
  .engine-chamber-labels { grid-template-columns: 1fr 1fr; gap: 14px 16px; }
  .engine-chamber-labels > :last-child { grid-column: 1 / -1; }
}

/* ---------- lifecycle strip (health plans) ---------- */
.engine-lifecycle {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 24px;
  margin-top: 32px;
}
.engine-lc { min-width: 0; overflow-wrap: break-word; }
.engine-lc + .engine-lc { border-left: 1px solid var(--border); padding-left: 24px; }
.engine-lc-t { display: block; font-size: 1.125rem; font-weight: 600; color: var(--ink); margin-bottom: 6px; }
.engine-lc p { color: var(--ink-soft); }
@media (max-width: 1099px) {
  .engine-lifecycle { grid-template-columns: 1fr; gap: 0; }
  .engine-lc + .engine-lc {
    border-left: 0; padding-left: 0;
    border-top: 1px solid var(--border); padding-top: 20px; margin-top: 20px;
  }
}

/* ---------- output states ----------
   On paper the machinery carries nothing, so it goes and the scenario list —
   which is the same content the animation shows — is forced open in its place.
   The UA hides a closed <details>' children with display:none, which an
   important declaration overrides. */
@media print {
  .engine-scene::before, .engine-scene::after, .engine-svg { display: none !important; }
  .engine-scene { background: #fff !important; border: 1pt solid #999; padding: 12pt; }
  /* Print hides the machine, so the workflow list has to come back or the
     page prints an empty frame. Undo .visually-hidden rather than fighting it. */
  .engine-list.visually-hidden {
    position: static !important; width: auto !important; height: auto !important;
    margin: 12pt 0 0 !important; overflow: visible !important;
    clip: auto !important; white-space: normal !important;
  }
  .engine-lifecycle, .engine-lc + .engine-lc { break-inside: avoid; }
}

/* The system repaints every colour, so anything that was carrying meaning
   through a fill alone needs an edge instead. Gradients and shadows come off
   so they cannot sit over system-painted text. */
@media (forced-colors: active) {
  .engine-scene::before, .engine-scene::after, .engine-glow { display: none; }
  .engine-scene, .engine-list li { border: 1px solid CanvasText; }
  .engine-list li { border-left-width: 3px; }
  .engine-svg { filter: none; }
  /* Two states here read through fill alone, which the system repaints away.
     The filled meter segment gets an outline the empty one does not, and the
     receiving dock keeps its rule as a real border rather than a background. */
  .engine-seg { fill: Canvas; stroke: CanvasText; stroke-width: 1; }
  .engine-seg.is-on { fill: Highlight; }
  /* The transformed packet reads through its fill alone, which the system
     repaints away — it needs the system's own figure/ground pair. */
  .engine-packet.is-result rect { fill: Highlight; stroke: CanvasText; }
  .engine-packet.is-result .engine-packet-glyph { stroke: HighlightText; }
  /* The tongue and the destination mark are fills too. */
  .engine-tongue path, .engine-tab { fill: Highlight; }
  .engine-signal-head, .engine-jaw, .engine-collar,
  .engine-stamp-head, .engine-bezel, .engine-deck { fill: Canvas; stroke: CanvasText; }
}
