/* ============================================================================
   SCENES.CSS — styling for the scroll-driven machine animations
   ----------------------------------------------------------------------------
   The geometry lives in assets/js/scenes.js. This file only decides how it
   looks. Every scene inherits the current machine's --hue, so a scene is
   automatically the right colour for its machine.
   ========================================================================= */

/* ---------------------------------------------------------- CONTAINERS --- */

/* A tall section whose inner stage is pinned while you scroll through it.
   The taller --scene-h is, the slower the animation plays. */
.scene {
  position: relative;
  height: var(--scene-h);
}
/* height: auto, not 100vh — the artwork is wide, so a full-viewport pin left a
   couple of hundred pixels of dead space above and below it. Sizing to content
   keeps it snug at every width; the animation still completes before the pin
   lets go, because that timing comes from the section height, not this. */
.scene__pin {
  position: sticky; top: 0;
  height: auto;
  padding-block: var(--space-6) var(--space-8);
  display: grid;
  align-content: center;
  overflow: hidden;
  background: var(--bg);
}
.scene__stage {
  position: relative;
  width: 100%;
  display: grid;
  place-items: center;
}
/* overflow: hidden matters — it is what makes the iron and the nozzle slide in
   from off-frame instead of spilling over the heading above. */
.scene__svg { width: 100%; height: auto; max-height: 62vh; overflow: hidden; }

/* Compact variant used on the index page — no pinning, animates as it passes.
   The SVG is deliberately oversized and cropped by the panel, so the machine
   reads at a glance instead of shrinking into the corner. */
.scene--strip { height: auto; }
.scene--strip .scene__pin { position: static; height: auto; padding: 0; background: none; overflow: hidden; }
.scene--strip .scene__svg { max-height: none; width: 165%; max-width: none; }
.scene--strip .scene__hud { display: none; }   /* readouts would be cut in half */

/* Reduced motion: collapse the tall scroll container, keep the finished frame */
@media (prefers-reduced-motion: reduce) {
  .scene { height: auto; }
  .scene__pin { position: static; height: auto; }
}

@media (max-width: 860px) {
  /* :not(.scene--strip) matters — this rule sits after the strip rules above,
     so an unscoped `.scene` here would win and give every mini-scene on the
     index page a 170vh container full of empty space. */
  .scene:not(.scene--strip) { height: var(--scene-h-mobile); }
  .scene:not(.scene--strip) .scene__svg { max-height: 46vh; }
}

/* ------------------------------------------------------------- SHARED --- */
.scene__hud text,
.scene__hud-l {
  font-family: var(--font-mono);
  font-size: 15px;
  font-weight: 500;
  letter-spacing: 0.14em;
  fill: var(--text-mute);
  text-transform: uppercase;
}
.scene__hud-r { text-anchor: end; fill: var(--hue); }

/* =========================================================================
   SCENE 1 — LASER
   ========================================================================= */
.lz-bed rect {
  fill: var(--surface);
  stroke: var(--line);
  stroke-width: 1;
}
.lz-honeycomb { fill: var(--text-mute); opacity: 0.7; }

.lz-sheet {
  fill: var(--surface-2);
  stroke: var(--line-strong);
  stroke-width: 1;
  stroke-dasharray: 5 5;
}

.lz-kerf-ghost {
  fill: none;
  stroke: var(--line-strong);
  stroke-width: 1.5;
  stroke-dasharray: 3 6;
}
.lz-kerf {
  fill: none;
  stroke: var(--hue);
  stroke-width: 2.5;
  stroke-linecap: round;
  filter: drop-shadow(0 0 6px var(--hue));
}

.lz-rail line { stroke: var(--line-strong); stroke-width: 1; }

.lz-carriage rect { fill: var(--surface-2); stroke: var(--text-soft); stroke-width: 1.5; }
.lz-carriage line { stroke: var(--text-mute); stroke-width: 1; }
.lz-carriage path { fill: var(--surface); stroke: var(--text-soft); stroke-width: 1.5; }
.lz-lens { fill: var(--hue); }

