// Animated visuals for each experience era
const { useEffect, useRef, useState } = React;

// 1. IBM/HCUP — animated data pipeline showing 50 states flowing through SAS macros to 130 databases
function PipelineVisual({ active }) {
  const [tick, setTick] = useState(0);
  useEffect(() => {
    if (!active) return;
    const id = setInterval(() => setTick(t => t + 1), 80);
    return () => clearInterval(id);
  }, [active]);

  // 50 input streams condensing through center, fanning out to 130 outputs
  const inputs = Array.from({ length: 50 });
  const outputs = Array.from({ length: 26 }); // visual subset of 130

  return (
    <div style={{ width: '100%', maxWidth: 560, aspectRatio: '1 / 1', position: 'relative' }}>
      <svg viewBox="0 0 400 400" style={{ width: '100%', height: '100%' }}>
        {/* Title chrome */}
        <text x="20" y="28" fill="#6b6862" fontFamily="JetBrains Mono" fontSize="9" letterSpacing="1.5">
          HCUP MASTER PROCESSOR · ANNUAL CYCLE
        </text>
        <line x1="20" y1="36" x2="380" y2="36" stroke="#2a2a32" strokeWidth="0.5" />

        {/* Input streams from left */}
        {inputs.map((_, i) => {
          const y = 56 + (i * (300 / 50));
          const phase = (tick * 0.04 + i * 0.13) % 1;
          const x = 30 + phase * 140;
          const opacity = 0.15 + Math.sin(phase * Math.PI) * 0.85;
          return (
            <g key={`in-${i}`}>
              <line x1="30" y1={y} x2="170" y2={y} stroke="#2a2a32" strokeWidth="0.3" />
              <circle cx={x} cy={y} r="1.4" fill="#d97a3a" opacity={opacity} />
            </g>
          );
        })}

        {/* Center: SAS Macro Engine */}
        <rect x="170" y="120" width="60" height="170" fill="#0d0d10" stroke="#d97a3a" strokeWidth="1" />
        <rect x="170" y="120" width="60" height="170" fill="url(#engineFill)" opacity="0.3" />
        <defs>
          <linearGradient id="engineFill" x1="0" y1="0" x2="0" y2="1">
            <stop offset="0%" stopColor="#d97a3a" stopOpacity="0.5" />
            <stop offset="100%" stopColor="#d97a3a" stopOpacity="0" />
          </linearGradient>
        </defs>
        {/* Engine internals: rotating macro names */}
        {['STDFMT', 'PRIVACY', 'DRG_GRP', 'ICD_MAP', 'CPT_VFY', 'STATE_X', 'LINK_AHA'].map((name, i) => {
          const visible = (Math.floor(tick / 12) % 7) === i;
          return (
            <text
              key={name}
              x="200" y={205}
              fill={visible ? '#ece7df' : '#2a2a32'}
              fontFamily="JetBrains Mono" fontSize="7"
              textAnchor="middle"
              letterSpacing="0.5"
            >
              {name}
            </text>
          );
        })}
        <text x="200" y="115" fill="#d97a3a" fontFamily="JetBrains Mono" fontSize="7" textAnchor="middle" letterSpacing="1">
          SAS MACRO
        </text>
        <text x="200" y="305" fill="#6b6862" fontFamily="JetBrains Mono" fontSize="7" textAnchor="middle" letterSpacing="1">
          ETL · QA · HIPAA
        </text>

        {/* Output streams to right */}
        {outputs.map((_, i) => {
          const y = 70 + (i * (260 / 26));
          const phase = ((tick * 0.04 + i * 0.09) + 0.5) % 1;
          const x = 230 + phase * 140;
          const opacity = 0.15 + Math.sin(phase * Math.PI) * 0.85;
          return (
            <g key={`out-${i}`}>
              <line x1="230" y1={y} x2="370" y2={y} stroke="#2a2a32" strokeWidth="0.3" />
              <circle cx={x} cy={y} r="1.6" fill="#ece7df" opacity={opacity} />
            </g>
          );
        })}

        {/* Counters */}
        <text x="20" y="350" fill="#b8b3ab" fontFamily="JetBrains Mono" fontSize="10" letterSpacing="1">
          50 STATES
        </text>
        <text x="20" y="365" fill="#6b6862" fontFamily="JetBrains Mono" fontSize="8">
          IN
        </text>

        <text x="380" y="350" fill="#b8b3ab" fontFamily="JetBrains Mono" fontSize="10" letterSpacing="1" textAnchor="end">
          130 DATABASES
        </text>
        <text x="380" y="365" fill="#6b6862" fontFamily="JetBrains Mono" fontSize="8" textAnchor="end">
          OUT
        </text>

        <text x="200" y="358" fill="#d97a3a" fontFamily="Instrument Serif" fontSize="22" textAnchor="middle" fontStyle="italic">
          {Math.min(130, Math.floor((tick % 200) * 0.65))}
        </text>
        <text x="200" y="375" fill="#6b6862" fontFamily="JetBrains Mono" fontSize="7" textAnchor="middle" letterSpacing="1">
          PROCESSING
        </text>
      </svg>
    </div>
  );
}

