// scene.jsx — Two systems converging into one
// Engineering (left) ↔ Behavioral (right) → Unified

const DUR = 24;

// ── Palette ─────────────────────────────────────────────────────────────
const BG = '#F4F1EA';                 // warm off-white
const INK = '#16161A';                // deep ink
const RULE = 'rgba(22,22,26,0.10)';   // hairlines
const FAINT = 'rgba(22,22,26,0.045)'; // grid

const ENG = 'oklch(58% 0.08 255)';      // muted indigo
const ENG_SOFT = 'oklch(58% 0.08 255 / 0.18)';
const BEH = 'oklch(62% 0.09 50)';       // warm clay
const BEH_SOFT = 'oklch(62% 0.09 50 / 0.18)';
const UNI = 'oklch(40% 0.04 280)';      // converged graphite-violet

// Phase weights from time (each 0..1, smoothed, summing roughly across overlap)
function phases(t) {
  // independent → complexify → cross → unify
  const p1 = animate({ from: 0, to: 1, start: 0,    end: 5,  ease: Easing.easeInOutCubic })(t); // emerge
  const p2 = animate({ from: 0, to: 1, start: 5,    end: 11, ease: Easing.easeInOutCubic })(t); // complexify
  const cross = animate({ from: 0, to: 1, start: 10, end: 17, ease: Easing.easeInOutCubic })(t); // mutual influence
  const unify = animate({ from: 0, to: 1, start: 16, end: 22.5, ease: Easing.easeInOutCubic })(t); // merge
  return { p1, p2, cross, unify };
}

// Smoothly mix between left/right anchored layout and a centered shared layout
function lerp(a, b, t) { return a + (b - a) * t; }

// Deterministic pseudo-random (so layout is stable)
function rand(seed) {
  const x = Math.sin(seed * 9301 + 49297) * 233280;
  return x - Math.floor(x);
}

// ── Engineering layout: orthogonal grid of nodes ────────────────────────
const ENG_NODES = (() => {
  const cols = 4, rows = 4;
  const x0 = 110, y0 = 180;
  const dx = 130, dy = 130;
  const nodes = [];
  for (let r = 0; r < rows; r++) {
    for (let c = 0; c < cols; c++) {
      nodes.push({
        id: `e${r}${c}`,
        x: x0 + c * dx,
        y: y0 + r * dy,
        col: c, row: r,
      });
    }
  }
  return nodes;
})();

// Engineering edges: orthogonal lattice + a few feedback loops
const ENG_EDGES = (() => {
  const e = [];
  // grid neighbors right/down
  for (let r = 0; r < 4; r++) {
    for (let c = 0; c < 4; c++) {
      const i = r * 4 + c;
      if (c < 3) e.push([i, i + 1, 'h']);
      if (r < 3) e.push([i, i + 4, 'v']);
    }
  }
  // a couple of diagonal feedback edges
  e.push([5, 10, 'fb']);
  e.push([2, 9, 'fb']);
  e.push([12, 7, 'fb']);
  return e;
})();

// ── Behavioral layout: scattered organic nodes ──────────────────────────
const BEH_NODES = (() => {
  const cx = 1180, cy = 450;
  const nodes = [];
  // 16 scattered, irregular positions
  const seeds = [
    [-220, -200], [-90, -250], [60, -210], [200, -180],
    [-260, -60], [-110, -90], [50, -50],  [220, -30],
    [-240, 80],  [-100, 60],  [80, 90],   [240, 60],
    [-200, 220], [-60, 200],  [110, 230], [230, 200],
  ];
  seeds.forEach(([dx, dy], i) => {
    // jitter for organic feel
    const jx = (rand(i + 1) - 0.5) * 18;
    const jy = (rand(i + 7) - 0.5) * 18;
    nodes.push({ id: `b${i}`, x: cx + dx + jx, y: cy + dy + jy, idx: i });
  });
  return nodes;
})();

// Behavioral edges: probabilistic-feeling, longer-range, no grid
const BEH_EDGES = [
  [0, 1], [1, 2], [2, 3],
  [0, 5], [1, 6], [3, 7],
  [4, 5], [5, 6], [6, 7],
  [4, 9], [5, 10], [7, 11],
  [8, 9], [9, 10], [10, 11],
  [8, 13], [10, 14], [11, 15],
  [12, 13], [13, 14], [14, 15],
  // probabilistic skips
  [1, 9], [3, 10], [6, 13], [10, 7], [4, 13],
];