.lz-beam {
  stroke: url(#lzBeam);
  stroke-width: 3;
  transition: opacity var(--dur-fast) linear;
}

.lz-flare { fill: url(#lzFlare); }
.lz-point { fill: #fff; }

.lz-sparks line {
  stroke: var(--hue-2);
  stroke-width: 1.5;
  stroke-linecap: round;
  transform-origin: 0 0;
  animation: lzSpark 620ms linear infinite;
  animation-delay: calc(var(--i) * -78ms);
}
@keyframes lzSpark {
  0%   { opacity: 0.9; transform: scale(0.15); }
  70%  { opacity: 0.35; }
  100% { opacity: 0; transform: scale(1.15); }
}

.lz-smoke {
  fill: var(--text-soft);
  opacity: 0;
  animation: lzSmoke 2.6s ease-out infinite;
  animation-delay: calc(var(--i) * -520ms);
}
@keyframes lzSmoke {
  0%   { opacity: 0.24; transform: translate(0, 0) scale(0.35); }
  100% { opacity: 0;    transform: translate(-26px, -150px) scale(2.1); }
}

/* =========================================================================
   SCENE 2 — 3D PRINTER
   ========================================================================= */
.pr-plate { fill: var(--surface); stroke: var(--line-strong); stroke-width: 1; }
.pr-plate-grid { stroke: var(--line); stroke-width: 1; }

.pr-ghost polygon {
  fill: none;
  stroke: var(--line-strong);
  stroke-width: 1;
  stroke-dasharray: 4 7;
}

/* The reveal. --p comes from the scroll driver, --n is the layer count and
   --i is this layer's index. opacity clamps itself, so layers switch on in
   sequence as p rises. */
.pr-layer {
  --r: clamp(0, calc(var(--p, 0) * var(--n) - var(--i)), 1);
  opacity: var(--r);
  transform: translateY(calc((1 - var(--r)) * -14px));
}
.pr-face-t { fill: var(--hue); }
.pr-face-r { fill: color-mix(in srgb, var(--hue) 62%, #000); }
.pr-face-f { fill: color-mix(in srgb, var(--hue) 40%, #000); }

.pr-layer polygon { stroke: var(--bg); stroke-width: 0.75; }

.pr-nozzle rect { fill: var(--surface-2); stroke: var(--text-soft); stroke-width: 1.5; }
.pr-nozzle line { stroke: var(--text-mute); stroke-width: 1; }
.pr-nozzle path { fill: var(--surface); stroke: var(--text-soft); stroke-width: 1.5; }
.pr-tip { fill: var(--hue); filter: drop-shadow(0 0 5px var(--hue)); }
.pr-filament { stroke: var(--hue-2); stroke-width: 2.5; stroke-dasharray: 7 5; }

/* =========================================================================
   MICRO SCENES
   The small animations beside a sticky section heading. All use a 64×64
   viewBox and inherit the machine's --hue, so they match whatever page they
   are on. Strokes are deliberately chunky — these render at ~54px.
   ========================================================================= */
/* overflow: hidden is what lets a tip or a pin animate in from off-frame
   without spilling outside its little bordered box. */
.micro { width: 100%; height: 100%; display: block; overflow: hidden; }

/* shared defaults — individual scenes override colour below */
.micro path, .micro circle, .micro rect, .micro ellipse {
  fill: none; stroke: var(--text-mute); stroke-width: 2.4;
  stroke-linecap: round; stroke-linejoin: round;
}
.micro text {
  font-family: var(--font-mono); font-size: 15px; font-weight: 700;
  fill: var(--hue); stroke: none;
}

/* --- goggles --- */
.mg-frame, .mg-strap { stroke: var(--hue); }
.mg-ghost { stroke: var(--line-strong); }
.mg-glint { stroke: var(--hue-2); stroke-width: 3; opacity: 0.9; }

/* --- countdown timer --- */
.mt-track { stroke: var(--line-strong); }
.mt-arc   { stroke: var(--hue); stroke-width: 3.4; }
.mt-btn   { stroke: var(--hue); fill: var(--hue-tint); }

/* --- fan --- */
.mf-case   { stroke: var(--line-strong); stroke-dasharray: 3 5; }
.mf-blades path { stroke: var(--hue); fill: var(--hue-tint); stroke-width: 2; }
.mf-hub    { stroke: var(--hue); fill: var(--bg); }

/* --- thermometer --- */
.mh-tube  { stroke: var(--line-strong); stroke-width: 8; }
.mh-bulb  { stroke: var(--line-strong); }
.mh-fill  { stroke: var(--hue); stroke-width: 5; filter: drop-shadow(0 0 4px var(--hue)); }
.mh-core  { stroke: none; fill: var(--hue); }
.mh-ticks { stroke: var(--text-mute); stroke-width: 1.8; }

/* --- wiping the tip --- */
.mw-sponge { stroke: var(--line-strong); fill: var(--surface-2); }
.mw-pores  { stroke: var(--text-mute); stroke-width: 1.8; }
.mw-shaft  { stroke: var(--text-mute); fill: var(--surface-2); }
.mw-chisel { stroke: var(--hue); fill: var(--hue-tint); }
.mw-oxide  { stroke: none; fill: var(--text-mute); }
.mw-fleck  {
  stroke: none; fill: var(--hue-2);
  animation: mwFleck 900ms ease-out infinite;
  animation-delay: calc(var(--i) * -300ms);
}
@keyframes mwFleck {
  0%   { opacity: 0.9; transform: translate(0, 0) scale(0.4); }
  100% { opacity: 0;   transform: translate(calc(-14px + var(--i) * 14px), -16px) scale(1); }
}

/* --- joint forming --- */
.mj-glow   { stroke: none; fill: var(--hue); filter: blur(6px); opacity: 0; }
.mj-board  { stroke: var(--line-strong); fill: var(--surface-2); }
.mj-pad    { stroke: none; fill: var(--hue-2); }
.mj-leg    { stroke: var(--text-mute); stroke-width: 3; }
.mj-fillet {
  stroke: none; fill: var(--hue-2);
  transform-box: view-box; transform-origin: 32px 45px;
}
.mj-tip path { stroke: var(--hue); fill: var(--hue-tint); }
.mj-smoke {
  stroke: var(--text-soft); stroke-width: 2;
  animation: mjSmoke 2.2s ease-out infinite;
  animation-delay: calc(var(--i) * -730ms);
}
@keyframes mjSmoke {
  0%   { opacity: 0;   transform: translate(0, 0) scale(0.3); }
  20%  { opacity: 0.5; }
  100% { opacity: 0;   transform: translate(calc(-4px + var(--i) * 4px), -22px) scale(1.4); }
}

/* --- location pin --- */
.mp-pin path { stroke: var(--hue); fill: var(--hue-tint); }
.mp-dot { stroke: none; fill: var(--hue); }
.mp-ripple {
  stroke: var(--hue); stroke-width: 2;
  animation: mpRipple 2s ease-out infinite;
  animation-delay: calc(var(--i) * -1s);
}
@keyframes mpRipple {
  0%   { opacity: 0.8; transform: scale(0.4); }
  100% { opacity: 0;   transform: scale(1.8); }
}
.mp-ripples { transform-box: view-box; transform-origin: 32px 54px; }
.mp-ripple  { transform-box: view-box; transform-origin: 32px 54px; }

/* --- video progress ring --- */
.mv-track { stroke: var(--line-strong); }
.mv-arc   { stroke: var(--hue); stroke-width: 3.4; }
.mv-tri   {
  stroke: none; fill: var(--hue);
  transform-box: view-box; transform-origin: 32px 32px;
}

/* =========================================================================
   SCENE 3 — SOLDERING
   ========================================================================= */
.sd-heat {
  fill: url(#sdHeat);
  transform-origin: 600px 372px;
  transform-box: view-box;
}

.sd-board rect { fill: var(--surface-2); stroke: var(--line-strong); stroke-width: 1; }
.sd-pad { fill: var(--hue-2); stroke: none; }
.sd-pad--dim { fill: var(--text-mute); opacity: 0.55; }
.sd-trace { stroke: var(--text-mute); stroke-width: 2; fill: none; opacity: 0.6; }

.sd-part rect { fill: var(--surface); stroke: var(--text-soft); stroke-width: 1.5; }
.sd-part line { stroke: var(--text-soft); stroke-width: 3; }

.sd-fillet {
  fill: var(--hue-2);
  transform-box: view-box;
  transform-origin: 600px 379px;   /* grows up out of the pad */
  filter: drop-shadow(0 0 8px var(--hue));
}

.sd-wire line {
  stroke: var(--text-soft);
  stroke-width: 5;
  stroke-linecap: round;
  stroke-dasharray: 1 0;
}

.sd-iron rect { fill: var(--surface-2); stroke: var(--text-soft); stroke-width: 1.5; }
.sd-grip { fill: var(--surface) !important; }
.sd-iron path { fill: var(--text-mute); stroke: var(--text-soft); stroke-width: 1; }
.sd-cable { stroke: var(--text-mute); stroke-width: 6; stroke-linecap: round; }
.sd-tip-hot {
  fill: url(#sdTip) !important;
  stroke: none !important;
  filter: drop-shadow(0 0 9px var(--hue));
}

.sd-smoke {
  fill: none;
  stroke: var(--text-soft);
  stroke-width: 2.5;
  stroke-linecap: round;
  opacity: 0;
  animation: sdSmoke 3.4s ease-out infinite;
  animation-delay: calc(var(--i) * -480ms);
}
@keyframes sdSmoke {
  0%   { opacity: 0;    transform: translate(0, 0) scale(0.3); }
  18%  { opacity: 0.42; }
  100% { opacity: 0;    transform: translate(calc(-22px + var(--i) * 7px), -220px) scale(1.6); }
}

.sd-shimmer {
  fill: none;
  stroke: var(--hue-2);
  stroke-width: 1.5;
  opacity: 0;
  animation: sdShimmer 1.9s ease-in-out infinite;
  animation-delay: calc(var(--i) * -620ms);
}
@keyframes sdShimmer {
  0%   { opacity: 0;    transform: translateY(-6px) scaleX(0.9); }
  40%  { opacity: 0.45; }
  100% { opacity: 0;    transform: translateY(-64px) scaleX(1.25); }
}