// 2. Foley Bezek — paper depositions scanning into structured rows
function DepositionsVisual({ active }) {
  const [tick, setTick] = useState(0);
  useEffect(() => {
    if (!active) return;
    const id = setInterval(() => setTick(t => t + 1), 80);
    return () => clearInterval(id);
  }, [active]);

  return (
    <div style={{ width: '100%', maxWidth: 560, aspectRatio: '1 / 1', position: 'relative' }}>
      <svg viewBox="0 0 400 400" style={{ width: '100%', height: '100%' }}>
        <text x="20" y="28" fill="#6b6862" fontFamily="JetBrains Mono" fontSize="9" letterSpacing="1.5">
          MEDICAL DEPOSITIONS · OCR EXTRACTION
        </text>
        <line x1="20" y1="36" x2="380" y2="36" stroke="#2a2a32" strokeWidth="0.5" />

        {/* Stack of papers on left */}
        {[0, 1, 2, 3, 4].map(i => {
          const offset = i * 4;
          const opacity = 1 - i * 0.12;
          return (
            <g key={i} opacity={opacity}>
              <rect x={50 + offset} y={80 + offset} width="100" height="130"
                    fill="#15151a" stroke="#2a2a32" strokeWidth="0.5" />
              {/* Squiggly text lines */}
              {[0, 1, 2, 3, 4, 5].map(line => (
                <line key={line}
                      x1={56 + offset} y1={92 + offset + line * 16}
                      x2={56 + offset + 80 - (line % 2) * 14} y2={92 + offset + line * 16}
                      stroke="#3a3a44" strokeWidth="0.6" />
              ))}
            </g>
          );
        })}

        {/* Scan beam */}
        {(() => {
          const scanY = 80 + ((tick * 2) % 130);
          return (
            <>
              <line x1="50" y1={scanY} x2="170" y2={scanY} stroke="#d97a3a" strokeWidth="1.5" opacity="0.9" />
              <rect x="50" y="80" width="120" height={Math.min(scanY - 80, 130)}
                    fill="#d97a3a" opacity="0.05" />
            </>
          );
        })()}

        {/* Arrow to right */}
        <line x1="180" y1="145" x2="220" y2="145" stroke="#6b6862" strokeWidth="0.5" strokeDasharray="2 2" />
        <polygon points="220,145 215,142 215,148" fill="#6b6862" />

        {/* Output: structured rows */}
        <g>
          <rect x="240" y="80" width="130" height="160" fill="#0d0d10" stroke="#2a2a32" strokeWidth="0.5" />
          <text x="248" y="94" fill="#d97a3a" fontFamily="JetBrains Mono" fontSize="7" letterSpacing="0.5">
            STATS.CSV
          </text>
          <line x1="245" y1="100" x2="365" y2="100" stroke="#2a2a32" strokeWidth="0.3" />
          {Array.from({ length: 10 }).map((_, i) => {
            const visible = i < Math.floor((tick % 60) / 5);
            return (
              <g key={i} opacity={visible ? 1 : 0.15}>
                <rect x={248} y={108 + i * 12} width={30 + (i * 3) % 25} height="3" fill="#b8b3ab" />
                <rect x={290} y={108 + i * 12} width={20 + (i * 7) % 30} height="3" fill="#6b6862" />
                <rect x={335} y={108 + i * 12} width={15} height="3" fill="#d97a3a" />
              </g>
            );
          })}
        </g>

        <text x="20" y="280" fill="#b8b3ab" fontFamily="Instrument Serif" fontSize="20" fontStyle="italic">
          unstructured → actionable
        </text>
        <text x="20" y="300" fill="#6b6862" fontFamily="JetBrains Mono" fontSize="9" letterSpacing="1.5">
          LITIGATION SUPPORT · 1998—1999
        </text>
      </svg>
    </div>
  );
}