// ── Unified target layout (where both sets converge to) ─────────────────
// Slight hex-ish lattice centered, blending rectilinear with organic offset.
const UNI_LEFT = (() => {
  // 16 positions on the left half of stage
  const cx = 540, cy = 450;
  const out = [];
  const cols = 4, rows = 4;
  const dx = 110, dy = 110;
  for (let r = 0; r < rows; r++) {
    for (let c = 0; c < cols; c++) {
      // half-step row offset for hex feel
      const offX = (r % 2 === 0 ? 0 : dx / 2);
      out.push({
        x: cx - (cols - 1) * dx / 2 + c * dx + offX - dx / 4,
        y: cy - (rows - 1) * dy / 2 + r * dy,
      });
    }
  }
  return out;
})();
const UNI_RIGHT = (() => {
  const cx = 1060, cy = 450;
  const out = [];
  const cols = 4, rows = 4;
  const dx = 110, dy = 110;
  for (let r = 0; r < rows; r++) {
    for (let c = 0; c < cols; c++) {
      const offX = (r % 2 === 0 ? dx / 2 : 0);
      out.push({
        x: cx - (cols - 1) * dx / 2 + c * dx + offX - dx / 4,
        y: cy - (rows - 1) * dy / 2 + r * dy,
      });
    }
  }
  return out;
})();

// ── Drawing helpers ─────────────────────────────────────────────────────

// Returns a node's animated position at time t
function engPos(i, t, ph) {
  const n = ENG_NODES[i];
  const u = UNI_LEFT[i];
  // gentle breathing (logical, small)
  const breath = Math.sin(t * 0.7 + i * 0.4) * 1.2;
  // soften grid: under cross influence, introduce small organic jitter
  const softX = Math.sin(t * 0.8 + i * 1.3) * 6 * ph.cross;
  const softY = Math.cos(t * 0.9 + i * 1.7) * 5 * ph.cross;
  // converge toward unified target
  const k = ph.unify;
  const x = lerp(n.x, u.x, k) + softX;
  const y = lerp(n.y, u.y, k) + softY + breath;
  return { x, y };
}

function behPos(i, t, ph) {
  const n = BEH_NODES[i];
  const u = UNI_RIGHT[i];
  // organic drift always present
  const driftX = Math.sin(t * 0.55 + i * 0.9) * 6;
  const driftY = Math.cos(t * 0.45 + i * 1.1) * 6;
  // structure encroaching from cross influence — drift dampens
  const damp = 1 - 0.7 * ph.cross;
  const k = ph.unify;
  const x = lerp(n.x, u.x, k) + driftX * damp;
  const y = lerp(n.y, u.y, k) + driftY * damp;
  return { x, y };
}

// Curvature of behavioral edges shrinks as system structuralizes
function behPath(p1, p2, t, idx, ph) {
  const mx = (p1.x + p2.x) / 2;
  const my = (p1.y + p2.y) / 2;
  const dx = p2.x - p1.x;
  const dy = p2.y - p1.y;
  const len = Math.hypot(dx, dy) || 1;
  const nx = -dy / len, ny = dx / len;
  // curvature decreases as cross/unify rises
  const baseCurve = (Math.sin(t * 0.6 + idx * 1.7) * 0.5 + 0.5) * 28 + 14;
  const curve = baseCurve * (1 - 0.85 * Math.max(ph.cross, ph.unify));
  // direction of curve flips by edge index for variety
  const sign = (idx % 2 === 0) ? 1 : -1;
  const cx = mx + nx * curve * sign;
  const cy = my + ny * curve * sign;
  return `M ${p1.x} ${p1.y} Q ${cx} ${cy} ${p2.x} ${p2.y}`;
}