// 3. SMS — accreditation: hospital nodes lighting up green/red as logistic model evaluates
function AccreditationVisual({ active }) {
  const [tick, setTick] = useState(0);
  useEffect(() => {
    if (!active) return;
    const id = setInterval(() => setTick(t => t + 1), 100);
    return () => clearInterval(id);
  }, [active]);

  const LOOP = 220;
  const loopTick = tick % LOOP;

  // Grid of hospital "nodes"
  const grid = [];
  for (let r = 0; r < 6; r++) {
    for (let c = 0; c < 8; c++) {
      grid.push({ r, c, seed: (r * 8 + c) * 7 });
    }
  }

  return (
    <div style={{ width: '100%', maxWidth: 560, aspectRatio: '1 / 1', position: 'relative' }}>
      <svg viewBox="0 0 400 400" style={{ width: '100%', height: '100%' }}>
        <text x="20" y="28" fill="#6b6862" fontFamily="JetBrains Mono" fontSize="9" letterSpacing="1.5">
          ORYX ACCREDITATION · LOGISTIC SCORING
        </text>
        <line x1="20" y1="36" x2="380" y2="36" stroke="#2a2a32" strokeWidth="0.5" />

        {/* Header columns */}
        <text x="40" y="60" fill="#6b6862" fontFamily="JetBrains Mono" fontSize="7" letterSpacing="1">
          PROVIDER
        </text>
        <text x="200" y="60" fill="#6b6862" fontFamily="JetBrains Mono" fontSize="7" letterSpacing="1">
          MODEL OUTPUT
        </text>
        <text x="340" y="60" fill="#6b6862" fontFamily="JetBrains Mono" fontSize="7" letterSpacing="1" textAnchor="end">
          STATUS
        </text>

        {/* Grid */}
        {grid.map(({ r, c, seed }) => {
          const x = 40 + c * 40;
          const y = 80 + r * 40;
          const evalPhase = (loopTick - seed * 0.4) * 0.05;
          const evaluated = evalPhase > 0;
          const settled = evalPhase > 1.2;
          const passes = (seed % 7) > 1;
          let color = '#2a2a32';
          if (evaluated && !settled) color = '#d97a3a';
          if (settled) color = passes ? '#7a9a6e' : '#9a3a3a';

          const pulse = evaluated && !settled ? Math.abs(Math.sin(evalPhase * 6)) : 0;

          return (
            <g key={`${r}-${c}`}>
              <rect x={x - 12} y={y - 12} width="24" height="24"
                    fill={color} opacity={settled ? 0.85 : (evaluated ? 0.5 + pulse * 0.5 : 0.2)} />
              <rect x={x - 12} y={y - 12} width="24" height="24"
                    fill="none" stroke={color} strokeWidth="0.5" opacity="0.6" />
            </g>
          );
        })}

        {/* Footer score */}
        {(() => {
          const total = grid.length;
          const evaluated = grid.filter(({ seed }) => (loopTick - seed * 0.4) * 0.05 > 0).length;
          const passing = grid.filter(({ seed }) => {
            const evalPhase = (loopTick - seed * 0.4) * 0.05;
            return evalPhase > 1.2 && (seed % 7) > 1;
          }).length;
          const failing = grid.filter(({ seed }) => {
            const evalPhase = (loopTick - seed * 0.4) * 0.05;
            return evalPhase > 1.2 && (seed % 7) <= 1;
          }).length;
          return (
            <>
              <text x="40" y="350" fill="#b8b3ab" fontFamily="JetBrains Mono" fontSize="9" letterSpacing="1">
                SCORED
              </text>
              <text x="40" y="368" fill="#ece7df" fontFamily="Instrument Serif" fontSize="22" fontStyle="italic">
                {evaluated}/{total}
              </text>

              <text x="180" y="350" fill="#7a9a6e" fontFamily="JetBrains Mono" fontSize="9" letterSpacing="1">
                ACCREDITED
              </text>
              <text x="180" y="368" fill="#7a9a6e" fontFamily="Instrument Serif" fontSize="22" fontStyle="italic">
                {passing}
              </text>

              <text x="320" y="350" fill="#9a3a3a" fontFamily="JetBrains Mono" fontSize="9" letterSpacing="1">
                FLAGGED
              </text>
              <text x="320" y="368" fill="#9a3a3a" fontFamily="Instrument Serif" fontSize="22" fontStyle="italic">
                {failing}
              </text>
            </>
          );
        })()}
      </svg>
    </div>
  );
}