// Engineering edges: orthogonal early, gain slight curve later
function engPath(p1, p2, t, kind, idx, ph) {
  // small added curvature comes from cross influence (organic creep)
  const mx = (p1.x + p2.x) / 2;
  const my = (p1.y + p2.y) / 2;
  const dx = p2.x - p1.x;
  const dy = p2.y - p1.y;
  const len = Math.hypot(dx, dy) || 1;
  const nx = -dy / len, ny = dx / len;
  const sign = (idx % 2 === 0) ? 1 : -1;
  const orgCurve = (Math.sin(t * 0.5 + idx * 0.9) * 0.5 + 0.5) * 18;
  const curve = orgCurve * ph.cross * 0.9 + (kind === 'fb' ? 22 : 0);

  if (kind === 'fb') {
    // feedback edges always curved a bit
    const cx = mx + nx * curve * sign;
    const cy = my + ny * curve * sign;
    return `M ${p1.x} ${p1.y} Q ${cx} ${cy} ${p2.x} ${p2.y}`;
  }
  if (ph.cross < 0.05) {
    // pure orthogonal: L-route via shared corner
    if (kind === 'h') return `M ${p1.x} ${p1.y} L ${p2.x} ${p2.y}`;
    if (kind === 'v') return `M ${p1.x} ${p1.y} L ${p2.x} ${p2.y}`;
  }
  const cx = mx + nx * curve * sign;
  const cy = my + ny * curve * sign;
  return `M ${p1.x} ${p1.y} Q ${cx} ${cy} ${p2.x} ${p2.y}`;
}

// ── Components ──────────────────────────────────────────────────────────

function Backdrop() {
  // subtle grid that softens over time
  const t = useTime();
  const ph = phases(t);
  // grid opacity max early, fades a bit by unify
  const gridOpacity = lerp(0.7, 0.25, ph.unify);
  return (
    <svg width="1600" height="900" style={{ position: 'absolute', inset: 0 }}>
      <defs>
        <pattern id="grid" x="0" y="0" width="40" height="40" patternUnits="userSpaceOnUse">
          <path d="M 40 0 L 0 0 0 40" fill="none" stroke={FAINT} strokeWidth="1" />
        </pattern>
      </defs>
      <rect width="1600" height="900" fill={BG} />
      <rect width="1600" height="900" fill="url(#grid)" opacity={gridOpacity} />
    </svg>
  );
}

function CenterDivider() {
  const t = useTime();
  const ph = phases(t);
  // divider hairline fades as systems converge
  const op = lerp(0.18, 0, Math.max(ph.cross * 0.6, ph.unify));
  return (
    <div style={{
      position: 'absolute',
      left: 800, top: 110,
      width: 1, height: 680,
      background: INK,
      opacity: op,
      transition: 'none',
    }} />
  );
}

function Header() {
  const t = useTime();
  const ph = phases(t);

  // labels fade
  const engOp = lerp(1, 0.0, Math.min(1, ph.unify * 1.4));
  const behOp = lerp(1, 0.0, Math.min(1, ph.unify * 1.4));
  const uniOp = ph.unify;

  return (
    <>
      {/* Top eyebrow */}
      <div style={{
        position: 'absolute', left: 80, top: 64,
        fontFamily: 'JetBrains Mono, ui-monospace, monospace',
        fontSize: 11, letterSpacing: '0.18em', textTransform: 'uppercase',
        color: 'rgba(22,22,26,0.5)',
      }}>
        Convergence — 0{Math.floor(t).toString().padStart(2,'0')} / 0{DUR}
      </div>

      {/* Engineering label */}
      <div style={{
        position: 'absolute', left: 80, top: 110,
        opacity: engOp, transition: 'none',
      }}>
        <div style={{
          fontFamily: 'JetBrains Mono, ui-monospace, monospace',
          fontSize: 10, letterSpacing: '0.22em', textTransform: 'uppercase',
          color: ENG, marginBottom: 8,
        }}>
          System A · Engineering
        </div>
        <div style={{
          fontFamily: 'Inter Tight, Inter, system-ui, sans-serif',
          fontWeight: 500, fontSize: 22, color: INK, letterSpacing: '-0.015em',
        }}>
          Structured · Deterministic
        </div>
      </div>

      {/* Behavioral label */}
      <div style={{
        position: 'absolute', right: 80, top: 110, textAlign: 'right',
        opacity: behOp, transition: 'none',
      }}>
        <div style={{
          fontFamily: 'JetBrains Mono, ui-monospace, monospace',
          fontSize: 10, letterSpacing: '0.22em', textTransform: 'uppercase',
          color: BEH, marginBottom: 8,
        }}>
          System B · Behavioral
        </div>
        <div style={{
          fontFamily: 'Inter Tight, Inter, system-ui, sans-serif',
          fontWeight: 500, fontSize: 22, color: INK, letterSpacing: '-0.015em',
        }}>
          Adaptive · Probabilistic
        </div>
      </div>

      {/* Unified label appears */}
      <div style={{
        position: 'absolute', left: '50%', top: 110, transform: 'translateX(-50%)',
        opacity: uniOp, textAlign: 'center', transition: 'none',
      }}>
        <div style={{
          fontFamily: 'JetBrains Mono, ui-monospace, monospace',
          fontSize: 10, letterSpacing: '0.22em', textTransform: 'uppercase',
          color: UNI, marginBottom: 8,
        }}>
          Unified
        </div>
        <div style={{
          fontFamily: 'Inter Tight, Inter, system-ui, sans-serif',
          fontWeight: 500, fontSize: 22, color: INK, letterSpacing: '-0.015em',
        }}>
          Structured · Adaptive
        </div>
      </div>

      {/* Bottom caption — phase indicator */}
      <PhaseCaption />
    </>
  );
}

function PhaseCaption() {
  const t = useTime();
  let label = 'Independent emergence';
  if (t > 5) label = 'Growing complexity';
  if (t > 11) label = 'Mutual influence';
  if (t > 17) label = 'Unified system';

  return (
    <div style={{
      position: 'absolute', left: '50%', bottom: 64,
      transform: 'translateX(-50%)',
      fontFamily: 'JetBrains Mono, ui-monospace, monospace',
      fontSize: 11, letterSpacing: '0.22em', textTransform: 'uppercase',
      color: 'rgba(22,22,26,0.55)',
    }}>
      <span style={{ color: 'rgba(22,22,26,0.35)' }}>Phase · </span>
      {label}
    </div>
  );
}

// Engineering side
function EngSystem() {
  const t = useTime();
  const ph = phases(t);

  // node reveal staggered
  const positions = ENG_NODES.map((_, i) => engPos(i, t, ph));

  // edges revealed in waves: grid first, feedback later, organic creep with cross
  return (
    <svg width="1600" height="900" style={{ position: 'absolute', inset: 0, pointerEvents: 'none' }}>
      <defs>
        <linearGradient id="engEdgeGrad" x1="0" y1="0" x2="1" y2="0">
          <stop offset="0%" stopColor={ENG} stopOpacity="0.7" />
          <stop offset="100%" stopColor={UNI} stopOpacity="0.7" />
        </linearGradient>
      </defs>

      {/* edges */}
      {ENG_EDGES.map(([a, b, kind], i) => {
        // reveal time
        const baseReveal = kind === 'fb' ? 6 + (i % 3) * 0.4 : 1.2 + i * 0.12;
        const r = animate({ from: 0, to: 1, start: baseReveal, end: baseReveal + 1.2,
                            ease: Easing.easeInOutCubic })(t);
        if (r <= 0.001) return null;

        const p1 = positions[a], p2 = positions[b];
        const d = engPath(p1, p2, t, kind, i, ph);
        const stroke = ph.unify > 0 ? mix(ENG, UNI, ph.unify) : ENG;
        const dashOn = ph.cross > 0.05 && (i % 5 === 0);

        // feedback loop pulse
        const isFb = kind === 'fb';
        const pulse = isFb ? (Math.sin(t * 1.6 + i) * 0.3 + 0.7) : 1;

        return (
          <path key={`ee${i}`}
            d={d}
            fill="none"
            stroke={stroke}
            strokeWidth={isFb ? 1.2 : 1}
            strokeOpacity={r * (isFb ? 0.55 * pulse : 0.5)}
            strokeDasharray={dashOn ? '3 4' : (isFb ? '2 3' : 'none')}
            strokeLinecap="round"
          />
        );
      })}

      {/* nodes */}
      {ENG_NODES.map((n, i) => {
        const reveal = animate({ from: 0, to: 1, start: 0.2 + i * 0.08, end: 1.0 + i * 0.08,
                                  ease: Easing.easeOutCubic })(t);
        if (reveal <= 0.001) return null;
        const p = positions[i];
        const c = ph.unify > 0 ? mix(ENG, UNI, ph.unify) : ENG;
        // engineering nodes: precise small squares; soften to roundedness as systems merge
        const radius = lerp(1, 7, ph.unify);
        const size = 11;
        return (
          <g key={n.id} transform={`translate(${p.x}, ${p.y})`} opacity={reveal}>
            <rect
              x={-size/2} y={-size/2}
              width={size} height={size}
              rx={radius} ry={radius}
              fill={BG}
              stroke={c}
              strokeWidth={1.3}
            />
            <rect
              x={-2.5} y={-2.5}
              width={5} height={5}
              rx={lerp(0, 2.5, ph.unify)}
              fill={c}
            />
          </g>
        );
      })}

      {/* feedback loop arcs — small loop badges on a few nodes */}
      {[5, 10, 6].map((idx, k) => {
        const start = 6.5 + k * 0.5;
        const r = animate({ from: 0, to: 1, start, end: start + 0.9, ease: Easing.easeOutCubic })(t);
        if (r <= 0.001) return null;
        const p = positions[idx];
        const wob = Math.sin(t * 1.4 + k) * 1.2;
        return (
          <g key={`fbloop${k}`} opacity={r * 0.55}>
            <circle cx={p.x + 18 + wob} cy={p.y - 18} r={9}
              fill="none" stroke={ENG} strokeWidth="1" strokeDasharray="2 3" />
            <path d={`M ${p.x + 18 + 9} ${p.y - 18} l -3 -2 l 0 4 z`} fill={ENG} />
          </g>
        );
      })}
    </svg>
  );
}