// 4. SysteMetrics — origins: 50-state stratified sampling map / mainframe
function OriginsVisual({ active }) {
  const [tick, setTick] = useState(0);
  useEffect(() => {
    if (!active) return;
    const id = setInterval(() => setTick(t => t + 1), 100);
    return () => clearInterval(id);
  }, [active]);

  // 50 hex-like nodes representing states
  const states = Array.from({ length: 50 });

  return (
    <div style={{ width: '100%', maxWidth: 560, aspectRatio: '1 / 1', position: 'relative' }}>
      <svg viewBox="0 0 400 400" style={{ width: '100%', height: '100%' }}>
        <text x="20" y="28" fill="#6b6862" fontFamily="JetBrains Mono" fontSize="9" letterSpacing="1.5">
          SUPERPRO · STRATIFIED SAMPLING · 50 STATES
        </text>
        <line x1="20" y1="36" x2="380" y2="36" stroke="#2a2a32" strokeWidth="0.5" />

        {/* Mainframe terminal aesthetic */}
        <rect x="30" y="60" width="340" height="220" fill="#0a0a0d" stroke="#2a2a32" strokeWidth="0.5" />
        <line x1="30" y1="76" x2="370" y2="76" stroke="#2a2a32" strokeWidth="0.3" />
        <text x="40" y="71" fill="#6b6862" fontFamily="JetBrains Mono" fontSize="7" letterSpacing="1">
          SAS 6.07 · PL/I · TSO/ISPF
        </text>
        <circle cx="360" cy="68" r="1.5" fill="#d97a3a" opacity={Math.sin(tick * 0.2) * 0.5 + 0.5} />

        {/* Grid of state nodes */}
        {states.map((_, i) => {
          const col = i % 10;
          const row = Math.floor(i / 10);
          const x = 60 + col * 30;
          const y = 100 + row * 32;
          const phase = (tick * 0.05 + i * 0.07) % (2 * Math.PI);
          const pulse = Math.sin(phase) > 0.7;
          const sampled = (i + Math.floor(tick / 20)) % 5 === 0;

          return (
            <g key={i}>
              {/* hex */}
              <polygon
                points={`${x},${y - 8} ${x + 7},${y - 4} ${x + 7},${y + 4} ${x},${y + 8} ${x - 7},${y + 4} ${x - 7},${y - 4}`}
                fill={sampled ? '#d97a3a' : '#1d1d24'}
                stroke={pulse ? '#d97a3a' : '#2a2a32'}
                strokeWidth="0.5"
                opacity={sampled ? 0.8 : 0.5}
              />
            </g>
          );
        })}

        {/* Console text below */}
        <text x="40" y="305" fill="#6b6862" fontFamily="JetBrains Mono" fontSize="8">
          {`> SAMPLING_RATE = 0.02`}
        </text>
        <text x="40" y="318" fill="#6b6862" fontFamily="JetBrains Mono" fontSize="8">
          {`> RECORDS_IN  = ${(tick * 1247 % 9999999).toString().padStart(7, '0')}`}
        </text>
        <text x="40" y="331" fill="#d97a3a" fontFamily="JetBrains Mono" fontSize="8">
          {`> RECORDS_OUT = ${(Math.floor(tick * 24.94) % 199999).toString().padStart(6, '0')}`}
          <tspan fill="#ece7df">{tick % 8 < 4 ? ' █' : '  '}</tspan>
        </text>

        <text x="20" y="370" fill="#b8b3ab" fontFamily="Instrument Serif" fontSize="18" fontStyle="italic">
          where HCUP began.
        </text>
      </svg>
    </div>
  );
}