// Behavioral side
function BehSystem() {
  const t = useTime();
  const ph = phases(t);

  const positions = BEH_NODES.map((_, i) => behPos(i, t, ph));

  return (
    <svg width="1600" height="900" style={{ position: 'absolute', inset: 0, pointerEvents: 'none' }}>
      {/* probabilistic clouds (radial halos) on a couple of nodes — subtle */}
      {[1, 6, 9, 14].map((idx, k) => {
        const start = 0.4 + k * 0.5;
        const r = animate({ from: 0, to: 1, start, end: start + 1.6, ease: Easing.easeOutCubic })(t);
        if (r <= 0.001) return null;
        const p = positions[idx];
        const breath = Math.sin(t * 0.7 + k) * 0.08 + 1;
        // halos shrink as system structures
        const size = lerp(40, 16, Math.max(ph.cross * 0.6, ph.unify)) * breath;
        return (
          <circle key={`halo${k}`}
            cx={p.x} cy={p.y} r={size}
            fill={BEH}
            opacity={r * 0.07 * (1 - ph.unify * 0.5)}
          />
        );
      })}

      {/* edges */}
      {BEH_EDGES.map(([a, b], i) => {
        const baseReveal = 0.8 + i * 0.18;
        const r = animate({ from: 0, to: 1, start: baseReveal, end: baseReveal + 1.2,
                            ease: Easing.easeInOutCubic })(t);
        if (r <= 0.001) return null;

        const p1 = positions[a], p2 = positions[b];
        const d = behPath(p1, p2, t, i, ph);
        const stroke = ph.unify > 0 ? mix(BEH, UNI, ph.unify) : BEH;

        // some edges flicker (probabilistic) early on, settle as cross/unify rise
        const flicker = i % 4 === 0
          ? (0.55 + 0.45 * Math.sin(t * 1.2 + i) * (1 - ph.cross))
          : 1;

        return (
          <path key={`be${i}`}
            d={d}
            fill="none"
            stroke={stroke}
            strokeWidth={1}
            strokeOpacity={r * 0.5 * flicker}
            strokeLinecap="round"
          />
        );
      })}

      {/* nodes */}
      {BEH_NODES.map((n, i) => {
        const reveal = animate({ from: 0, to: 1, start: 0.1 + i * 0.07, end: 0.9 + i * 0.07,
                                  ease: Easing.easeOutCubic })(t);
        if (reveal <= 0.001) return null;
        const p = positions[i];
        const c = ph.unify > 0 ? mix(BEH, UNI, ph.unify) : BEH;
        // behavioral nodes: organic circles; gain a subtle ring (structure) as cross rises
        const breath = Math.sin(t * 0.9 + i * 0.6) * 0.4;
        const radius = 5.5 + breath;
        const ringWidth = lerp(0, 1.2, ph.cross);
        return (
          <g key={n.id} transform={`translate(${p.x}, ${p.y})`} opacity={reveal}>
            {/* ring picked up from engineering side */}
            <circle r={radius + 4} fill="none" stroke={c}
              strokeWidth={ringWidth} strokeOpacity={0.35} />
            <circle r={radius} fill={c} />
          </g>
        );
      })}
    </svg>
  );
}