// 5. Tec de Monterrey — computer architecture blueprint assembling itself
function UniversityVisual({ active }) {
  const [tick, setTick] = useState(0);
  useEffect(() => {
    if (!active) return;
    const id = setInterval(() => setTick(t => t + 1), 80);
    return () => clearInterval(id);
  }, [active]);

  const LOOP = 300;
  const t = tick % LOOP;

  // Blueprint grid dots
  const gridDots = [];
  for (let gx = 0; gx < 9; gx++) {
    for (let gy = 0; gy < 7; gy++) {
      gridDots.push({ x: 30 + gx * 44, y: 48 + gy * 42 });
    }
  }

  // Blocks appear sequentially as tick grows
  const showCPU    = t > 10;
  const showBusH   = t > 30;
  const showRAM    = t > 50;
  const showALU    = t > 70;
  const showBusV   = t > 90;
  const showIO     = t > 110;
  const showDisk   = t > 130;
  const showLabels = t > 150;

  // Animated data pulses along horizontal bus (CPU → RAM)
  const hPulses = [0, 0.33, 0.66].map(offset => {
    const phase = ((t * 0.012) + offset) % 1;
    return { x: 152 + phase * 120, y: 180 };
  });

  // Animated data pulses along vertical bus (BUS → I/O)
  const vPulses = [0, 0.5].map(offset => {
    const phase = ((t * 0.01) + offset) % 1;
    return { x: 212, y: 195 + phase * 80 };
  });

  // Courses cycle at bottom
  const courses = ['ALGORITHMS', 'DATA STRUCTURES', 'SYSTEMS DESIGN', 'COMPUTER ARCH.', 'DISCRETE MATH', 'COMPILERS'];
  const courseIdx = Math.floor(t / 30) % courses.length;

  // Degree progress (fills over the loop)
  const progress = Math.min(100, Math.round((t / 180) * 100));

  const blockStyle = (show) => ({
    opacity: show ? 1 : 0,
    transition: show ? 'opacity 0.6s ease' : 'none',
  });

  return (
    <div style={{ width: '100%', maxWidth: 560, aspectRatio: '1 / 1', position: 'relative' }}>
      <svg viewBox="0 0 400 400" style={{ width: '100%', height: '100%' }}>

        {/* Blueprint grid */}
        {gridDots.map((d, i) => (
          <circle key={i} cx={d.x} cy={d.y} r="0.8" fill="#2a2a32" opacity="0.6" />
        ))}

        {/* Title */}
        <text x="20" y="28" fill="#6b6862" fontFamily="JetBrains Mono" fontSize="9" letterSpacing="1.5">
          TEC DE MONTERREY · COMP. SYSTEMS ENG.
        </text>
        <line x1="20" y1="36" x2="380" y2="36" stroke="#2a2a32" strokeWidth="0.5" />

        {/* ── CPU block (center-left) ── */}
        <g opacity={showCPU ? 1 : 0}>
          <rect x="60" y="155" width="90" height="50" fill="#0d0d10" stroke="#d97a3a" strokeWidth="1.2" />
          {/* inner grid lines */}
          <line x1="78" y1="155" x2="78" y2="205" stroke="#d97a3a" strokeWidth="0.3" opacity="0.4" />
          <line x1="96" y1="155" x2="96" y2="205" stroke="#d97a3a" strokeWidth="0.3" opacity="0.4" />
          <line x1="114" y1="155" x2="114" y2="205" stroke="#d97a3a" strokeWidth="0.3" opacity="0.4" />
          <line x1="132" y1="155" x2="132" y2="205" stroke="#d97a3a" strokeWidth="0.3" opacity="0.4" />
          <line x1="60" y1="172" x2="150" y2="172" stroke="#d97a3a" strokeWidth="0.3" opacity="0.4" />
          <line x1="60" y1="188" x2="150" y2="188" stroke="#d97a3a" strokeWidth="0.3" opacity="0.4" />
          {/* CPU pin lines */}
          {[0,1,2,3,4].map(i => (
            <line key={i} x1={72 + i * 18} y1="155" x2={72 + i * 18} y2="148" stroke="#d97a3a" strokeWidth="0.6" opacity="0.5" />
          ))}
          {[0,1,2,3,4].map(i => (
            <line key={i} x1={72 + i * 18} y1="205" x2={72 + i * 18} y2="212" stroke="#d97a3a" strokeWidth="0.6" opacity="0.5" />
          ))}
        </g>

        {/* CPU label */}
        {showLabels && (
          <text x="105" y="185" fill="#d97a3a" fontFamily="JetBrains Mono" fontSize="10" textAnchor="middle" letterSpacing="1" fontWeight="bold">
            CPU
          </text>
        )}

        {/* ── Horizontal BUS ── */}
        {showBusH && (
          <g>
            <line x1="150" y1="177" x2="274" y2="177" stroke="#6b6862" strokeWidth="2.5" />
            <line x1="150" y1="183" x2="274" y2="183" stroke="#6b6862" strokeWidth="2.5" />
            {/* pulse particles */}
            {hPulses.map((p, i) => (
              <circle key={i} cx={p.x} cy={p.y} r="2.5" fill="#d97a3a" opacity="0.9" />
            ))}
          </g>
        )}

        {/* ── RAM block (right) ── */}
        <g opacity={showRAM ? 1 : 0}>
          <rect x="274" y="148" width="90" height="64" fill="#0d0d10" stroke="#b8b3ab" strokeWidth="1" />
          {/* memory cell rows */}
          {[0,1,2,3,4,5,6].map(r => (
            <g key={r}>
              {[0,1,2,3].map(c => {
                const lit = (Math.floor(tick / 6) + r * 4 + c) % 8 === 0;
                return (
                  <rect key={c} x={278 + c * 20} y={152 + r * 8} width="16" height="5"
                        fill={lit ? '#d97a3a' : '#1d1d24'} stroke="#2a2a32" strokeWidth="0.3" />
                );
              })}
            </g>
          ))}
        </g>
        {showLabels && (
          <text x="319" y="225" fill="#b8b3ab" fontFamily="JetBrains Mono" fontSize="9" textAnchor="middle" letterSpacing="1">
            RAM
          </text>
        )}

        {/* ── ALU sub-block inside CPU area ── */}
        {showALU && (
          <g>
            <polygon points="62,108 148,108 148,148 105,135 62,148"
                     fill="#0d0d10" stroke="#d97a3a" strokeWidth="0.8" opacity="0.85" />
            {showLabels && (
              <text x="105" y="128" fill="#d97a3a" fontFamily="JetBrains Mono" fontSize="8" textAnchor="middle" letterSpacing="1" opacity="0.8">
                ALU
              </text>
            )}
            <line x1="105" y1="135" x2="105" y2="155" stroke="#d97a3a" strokeWidth="0.8" />
          </g>
        )}

        {/* ── Vertical BUS (center down) ── */}
        {showBusV && (
          <g>
            <line x1="209" y1="183" x2="209" y2="275" stroke="#6b6862" strokeWidth="2.5" />
            <line x1="215" y1="183" x2="215" y2="275" stroke="#6b6862" strokeWidth="2.5" />
            {vPulses.map((p, i) => (
              <circle key={i} cx={p.x} cy={p.y} r="2.5" fill="#ece7df" opacity="0.85" />
            ))}
          </g>
        )}

        {/* ── I/O block ── */}
        <g opacity={showIO ? 1 : 0}>
          <rect x="164" y="275" width="90" height="40" fill="#0d0d10" stroke="#7a9a6e" strokeWidth="1" />
          {/* port indicators */}
          {[0,1,2,3,4,5].map(i => {
            const active2 = (Math.floor(tick / 10) + i) % 3 === 0;
            return (
              <rect key={i} x={170 + i * 13} y="285" width="9" height="20"
                    fill={active2 ? '#7a9a6e' : '#1d1d24'} stroke="#2a2a32" strokeWidth="0.3" />
            );
          })}
          {showLabels && (
            <text x="209" y="328" fill="#7a9a6e" fontFamily="JetBrains Mono" fontSize="9" textAnchor="middle" letterSpacing="1">
              I / O
            </text>
          )}
        </g>

        {/* ── Storage block ── */}
        <g opacity={showDisk ? 1 : 0}>
          <rect x="164" y="335" width="90" height="30" fill="#0d0d10" stroke="#6b6862" strokeWidth="0.8" />
          <circle cx="186" cy="350" r="8" fill="none" stroke="#6b6862" strokeWidth="0.8" />
          <circle cx="186" cy="350" r="3" fill="#6b6862" />
          {/* spinning arm */}
          <line
            x1="186" y1="350"
            x2={186 + Math.cos(tick * 0.15) * 7}
            y2={350 + Math.sin(tick * 0.15) * 7}
            stroke="#d97a3a" strokeWidth="0.8"
          />
          {showLabels && (
            <text x="230" y="353" fill="#6b6862" fontFamily="JetBrains Mono" fontSize="8" letterSpacing="0.5">
              DISK
            </text>
          )}
        </g>

        {/* ── Connector line: horizontal bus to RAM ── */}
        {showBusV && (
          <line x1="212" y1="180" x2="212" y2="180" stroke="none" />
        )}

        {/* ── Course cycling label (left) + era label (right), same baseline ── */}
        <text x="20" y="68" fill="#d97a3a" fontFamily="JetBrains Mono" fontSize="8" letterSpacing="1.5" opacity="0.7">
          COURSE
        </text>
        <text x="380" y="68" fill="#6b6862" fontFamily="JetBrains Mono" fontSize="8" letterSpacing="1.5" opacity="0.7" textAnchor="end">
          ERA
        </text>
        <text x="20" y="83" fill="#ece7df" fontFamily="Instrument Serif" fontSize="19" fontStyle="italic">
          {courses[courseIdx]}
        </text>
        <text x="380" y="83" fill="#b8b3ab" fontFamily="Instrument Serif" fontSize="19" fontStyle="italic" textAnchor="end">
          where it all began.
        </text>

        {/* ── Degree progress bar (centered) ── */}
        <text x="20" y="380" fill="#6b6862" fontFamily="JetBrains Mono" fontSize="8" letterSpacing="1">
          B.S. COMPILED
        </text>
        <text x="380" y="380" fill="#d97a3a" fontFamily="JetBrains Mono" fontSize="8" textAnchor="end">
          {progress}%
        </text>
        <rect x="20" y="387" width="360" height="3" fill="#2a2a32" />
        <rect x="20" y="387" width={360 * progress / 100} height="3" fill="#d97a3a" />
      </svg>
    </div>
  );
}

const VISUAL_MAP = {
  pipeline: PipelineVisual,
  depositions: DepositionsVisual,
  accreditation: AccreditationVisual,
  origins: OriginsVisual,
  university: UniversityVisual,
};

window.VISUAL_MAP = VISUAL_MAP;