// ── Bridges: edges that cross the divider during the cross/unify phases ─
function Bridges() {
  const t = useTime();
  const ph = phases(t);
  if (ph.cross <= 0.01) return null;

  // pair up engineering and behavioral nodes for bridges
  const pairs = [
    [3, 0],   // top row
    [7, 4],
    [11, 8],
    [15, 12], // bottom row
    [6, 5],
    [10, 9],
    [2, 1],
    [14, 13],
  ];

  const engPositions = ENG_NODES.map((_, i) => engPos(i, t, ph));
  const behPositions = BEH_NODES.map((_, i) => behPos(i, t, ph));

  return (
    <svg width="1600" height="900" style={{ position: 'absolute', inset: 0, pointerEvents: 'none' }}>
      {pairs.map(([ei, bi], k) => {
        const start = 11 + k * 0.35;
        const r = animate({ from: 0, to: 1, start, end: start + 1.4,
                            ease: Easing.easeInOutCubic })(t);
        if (r <= 0.001) return null;

        const p1 = engPositions[ei];
        const p2 = behPositions[bi];
        const mx = (p1.x + p2.x) / 2;
        const my = (p1.y + p2.y) / 2;
        const dx = p2.x - p1.x;
        const dy = p2.y - p1.y;
        const len = Math.hypot(dx, dy) || 1;
        const nx = -dy / len, ny = dx / len;
        // moderate curvature — gets straighter as unify rises
        const curve = lerp(28, 6, ph.unify);
        const sign = (k % 2 === 0) ? 1 : -1;
        const cx = mx + nx * curve * sign;
        const cy = my + ny * curve * sign;
        const d = `M ${p1.x} ${p1.y} Q ${cx} ${cy} ${p2.x} ${p2.y}`;

        // animated dash — flow along bridge while still "negotiating"
        const flow = (1 - ph.unify) * 0.7;
        const dashOff = -((t * 30) % 100);

        return (
          <path key={`bridge${k}`}
            d={d}
            fill="none"
            stroke={UNI}
            strokeWidth={1}
            strokeOpacity={r * (0.32 + 0.18 * flow)}
            strokeDasharray={flow > 0.05 ? '4 5' : 'none'}
            strokeDashoffset={dashOff}
            strokeLinecap="round"
          />
        );
      })}
    </svg>
  );
}

// ── Color mixing utility ───────────────────────────────────────────────
// Blend two oklch strings by parsing them. Falls back to CSS color-mix.
function mix(a, b, t) {
  return `color-mix(in oklch, ${a} ${(1 - t) * 100}%, ${b} ${t * 100}%)`;
}

// ── Composition counter (lower-right meta) ─────────────────────────────
function Meta() {
  const t = useTime();
  const ph = phases(t);
  const eng = (4 + Math.floor(ph.p1 * 12 + ph.p2 * 4));
  const beh = (4 + Math.floor(ph.p1 * 12 + ph.p2 * 4));
  const links = Math.floor((ph.cross * 8) + (ph.unify * 4));

  const Row = ({ label, val, color = INK }) => (
    <div style={{ display: 'flex', justifyContent: 'space-between', gap: 18 }}>
      <span style={{ color: 'rgba(22,22,26,0.5)' }}>{label}</span>
      <span style={{ color, fontVariantNumeric: 'tabular-nums' }}>{val}</span>
    </div>
  );

  return (
    <div style={{
      position: 'absolute', right: 80, bottom: 64,
      fontFamily: 'JetBrains Mono, ui-monospace, monospace',
      fontSize: 10, letterSpacing: '0.08em', textTransform: 'uppercase',
      color: INK, minWidth: 180,
      display: 'flex', flexDirection: 'column', gap: 4,
    }}>
      <Row label="nodes · A" val={String(eng).padStart(2,'0')} color={mix(ENG, UNI, ph.unify)} />
      <Row label="nodes · B" val={String(beh).padStart(2,'0')} color={mix(BEH, UNI, ph.unify)} />
      <Row label="bridges"   val={String(links).padStart(2,'0')} color={UNI} />
    </div>
  );
}

function CornerMark() {
  return (
    <div style={{
      position: 'absolute', right: 80, top: 64,
      fontFamily: 'JetBrains Mono, ui-monospace, monospace',
      fontSize: 11, letterSpacing: '0.18em', textTransform: 'uppercase',
      color: 'rgba(22,22,26,0.5)',
    }}>
      Two systems · One network
    </div>
  );
}

// ── Root scene ─────────────────────────────────────────────────────────
function Scene() {
  return (
    <>
      <Backdrop />
      <CenterDivider />
      <Header />
      <CornerMark />
      <EngSystem />
      <BehSystem />
      <Bridges />
      <Meta />
    </>
  );
}

window.Scene = Scene;
window.SCENE_DUR = DUR;
window.SCENE_BG = BG;
