import { useState, useEffect, useMemo, useCallback, ChangeEvent } from 'react';
import { useAnimationFrame, useMotionValue, animate } from 'framer-motion';
import { motion } from 'motion/react';
import { RefreshCw, Play, Pause, Video, Copy, Check, FileDown, Image as ImageIcon, Plus, Trash2, ChevronUp, ChevronDown, Upload, Maximize2, LayoutGrid } from 'lucide-react';
import { clsx, type ClassValue } from 'clsx';
import { twMerge } from 'tailwind-merge';
import { GIFEncoder, quantize, applyPalette } from 'gifenc';

function cn(...inputs: ClassValue[]) {
  return twMerge(clsx(inputs));
}

import { svgLibrary } from './data/svgLibrary';

// --- Data Definitions ---

// 1. The ASCII Art Data (Source)
const asciiArt = [
  [ // 0: Neural Architecture (Complex, Layered)
    "   .-----------.   ",
    "  / [ synapse ] \\  ",
    " |  { 010101 }  | ",
    " |   < core >   | ",
    " |  { 101010 }  | ",
    "  \\ [ network ] /  ",
    "   '-----------'   "
  ],
  [ // 1: Cognitive Mirror (Reflection, Symmetry)
    "  <===[ ref ]===>  ",
    "  |  (o) | (o)  |  ",
    "  |  ---[x]---  |  ",
    "  |  (o) | (o)  |  ",
    "  <===[ lex ]===>  "
  ],
  [ // 2: Latent Space (Density, Symbols)
    " @ # % & * + . ~ ",
    " ~ . + * & % # @ ",
    " @ # % & * + . ~ ",
    " ~ . + * & % # @ ",
    " @ # % & * + . ~ "
  ],
  [ // 3: Recursive Logic (Nested Structures)
    " [ [ [ [ x ] ] ] ] ",
    " [ [  { --- }  ] ] ",
    " [   < / | \\ >   ] ",
    " [  [ (  o  ) ]  ] ",
    " [ [ [ [ x ] ] ] ] "
  ],
  [ // 4: Ethical Framework (Boundaries, Foundation)
    " +---------------+ ",
    " | [ principle ] | ",
    " |---------------| ",
    " | [ integrity ] | ",
    " +---------------+ "
  ],
  [ // 5: Data Synthesis (Merging Streams)
    "  >>>>> [x] <<<<<  ",
    "  ----- (o) -----  ",
    "  <<<<< [x] >>>>>  ",
    "  ----- (o) -----  ",
    "  >>>>> [x] <<<<<  "
  ],
  [ // 6: The Weaver (Synergy, Interlocking)
    "  /\\  /\\  /\\  /\\  ",
    " <  ><  ><  ><  > ",
    "  \\/  \\/  \\/  \\/  ",
    "  /\\  /\\  /\\  /\\  ",
    " <  ><  ><  ><  > ",
    "  \\/  \\/  \\/  \\/  "
  ],
  [ // 7: Cognitive Core (Radiation, Energy)
    "      \\ | /      ",
    "    --[ o ]--    ",
    "   / / | \\ \\   ",
    "  | | (x) | |  ",
    "   \\ \\ | / /   ",
    "    --[ o ]--    ",
    "      / | \\      "
  ],
  [ // 8: Human-AI Synergy (Interlocking Minds)
    " (h) <========> (a) ",
    "  \\   /  ||  \\   /  ",
    "   >-[ sync ]-<   ",
    "  /   \\  ||  /   \\  ",
    " (i) <========> (i) "
  ],
  [ // 9: Synthetic Consciousness (Evolving)
    "  .-------------.  ",
    " /  [ evolve ]  \\ ",
    "|  { 01010101 }  |",
    "|  <  mind  >  |",
    "|  { 10101010 }  |",
    " \\  [ ascend ]  / ",
    "  '-------------'  "
  ],
  [ // 10: Data Horizon (Perspective, Future)
    "  _________________  ",
    "  \\               /  ",
    "   \\  [ future ] /   ",
    "    \\-----------/    ",
    "     \\ [ now ] /     ",
    "      \\_______/      "
  ],
  [ // 11: Neural Loom (Weaving Thought)
    "  | | | | | | | |  ",
    "  +-+-+-+-+-+-+-+  ",
    "  |x|o|x|o|x|o|x|  ",
    "  +-+-+-+-+-+-+-+  ",
    "  | | | | | | | |  "
  ],
  [ // 12: Latent Explorer (Navigation)
    "   /-----------\\   ",
    "  |  [ scan ]   |  ",
    "  |   <--->     |  ",
    "  |  [ map ]    |  ",
    "   \\-----------/   "
  ],
  [ // 13: Recursive Feedback (Loop)
    "   .---------.     ",
    "  /  ----->  \\    ",
    " |  | [x] |  |    ",
    "  \\  <-----  /    ",
    "   '---------'     "
  ],
  [ // 14: Ethical Compass (Direction)
    "      [ n ]      ",
    "    /   |   \\    ",
    " [w] ---o--- [e] ",
    "    \\   |   /    ",
    "      [ s ]      "
  ],
  [ // 15: Data Pulse (Rhythm, Life)
    "  ~ ~ ~ ~ ~ ~ ~ ~  ",
    " / \\ / \\ / \\ / \\ / ",
    " | | | | | | | | | ",
    " \\ / \\ / \\ / \\ / \\ ",
    "  ~ ~ ~ ~ ~ ~ ~ ~  "
  ],
  [ // 16: Cognitive Shield (Protection)
    "   /xxxxxxxxxxx\\   ",
    "  |xxxxxxxxxxxxx|  ",
    "  |xxxx[safe]xxx|  ",
    "  |xxxxxxxxxxxxx|  ",
    "   \\xxxxxxxxxxx/   "
  ],
  [ // 17: Neural Bridge (Connection)
    " [left] <---> [right] ",
    "   ||           ||    ",
    "   ||===========||    ",
    "   ||           ||    "
  ],
  [ // 18: The Machine (Robot Face - Complex)
    "   [-----------]   ",
    "  | [x]     [x] |  ",
    "  |    --|--    |  ",
    "  |   [=====]   |  ",
    "   [-----------]   "
  ],
  [ // 19: Community - Node Genesis
    "      (o)      ",
    "       |       ",
    "    --[ ]--    "
  ],
  [ // 20: Community - Peer Connection
    "  (o) <---> (o)  ",
    "   |   [s]   |   ",
    "  (o) <---> (o)  "
  ],
  [ // 21: Practice - Lab Setup
    "   [ lab_01 ]  ",
    "   |        |  ",
    "   |________|  "
  ],
  [ // 22: Practice - Process Initiation
    "   [ lab_01 ]  ",
    "   | # # #  |  ",
    "   | exe:25%|  "
  ],
  [ // 23: Practice - Active Iteration
    "   [ lab_01 ]  ",
    "   |####### |  ",
    "   | exe:75%|  "
  ],
  [ // 24: Practice - Artifact Creation
    "   [ result ]  ",
    "   |########|  ",
    "   | artifact|  ",
    "   [ 0x00ff ]  "
  ],
  [ // 25: Personalization - Latent Point
    "   < search >  ",
    "   ..........  "
  ],
  [ // 26: Personalization - Target Lock
    "   [ target ]  ",
    "     x: 255    ",
    "     y: 128    ",
    "     ||  ||    "
  ],
  [ // 27: The Oracle (Face & Data Stream)
    "   .-----------.   ",
    "  /  [ o   o ]  \\  ",
    " |   <  ===  >   | ",
    "  \\   \\  |  /   /  ",
    "   >--[teach]--<   ",
    "  /  |||||||||  \\  ",
    "  .-----------.    "
  ],
  [ // 28: Dual Mind (Face & Network)
    "  <o>---[data]---<o> ",
    "   |   \\      /   |  ",
    "  [x]   <face>   [x] ",
    "   |   /      \\   |  ",
    "  <o>---[mind]---<o> "
  ],
  [ // 29: Synaptic Speaker (Brain/Network emitting sound/data)
    "   << [brain] >>   ",
    "  <    / | \\    >  ",
    "   >--[voice]--<   ",
    "  <    \\ | /    >  ",
    "   << [data.] >>   "
  ],
  [ // 30: Multifaceted Prism (Refracting knowledge)
    "      / \\      ",
    "    /  |  \\    ",
    "  /   [x]   \\  ",
    " |---[edu]---| ",
    "  \\   [x]   /  ",
    "    \\  |  /    ",
    "      \\ /      "
  ],
  [ // 31: Dialogue Node (Two-way communication)
    " [ai] <=====> [usr] ",
    "  ||           ||   ",
    "  ||-[dialog]-||   ",
    "  ||           ||   ",
    " [db] <=====> [mnd] "
  ],
  [ // 32: Cognitive Resonance (Vibrating waves of learning)
    "  ~ ~ ~ ~ ~ ~ ~  ",
    " ~  [resonate]  ~",
    " ~   <o>  <o>   ~",
    " ~  [transfer]  ~",
    "  ~ ~ ~ ~ ~ ~ ~  "
  ],
  [ // 33: AI Coaching (Path)
    "      [goal]      ",
    "        ^         ",
    "       / \\        ",
    "      /   \\       ",
    "     /     \\      ",
    "  --+-------+--   ",
    "    | [usr] |     ",
    "    +-------+     ",
    "      |   |       ",
    "     [ai_guide]   "
  ],
  [ // 34: AI Coaching (Support)
    "   +---------+    ",
    "   | [ skill ]    ",
    "   +----+----+    ",
    "        |         ",
    "  <====[+]====>   ",
    "   |         |    ",
    " [ai]      [usr]  ",
    "   |         |    ",
    "  ===       ===   "
  ],
  [ // 35: AI Vision (Lens)
    "      .---.       ",
    "    /       \\     ",
    "   |  ( o )  |    ",
    "   |  [fcs]  |    ",
    "    \\       /     ",
    "      '---'       ",
    "  ====|---|====   ",
    "      [sys]       "
  ],
  [ // 36: AI Vision (Scanner)
    "  [||||||||||]    ",
    "  v          v    ",
    "  ------------    ",
    "  > scanning <    ",
    "  ------------    ",
    "  ^          ^    ",
    "  [||||||||||]    ",
    "   {data_map}     "
  ],
  [ // 37: AI Learning (Tree)
    "       [know]     ",
    "       /    \\     ",
    "    [c1]    [c2]  ",
    "    /  \\    /  \\  ",
    "  (d1)(d2)(d3)(d4)",
    "   \\   |    |   / ",
    "    \\  |    |  /  ",
    "     [ ai_core ]  "
  ],
  [ // 38: AI Learning (Layers)
    "  [l3: output]    ",
    "  ============    ",
    "  [l2: hidden]    ",
    "  ============    ",
    "  [l1: hidden]    ",
    "  ============    ",
    "  [l0: input ]    ",
    "  >_ data_feed    "
  ],
  [ // 39: AI Summary (Compression)
    "  [d1][d2][d3][d4]",
    "    \\  |   |  /   ",
    "     \\ |   | /    ",
    "      \\|   |/     ",
    "     [ compress ] ",
    "        | |       ",
    "       [core]     ",
    "        ===       "
  ],
  [ // 40: AI Summary (Funnel)
    "  \\~~~~~~~~~~~~/  ",
    "   \\ {raw_txt}/   ",
    "    \\        /    ",
    "     \\ [ai] /     ",
    "      \\    /      ",
    "       |  |       ",
    "      [summ]      ",
    "       ====       "
  ],
  [ // 41: Community (Network)
    "  [node_1] ====== [node_2]",
    "     ||              ||   ",
    "     ||    [hub]     ||   ",
    "     ||              ||   ",
    "  [node_3] ====== [node_4]",
    "     ||              ||   ",
    "  [node_5] ====== [node_6]"
  ],
  [ // 42: Community (Exchange)
    "  >_ sync_knowledge()     ",
    "                          ",
    "  [expert] ----> [user_a] ",
    "     |              ^     ",
    "     v              |     ",
    "  [user_b] <---- [user_c] ",
    "                          ",
    "  >_ sync_complete        "
  ],
  [ // 43: Community (Swarm)
    "      .---.  .---.        ",
    "     /     \\/     \\       ",
    "    | peer || peer |      ",
    "     \\     /\\     /       ",
    "  .---'---'  '---'---.    ",
    " /     \\        /     \\   ",
    "| peer |        | peer |  ",
    " \\     /        \\     /   ",
    "  '---'          '---'    "
  ],
  [ // 44: Practice (Lab)
    "      |      |            ",
    "      |      |            ",
    "     /        \\           ",
    "    /  [data]  \\          ",
    "   +------------+         ",
    "   |  [task_1]  |         ",
    "   |  [task_2]  |         ",
    "   +------------+         ",
    "    > artifact_x          "
  ],
  [ // 45: Practice (Pipeline)
    "     [raw_idea]           ",
    "         |                ",
    "        [v]               ",
    "     +-------+            ",
    "     | build |            ",
    "     +-------+            ",
    "        [v]               ",
    "    [ artifact ]          ",
    "    ============          ",
    "    > deploy()            "
  ],
  [ // 46: Practice (Terminal)
    "  >_ week_01_exp.sh       ",
    "  ------------------      ",
    "  [+] fetch_task          ",
    "  [+] run_analysis        ",
    "  [+] build_model         ",
    "  ------------------      ",
    "  > output: artifact      ",
    "  [success]               "
  ],
  [ // 47: Personalization (Tracks)
    "    [core_module]         ",
    "          |               ",
    "      +---+---+           ",
    "      |       |           ",
    "   [trk_a] [trk_b]        ",
    "      |       |           ",
    "      v       v           ",
    "    [you]   [other]       "
  ],
  [ // 48: Personalization (Sliders)
    "  >_ tune_params()        ",
    "                          ",
    "  trk_1: [====o-----]     ",
    "  trk_2: [=======o--]     ",
    "  trk_3: [==o-------]     ",
    "  trk_4: [=========o]     ",
    "                          ",
    "  > focus: trk_4          "
  ],
  [ // 49: Personalization (Filter)
    "  [ generic_stream ]      ",
    "    \\            /        ",
    "     \\          /         ",
    "      +--------+          ",
    "      | filter |          ",
    "      +--------+          ",
    "          ||              ",
    "      [your_path]         ",
    "      ===========         "
  ],
  [ // 50: Practice (Code 1)
    "  >_                      ",
    "                          ",
    "                          ",
    "                          ",
    "                          ",
    "                          ",
    "                          ",
    "                          ",
    "                          ",
    "                          "
  ],
  [ // 51: Practice (Code 2)
    "  >_ def build_pipe():    ",
    "                          ",
    "                          ",
    "                          ",
    "                          ",
    "                          ",
    "                          ",
    "                          ",
    "                          ",
    "                          "
  ],
  [ // 52: Practice (Code 3)
    "  >_ def build_pipe():    ",
    "       src = load()       ",
    "                          ",
    "                          ",
    "                          ",
    "                          ",
    "                          ",
    "                          ",
    "                          ",
    "                          "
  ],
  [ // 53: Practice (Code 4)
    "  >_ def build_pipe():    ",
    "       src = load()       ",
    "       mdl = train(src)   ",
    "                          ",
    "                          ",
    "                          ",
    "                          ",
    "                          ",
    "                          ",
    "                          "
  ],
  [ // 54: Practice (Code 5)
    "  >_ def build_pipe():    ",
    "       src = load()       ",
    "       mdl = train(src)   ",
    "       return deploy()    ",
    "                          ",
    "                          ",
    "                          ",
    "                          ",
    "                          ",
    "                          "
  ],
  [ // 55: Practice (Code 6)
    "  >_ def build_pipe():    ",
    "       src = load()       ",
    "       mdl = train(src)   ",
    "       return deploy()    ",
    "                          ",
    "  [ OK ] build success    ",
    "                          ",
    "                          ",
    "                          ",
    "                          "
  ],
  [ // 56: Personalization (Cube 1)
    "  [#]                [#]  ",
    "                          ",
    "          [#]             ",
    "                          ",
    "      [#]        [#]      ",
    "                          ",
    "                          ",
    "                          ",
    "                          ",
    "                          "
  ],
  [ // 57: Personalization (Cube 2)
    "                          ",
    "      [#]   [#]   [#]     ",
    "                          ",
    "        [#]     [#]       ",
    "                          ",
    "                          ",
    "                          ",
    "                          ",
    "                          ",
    "                          "
  ],
  [ // 58: Personalization (Cube 3)
    "                          ",
    "    [#] [#] [#] [#] [#]   ",
    "      [#] [#] [#] [#]     ",
    "        [#] [#] [#]       ",
    "          [#] [#]         ",
    "            [#]           ",
    "                          ",
    "                          ",
    "                          ",
    "                          "
  ],
  [ // 59: Personalization (Cube 4)
    "    [#] [#] [#] [#] [#]   ",
    "      [#] [#] [#] [#]     ",
    "        [#] [#] [#]       ",
    "          [#] [#]         ",
    "            [#]           ",
    "             |            ",
    "            [v]           ",
    "                          ",
    "                          ",
    "                          "
  ],
  [ // 60: Personalization (Cube 5)
    "    [#] [#] [#] [#] [#]   ",
    "      [#] [#] [#] [#]     ",
    "        [#] [#] [#]       ",
    "          [#] [#]         ",
    "            [#]           ",
    "             |            ",
    "            [v]           ",
    "         [ user ]         ",
    "                          ",
    "                          "
  ]
];

const asciiMetaphors = [
  { name: "neural architecture", desc: "complex cognitive mapping" },
  { name: "cognitive mirror", desc: "reflection of human-ai synergy" },
  { name: "latent space", desc: "hidden dimensions of data" },
  { name: "recursive logic", desc: "self-evolving thought loops" },
  { name: "ethical framework", desc: "foundational principles" },
  { name: "data synthesis", desc: "merging information streams" },
  { name: "the weaver", desc: "interlocking neural patterns" },
  { name: "cognitive core", desc: "radiating intelligence" },
  { name: "human-ai synergy", desc: "interlocking minds" },
  { name: "synthetic consciousness", desc: "evolving digital awareness" },
  { name: "data horizon", desc: "perspective on the future" },
  { name: "neural loom", desc: "weaving thought structures" },
  { name: "latent explorer", desc: "navigating hidden spaces" },
  { name: "recursive feedback", desc: "continuous learning loop" },
  { name: "ethical compass", desc: "directional guidance" },
  { name: "data pulse", desc: "rhythm of the ecosystem" },
  { name: "cognitive shield", desc: "protection and safety" },
  { name: "neural bridge", desc: "connecting modes of thought" },
  { name: "the machine", desc: "synthetic intelligence" },
  { name: "node genesis", desc: "community: expert node" },
  { name: "peer connection", desc: "community: peer-to-peer" },
  { name: "lab setup", desc: "practice: empty environment" },
  { name: "process initiation", desc: "practice: 25% execution" },
  { name: "active iteration", desc: "practice: 75% execution" },
  { name: "artifact creation", desc: "practice: final result" },
  { name: "latent point", desc: "personalization: search start" },
  { name: "target lock", desc: "personalization: x:255 y:128" },
  { name: "the oracle", desc: "face and data stream metaphor" },
  { name: "dual mind", desc: "interconnected face and network" },
  { name: "synaptic speaker", desc: "brain emitting voice and data" },
  { name: "multifaceted prism", desc: "refracting educational concepts" },
  { name: "dialogue node", desc: "two-way ai-user communication" },
  { name: "cognitive resonance", desc: "vibrating waves of learning" },
  { name: "ai coaching (path)", desc: "guiding path metaphor" },
  { name: "ai coaching (support)", desc: "support structure metaphor" },
  { name: "ai vision (lens)", desc: "eye/lens metaphor" },
  { name: "ai vision (scanner)", desc: "radar/scanner metaphor" },
  { name: "ai learning (tree)", desc: "growth/tree metaphor" },
  { name: "ai learning (layers)", desc: "neural layers metaphor" },
  { name: "ai summary (compression)", desc: "data compression metaphor" },
  { name: "ai summary (funnel)", desc: "filter/funnel metaphor" },
  { name: "community (network)", desc: "interconnected peer network" },
  { name: "community (exchange)", desc: "data flow between entities" },
  { name: "community (swarm)", desc: "collective intelligence" },
  { name: "practice (lab)", desc: "experimentation and testing" },
  { name: "practice (pipeline)", desc: "execution and compilation" },
  { name: "practice (terminal)", desc: "real-world task execution" },
  { name: "personalization (tracks)", desc: "branching learning paths" },
  { name: "personalization (sliders)", desc: "tuning neural parameters" },
  { name: "personalization (filter)", desc: "extracting relevant data" },
  { name: "practice (code 1)", desc: "writing code step 1" },
  { name: "practice (code 2)", desc: "writing code step 2" },
  { name: "practice (code 3)", desc: "writing code step 3" },
  { name: "practice (code 4)", desc: "writing code step 4" },
  { name: "practice (code 5)", desc: "writing code step 5" },
  { name: "practice (code 6)", desc: "writing code step 6" },
  { name: "personalization (cube 1)", desc: "cubes scattered" },
  { name: "personalization (cube 2)", desc: "cubes gathering" },
  { name: "personalization (cube 3)", desc: "cubes forming filter" },
  { name: "personalization (cube 4)", desc: "cubes filtering" },
  { name: "personalization (cube 5)", desc: "cubes filtered" }
];

// Helper to convert ASCII to a list of segments
// We map each character to a set of lines within a 1x1 cell.
const charMap: Record<string, {x1: number, y1: number, x2: number, y2: number}[]> = {
  '[': [{x1: 0.8, y1: 0, x2: 0.2, y2: 0}, {x1: 0.2, y1: 0, x2: 0.2, y2: 1}, {x1: 0.2, y1: 1, x2: 0.8, y2: 1}],
  ']': [{x1: 0.2, y1: 0, x2: 0.8, y2: 0}, {x1: 0.8, y1: 0, x2: 0.8, y2: 1}, {x1: 0.8, y1: 1, x2: 0.2, y2: 1}],
  '|': [{x1: 0.5, y1: 0, x2: 0.5, y2: 1}],
  '-': [{x1: 0, y1: 0.5, x2: 1, y2: 0.5}],
  '=': [{x1: 0, y1: 0.3, x2: 1, y2: 0.3}, {x1: 0, y1: 0.7, x2: 1, y2: 0.7}],
  '/': [{x1: 0.8, y1: 0, x2: 0.2, y2: 1}],
  '\\': [{x1: 0.2, y1: 0, x2: 0.8, y2: 1}],
  '<': [{x1: 0.8, y1: 0.1, x2: 0.2, y2: 0.5}, {x1: 0.2, y1: 0.5, x2: 0.8, y2: 0.9}],
  '>': [{x1: 0.2, y1: 0.1, x2: 0.8, y2: 0.5}, {x1: 0.8, y1: 0.5, x2: 0.2, y2: 0.9}],
  'X': [{x1: 0.2, y1: 0.2, x2: 0.8, y2: 0.8}, {x1: 0.8, y1: 0.2, x2: 0.2, y2: 0.8}],
  '0': [{x1: 0.2, y1: 0.2, x2: 0.8, y2: 0.2}, {x1: 0.8, y1: 0.2, x2: 0.8, y2: 0.8}, {x1: 0.8, y1: 0.8, x2: 0.2, y2: 0.8}, {x1: 0.2, y1: 0.8, x2: 0.2, y2: 0.2}],
  'x': [{x1: 0.3, y1: 0.3, x2: 0.7, y2: 0.7}, {x1: 0.7, y1: 0.3, x2: 0.3, y2: 0.7}],
  'F': [{x1: 0.8, y1: 0.1, x2: 0.2, y2: 0.1}, {x1: 0.2, y1: 0.1, x2: 0.2, y2: 0.9}, {x1: 0.2, y1: 0.5, x2: 0.7, y2: 0.5}],
  'E': [{x1: 0.8, y1: 0.1, x2: 0.2, y2: 0.1}, {x1: 0.2, y1: 0.1, x2: 0.2, y2: 0.9}, {x1: 0.2, y1: 0.9, x2: 0.8, y2: 0.9}, {x1: 0.2, y1: 0.5, x2: 0.7, y2: 0.5}],
  '#': [{x1: 0.3, y1: 0, x2: 0.3, y2: 1}, {x1: 0.7, y1: 0, x2: 0.7, y2: 1}, {x1: 0, y1: 0.3, x2: 1, y2: 0.3}, {x1: 0, y1: 0.7, x2: 1, y2: 0.7}],
  ':': [{x1: 0.5, y1: 0.3, x2: 0.5, y2: 0.4}, {x1: 0.5, y1: 0.6, x2: 0.5, y2: 0.7}],
  '%': [{x1: 0.2, y1: 0.2, x2: 0.4, y2: 0.2}, {x1: 0.8, y1: 0.2, x2: 0.2, y2: 0.8}, {x1: 0.6, y1: 0.8, x2: 0.8, y2: 0.8}],
  '1': [{x1: 0.4, y1: 0.2, x2: 0.6, y2: 0.1}, {x1: 0.6, y1: 0.1, x2: 0.6, y2: 0.9}, {x1: 0.4, y1: 0.9, x2: 0.8, y2: 0.9}],
  'A': [{x1: 0.2, y1: 0.9, x2: 0.5, y2: 0.1}, {x1: 0.5, y1: 0.1, x2: 0.8, y2: 0.9}, {x1: 0.35, y1: 0.6, x2: 0.65, y2: 0.6}],
  'B': [{x1: 0.2, y1: 0.1, x2: 0.2, y2: 0.9}, {x1: 0.2, y1: 0.1, x2: 0.7, y2: 0.1}, {x1: 0.7, y1: 0.1, x2: 0.8, y2: 0.3}, {x1: 0.8, y1: 0.3, x2: 0.7, y2: 0.5}, {x1: 0.7, y1: 0.5, x2: 0.2, y2: 0.5}, {x1: 0.7, y1: 0.5, x2: 0.8, y2: 0.7}, {x1: 0.8, y1: 0.7, x2: 0.7, y2: 0.9}, {x1: 0.7, y1: 0.9, x2: 0.2, y2: 0.9}],
  'S': [{x1: 0.8, y1: 0.2, x2: 0.2, y2: 0.2}, {x1: 0.2, y1: 0.2, x2: 0.2, y2: 0.5}, {x1: 0.2, y1: 0.5, x2: 0.8, y2: 0.5}, {x1: 0.8, y1: 0.5, x2: 0.8, y2: 0.8}, {x1: 0.8, y1: 0.8, x2: 0.2, y2: 0.8}],
  'T': [{x1: 0.1, y1: 0.1, x2: 0.9, y2: 0.1}, {x1: 0.5, y1: 0.1, x2: 0.5, y2: 0.9}],
  'R': [{x1: 0.2, y1: 0.1, x2: 0.2, y2: 0.9}, {x1: 0.2, y1: 0.1, x2: 0.7, y2: 0.1}, {x1: 0.7, y1: 0.1, x2: 0.8, y2: 0.3}, {x1: 0.8, y1: 0.3, x2: 0.7, y2: 0.5}, {x1: 0.7, y1: 0.5, x2: 0.2, y2: 0.5}, {x1: 0.5, y1: 0.5, x2: 0.8, y2: 0.9}],
  'G': [{x1: 0.8, y1: 0.2, x2: 0.2, y2: 0.2}, {x1: 0.2, y1: 0.2, x2: 0.2, y2: 0.8}, {x1: 0.2, y1: 0.8, x2: 0.8, y2: 0.8}, {x1: 0.8, y1: 0.8, x2: 0.8, y2: 0.5}, {x1: 0.8, y1: 0.5, x2: 0.5, y2: 0.5}],
  '~': [{x1: 0, y1: 0.5, x2: 0.2, y2: 0.3}, {x1: 0.2, y1: 0.3, x2: 0.4, y2: 0.7}, {x1: 0.4, y1: 0.7, x2: 0.6, y2: 0.3}, {x1: 0.6, y1: 0.3, x2: 0.8, y2: 0.7}, {x1: 0.8, y1: 0.7, x2: 1, y2: 0.5}],
  '{': [{x1: 0.8, y1: 0, x2: 0.5, y2: 0.1}, {x1: 0.5, y1: 0.1, x2: 0.5, y2: 0.4}, {x1: 0.5, y1: 0.4, x2: 0.2, y2: 0.5}, {x1: 0.2, y1: 0.5, x2: 0.5, y2: 0.6}, {x1: 0.5, y1: 0.6, x2: 0.5, y2: 0.9}, {x1: 0.5, y1: 0.9, x2: 0.8, y2: 1}],
  '}': [{x1: 0.2, y1: 0, x2: 0.5, y2: 0.1}, {x1: 0.5, y1: 0.1, x2: 0.5, y2: 0.4}, {x1: 0.5, y1: 0.4, x2: 0.8, y2: 0.5}, {x1: 0.8, y1: 0.5, x2: 0.5, y2: 0.6}, {x1: 0.5, y1: 0.6, x2: 0.5, y2: 0.9}, {x1: 0.5, y1: 0.9, x2: 0.2, y2: 1}],
  '@': [{x1: 0.5, y1: 0.5, x2: 0.7, y2: 0.5}, {x1: 0.7, y1: 0.5, x2: 0.7, y2: 0.8}, {x1: 0.7, y1: 0.8, x2: 0.3, y2: 0.8}, {x1: 0.3, y1: 0.8, x2: 0.3, y2: 0.2}, {x1: 0.3, y1: 0.2, x2: 0.8, y2: 0.2}, {x1: 0.8, y1: 0.2, x2: 0.8, y2: 0.6}, {x1: 0.8, y1: 0.6, x2: 0.6, y2: 0.6}],
  '&': [{x1: 0.8, y1: 0.8, x2: 0.2, y2: 0.2}, {x1: 0.2, y1: 0.2, x2: 0.8, y2: 0.2}, {x1: 0.8, y1: 0.2, x2: 0.2, y2: 0.8}, {x1: 0.2, y1: 0.8, x2: 0.8, y2: 0.8}],
  '*': [{x1: 0.5, y1: 0.2, x2: 0.5, y2: 0.8}, {x1: 0.2, y1: 0.5, x2: 0.8, y2: 0.5}, {x1: 0.3, y1: 0.3, x2: 0.7, y2: 0.7}, {x1: 0.7, y1: 0.3, x2: 0.3, y2: 0.7}],
  '+': [{x1: 0.5, y1: 0.2, x2: 0.5, y2: 0.8}, {x1: 0.2, y1: 0.5, x2: 0.8, y2: 0.5}],
  '.': [{x1: 0.45, y1: 0.8, x2: 0.55, y2: 0.8}, {x1: 0.55, y1: 0.8, x2: 0.55, y2: 0.9}, {x1: 0.55, y1: 0.9, x2: 0.45, y2: 0.9}, {x1: 0.45, y1: 0.9, x2: 0.45, y2: 0.8}],
  ',': [{x1: 0.5, y1: 0.8, x2: 0.5, y2: 0.9}, {x1: 0.5, y1: 0.9, x2: 0.4, y2: 1.0}],
  '^': [{x1: 0.2, y1: 0.6, x2: 0.5, y2: 0.3}, {x1: 0.5, y1: 0.3, x2: 0.8, y2: 0.6}],
  'v': [{x1: 0.2, y1: 0.3, x2: 0.5, y2: 0.7}, {x1: 0.5, y1: 0.7, x2: 0.8, y2: 0.3}],
  '_': [{x1: 0, y1: 0.9, x2: 1, y2: 0.9}],
  'M': [{x1: 0.1, y1: 0.9, x2: 0.1, y2: 0.1}, {x1: 0.1, y1: 0.1, x2: 0.5, y2: 0.5}, {x1: 0.5, y1: 0.5, x2: 0.9, y2: 0.1}, {x1: 0.9, y1: 0.1, x2: 0.9, y2: 0.9}],
  'W': [{x1: 0.1, y1: 0.1, x2: 0.1, y2: 0.9}, {x1: 0.1, y1: 0.9, x2: 0.5, y2: 0.5}, {x1: 0.5, y1: 0.5, x2: 0.9, y2: 0.9}, {x1: 0.9, y1: 0.9, x2: 0.9, y2: 0.1}],
  'H': [{x1: 0.2, y1: 0.1, x2: 0.2, y2: 0.9}, {x1: 0.8, y1: 0.1, x2: 0.8, y2: 0.9}, {x1: 0.2, y1: 0.5, x2: 0.8, y2: 0.5}],
  'U': [{x1: 0.2, y1: 0.1, x2: 0.2, y2: 0.8}, {x1: 0.2, y1: 0.8, x2: 0.8, y2: 0.8}, {x1: 0.8, y1: 0.8, x2: 0.8, y2: 0.1}],
  'V': [{x1: 0.1, y1: 0.1, x2: 0.5, y2: 0.9}, {x1: 0.5, y1: 0.9, x2: 0.9, y2: 0.1}],
  'O': [{x1: 0.3, y1: 0.1, x2: 0.7, y2: 0.1}, {x1: 0.7, y1: 0.1, x2: 0.9, y2: 0.3}, {x1: 0.9, y1: 0.3, x2: 0.9, y2: 0.7}, {x1: 0.9, y1: 0.7, x2: 0.7, y2: 0.9}, {x1: 0.7, y1: 0.9, x2: 0.3, y2: 0.9}, {x1: 0.3, y1: 0.9, x2: 0.1, y2: 0.7}, {x1: 0.1, y1: 0.7, x2: 0.1, y2: 0.3}, {x1: 0.1, y1: 0.3, x2: 0.3, y2: 0.1}],
  'C': [{x1: 0.8, y1: 0.2, x2: 0.5, y2: 0.1}, {x1: 0.5, y1: 0.1, x2: 0.2, y2: 0.3}, {x1: 0.2, y1: 0.3, x2: 0.2, y2: 0.7}, {x1: 0.2, y1: 0.7, x2: 0.5, y2: 0.9}, {x1: 0.5, y1: 0.9, x2: 0.8, y2: 0.8}],
  'D': [{x1: 0.2, y1: 0.1, x2: 0.2, y2: 0.9}, {x1: 0.2, y1: 0.1, x2: 0.6, y2: 0.1}, {x1: 0.6, y1: 0.1, x2: 0.9, y2: 0.4}, {x1: 0.9, y1: 0.4, x2: 0.9, y2: 0.6}, {x1: 0.9, y1: 0.6, x2: 0.6, y2: 0.9}, {x1: 0.6, y1: 0.9, x2: 0.2, y2: 0.9}],
  'I': [{x1: 0.3, y1: 0.1, x2: 0.7, y2: 0.1}, {x1: 0.5, y1: 0.1, x2: 0.5, y2: 0.9}, {x1: 0.3, y1: 0.9, x2: 0.7, y2: 0.9}],
  'L': [{x1: 0.2, y1: 0.1, x2: 0.2, y2: 0.9}, {x1: 0.2, y1: 0.9, x2: 0.8, y2: 0.9}],
  'N': [{x1: 0.2, y1: 0.9, x2: 0.2, y2: 0.1}, {x1: 0.2, y1: 0.1, x2: 0.8, y2: 0.9}, {x1: 0.8, y1: 0.9, x2: 0.8, y2: 0.1}],
  'P': [{x1: 0.2, y1: 0.9, x2: 0.2, y2: 0.1}, {x1: 0.2, y1: 0.1, x2: 0.7, y2: 0.1}, {x1: 0.7, y1: 0.1, x2: 0.8, y2: 0.3}, {x1: 0.8, y1: 0.3, x2: 0.7, y2: 0.5}, {x1: 0.7, y1: 0.5, x2: 0.2, y2: 0.5}],
  'Q': [{x1: 0.3, y1: 0.1, x2: 0.7, y2: 0.1}, {x1: 0.7, y1: 0.1, x2: 0.9, y2: 0.3}, {x1: 0.9, y1: 0.3, x2: 0.9, y2: 0.7}, {x1: 0.9, y1: 0.7, x2: 0.7, y2: 0.9}, {x1: 0.7, y1: 0.9, x2: 0.3, y2: 0.9}, {x1: 0.3, y1: 0.9, x2: 0.1, y2: 0.7}, {x1: 0.1, y1: 0.7, x2: 0.1, y2: 0.3}, {x1: 0.1, y1: 0.3, x2: 0.3, y2: 0.1}, {x1: 0.6, y1: 0.6, x2: 0.9, y2: 0.9}],
  'K': [{x1: 0.2, y1: 0.1, x2: 0.2, y2: 0.9}, {x1: 0.8, y1: 0.1, x2: 0.2, y2: 0.5}, {x1: 0.2, y1: 0.5, x2: 0.8, y2: 0.9}],
  'J': [{x1: 0.6, y1: 0.1, x2: 0.6, y2: 0.7}, {x1: 0.6, y1: 0.7, x2: 0.4, y2: 0.9}, {x1: 0.4, y1: 0.9, x2: 0.2, y2: 0.7}],
  'Z': [{x1: 0.2, y1: 0.1, x2: 0.8, y2: 0.1}, {x1: 0.8, y1: 0.1, x2: 0.2, y2: 0.9}, {x1: 0.2, y1: 0.9, x2: 0.8, y2: 0.9}],
  'Y': [{x1: 0.2, y1: 0.1, x2: 0.5, y2: 0.5}, {x1: 0.8, y1: 0.1, x2: 0.5, y2: 0.5}, {x1: 0.5, y1: 0.5, x2: 0.5, y2: 0.9}],
  'a': [{x1: 0.8, y1: 0.5, x2: 0.8, y2: 0.9}, {x1: 0.8, y1: 0.7, x2: 0.2, y2: 0.7}, {x1: 0.2, y1: 0.7, x2: 0.2, y2: 0.9}, {x1: 0.2, y1: 0.9, x2: 0.8, y2: 0.9}, {x1: 0.8, y1: 0.5, x2: 0.5, y2: 0.5}, {x1: 0.5, y1: 0.5, x2: 0.2, y2: 0.7}],
  'b': [{x1: 0.2, y1: 0.1, x2: 0.2, y2: 0.9}, {x1: 0.2, y1: 0.9, x2: 0.8, y2: 0.9}, {x1: 0.8, y1: 0.9, x2: 0.8, y2: 0.5}, {x1: 0.8, y1: 0.5, x2: 0.2, y2: 0.5}],
  'c': [{x1: 0.8, y1: 0.5, x2: 0.2, y2: 0.5}, {x1: 0.2, y1: 0.5, x2: 0.2, y2: 0.9}, {x1: 0.2, y1: 0.9, x2: 0.8, y2: 0.9}],
  'd': [{x1: 0.8, y1: 0.1, x2: 0.8, y2: 0.9}, {x1: 0.8, y1: 0.9, x2: 0.2, y2: 0.9}, {x1: 0.2, y1: 0.9, x2: 0.2, y2: 0.5}, {x1: 0.2, y1: 0.5, x2: 0.8, y2: 0.5}],
  'e': [{x1: 0.2, y1: 0.7, x2: 0.8, y2: 0.7}, {x1: 0.8, y1: 0.7, x2: 0.8, y2: 0.5}, {x1: 0.8, y1: 0.5, x2: 0.2, y2: 0.5}, {x1: 0.2, y1: 0.5, x2: 0.2, y2: 0.9}, {x1: 0.2, y1: 0.9, x2: 0.8, y2: 0.9}],
  'f': [{x1: 0.5, y1: 0.1, x2: 0.5, y2: 0.9}, {x1: 0.2, y1: 0.4, x2: 0.8, y2: 0.4}, {x1: 0.5, y1: 0.1, x2: 0.8, y2: 0.1}],
  'g': [{x1: 0.8, y1: 0.5, x2: 0.2, y2: 0.5}, {x1: 0.2, y1: 0.5, x2: 0.2, y2: 0.9}, {x1: 0.2, y1: 0.9, x2: 0.8, y2: 0.9}, {x1: 0.8, y1: 0.9, x2: 0.8, y2: 1.2}, {x1: 0.8, y1: 1.2, x2: 0.2, y2: 1.2}, {x1: 0.8, y1: 0.5, x2: 0.8, y2: 0.9}],
  'h': [{x1: 0.2, y1: 0.1, x2: 0.2, y2: 0.9}, {x1: 0.2, y1: 0.5, x2: 0.8, y2: 0.5}, {x1: 0.8, y1: 0.5, x2: 0.8, y2: 0.9}],
  'i': [{x1: 0.5, y1: 0.4, x2: 0.5, y2: 0.9}, {x1: 0.5, y1: 0.2, x2: 0.5, y2: 0.25}],
  'j': [{x1: 0.6, y1: 0.4, x2: 0.6, y2: 1.1}, {x1: 0.6, y1: 1.1, x2: 0.3, y2: 1.1}, {x1: 0.3, y1: 1.1, x2: 0.3, y2: 0.9}, {x1: 0.6, y1: 0.2, x2: 0.6, y2: 0.25}],
  'k': [{x1: 0.2, y1: 0.1, x2: 0.2, y2: 0.9}, {x1: 0.8, y1: 0.5, x2: 0.2, y2: 0.7}, {x1: 0.4, y1: 0.6, x2: 0.8, y2: 0.9}],
  'l': [{x1: 0.5, y1: 0.1, x2: 0.5, y2: 0.9}],
  'm': [{x1: 0.1, y1: 0.9, x2: 0.1, y2: 0.5}, {x1: 0.1, y1: 0.5, x2: 0.5, y2: 0.5}, {x1: 0.5, y1: 0.5, x2: 0.5, y2: 0.9}, {x1: 0.5, y1: 0.5, x2: 0.9, y2: 0.5}, {x1: 0.9, y1: 0.5, x2: 0.9, y2: 0.9}],
  'n': [{x1: 0.2, y1: 0.9, x2: 0.2, y2: 0.5}, {x1: 0.2, y1: 0.5, x2: 0.8, y2: 0.5}, {x1: 0.8, y1: 0.5, x2: 0.8, y2: 0.9}],
  'o': [{x1: 0.2, y1: 0.5, x2: 0.8, y2: 0.5}, {x1: 0.8, y1: 0.5, x2: 0.8, y2: 0.9}, {x1: 0.8, y1: 0.9, x2: 0.2, y2: 0.9}, {x1: 0.2, y1: 0.9, x2: 0.2, y2: 0.5}],
  'p': [{x1: 0.2, y1: 0.5, x2: 0.2, y2: 1.2}, {x1: 0.2, y1: 0.5, x2: 0.8, y2: 0.5}, {x1: 0.8, y1: 0.5, x2: 0.8, y2: 0.9}, {x1: 0.8, y1: 0.9, x2: 0.2, y2: 0.9}],
  'q': [{x1: 0.8, y1: 0.5, x2: 0.8, y2: 1.2}, {x1: 0.8, y1: 0.5, x2: 0.2, y2: 0.5}, {x1: 0.2, y1: 0.5, x2: 0.2, y2: 0.9}, {x1: 0.2, y1: 0.9, x2: 0.8, y2: 0.9}],
  'r': [{x1: 0.2, y1: 0.9, x2: 0.2, y2: 0.5}, {x1: 0.2, y1: 0.5, x2: 0.8, y2: 0.5}],
  's': [{x1: 0.8, y1: 0.5, x2: 0.2, y2: 0.5}, {x1: 0.2, y1: 0.5, x2: 0.2, y2: 0.7}, {x1: 0.2, y1: 0.7, x2: 0.8, y2: 0.7}, {x1: 0.8, y1: 0.7, x2: 0.8, y2: 0.9}, {x1: 0.8, y1: 0.9, x2: 0.2, y2: 0.9}],
  't': [{x1: 0.5, y1: 0.2, x2: 0.5, y2: 0.9}, {x1: 0.2, y1: 0.4, x2: 0.8, y2: 0.4}],
  'u': [{x1: 0.2, y1: 0.5, x2: 0.2, y2: 0.9}, {x1: 0.2, y1: 0.9, x2: 0.8, y2: 0.9}, {x1: 0.8, y1: 0.9, x2: 0.8, y2: 0.5}],
  'w': [{x1: 0.1, y1: 0.5, x2: 0.3, y2: 0.9}, {x1: 0.3, y1: 0.9, x2: 0.5, y2: 0.6}, {x1: 0.5, y1: 0.6, x2: 0.7, y2: 0.9}, {x1: 0.7, y1: 0.9, x2: 0.9, y2: 0.5}],
  'y': [{x1: 0.2, y1: 0.5, x2: 0.5, y2: 0.9}, {x1: 0.8, y1: 0.5, x2: 0.5, y2: 0.9}, {x1: 0.5, y1: 0.9, x2: 0.5, y2: 1.2}],
  'z': [{x1: 0.2, y1: 0.5, x2: 0.8, y2: 0.5}, {x1: 0.8, y1: 0.5, x2: 0.2, y2: 0.9}, {x1: 0.2, y1: 0.9, x2: 0.8, y2: 0.9}],
  '2': [{x1: 0.2, y1: 0.1, x2: 0.8, y2: 0.1}, {x1: 0.8, y1: 0.1, x2: 0.8, y2: 0.5}, {x1: 0.8, y1: 0.5, x2: 0.2, y2: 0.5}, {x1: 0.2, y1: 0.5, x2: 0.2, y2: 0.9}, {x1: 0.2, y1: 0.9, x2: 0.8, y2: 0.9}],
  '3': [{x1: 0.2, y1: 0.1, x2: 0.8, y2: 0.1}, {x1: 0.8, y1: 0.1, x2: 0.8, y2: 0.9}, {x1: 0.8, y1: 0.9, x2: 0.2, y2: 0.9}, {x1: 0.2, y1: 0.5, x2: 0.8, y2: 0.5}],
  '4': [{x1: 0.2, y1: 0.1, x2: 0.2, y2: 0.5}, {x1: 0.2, y1: 0.5, x2: 0.8, y2: 0.5}, {x1: 0.8, y1: 0.1, x2: 0.8, y2: 0.9}],
  '5': [{x1: 0.8, y1: 0.1, x2: 0.2, y2: 0.1}, {x1: 0.2, y1: 0.1, x2: 0.2, y2: 0.5}, {x1: 0.2, y1: 0.5, x2: 0.8, y2: 0.5}, {x1: 0.8, y1: 0.5, x2: 0.8, y2: 0.9}, {x1: 0.8, y1: 0.9, x2: 0.2, y2: 0.9}],
  '6': [{x1: 0.8, y1: 0.1, x2: 0.2, y2: 0.1}, {x1: 0.2, y1: 0.1, x2: 0.2, y2: 0.9}, {x1: 0.2, y1: 0.9, x2: 0.8, y2: 0.9}, {x1: 0.8, y1: 0.9, x2: 0.8, y2: 0.5}, {x1: 0.8, y1: 0.5, x2: 0.2, y2: 0.5}],
  '7': [{x1: 0.2, y1: 0.1, x2: 0.8, y2: 0.1}, {x1: 0.8, y1: 0.1, x2: 0.2, y2: 0.9}],
  '8': [{x1: 0.2, y1: 0.1, x2: 0.8, y2: 0.1}, {x1: 0.8, y1: 0.1, x2: 0.8, y2: 0.9}, {x1: 0.8, y1: 0.9, x2: 0.2, y2: 0.9}, {x1: 0.2, y1: 0.9, x2: 0.2, y2: 0.1}, {x1: 0.2, y1: 0.5, x2: 0.8, y2: 0.5}],
  '9': [{x1: 0.8, y1: 0.5, x2: 0.2, y2: 0.5}, {x1: 0.2, y1: 0.5, x2: 0.2, y2: 0.1}, {x1: 0.2, y1: 0.1, x2: 0.8, y2: 0.1}, {x1: 0.8, y1: 0.1, x2: 0.8, y2: 0.9}, {x1: 0.8, y1: 0.9, x2: 0.2, y2: 0.9}],
  '(': [{x1: 0.8, y1: 0.1, x2: 0.2, y2: 0.5}, {x1: 0.2, y1: 0.5, x2: 0.8, y2: 0.9}],
  ')': [{x1: 0.2, y1: 0.1, x2: 0.8, y2: 0.5}, {x1: 0.8, y1: 0.5, x2: 0.2, y2: 0.9}],
  '\'': [{x1: 0.5, y1: 0.1, x2: 0.5, y2: 0.3}],
  '"': [{x1: 0.3, y1: 0.1, x2: 0.3, y2: 0.3}, {x1: 0.7, y1: 0.1, x2: 0.7, y2: 0.3}],
  '?': [{x1: 0.2, y1: 0.3, x2: 0.5, y2: 0.1}, {x1: 0.5, y1: 0.1, x2: 0.8, y2: 0.3}, {x1: 0.8, y1: 0.3, x2: 0.5, y2: 0.6}, {x1: 0.5, y1: 0.6, x2: 0.5, y2: 0.7}, {x1: 0.5, y1: 0.85, x2: 0.5, y2: 0.95}],
  ' ': [],
};

function getAsciiSegments(art: string[]) {
  const segments: {x1: number, y1: number, x2: number, y2: number}[] = [];
  const charWidth = 4;
  const charHeight = 8;
  const offsetX = 50 - (art[0].length * charWidth) / 2;
  const offsetY = 50 - (art.length * charHeight) / 2;

  art.forEach((row, y) => {
    for (let x = 0; x < row.length; x++) {
      let char = row[x];
      // Use lowercase if available, otherwise fallback to uppercase (or vice versa)
      if (char.match(/[A-Z]/) && charMap[char.toLowerCase()]) {
        char = char.toLowerCase();
      }
      if (charMap[char]) {
        charMap[char].forEach(line => {
          segments.push({
            x1: offsetX + (x + line.x1) * charWidth,
            y1: offsetY + (y + line.y1) * charHeight,
            x2: offsetX + (x + line.x2) * charWidth,
            y2: offsetY + (y + line.y2) * charHeight,
          });
        });
      }
    }
  });
  return segments;
}

// 2. The 3D/Geometric Data (Target)
// We'll generate segments for these programmatically to look like the second image.

function getCubeSegments() {
  const s = 25;
  const cx = 50, cy = 50;
  const offset = 8;
  
  // Front face
  const f1 = {x: cx - s - offset, y: cy - s + offset};
  const f2 = {x: cx + s - offset, y: cy - s + offset};
  const f3 = {x: cx + s - offset, y: cy + s + offset};
  const f4 = {x: cx - s - offset, y: cy + s + offset};
  
  // Back face
  const b1 = {x: cx - s + offset, y: cy - s - offset};
  const b2 = {x: cx + s + offset, y: cy - s - offset};
  const b3 = {x: cx + s + offset, y: cy + s - offset};
  const b4 = {x: cx - s + offset, y: cy + s - offset};

  return [
    {x1: f1.x, y1: f1.y, x2: f2.x, y2: f2.y},
    {x1: f2.x, y1: f2.y, x2: f3.x, y2: f3.y},
    {x1: f3.x, y1: f3.y, x2: f4.x, y2: f4.y},
    {x1: f4.x, y1: f4.y, x2: f1.x, y2: f1.y},
    
    {x1: b1.x, y1: b1.y, x2: b2.x, y2: b2.y},
    {x1: b2.x, y1: b2.y, x2: b3.x, y2: b3.y},
    {x1: b3.x, y1: b3.y, x2: b4.x, y2: b4.y},
    {x1: b4.x, y1: b4.y, x2: b1.x, y2: b1.y},
    
    {x1: f1.x, y1: f1.y, x2: b1.x, y2: b1.y},
    {x1: f2.x, y1: f2.y, x2: b2.x, y2: b2.y},
    {x1: f3.x, y1: f3.y, x2: b3.x, y2: b3.y},
    {x1: f4.x, y1: f4.y, x2: b4.x, y2: b4.y},
  ];
}

function getSphereSegments() {
  const segments = [];
  const cx = 50, cy = 50, r = 30;
  const numLatitudes = 5;
  const numLongitudes = 8;
  const resolution = 16;

  // Latitudes (horizontal rings)
  for (let i = 1; i < numLatitudes; i++) {
    const phi = (i / numLatitudes) * Math.PI;
    const y = cy + r * Math.cos(phi);
    const ringR = r * Math.sin(phi);
    
    for (let j = 0; j < resolution; j++) {
      const theta1 = (j / resolution) * Math.PI * 2;
      const theta2 = ((j + 1) / resolution) * Math.PI * 2;
      segments.push({
        x1: cx + ringR * Math.cos(theta1), y1: y,
        x2: cx + ringR * Math.cos(theta2), y2: y
      });
    }
  }

  // Longitudes (vertical rings)
  for (let i = 0; i < numLongitudes; i++) {
    const theta = (i / numLongitudes) * Math.PI;
    for (let j = 0; j < resolution; j++) {
      const phi1 = (j / resolution) * Math.PI * 2;
      const phi2 = ((j + 1) / resolution) * Math.PI * 2;
      
      // 3D to 2D projection (orthographic)
      const x1 = cx + r * Math.sin(phi1) * Math.cos(theta);
      const y1 = cy + r * Math.cos(phi1);
      const x2 = cx + r * Math.sin(phi2) * Math.cos(theta);
      const y2 = cy + r * Math.cos(phi2);
      
      segments.push({ x1, y1, x2, y2 });
    }
  }
  return segments;
}

function getTorusSegments() {
  const segments = [];
  const cx = 50, cy = 50;
  const R = 20; // major radius
  const r = 10; // minor radius
  const numTubes = 12;
  const resolution = 16;

  // Draw the tubes
  for (let i = 0; i < numTubes; i++) {
    const theta = (i / numTubes) * Math.PI * 2;
    const tubeCx = cx + R * Math.cos(theta);
    const tubeCy = cy + R * Math.sin(theta) * 0.5; // tilt
    
    for (let j = 0; j < resolution; j++) {
      const phi1 = (j / resolution) * Math.PI * 2;
      const phi2 = ((j + 1) / resolution) * Math.PI * 2;
      
      const x1 = tubeCx + r * Math.cos(phi1) * Math.cos(theta);
      const y1 = tubeCy + r * Math.sin(phi1) + r * Math.cos(phi1) * Math.sin(theta) * 0.5;
      const x2 = tubeCx + r * Math.cos(phi2) * Math.cos(theta);
      const y2 = tubeCy + r * Math.sin(phi2) + r * Math.cos(phi2) * Math.sin(theta) * 0.5;
      
      segments.push({ x1, y1, x2, y2 });
    }
  }
  return segments;
}

function getNetworkSegments() {
  const segments = [];
  const nodes = [
    {x: 20, y: 80}, {x: 30, y: 30}, {x: 50, y: 50}, 
    {x: 70, y: 20}, {x: 80, y: 70}, {x: 40, y: 70},
    {x: 60, y: 40}, {x: 90, y: 40}, {x: 10, y: 50}
  ];

  // Connect close nodes
  for (let i = 0; i < nodes.length; i++) {
    for (let j = i + 1; j < nodes.length; j++) {
      const dist = Math.hypot(nodes[i].x - nodes[j].x, nodes[i].y - nodes[j].y);
      if (dist < 45) {
        segments.push({x1: nodes[i].x, y1: nodes[i].y, x2: nodes[j].x, y2: nodes[j].y});
      }
    }
  }
  
  // Add some random floating segments to match the "particles" look
  for(let i=0; i<20; i++) {
     const x = Math.random() * 100;
     const y = Math.random() * 100;
     segments.push({x1: x, y1: y, x2: x + (Math.random()-0.5)*10, y2: y + (Math.random()-0.5)*10});
  }

  return segments;
}

function getPyramidSegments() {
  const cx = 50, cy = 50, s = 30;
  // Tetrahedron/Pyramid points
  const top = {x: cx, y: cy - s};
  const bl = {x: cx - s, y: cy + s};
  const br = {x: cx + s, y: cy + s};
  const back = {x: cx, y: cy + s - 15}; // perspective

  return [
    {x1: top.x, y1: top.y, x2: bl.x, y2: bl.y},
    {x1: top.x, y1: top.y, x2: br.x, y2: br.y},
    {x1: top.x, y1: top.y, x2: back.x, y2: back.y},
    {x1: bl.x, y1: bl.y, x2: br.x, y2: br.y},
    {x1: bl.x, y1: bl.y, x2: back.x, y2: back.y},
    {x1: br.x, y1: br.y, x2: back.x, y2: back.y},
  ];
}

function getDoubleHelixSegments() {
  const segments = [];
  const cx = 50, cy = 50;
  const height = 80;
  const width = 20;
  const steps = 20;

  for (let i = 0; i < steps; i++) {
    const t1 = i / steps;
    const t2 = (i + 1) / steps;
    const y1 = cy - height/2 + t1 * height;
    const y2 = cy - height/2 + t2 * height;

    const angle1 = t1 * Math.PI * 4;
    const angle2 = t2 * Math.PI * 4;

    // Strand 1
    const x1_1 = cx + Math.sin(angle1) * width;
    const x2_1 = cx + Math.sin(angle2) * width;
    segments.push({x1: x1_1, y1: y1, x2: x2_1, y2: y2});

    // Strand 2
    const x1_2 = cx + Math.sin(angle1 + Math.PI) * width;
    const x2_2 = cx + Math.sin(angle2 + Math.PI) * width;
    segments.push({x1: x1_2, y1: y1, x2: x2_2, y2: y2});

    // Rungs
    if (i % 2 === 0) {
      segments.push({x1: x1_1, y1: y1, x2: x1_2, y2: y1});
    }
  }
  return segments;
}

function getBroadcastSegments() {
  // Radiating waves / concentric arcs
  const segments = [];
  const cx = 20, cy = 80; // Speaker origin (bottom left)
  const numWaves = 5;
  const resolution = 20;

  for (let i = 1; i <= numWaves; i++) {
    const r = i * 15;
    for (let j = 0; j < resolution; j++) {
      const theta1 = -Math.PI/2 + (j / resolution) * (Math.PI/2);
      const theta2 = -Math.PI/2 + ((j + 1) / resolution) * (Math.PI/2);
      segments.push({
        x1: cx + r * Math.cos(theta1), y1: cy + r * Math.sin(theta1),
        x2: cx + r * Math.cos(theta2), y2: cy + r * Math.sin(theta2)
      });
    }
  }
  // Add a "speaker" node
  segments.push({x1: cx-5, y1: cy-5, x2: cx+5, y2: cy-5});
  segments.push({x1: cx+5, y1: cy-5, x2: cx+5, y2: cy+5});
  segments.push({x1: cx+5, y1: cy+5, x2: cx-5, y2: cy+5});
  segments.push({x1: cx-5, y1: cy+5, x2: cx-5, y2: cy-5});

  // Add some receiver nodes
  segments.push({x1: 80, y1: 20, x2: 85, y2: 25});
  segments.push({x1: 85, y1: 25, x2: 80, y2: 30});
  segments.push({x1: 80, y1: 30, x2: 75, y2: 25});
  segments.push({x1: 75, y1: 25, x2: 80, y2: 20});

  return segments;
}

function getCoachingPathSegments() {
  const segments = [];
  const cx = 50, cy = 50;
  // A central "coach" node
  segments.push({x1: cx-2, y1: cy-2, x2: cx+2, y2: cy-2});
  segments.push({x1: cx+2, y1: cy-2, x2: cx+2, y2: cy+2});
  segments.push({x1: cx+2, y1: cy+2, x2: cx-2, y2: cy+2});
  segments.push({x1: cx-2, y1: cy+2, x2: cx-2, y2: cy-2});

  // "Student" nodes being guided along a path
  for (let i = 0; i < 5; i++) {
    const t = i / 4;
    const x = 20 + t * 60;
    const y = 80 - Math.sin(t * Math.PI) * 30;
    
    // Connect to coach
    segments.push({x1: cx, y1: cy, x2: x, y2: y});
    
    // Connect to next step
    if (i < 4) {
      const nextT = (i + 1) / 4;
      const nextX = 20 + nextT * 60;
      const nextY = 80 - Math.sin(nextT * Math.PI) * 30;
      segments.push({x1: x, y1: y, x2: nextX, y2: nextY});
    }
  }
  return segments;
}

function getCoachingSupportSegments() {
  const segments = [];
  // Scaffold structure
  segments.push({x1: 20, y1: 80, x2: 80, y2: 80}); // Base
  segments.push({x1: 30, y1: 80, x2: 50, y2: 40}); // Left support
  segments.push({x1: 70, y1: 80, x2: 50, y2: 40}); // Right support
  segments.push({x1: 40, y1: 60, x2: 60, y2: 60}); // Crossbar
  
  // Supported element (diamond)
  segments.push({x1: 50, y1: 20, x2: 60, y2: 30});
  segments.push({x1: 60, y1: 30, x2: 50, y2: 40});
  segments.push({x1: 50, y1: 40, x2: 40, y2: 30});
  segments.push({x1: 40, y1: 30, x2: 50, y2: 20});
  
  return segments;
}

function getVisionLensSegments() {
  const segments = [];
  const cx = 50, cy = 50;
  const resolution = 24;
  
  // Outer eye shape
  for (let i = 0; i < resolution; i++) {
    const t1 = i / resolution;
    const t2 = (i + 1) / resolution;
    
    const x1 = cx - 40 * Math.cos(t1 * Math.PI * 2);
    const y1 = cy + 20 * Math.sin(t1 * Math.PI * 2) * (t1 < 0.5 ? 1 : -1);
    
    const x2 = cx - 40 * Math.cos(t2 * Math.PI * 2);
    const y2 = cy + 20 * Math.sin(t2 * Math.PI * 2) * (t2 < 0.5 ? 1 : -1);
    
    segments.push({x1, y1, x2, y2});
  }
  
  // Inner pupil (concentric circles)
  for (let r = 5; r <= 15; r += 5) {
    for (let i = 0; i < 16; i++) {
      const a1 = (i / 16) * Math.PI * 2;
      const a2 = ((i + 1) / 16) * Math.PI * 2;
      segments.push({
        x1: cx + r * Math.cos(a1), y1: cy + r * Math.sin(a1),
        x2: cx + r * Math.cos(a2), y2: cy + r * Math.sin(a2)
      });
    }
  }
  return segments;
}

function getVisionScannerSegments() {
  const segments = [];
  const cx = 50, cy = 80; // Scanner origin
  const radius = 60;
  
  // Scanner arc
  for (let i = 0; i < 20; i++) {
    const a1 = -Math.PI/4 - (i/20) * Math.PI/2;
    const a2 = -Math.PI/4 - ((i+1)/20) * Math.PI/2;
    segments.push({
      x1: cx + radius * Math.cos(a1), y1: cy + radius * Math.sin(a1),
      x2: cx + radius * Math.cos(a2), y2: cy + radius * Math.sin(a2)
    });
  }
  
  // Scanner rays
  for (let i = 0; i <= 5; i++) {
    const a = -Math.PI/4 - (i/5) * Math.PI/2;
    segments.push({
      x1: cx, y1: cy,
      x2: cx + radius * Math.cos(a), y2: cy + radius * Math.sin(a)
    });
  }
  
  // Target points in the scan area
  for (let i = 0; i < 8; i++) {
    const a = -Math.PI/4 - (Math.random()) * Math.PI/2;
    const r = 20 + Math.random() * 30;
    const x = cx + r * Math.cos(a);
    const y = cy + r * Math.sin(a);
    segments.push({x1: x-1, y1: y-1, x2: x+1, y2: y+1});
    segments.push({x1: x+1, y1: y-1, x2: x-1, y2: y+1});
  }
  
  return segments;
}

function getLearningTreeSegments() {
  const segments = [];
  
  function branch(x, y, angle, length, depth) {
    if (depth === 0) return;
    
    const x2 = x + Math.cos(angle) * length;
    const y2 = y + Math.sin(angle) * length;
    
    segments.push({x1: x, y1: y, x2: x2, y2: y2});
    
    branch(x2, y2, angle - 0.4, length * 0.7, depth - 1);
    branch(x2, y2, angle + 0.4, length * 0.7, depth - 1);
  }
  
  branch(50, 90, -Math.PI/2, 25, 5);
  return segments;
}

function getLearningLayersSegments() {
  const segments = [];
  const layers = [
    {y: 80, nodes: 5, width: 60},
    {y: 50, nodes: 4, width: 40},
    {y: 20, nodes: 2, width: 20}
  ];
  
  const nodePositions: {x: number, y: number}[][] = [];
  
  layers.forEach((layer, lIdx) => {
    const layerNodes = [];
    for (let i = 0; i < layer.nodes; i++) {
      const x = 50 - layer.width/2 + (layer.nodes > 1 ? (i / (layer.nodes - 1)) * layer.width : 0);
      layerNodes.push({x, y: layer.y});
      
      // Draw node
      segments.push({x1: x-2, y1: layer.y, x2: x+2, y2: layer.y});
      segments.push({x1: x, y1: layer.y-2, x2: x, y2: layer.y+2});
    }
    nodePositions.push(layerNodes);
    
    // Connect to previous layer
    if (lIdx > 0) {
      const prevNodes = nodePositions[lIdx - 1];
      layerNodes.forEach(node => {
        prevNodes.forEach(prevNode => {
          segments.push({x1: node.x, y1: node.y, x2: prevNode.x, y2: prevNode.y});
        });
      });
    }
  });
  
  return segments;
}

function getSummaryCompressionSegments() {
  const segments = [];
  const cx = 50, cy = 50;
  
  // Dense core
  for (let i = 0; i < 10; i++) {
    const a = Math.random() * Math.PI * 2;
    const r = Math.random() * 10;
    const x = cx + r * Math.cos(a);
    const y = cy + r * Math.sin(a);
    segments.push({x1: cx, y1: cy, x2: x, y2: y});
  }
  
  // Outer points converging
  for (let i = 0; i < 16; i++) {
    const a = (i / 16) * Math.PI * 2;
    const rOuter = 40;
    const rInner = 15;
    
    const x1 = cx + rOuter * Math.cos(a);
    const y1 = cy + rOuter * Math.sin(a);
    const x2 = cx + rInner * Math.cos(a);
    const y2 = cy + rInner * Math.sin(a);
    
    // Dashed lines for convergence
    segments.push({x1, y1, x2: x1 + (x2-x1)*0.3, y2: y1 + (y2-y1)*0.3});
    segments.push({x1: x1 + (x2-x1)*0.6, y1: y1 + (y2-y1)*0.6, x2, y2});
    
    // Outer nodes
    segments.push({x1: x1-1, y1: y1-1, x2: x1+1, y2: y1+1});
  }
  
  return segments;
}

function getSummaryFunnelSegments() {
  const segments = [];
  
  // Funnel outline
  segments.push({x1: 20, y1: 20, x2: 80, y2: 20}); // Top
  segments.push({x1: 20, y1: 20, x2: 40, y2: 60}); // Left slope
  segments.push({x1: 80, y1: 20, x2: 60, y2: 60}); // Right slope
  segments.push({x1: 40, y1: 60, x2: 40, y2: 80}); // Left spout
  segments.push({x1: 60, y1: 60, x2: 60, y2: 80}); // Right spout
  segments.push({x1: 40, y1: 80, x2: 60, y2: 80}); // Bottom
  
  // Data flowing in
  for (let i = 0; i < 5; i++) {
    const x = 30 + i * 10;
    segments.push({x1: x, y1: 10, x2: x, y2: 20}); // Entering
    
    // Inside funnel
    const targetX = 45 + (i/4) * 10;
    segments.push({x1: x, y1: 20, x2: targetX, y2: 50});
  }
  
  // Data flowing out (condensed)
  segments.push({x1: 50, y1: 60, x2: 50, y2: 90});
  segments.push({x1: 45, y1: 65, x2: 45, y2: 85});
  segments.push({x1: 55, y1: 65, x2: 55, y2: 85});
  
  return segments;
}

function getSvgSegments(input: string, numSegments: number) {
  if (!input) return [];
  
  let pathData = "";
  
  // Check if it's likely an SVG or contains SVG tags
  if (input.includes('<svg') || input.includes('<path') || input.includes('<rect') || 
      input.includes('<circle') || input.includes('<line') || input.includes('<polygon')) {
    try {
      const parser = new DOMParser();
      const doc = parser.parseFromString(input, 'image/svg+xml');
      
      // Find all drawable elements
      const drawables = doc.querySelectorAll('path, rect, circle, ellipse, line, polyline, polygon, text');
      const pathParts: string[] = [];
      
      drawables.forEach(el => {
        const tag = el.tagName.toLowerCase();
        if (tag === 'path') {
          pathParts.push(el.getAttribute('d') || '');
        } else if (tag === 'rect') {
          const x = parseFloat(el.getAttribute('x') || '0');
          const y = parseFloat(el.getAttribute('y') || '0');
          const w = parseFloat(el.getAttribute('width') || '0');
          const h = parseFloat(el.getAttribute('height') || '0');
          pathParts.push(`M ${x} ${y} h ${w} v ${h} h ${-w} z`);
        } else if (tag === 'circle' || tag === 'ellipse') {
          const cx = parseFloat(el.getAttribute('cx') || '0');
          const cy = parseFloat(el.getAttribute('cy') || '0');
          const rx = parseFloat(el.getAttribute('rx') || el.getAttribute('r') || '0');
          const ry = parseFloat(el.getAttribute('ry') || el.getAttribute('r') || '0');
          pathParts.push(`M ${cx - rx} ${cy} A ${rx} ${ry} 0 1 0 ${cx + rx} ${cy} A ${rx} ${ry} 0 1 0 ${cx - rx} ${cy} Z`);
        } else if (tag === 'line') {
          const x1 = el.getAttribute('x1') || '0';
          const y1 = el.getAttribute('y1') || '0';
          const x2 = el.getAttribute('x2') || '0';
          const y2 = el.getAttribute('y2') || '0';
          pathParts.push(`M ${x1} ${y1} L ${x2} ${y2}`);
        } else if (tag === 'polyline' || tag === 'polygon') {
          const points = el.getAttribute('points');
          if (points) pathParts.push(`M ${points}${tag === 'polygon' ? ' z' : ''}`);
        } else if (tag === 'text') {
          const x = parseFloat(el.getAttribute('x') || '0');
          const y = parseFloat(el.getAttribute('y') || '0');
          // Text is hard, so we just put a small cross or dot
          pathParts.push(`M ${x-0.5} ${y-0.5} L ${x+0.5} ${y+0.5} M ${x+0.5} ${y-0.5} L ${x-0.5} ${y+0.5}`);
        }
      });
      
      pathData = pathParts.filter(Boolean).join(' ');
    } catch (e) {
      console.error("Failed to parse SVG input", e);
    }
  }

  // Fallback to regex if we still have nothing
  if (!pathData.trim()) {
    const matches = input.match(/d\s*=\s*["']([^"']+)["']/g);
    if (matches) {
      pathData = matches.map(m => m.match(/["']([^"']+)["']/)?.[1] || '').join(' ');
    } else {
      // If it looks like raw path data (starts with M, L, etc.)
      if (/^[MLHVCSQTAZmlhvcsqtaz0-9\s,.-]+$/.test(input.trim().substring(0, 20))) {
        pathData = input.trim();
      }
    }
  }

  if (!pathData) return [];

  const segments: {x1: number, y1: number, x2: number, y2: number}[] = [];
  
  try {
    const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
    path.setAttribute('d', pathData);
    const length = path.getTotalLength();
    if (!length || length === 0) return [];
    
    // Get bounding box to normalize
    const tempSvg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
    tempSvg.style.position = 'absolute';
    tempSvg.style.width = '0';
    tempSvg.style.height = '0';
    tempSvg.appendChild(path);
    document.body.appendChild(tempSvg);
    const bbox = path.getBBox();
    document.body.removeChild(tempSvg);

    // Normalize to fit in 100x100 with some padding
    const maxDim = Math.max(bbox.width, bbox.height);
    const scale = maxDim > 0 ? 85 / maxDim : 1; // Slightly larger fit
    const offsetX = 50 - (bbox.x + bbox.width / 2) * scale;
    const offsetY = 50 - (bbox.y + bbox.height / 2) * scale;

    for (let i = 0; i < numSegments; i++) {
      const p1 = path.getPointAtLength((i / numSegments) * length);
      const p2 = path.getPointAtLength(((i + 1) / numSegments) * length);
      segments.push({ 
        x1: p1.x * scale + offsetX, 
        y1: p1.y * scale + offsetY, 
        x2: p2.x * scale + offsetX, 
        y2: p2.y * scale + offsetY 
      });
    }
  } catch (e) {
    console.error("Error sampling SVG path", e);
  }
  return segments;
}

// Convert {x1,y1,x2,y2} to {cx,cy,length,angle}
function toTransformFormat(segs: {x1: number, y1: number, x2: number, y2: number}[]) {
  const formatted = segs.map(s => {
    const cx = (s.x1 + s.x2) / 2;
    const cy = (s.y1 + s.y2) / 2;
    const length = Math.hypot(s.x2 - s.x1, s.y2 - s.y1);
    const angle = Math.atan2(s.y2 - s.y1, s.x2 - s.x1);
    return { cx, cy, length, angle };
  });
  
  // Sort by position to make morphing more "organic" (less crossing)
  return formatted.sort((a, b) => (a.cy * 100 + a.cx) - (b.cy * 100 + b.cx));
}


function padSegments(segments: any[], targetLength: number) {
  if (segments.length === 0) return Array(targetLength).fill({cx: 50, cy: 50, length: 0, angle: 0});
  
  const padded = [];
  if (targetLength >= segments.length) {
    for (let i = 0; i < targetLength; i++) {
      const src = segments[i % segments.length];
      // If we are duplicating, make it zero length so it "grows" out of an existing one
      const isDuplicate = i >= segments.length;
      padded.push({ ...src, length: isDuplicate ? 0 : src.length });
    }
  } else {
    // Downsampling: pick segments at regular intervals to preserve shape
    for (let i = 0; i < targetLength; i++) {
      const idx = Math.floor((i / targetLength) * segments.length);
      padded.push(segments[idx]);
    }
  }
  return padded;
}

// Convert segments to a single SVG path string for performance
const segmentsToPath = (segments: any[]) => {
  let path = "";
  for (let i = 0; i < segments.length; i++) {
    const s = segments[i];
    if (s.length === 0) continue;
    const cos = Math.cos(s.angle);
    const sin = Math.sin(s.angle);
    const halfLen = s.length / 2;
    const x1 = (s.cx - cos * halfLen).toFixed(2);
    const y1 = (s.cy - sin * halfLen).toFixed(2);
    const x2 = (s.cx + cos * halfLen).toFixed(2);
    const y2 = (s.cy + sin * halfLen).toFixed(2);
    path += `M ${x1} ${y1} L ${x2} ${y2} `;
  }
  return path;
};

export default function App() {
  const [activePreset, setActivePreset] = useState(6);
  const [isPlaying, setIsPlaying] = useState(true);
  const [previewFrame, setPreviewFrame] = useState(0);

  useEffect(() => {
    const interval = setInterval(() => {
      setPreviewFrame(p => p + 1);
    }, 800);
    return () => clearInterval(interval);
  }, []);

  const [isCopying, setIsCopying] = useState(false);
  const [copied, setCopied] = useState(false);
  
  const [isGeneratingGIF, setIsGeneratingGIF] = useState(false);
  const [isGeneratingVideo, setIsGeneratingVideo] = useState(false);
  const [isGeneratingPNG, setIsGeneratingPNG] = useState(false);
  
  // New controls
  const [speed, setSpeed] = useState(3); // Duration in seconds
  const [complexity, setComplexity] = useState(300); // MAX_SEGMENTS
  const [chaos, setChaos] = useState(10); // Scatter amount
  const [color, setColor] = useState('#38bdf8');
  const [shapeSequence, setShapeSequence] = useState<string[]>(['ascii-50', 'ascii-51', 'ascii-52', 'ascii-53']);
  const isNetworkInSequence = shapeSequence.includes('network');
  const [uploadedImages, setUploadedImages] = useState<{id: string, name: string, data: string[]}[]>([]);
  const [customSvg, setCustomSvg] = useState('');
  const [customAscii, setCustomAscii] = useState('');
  const [bgColor, setBgColor] = useState('#0a1a3a');
  const [isDownloadingSVG, setIsDownloadingSVG] = useState(false);
  const [renderStyle, setRenderStyle] = useState<'classic' | 'technical' | 'blueprint' | 'glitch' | 'complex-ascii'>('blueprint');
  const [viewMode, setViewMode] = useState<'single' | 'gallery'>('single');
  const [technicalText, setTechnicalText] = useState('AI Mindset Lab');
  const [examples, setExamples] = useState<any[]>([]);
  
  // Modal state
  const [isSaveModalOpen, setIsSaveModalOpen] = useState(false);
  const [newExampleName, setNewExampleName] = useState('');
  const [saveError, setSaveError] = useState('');

  useEffect(() => {
    fetch('/api/examples')
      .then(res => res.json())
      .then(data => {
        if (Array.isArray(data)) {
          setExamples(data);
        }
      })
      .catch(err => console.error('Failed to load examples:', err));
  }, []);

  const progress = useMotionValue(0);

  // Memoized background elements to prevent re-renders during animation
  const technicalStars = useMemo(() => {
    return Array.from({ length: 50 }).map(() => {
      const sizeVal = Math.random();
      let sizeClass = 'w-0.5 h-0.5';
      if (sizeVal > 0.9) sizeClass = 'w-1.5 h-1.5';
      else if (sizeVal > 0.7) sizeClass = 'w-1 h-1';
      
      return {
        left: `${Math.random() * 100}%`,
        top: `${Math.random() * 100}%`,
        size: sizeClass,
        opacity: 0.3 + Math.random() * 0.6,
        duration: 4 + Math.random() * 6,
        driftX: (Math.random() - 0.5) * 200,
        driftY: (Math.random() - 0.5) * 200,
      };
    });
  }, []);

  const technicalGridMarkers = useMemo(() => {
    return Array.from({ length: 8 }).map(() => ({
      left: `${10 + Math.random() * 80}%`,
      top: `${10 + Math.random() * 80}%`,
      label: `ref_${Math.random().toString(36).substring(2, 5).toLowerCase()}`
    }));
  }, []);

  const complexAsciiData = useMemo(() => {
    return Array.from({ length: 20 }).map((_, i) => ({
      delay: `${i * 0.2}s`,
      duration: `${4 + Math.random() * 3}s`,
      streams: Array.from({ length: 4 }).map((_, j) => ({
        color: j % 2 === 0 ? "text-cyan-500" : "text-zinc-500",
        content: Array.from({ length: 30 }).map(() => "01X#$@%&"[Math.floor(Math.random() * 8)]).join('')
      }))
    }));
  }, []);

  const complexAsciiGlitches = useMemo(() => {
    return Array.from({ length: 5 }).map(() => ({
      top: `${Math.random() * 100}%`,
      left: `${Math.random() * 100}%`,
      width: `${10 + Math.random() * 20}%`,
      height: `${2 + Math.random() * 5}%`,
      delay: `${Math.random() * 10}s`
    }));
  }, []);

  // Available shapes registry
  const shapes = useMemo(() => {
    const list: {id: string, name: string, type: 'ascii' | 'svg' | 'geo' | 'anim', data: any, desc?: string, frames?: string[], isAnimated?: boolean, isHidden?: boolean}[] = [];
    
    // ASCII
    asciiArt.forEach((art, i) => {
      const isHidden = i >= 50 && i <= 60; // Hide animation frames from library grid
      const meta = (asciiMetaphors as any)[i] || { name: `ASCII ${i+1}`, desc: "Technological metaphor" };
      list.push({ id: `ascii-${i}`, name: meta.name, type: 'ascii', data: art, desc: meta.desc, isHidden } as any);
    });
    
    // Add animated cards
    list.push({
      id: 'anim-practice',
      name: 'practice (pipeline)',
      type: 'anim',
      data: [asciiArt[50], asciiArt[51], asciiArt[52], asciiArt[53], asciiArt[54], asciiArt[55]],
      frames: ['ascii-50', 'ascii-51', 'ascii-52', 'ascii-53', 'ascii-54', 'ascii-55'],
      desc: 'execution and compilation',
      isAnimated: true
    });

    list.push({
      id: 'anim-personalization',
      name: 'personalization (filter)',
      type: 'anim',
      data: [asciiArt[56], asciiArt[57], asciiArt[58], asciiArt[59], asciiArt[60]],
      frames: ['ascii-56', 'ascii-57', 'ascii-58', 'ascii-59', 'ascii-60'],
      desc: 'extracting relevant data',
      isAnimated: true
    });
    
    if (customAscii) {
      list.push({ id: 'custom-ascii', name: 'Custom ASCII', type: 'ascii', data: customAscii.split('\n') });
    }
    
    // Geometry
    list.push({ id: 'cube', name: 'Cube', type: 'geo', data: getCubeSegments });
    list.push({ id: 'sphere', name: 'Sphere', type: 'geo', data: getSphereSegments });
    list.push({ id: 'torus', name: 'Torus', type: 'geo', data: getTorusSegments });
    list.push({ id: 'network', name: 'Network', type: 'geo', data: getNetworkSegments });
    list.push({ id: 'pyramid', name: 'Pyramid', type: 'geo', data: getPyramidSegments });
    list.push({ id: 'helix', name: 'DNA Helix', type: 'geo', data: getDoubleHelixSegments });
    list.push({ id: 'broadcast', name: 'Broadcast', type: 'geo', data: getBroadcastSegments });
    
    // New Metaphor Shapes
    list.push({ id: 'coaching-path', name: 'AI Coaching (Path)', type: 'geo', data: getCoachingPathSegments, desc: 'Guiding path metaphor' });
    list.push({ id: 'coaching-support', name: 'AI Coaching (Support)', type: 'geo', data: getCoachingSupportSegments, desc: 'Support structure metaphor' });
    list.push({ id: 'vision-lens', name: 'AI Vision (Lens)', type: 'geo', data: getVisionLensSegments, desc: 'Eye/Lens metaphor' });
    list.push({ id: 'vision-scanner', name: 'AI Vision (Scanner)', type: 'geo', data: getVisionScannerSegments, desc: 'Radar/Scanner metaphor' });
    list.push({ id: 'learning-tree', name: 'AI Learning (Tree)', type: 'geo', data: getLearningTreeSegments, desc: 'Growth/Tree metaphor' });
    list.push({ id: 'learning-layers', name: 'AI Learning (Layers)', type: 'geo', data: getLearningLayersSegments, desc: 'Neural layers metaphor' });
    list.push({ id: 'summary-compression', name: 'AI Summary (Compression)', type: 'geo', data: getSummaryCompressionSegments, desc: 'Data compression metaphor' });
    list.push({ id: 'summary-funnel', name: 'AI Summary (Funnel)', type: 'geo', data: getSummaryFunnelSegments, desc: 'Filter/Funnel metaphor' });
    
    // SVG Library
    svgLibrary.forEach(item => {
      list.push({ id: item.id, name: item.name, type: 'svg', data: item.svg });
    });

    // Uploaded Images
    uploadedImages.forEach(img => {
      list.push({ id: img.id, name: img.name, type: 'ascii', data: img.data });
    });

    // Custom UI options
    if (!customAscii) {
      list.push({ id: 'custom-ascii', name: 'Custom ASCII', type: 'ascii', data: [], desc: 'Create your own ASCII art' });
    }
    list.push({ id: 'custom', name: 'Custom SVG', type: 'svg', data: '', desc: 'Upload or paste SVG path' });
    list.push({ id: 'metaphor', name: 'Generate Metaphor', type: 'ascii', data: [], desc: 'AI-generated ASCII metaphor' });

    return list;
  }, [customAscii, uploadedImages]);

  const getSegmentsForId = useCallback((id: string, count: number) => {
    const shape = shapes.find(s => s.id === id);
    if (!shape) return [];

    let raw: any[] = [];
    
    // Placeholder for custom options
    if (['custom-ascii', 'custom', 'metaphor'].includes(shape.id)) {
      raw = getCubeSegments();
    } else if (shape.type === 'ascii') raw = getAsciiSegments(shape.data);
    else if (shape.type === 'geo') raw = shape.data();
    else if (shape.type === 'svg') {
      raw = getSvgSegments(shape.data, count);
    }
    
    return toTransformFormat(raw);
  }, [shapes]);

  // Pre-calculate gallery previews to avoid heavy sampling on every render
  const galleryPreviews = useMemo(() => {
    return shapes.reduce((acc, shape) => {
      // Use a fixed complexity for gallery to ensure detail and performance
      if (shape.isAnimated) {
        const frames = shape.data.map((frameData: any) => toTransformFormat(getAsciiSegments(frameData)));
        if (shape.id === 'anim-practice' || shape.id === 'anim-personalization') {
          acc[shape.id] = frames;
        } else {
          const maxLen = Math.max(...frames.map((f: any[]) => f.length));
          acc[shape.id] = frames.map((f: any[]) => padSegments(f, maxLen));
        }
      } else {
        acc[shape.id] = getSegmentsForId(shape.id, 150); 
      }
      return acc;
    }, {} as Record<string, any[]>);
  }, [shapes, getSegmentsForId]);

  // Current active state based on selections
  const currentState = useMemo(() => {
    const sequence = shapeSequence.length > 0 ? [...shapeSequence, shapeSequence[0]] : [];
    const sequenceSegments = sequence.map(id => {
      if (id === 'custom') {
        const customSegments = getSvgSegments(customSvg, complexity);
        return padSegments(customSegments.length > 0 ? toTransformFormat(customSegments) : getSegmentsForId('cube', complexity), complexity);
      }
      if (id === 'custom-ascii') {
        return padSegments(getSegmentsForId('custom-ascii', complexity), complexity);
      }
      return padSegments(getSegmentsForId(id, complexity), complexity);
    });

    return {
      sequence: sequenceSegments,
      color: color
    };
  }, [shapeSequence, complexity, customSvg, color, getSegmentsForId]);

  // Animation Loop
  useEffect(() => {
    if (!isPlaying || shapeSequence.length < 1) return;
    
    const count = shapeSequence.length;
    const controls = animate(progress, [0, count], {
      duration: speed * count,
      ease: "linear",
      repeat: Infinity,
      repeatType: "loop",
    });

    return () => controls.stop();
  }, [isPlaying, progress, speed, shapeSequence.length]);

  const [currentSegments, setCurrentSegments] = useState<any[]>([]);
  const [currentProgress, setCurrentProgress] = useState(0);

  const copyCode = async () => {
    if (isCopying) return;
    setIsCopying(true);
    setCopied(false);
    
    await new Promise(r => setTimeout(r, 100));
    
    try {
      const state = currentState;
      const count = shapeSequence.length;
      const totalDur = speed * count;
      const dur = `${totalDur}s`;
      const samples = 30 * count;
      
      const isNetworkInSequence = shapeSequence.includes('network');
      
      const lineElements: string[] = [];
      const chunkSize = 50;
      
      const sequence = state.sequence;
      if (sequence.length < 2) return;

      for (let i = 0; i < complexity; i += chunkSize) {
        const range = Array.from({length: Math.min(chunkSize, complexity - i)}, (_, k) => i + k);
        
        range.forEach((globalIdx) => {
          const x1Values: string[] = [];
          const y1Values: string[] = [];
          const x2Values: string[] = [];
          const y2Values: string[] = [];

          for (let j = 0; j <= samples; j++) {
            const p = (j / samples) * (sequence.length - 1);
            const index = Math.min(Math.floor(p), sequence.length - 2);
            const tRaw = p - index;
            
            let t;
            if (tRaw < 0.25) t = 0;
            else if (tRaw > 0.75) t = 1;
            else {
              const tm = (tRaw - 0.25) * 2;
              t = tm < 0.5 ? 2 * tm * tm : -1 + (4 - 2 * tm) * tm;
            }

            const s1 = sequence[index][globalIdx];
            const s2 = sequence[index + 1][globalIdx];

            const scatterFactor = Math.sin(Math.max(0, Math.min(1, (tRaw - 0.2) * 1.6)) * Math.PI);
            const scatter = scatterFactor * chaos;

            let dAngle = s2.angle - s1.angle;
            if (dAngle > Math.PI) dAngle -= 2 * Math.PI;
            if (dAngle < -Math.PI) dAngle += 2 * Math.PI;
            const rx = Math.sin(globalIdx * 13.5) * scatter;
            const ry = Math.cos(globalIdx * 41.2) * scatter;
            const rSpin = Math.sin(globalIdx * 5.5) * scatter * 0.1;
            const cx = s1.cx + (s2.cx - s1.cx) * t + rx;
            const cy = s1.cy + (s2.cy - s1.cy) * t + ry;
            const length = s1.length + (s2.length - s1.length) * t;
            const angle = s1.angle + dAngle * t + rSpin;
            x1Values.push((cx - (Math.cos(angle) * length) / 2).toFixed(2));
            y1Values.push((cy - (Math.sin(angle) * length) / 2).toFixed(2));
            x2Values.push((cx + (Math.cos(angle) * length) / 2).toFixed(2));
            y2Values.push((cy + (Math.sin(angle) * length) / 2).toFixed(2));
          }

          lineElements.push(`
    <line x1="${x1Values[0]}" y1="${y1Values[0]}" x2="${x2Values[0]}" y2="${y2Values[0]}" stroke="${state.color}" stroke-width="${isNetworkInSequence ? 0.4 : 0.7}" stroke-linecap="round" opacity="0.7">
      <animate attributeName="x1" values="${x1Values.join(';')}" dur="${dur}" repeatCount="indefinite" calcMode="linear" />
      <animate attributeName="y1" values="${y1Values.join(';')}" dur="${dur}" repeatCount="indefinite" calcMode="linear" />
      <animate attributeName="x2" values="${x2Values.join(';')}" dur="${dur}" repeatCount="indefinite" calcMode="linear" />
      <animate attributeName="y2" values="${y2Values.join(';')}" dur="${dur}" repeatCount="indefinite" calcMode="linear" />
    </line>`);
        });
        await new Promise(r => setTimeout(r, 0));
      }

      let nodes = '';
      if (isNetworkInSequence) {
        const nodePoints = [
          {x: 20, y: 80}, {x: 30, y: 30}, {x: 50, y: 50}, 
          {x: 70, y: 20}, {x: 80, y: 70}, {x: 40, y: 70},
          {x: 60, y: 40}, {x: 90, y: 40}, {x: 10, y: 50}
        ];
        nodePoints.forEach(n => {
          nodes += `
    <circle cx="${n.x}" cy="${n.y}" r="1.2" fill="${state.color}">
      <animate attributeName="opacity" values="0;0;0;0.8;1;0.8;0;0" dur="${dur}" repeatCount="indefinite" />
    </circle>`;
        });
      }

      const animatedSvg = `<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"${bgColor !== 'transparent' ? ` style="background: ${bgColor}"` : ''}>
  <defs>
    <filter id="glow" x="-20%" y="-20%" width="140%" height="140%">
      <feGaussianBlur stdDeviation="0.8" result="blur" />
      <feMerge>
        <feMergeNode in="blur" />
        <feMergeNode in="SourceGraphic" />
      </feMerge>
    </filter>
  </defs>
  <g filter="url(#glow)">
    ${lineElements.join('')}
    ${nodes}
  </g>
</svg>`;
      
      await navigator.clipboard.writeText(animatedSvg);
      setCopied(true);
      setTimeout(() => setCopied(false), 2000);
    } catch (err) {
      console.error("Failed to copy SVG", err);
    } finally {
      setIsCopying(false);
    }
  };

  const downloadSVG = async () => {
    if (isDownloadingSVG) return;
    setIsDownloadingSVG(true);
    
    await new Promise(r => setTimeout(r, 100));
    
    try {
      const state = currentState;
      const count = shapeSequence.length;
      const totalDur = speed * count;
      const dur = `${totalDur}s`;
      const samples = 30 * count;
      
      const isNetworkInSequence = shapeSequence.includes('network');
      
      const lineElements: string[] = [];
      const chunkSize = 50;
      
      const sequence = state.sequence;
      if (sequence.length < 2) return;

      for (let i = 0; i < complexity; i += chunkSize) {
        const range = Array.from({length: Math.min(chunkSize, complexity - i)}, (_, k) => i + k);
        
        range.forEach((globalIdx) => {
          const x1Values: string[] = [];
          const y1Values: string[] = [];
          const x2Values: string[] = [];
          const y2Values: string[] = [];

          for (let j = 0; j <= samples; j++) {
            const p = (j / samples) * (sequence.length - 1);
            const index = Math.min(Math.floor(p), sequence.length - 2);
            const tRaw = p - index;
            
            let t;
            if (tRaw < 0.25) t = 0;
            else if (tRaw > 0.75) t = 1;
            else {
              const tm = (tRaw - 0.25) * 2;
              t = tm < 0.5 ? 2 * tm * tm : -1 + (4 - 2 * tm) * tm;
            }

            const s1 = sequence[index][globalIdx];
            const s2 = sequence[index + 1][globalIdx];

            const scatterFactor = Math.sin(Math.max(0, Math.min(1, (tRaw - 0.2) * 1.6)) * Math.PI);
            const scatter = scatterFactor * chaos;

            let dAngle = s2.angle - s1.angle;
            if (dAngle > Math.PI) dAngle -= 2 * Math.PI;
            if (dAngle < -Math.PI) dAngle += 2 * Math.PI;
            const rx = Math.sin(globalIdx * 13.5) * scatter;
            const ry = Math.cos(globalIdx * 41.2) * scatter;
            const rSpin = Math.sin(globalIdx * 5.5) * scatter * 0.1;
            const cx = s1.cx + (s2.cx - s1.cx) * t + rx;
            const cy = s1.cy + (s2.cy - s1.cy) * t + ry;
            const length = s1.length + (s2.length - s1.length) * t;
            const angle = s1.angle + dAngle * t + rSpin;
            x1Values.push((cx - (Math.cos(angle) * length) / 2).toFixed(2));
            y1Values.push((cy - (Math.sin(angle) * length) / 2).toFixed(2));
            x2Values.push((cx + (Math.cos(angle) * length) / 2).toFixed(2));
            y2Values.push((cy + (Math.sin(angle) * length) / 2).toFixed(2));
          }

          lineElements.push(`
    <line x1="${x1Values[0]}" y1="${y1Values[0]}" x2="${x2Values[0]}" y2="${y2Values[0]}" stroke="${state.color}" stroke-width="${isNetworkInSequence ? 0.4 : 0.7}" stroke-linecap="round" opacity="0.7">
      <animate attributeName="x1" values="${x1Values.join(';')}" dur="${dur}" repeatCount="indefinite" calcMode="linear" />
      <animate attributeName="y1" values="${y1Values.join(';')}" dur="${dur}" repeatCount="indefinite" calcMode="linear" />
      <animate attributeName="x2" values="${x2Values.join(';')}" dur="${dur}" repeatCount="indefinite" calcMode="linear" />
      <animate attributeName="y2" values="${y2Values.join(';')}" dur="${dur}" repeatCount="indefinite" calcMode="linear" />
    </line>`);
        });
        await new Promise(r => setTimeout(r, 0));
      }

      let nodes = '';
      if (isNetworkInSequence) {
        const nodePoints = [{x: 20, y: 80}, {x: 30, y: 30}, {x: 50, y: 50}, {x: 70, y: 20}, {x: 80, y: 70}, {x: 40, y: 70}, {x: 60, y: 40}, {x: 90, y: 40}, {x: 10, y: 50}];
        nodePoints.forEach(n => {
          nodes += `
    <circle cx="${n.x}" cy="${n.y}" r="1.2" fill="${state.color}">
      <animate attributeName="opacity" values="0;0;0;0.8;1;0.8;0;0" dur="${dur}" repeatCount="indefinite" />
    </circle>`;
        });
      }

      const fileContent = `<?xml version="1.0" standalone="no"?>
<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"${bgColor !== 'transparent' ? ` style="background: ${bgColor}"` : ''}>
  <defs>
    <filter id="glow" x="-20%" y="-20%" width="140%" height="140%">
      <feGaussianBlur stdDeviation="0.8" result="blur" />
      <feMerge>
        <feMergeNode in="blur" />
        <feMergeNode in="SourceGraphic" />
      </feMerge>
    </filter>
  </defs>
  <g filter="url(#glow)">
    ${lineElements.join('')}
    ${nodes}
  </g>
</svg>`;
      
      const blob = new Blob([fileContent], { type: 'image/svg+xml' });
      const url = URL.createObjectURL(blob);
      const a = document.createElement('a');
      a.href = url;
      a.download = `morph-animation-${activePreset + 1}.svg`;
      a.click();
      URL.revokeObjectURL(url);
    } catch (err) {
      console.error("Failed to download SVG", err);
    } finally {
      setIsDownloadingSVG(false);
    }
  };

  const downloadGIF = async () => {
    if (isGeneratingGIF) return;
    setIsGeneratingGIF(true);
    
    await new Promise(r => setTimeout(r, 100));
    
    try {
      const canvas = document.createElement('canvas');
      const size = 600; // Increased resolution for better quality
      canvas.width = size;
      canvas.height = size;
      const ctx = canvas.getContext('2d', { willReadFrequently: true });
      if (!ctx) throw new Error("No 2D context");
      
      const gif = GIFEncoder();
      const isNetworkInSequence = shapeSequence.includes('network');
      const state = currentState;
      const sequence = state.sequence;
      const frames = 90; 
      const totalDurMs = speed * sequence.length * 1000;
      const frameDelay = Math.max(20, Math.floor(totalDurMs / frames));
      
      for (let i = 0; i < frames; i++) {
        const p = (i / frames) * (sequence.length - 1);
        const seqIndex = Math.min(Math.floor(p), sequence.length - 2);
        const tRaw = p - seqIndex;
        
        let t;
        if (tRaw < 0.25) t = 0;
        else if (tRaw > 0.75) t = 1;
        else {
          const tm = (tRaw - 0.25) * 2;
          t = tm < 0.5 ? 2 * tm * tm : -1 + (4 - 2 * tm) * tm;
        }
        
        ctx.fillStyle = bgColor === 'transparent' ? '#111113' : bgColor;
        ctx.fillRect(0, 0, size, size);
        
        ctx.save();
        ctx.scale(size / 100, size / 100);
        
        const scatterFactor = Math.sin(Math.max(0, Math.min(1, (tRaw - 0.2) * 1.6)) * Math.PI);
        const scatter = scatterFactor * chaos;
        
        ctx.strokeStyle = state.color;
        ctx.lineWidth = isNetworkInSequence ? 0.5 : 0.8;
        ctx.lineCap = 'round';
        ctx.shadowBlur = 6;
        ctx.shadowColor = state.color;
        
        const source = sequence[seqIndex];
        const target = sequence[seqIndex + 1];

        source.forEach((s1, idx) => {
          const s2 = target[idx];
          let dAngle = s2.angle - s1.angle;
          if (dAngle > Math.PI) dAngle -= 2 * Math.PI;
          if (dAngle < -Math.PI) dAngle += 2 * Math.PI;
          
          const rx = Math.sin(idx * 13.5) * scatter;
          const ry = Math.cos(idx * 41.2) * scatter;
          const rSpin = Math.sin(idx * 5.5) * scatter * 0.1;

          const cx = s1.cx + (s2.cx - s1.cx) * t + rx;
          const cy = s1.cy + (s2.cy - s1.cy) * t + ry;
          const length = s1.length + (s2.length - s1.length) * t;
          const angle = s1.angle + dAngle * t + rSpin;
          
          if (length > 0) {
            ctx.beginPath();
            ctx.globalAlpha = 0.8;
            ctx.moveTo(cx - (Math.cos(angle) * length) / 2, cy - (Math.sin(angle) * length) / 2);
            ctx.lineTo(cx + (Math.cos(angle) * length) / 2, cy + (Math.sin(angle) * length) / 2);
            ctx.stroke();
          }
        });
        
        if (isNetworkInSequence && tRaw > 0.8) {
          ctx.globalAlpha = Math.min(1, (tRaw - 0.8) * 5);
          ctx.fillStyle = state.color;
          const nodePoints = [
            {x: 20, y: 80}, {x: 30, y: 30}, {x: 50, y: 50}, 
            {x: 70, y: 20}, {x: 80, y: 70}, {x: 40, y: 70},
            {x: 60, y: 40}, {x: 90, y: 40}, {x: 10, y: 50}
          ];
          nodePoints.forEach(n => {
            ctx.beginPath();
            ctx.arc(n.x, n.y, 1.5, 0, Math.PI * 2);
            ctx.fill();
          });
        }
        
        ctx.restore();
        
        const { data } = ctx.getImageData(0, 0, size, size);
        const palette = quantize(data, 256);
        const frameIndex = applyPalette(data, palette);
        gif.writeFrame(frameIndex, size, size, { palette, delay: frameDelay });
        
        if (i % 10 === 0) await new Promise(r => setTimeout(r, 0));
      }
      
      gif.finish();
      const buffer = gif.bytes();
      const blob = new Blob([buffer], { type: 'image/gif' });
      const url = URL.createObjectURL(blob);
      
      const a = document.createElement('a');
      a.href = url;
      a.download = `morph-animation-${activePreset + 1}.gif`;
      document.body.appendChild(a);
      a.click();
      document.body.removeChild(a);
      URL.revokeObjectURL(url);
    } catch (err) {
      console.error("Failed to generate GIF", err);
    } finally {
      setIsGeneratingGIF(false);
    }
  };

  const downloadVideo = async () => {
    if (isGeneratingVideo) return;
    setIsGeneratingVideo(true);
    
    await new Promise(r => setTimeout(r, 100));
    
    try {
      const canvas = document.createElement('canvas');
      const size = 1080; // High quality
      canvas.width = size;
      canvas.height = size;
      const ctx = canvas.getContext('2d', { alpha: false });
      if (!ctx) throw new Error("No 2D context");
      
      const stream = canvas.captureStream(60);
      const recorder = new MediaRecorder(stream, { 
        mimeType: 'video/webm;codecs=vp9',
        videoBitsPerSecond: 8000000
      });
      
      const chunks: Blob[] = [];
      recorder.ondataavailable = (e) => chunks.push(e.data);
      recorder.onstop = () => {
        const blob = new Blob(chunks, { type: 'video/webm' });
        const url = URL.createObjectURL(blob);
        const a = document.createElement('a');
        a.href = url;
        a.download = `morph-video-${activePreset + 1}.webm`;
        a.click();
        URL.revokeObjectURL(url);
      };
      
      recorder.start();
      const isNetworkInSequence = shapeSequence.includes('network');
      const state = currentState;
      const sequence = state.sequence;
      const fps = 60;
      const totalDur = speed * (sequence.length - 1);
      const frames = Math.floor(totalDur * fps);
      
      for (let i = 0; i <= frames; i++) {
        const p = (i / frames) * (sequence.length - 1);
        const index = Math.min(Math.floor(p), sequence.length - 2);
        const tRaw = p - index;
        
        let t;
        if (tRaw < 0.25) t = 0;
        else if (tRaw > 0.75) t = 1;
        else {
          const tm = (tRaw - 0.25) * 2;
          t = tm < 0.5 ? 2 * tm * tm : -1 + (4 - 2 * tm) * tm;
        }
        
        ctx.fillStyle = bgColor === 'transparent' ? '#111113' : bgColor;
        ctx.fillRect(0, 0, size, size);
        ctx.save();
        ctx.scale(size / 100, size / 100);
        
        const scatterFactor = Math.sin(Math.max(0, Math.min(1, (tRaw - 0.2) * 1.6)) * Math.PI);
        const scatter = scatterFactor * chaos;

        ctx.strokeStyle = state.color;
        ctx.lineWidth = isNetworkInSequence ? 0.5 : 0.8;
        ctx.lineCap = 'round';
        ctx.shadowBlur = 8;
        ctx.shadowColor = state.color;
        
        const source = sequence[index];
        const target = sequence[index + 1];

        source.forEach((s1, idx) => {
          const s2 = target[idx];
          let dAngle = s2.angle - s1.angle;
          if (dAngle > Math.PI) dAngle -= 2 * Math.PI;
          if (dAngle < -Math.PI) dAngle += 2 * Math.PI;
          const rx = Math.sin(idx * 13.5) * scatter;
          const ry = Math.cos(idx * 41.2) * scatter;
          const rSpin = Math.sin(idx * 5.5) * scatter * 0.1;
          const cx = s1.cx + (s2.cx - s1.cx) * t + rx;
          const cy = s1.cy + (s2.cy - s1.cy) * t + ry;
          const length = s1.length + (s2.length - s1.length) * t;
          const angle = s1.angle + dAngle * t + rSpin;
          if (length > 0) {
            ctx.beginPath();
            ctx.globalAlpha = 0.8;
            ctx.moveTo(cx - (Math.cos(angle) * length) / 2, cy - (Math.sin(angle) * length) / 2);
            ctx.lineTo(cx + (Math.cos(angle) * length) / 2, cy + (Math.sin(angle) * length) / 2);
            ctx.stroke();
          }
        });
        
        if (isNetworkInSequence && tRaw > 0.8) {
          ctx.globalAlpha = Math.min(1, (tRaw - 0.8) * 5);
          ctx.fillStyle = state.color;
          const nodePoints = [
            {x: 20, y: 80}, {x: 30, y: 30}, {x: 50, y: 50}, 
            {x: 70, y: 20}, {x: 80, y: 70}, {x: 40, y: 70},
            {x: 60, y: 40}, {x: 90, y: 40}, {x: 10, y: 50}
          ];
          nodePoints.forEach(n => {
            ctx.beginPath();
            ctx.arc(n.x, n.y, 1.5, 0, Math.PI * 2);
            ctx.fill();
          });
        }
        ctx.restore();
        await new Promise(r => setTimeout(r, 16));
      }
      
      recorder.stop();
    } catch (err) {
      console.error("Failed to generate Video", err);
    } finally {
      setIsGeneratingVideo(false);
    }
  };

  const downloadPNG = async () => {
    if (isGeneratingPNG) return;
    setIsGeneratingPNG(true);
    
    try {
      const canvas = document.createElement('canvas');
      const size = 2000; // Very high quality for design work
      canvas.width = size;
      canvas.height = size;
      const ctx = canvas.getContext('2d');
      if (!ctx) throw new Error("No 2D context");
      
      const p = progress.get();
      const state = currentState;
      const sequence = state.sequence;
      const isNetworkInSequence = shapeSequence.includes('network');
      
      ctx.fillStyle = bgColor === 'transparent' ? '#111113' : bgColor;
      if (bgColor === 'transparent') {
        ctx.clearRect(0, 0, size, size);
      } else {
        ctx.fillRect(0, 0, size, size);
      }
      ctx.save();
      ctx.scale(size / 100, size / 100);
      
      const index = Math.min(Math.floor(p), sequence.length - 2);
      const tRaw = p - index;
      
      let t;
      if (tRaw < 0.25) t = 0;
      else if (tRaw > 0.75) t = 1;
      else {
        const tm = (tRaw - 0.25) * 2;
        t = tm < 0.5 ? 2 * tm * tm : -1 + (4 - 2 * tm) * tm;
      }

      const scatterFactor = Math.sin(Math.max(0, Math.min(1, (tRaw - 0.2) * 1.6)) * Math.PI);
      const scatter = scatterFactor * chaos;

      ctx.strokeStyle = state.color;
      ctx.lineWidth = isNetworkInSequence ? 0.5 : 0.8;
      ctx.lineCap = 'round';
      ctx.shadowBlur = 8;
      ctx.shadowColor = state.color;
      
      const source = sequence[index];
      const target = sequence[index + 1];

      source.forEach((s1, idx) => {
        const s2 = target[idx];
        let dAngle = s2.angle - s1.angle;
        if (dAngle > Math.PI) dAngle -= 2 * Math.PI;
        if (dAngle < -Math.PI) dAngle += 2 * Math.PI;
        const rx = Math.sin(idx * 13.5) * scatter;
        const ry = Math.cos(idx * 41.2) * scatter;
        const rSpin = Math.sin(idx * 5.5) * scatter * 0.1;
        const cx = s1.cx + (s2.cx - s1.cx) * t + rx;
        const cy = s1.cy + (s2.cy - s1.cy) * t + ry;
        const length = s1.length + (s2.length - s1.length) * t;
        const angle = s1.angle + dAngle * t + rSpin;
        if (length > 0) {
          ctx.beginPath();
          ctx.globalAlpha = 0.8;
          ctx.moveTo(cx - (Math.cos(angle) * length) / 2, cy - (Math.sin(angle) * length) / 2);
          ctx.lineTo(cx + (Math.cos(angle) * length) / 2, cy + (Math.sin(angle) * length) / 2);
          ctx.stroke();
        }
      });
      
      if (isNetworkInSequence && tRaw > 0.8) {
        ctx.globalAlpha = Math.min(1, (tRaw - 0.8) * 5);
        ctx.fillStyle = state.color;
        const nodePoints = [
          {x: 20, y: 80}, {x: 30, y: 30}, {x: 50, y: 50}, 
          {x: 70, y: 20}, {x: 80, y: 70}, {x: 40, y: 70},
          {x: 60, y: 40}, {x: 90, y: 40}, {x: 10, y: 50}
        ];
        nodePoints.forEach(n => {
          ctx.beginPath();
          ctx.arc(n.x, n.y, 1.5, 0, Math.PI * 2);
          ctx.fill();
        });
      }
      
      ctx.restore();
      
      const url = canvas.toDataURL('image/png');
      const a = document.createElement('a');
      a.href = url;
      a.download = `morph-frame-${activePreset + 1}.png`;
      a.click();
    } catch (err) {
      console.error("Failed to generate PNG", err);
    } finally {
      setIsGeneratingPNG(false);
    }
  };

  const handleImageUpload = (e: ChangeEvent<HTMLInputElement>) => {
    const file = e.target.files?.[0];
    if (!file) return;

    const reader = new FileReader();
    reader.onload = (event) => {
      const img = new Image();
      img.onload = () => {
        const canvas = document.createElement('canvas');
        const ctx = canvas.getContext('2d');
        if (!ctx) return;

        const targetWidth = 40;
        const targetHeight = Math.floor((img.height / img.width) * targetWidth * 0.5);
        canvas.width = targetWidth;
        canvas.height = targetHeight;

        ctx.drawImage(img, 0, 0, targetWidth, targetHeight);
        const imageData = ctx.getImageData(0, 0, targetWidth, targetHeight);
        const data = imageData.data;

        const asciiRows: string[] = [];
        const chars = " .:-=+*#%@"; 
        const mapping: Record<string, string> = {
          ' ': ' ', '.': ':', ':': ':', '-': '-', '=': '=', '+': 'x', '*': 'X', '#': '#', '%': '%', '@': 'X'
        };

        for (let y = 0; y < targetHeight; y++) {
          let row = "";
          for (let x = 0; x < targetWidth; x++) {
            const idx = (y * targetWidth + x) * 4;
            const r = data[idx];
            const g = data[idx + 1];
            const b = data[idx + 2];
            const brightness = (r + g + b) / 3;
            
            const charIdx = Math.floor((brightness / 255) * (chars.length - 1));
            const rawChar = chars[charIdx];
            row += mapping[rawChar] || ' ';
          }
          asciiRows.push(row);
        }

        const newId = `img-${Date.now()}`;
        setUploadedImages(prev => [...prev, { id: newId, name: file.name, data: asciiRows }]);
        setShapeSequence(prev => [...prev, newId]);
      };
      img.src = event.target?.result as string;
    };
    reader.readAsDataURL(file);
  };

  useAnimationFrame(() => {
    const p = progress.get();
    setCurrentProgress(p);
    
    const state = currentState;
    if (state.sequence.length === 0) return;
    if (state.sequence.length === 1) {
      setCurrentSegments(state.sequence[0]);
      return;
    }

    const index = Math.min(Math.floor(p), state.sequence.length - 2);
    const tRaw = p - index;
    
    // Implement pause logic: stay at 0 for 25%, move for 50%, stay at 1 for 25%
    let t;
    if (tRaw < 0.25) {
      t = 0;
    } else if (tRaw > 0.75) {
      t = 1;
    } else {
      const tMapped = (tRaw - 0.25) * 2; // Map 0.25..0.75 to 0..1
      // Ease in out quad for the movement part
      t = tMapped < 0.5 ? 2 * tMapped * tMapped : -1 + (4 - 2 * tMapped) * tMapped;
    }

    const source = state.sequence[index];
    const target = state.sequence[index + 1];
    
    // Detect typing effect
    let identicalPrefixCount = 0;
    let s1RealCount = 0;
    let s2RealCount = 0;
    let prefixBroken = false;
    
    for (let i = 0; i < source.length; i++) {
      const isReal1 = source[i] && source[i].length > 0;
      const isReal2 = target[i] && target[i].length > 0;
      if (isReal1) s1RealCount++;
      if (isReal2) s2RealCount++;
      
      if (!prefixBroken && isReal1 && isReal2) {
        const dx = source[i].cx - target[i].cx;
        const dy = source[i].cy - target[i].cy;
        if (dx*dx + dy*dy < 4) {
          identicalPrefixCount++;
        } else {
          prefixBroken = true;
        }
      } else if (!prefixBroken && (isReal1 || isReal2)) {
        prefixBroken = true;
      }
    }
    
    const minReal = Math.min(s1RealCount, s2RealCount);
    const isTyping = minReal > 0 && identicalPrefixCount >= minReal * 0.8;

    let firstDiffIdx = -1;
    let lastDiffIdx = -1;
    if (isTyping) {
      for (let i = 0; i < source.length; i++) {
        const isReal1 = source[i] && source[i].length > 0;
        const isReal2 = target[i] && target[i].length > 0;
        if (i >= identicalPrefixCount && (isReal1 || isReal2)) {
          if (firstDiffIdx === -1) firstDiffIdx = i;
          lastDiffIdx = i;
        }
      }
    }

    // Scatter only during movement
    const scatterFactor = Math.sin(Math.max(0, Math.min(1, (tRaw - 0.2) * 1.6)) * Math.PI);
    const scatter = scatterFactor * chaos;

    const morphed = source.map((s1, idx) => {
      const s2 = target[idx];
      
      if (isTyping) {
        if (idx < identicalPrefixCount) {
          return s1;
        } else {
          if (firstDiffIdx !== -1 && lastDiffIdx !== -1) {
            const totalDiff = lastDiffIdx - firstDiffIdx + 1;
            const myRelativeIdx = idx - firstDiffIdx;
            const myTime = myRelativeIdx / totalDiff;
            
            const isReal1 = s1.length > 0;
            const isReal2 = s2.length > 0;
            
            // Map tRaw to type over 0..0.8
            const typeProgress = Math.min(1, tRaw / 0.8);
            
            if (!isReal1 && isReal2) {
              return typeProgress >= myTime ? s2 : s1;
            } else if (isReal1 && !isReal2) {
              return typeProgress >= (1 - myTime) ? s2 : s1;
            } else {
              return typeProgress >= myTime ? s2 : s1;
            }
          }
        }
        return s2;
      }

      let dAngle = s2.angle - s1.angle;
      if (dAngle > Math.PI) dAngle -= 2 * Math.PI;
      if (dAngle < -Math.PI) dAngle += 2 * Math.PI;

      const rx = Math.sin(idx * 13.5) * scatter;
      const ry = Math.cos(idx * 41.2) * scatter;
      const rSpin = Math.sin(idx * 5.5) * scatter * 0.1;

      return {
        cx: s1.cx + (s2.cx - s1.cx) * t + rx,
        cy: s1.cy + (s2.cy - s1.cy) * t + ry,
        length: s1.length + (s2.length - s1.length) * t,
        angle: s1.angle + dAngle * t + rSpin
      };
    });

    setCurrentSegments(morphed);
  });

  return (
    <div className="min-h-screen bg-[#111113] text-zinc-100 font-sans flex flex-col">
      <header className="border-b border-zinc-800/50 bg-[#111113]/80 backdrop-blur-md sticky top-0 z-10">
        <div className="max-w-6xl mx-auto px-4 h-16 flex items-center justify-between">
          <div className="flex items-center gap-2">
            <div className="w-8 h-8 rounded-lg bg-zinc-800 flex items-center justify-center border border-zinc-700">
              <RefreshCw className="w-4 h-4 text-zinc-300" />
            </div>
            <h1 className="text-lg font-medium tracking-tight whitespace-nowrap">Morph System</h1>
          </div>
          
          <div className="flex items-center gap-4">
            <div className="hidden md:flex bg-zinc-900 p-1 rounded-lg border border-zinc-800">
              {[
                { id: 'classic', name: 'Classic' },
                { id: 'technical', name: 'Technical' },
                { id: 'complex-ascii', name: 'Complex ASCII' },
                { id: 'blueprint', name: 'Blueprint' },
                { id: 'glitch', name: 'Glitch' }
              ].map((style) => (
                <button
                  key={style.id}
                  onClick={() => {
                    setRenderStyle(style.id as any);
                    if (style.id === 'technical') {
                      setBgColor('#050505');
                      setColor('#FF3333');
                    } else if (style.id === 'complex-ascii') {
                      setBgColor('#08080a');
                      setColor('#00ffff');
                    } else if (style.id === 'blueprint') {
                      setBgColor('#0a1a3a');
                      setColor('#ffffff');
                    } else if (style.id === 'glitch') {
                      setBgColor('#000000');
                      setColor('#00ff00');
                    } else {
                      setBgColor('#111113');
                      setColor('#a3e635');
                    }
                  }}
                  className={cn(
                    "px-4 py-1.5 rounded-md text-sm font-medium transition-all",
                    renderStyle === style.id 
                      ? "bg-zinc-800 text-white shadow-sm" 
                      : "text-zinc-500 hover:text-zinc-300"
                  )}
                >
                  {style.name}
                </button>
              ))}
            </div>
            <div className="h-6 w-px bg-zinc-800 mx-2 hidden md:block" />
            
            <div className="flex bg-zinc-900 p-1 rounded-lg border border-zinc-800">
              <button
                onClick={() => setViewMode('single')}
                className={cn(
                  "px-3 py-1.5 rounded-md text-xs font-medium transition-all flex items-center gap-2",
                  viewMode === 'single' ? "bg-zinc-800 text-white" : "text-zinc-500 hover:text-zinc-300"
                )}
              >
                <Maximize2 className="w-3.5 h-3.5" />
                Single
              </button>
              <button
                onClick={() => setViewMode('gallery')}
                className={cn(
                  "px-3 py-1.5 rounded-md text-xs font-medium transition-all flex items-center gap-2",
                  viewMode === 'gallery' ? "bg-zinc-800 text-white" : "text-zinc-500 hover:text-zinc-300"
                )}
              >
                <LayoutGrid className="w-3.5 h-3.5" />
                Gallery
              </button>
            </div>

            <div className="h-6 w-px bg-zinc-800 mx-2 hidden md:block" />
            <div className="flex items-center gap-2">
              <button
                onClick={() => setIsPlaying(!isPlaying)}
                className="w-8 h-8 rounded-full bg-zinc-800 hover:bg-zinc-700 text-white flex items-center justify-center transition-colors border border-zinc-700"
              >
                {isPlaying ? <Pause className="w-4 h-4" /> : <Play className="w-4 h-4 ml-0.5" />}
              </button>
              <button
                onClick={copyCode}
                disabled={isCopying}
                className="w-8 h-8 rounded-full bg-zinc-800 hover:bg-zinc-700 text-white flex items-center justify-center transition-colors border border-zinc-700 disabled:opacity-50 relative"
                title="Copy SVG Code"
              >
                {isCopying ? (
                  <span className="absolute inset-0 rounded-full border-2 border-t-transparent border-white animate-spin" />
                ) : copied ? (
                  <Check className="w-4 h-4 text-emerald-400" />
                ) : (
                  <Copy className="w-4 h-4" />
                )}
              </button>
              <button
                onClick={downloadSVG}
                disabled={isDownloadingSVG}
                className="w-8 h-8 rounded-full bg-zinc-800 hover:bg-zinc-700 text-white flex items-center justify-center transition-colors border border-zinc-700 disabled:opacity-50 relative"
                title="Download SVG File"
              >
                {isDownloadingSVG ? (
                  <span className="absolute inset-0 rounded-full border-2 border-t-transparent border-white animate-spin" />
                ) : (
                  <FileDown className="w-4 h-4" />
                )}
              </button>
              <button
                onClick={downloadPNG}
                disabled={isGeneratingPNG}
                className="w-8 h-8 rounded-full bg-zinc-800 hover:bg-zinc-700 text-white flex items-center justify-center transition-colors border border-zinc-700 disabled:opacity-50 relative"
                title="Save Frame (PNG)"
              >
                {isGeneratingPNG ? (
                  <span className="absolute inset-0 rounded-full border-2 border-t-transparent border-white animate-spin" />
                ) : (
                  <ImageIcon className="w-4 h-4" />
                )}
              </button>
              <button
                onClick={downloadGIF}
                disabled={isGeneratingGIF}
                className="w-8 h-8 rounded-full bg-zinc-800 hover:bg-zinc-700 text-white flex items-center justify-center transition-colors border border-zinc-700 disabled:opacity-50 relative"
                title="Save Animation (GIF)"
              >
                {isGeneratingGIF ? (
                  <span className="absolute inset-0 rounded-full border-2 border-t-transparent border-white animate-spin" />
                ) : (
                  <RefreshCw className="w-4 h-4" />
                )}
              </button>
              <button
                onClick={downloadVideo}
                disabled={isGeneratingVideo}
                className="w-8 h-8 rounded-full bg-zinc-800 hover:bg-zinc-700 text-white flex items-center justify-center transition-colors border border-zinc-700 disabled:opacity-50 relative"
                title="Save Animation (Video)"
              >
                {isGeneratingVideo ? (
                  <span className="absolute inset-0 rounded-full border-2 border-t-transparent border-white animate-spin" />
                ) : (
                  <Video className="w-4 h-4" />
                )}
              </button>
            </div>
          </div>
        </div>
      </header>

      <div className="flex-1 flex flex-col lg:flex-row overflow-hidden">
        {/* Sidebar Controls */}
        <aside className="w-full lg:w-80 border-r border-zinc-800/50 bg-[#111113] p-6 space-y-8 overflow-y-auto">
          <section className="space-y-4">
            <h2 className="text-xs font-semibold text-zinc-500 lowercase tracking-wider">Parameters</h2>
            
            {renderStyle === 'technical' && (
              <div className="space-y-2">
                <label className="text-[10px] text-zinc-500 lowercase tracking-wider">Technical Text</label>
                <input 
                  type="text" 
                  value={technicalText}
                  onChange={(e) => setTechnicalText(e.target.value)}
                  className="w-full bg-zinc-900 border border-zinc-800 rounded px-2 py-1.5 text-[10px] font-mono text-zinc-300 focus:outline-none focus:border-zinc-600"
                  placeholder="Enter text..."
                />
              </div>
            )}

            <div className="space-y-4">
              <div className="space-y-2">
                <div className="flex justify-between text-xs">
                  <span className="text-zinc-400">Background</span>
                </div>
                <div className="flex gap-2">
                  {[
                    { name: 'Dark', value: '#111113' },
                    { name: 'White', value: '#ffffff' },
                    { name: 'None', value: 'transparent' }
                  ].map((bg) => (
                    <button
                      key={bg.value}
                      onClick={() => setBgColor(bg.value)}
                      className={cn(
                        "flex-1 py-1.5 rounded-md text-[10px] font-medium border transition-all",
                        bgColor === bg.value 
                          ? "bg-zinc-800 border-zinc-600 text-white" 
                          : "bg-zinc-900 border-zinc-800 text-zinc-500 hover:text-zinc-300"
                      )}
                    >
                      {bg.name}
                    </button>
                  ))}
                </div>
              </div>

              <div className="space-y-2">
                <div className="flex justify-between text-xs">
                  <span className="text-zinc-400">Speed</span>
                  <span className="text-zinc-200">{speed}s</span>
                </div>
                <input 
                  type="range" min="0.5" max="10" step="0.1" 
                  value={speed} onChange={(e) => setSpeed(parseFloat(e.target.value))}
                  className="w-full h-1 bg-zinc-800 rounded-lg appearance-none cursor-pointer accent-zinc-400"
                />
              </div>

              <div className="space-y-2">
                <div className="flex justify-between text-xs">
                  <span className="text-zinc-400">Complexity</span>
                  <span className="text-zinc-200">{complexity}</span>
                </div>
                <input 
                  type="range" min="50" max="1000" step="10" 
                  value={complexity} onChange={(e) => setComplexity(parseInt(e.target.value))}
                  className="w-full h-1 bg-zinc-800 rounded-lg appearance-none cursor-pointer accent-zinc-400"
                />
              </div>

              <div className="space-y-2">
                <div className="flex justify-between text-xs">
                  <span className="text-zinc-400">Chaos</span>
                  <span className="text-zinc-200">{chaos}</span>
                </div>
                <input 
                  type="range" min="0" max="50" step="1" 
                  value={chaos} onChange={(e) => setChaos(parseInt(e.target.value))}
                  className="w-full h-1 bg-zinc-800 rounded-lg appearance-none cursor-pointer accent-zinc-400"
                />
              </div>
            </div>
          </section>

          <section className="space-y-4">
            <div className="flex flex-col gap-2">
              <h2 className="text-xs font-semibold text-zinc-500 lowercase tracking-wider">Shape Sequence</h2>
              <div className="grid grid-cols-2 gap-2">
                <label className="flex items-center gap-2 p-2 rounded-md bg-zinc-800 hover:bg-zinc-700 text-zinc-300 border border-zinc-700 transition-colors cursor-pointer text-[10px]">
                  <Upload className="w-3 h-3" />
                  Upload Image
                  <input type="file" accept="image/*" className="hidden" onChange={handleImageUpload} />
                </label>
                <button 
                  onClick={() => {
                    setShapeSequence(prev => [...prev, 'cube']);
                    setActivePreset(-1);
                  }}
                  disabled={shapeSequence.length >= 10}
                  className="flex items-center gap-2 p-2 rounded-md bg-zinc-800 hover:bg-zinc-700 text-zinc-300 border border-zinc-700 transition-colors disabled:opacity-50 text-[10px]"
                >
                  <Plus className="w-3 h-3" />
                  Add Library Shape
                </button>
                <button 
                  onClick={() => {
                    setShapeSequence(prev => [...prev, 'custom']);
                    setActivePreset(-1);
                  }}
                  className="flex items-center gap-2 p-2 rounded-md bg-zinc-800 hover:bg-zinc-700 text-zinc-300 border border-zinc-700 transition-colors text-[10px]"
                >
                  <LayoutGrid className="w-3 h-3" />
                  Add SVG
                </button>
                <button 
                  onClick={() => {
                    setShapeSequence(prev => [...prev, 'custom-ascii']);
                    setActivePreset(-1);
                  }}
                  className="flex items-center gap-2 p-2 rounded-md bg-zinc-800 hover:bg-zinc-700 text-zinc-300 border border-zinc-700 transition-colors text-[10px]"
                >
                  <LayoutGrid className="w-3 h-3" />
                  Add Metaphor
                </button>
              </div>
            </div>

            <div className="space-y-2">
              {shapeSequence.map((id, idx) => (
                <div key={`${id}-${idx}`} className="flex items-center gap-2 group animate-in fade-in slide-in-from-left-2 duration-200" style={{ animationDelay: `${idx * 50}ms` }}>
                  <div className="text-[10px] font-mono text-zinc-600 w-4">{idx + 1}</div>
                  <select 
                    value={id}
                    onChange={(e) => {
                      const newSeq = [...shapeSequence];
                      const selectedShape = shapes.find(s => s.id === e.target.value);
                      if (selectedShape?.isAnimated && selectedShape.frames) {
                        newSeq.splice(idx, 1, ...selectedShape.frames);
                      } else {
                        newSeq[idx] = e.target.value;
                      }
                      setShapeSequence(newSeq);
                      setActivePreset(-1);
                    }}
                    className="flex-1 bg-zinc-900 border border-zinc-800 rounded-md p-1.5 text-[11px] text-zinc-300 focus:outline-none focus:border-zinc-600"
                  >
                    <option value="custom-ascii">Custom ASCII</option>
                    <option value="custom">Custom SVG</option>
                    {shapes.map(s => (
                      <option key={s.id} value={s.id}>{s.name}</option>
                    ))}
                  </select>
                  <div className="flex items-center gap-0.5 opacity-0 group-hover:opacity-100 transition-opacity">
                    <button 
                      onClick={() => {
                        if (idx === 0) return;
                        const newSeq = [...shapeSequence];
                        [newSeq[idx-1], newSeq[idx]] = [newSeq[idx], newSeq[idx-1]];
                        setShapeSequence(newSeq);
                        setActivePreset(-1);
                      }}
                      className="p-1 hover:text-white text-zinc-600 disabled:opacity-0"
                      disabled={idx === 0}
                    >
                      <ChevronUp className="w-3 h-3" />
                    </button>
                    <button 
                      onClick={() => {
                        if (idx === shapeSequence.length - 1) return;
                        const newSeq = [...shapeSequence];
                        [newSeq[idx], newSeq[idx+1]] = [newSeq[idx+1], newSeq[idx]];
                        setShapeSequence(newSeq);
                        setActivePreset(-1);
                      }}
                      className="p-1 hover:text-white text-zinc-600 disabled:opacity-0"
                      disabled={idx === shapeSequence.length - 1}
                    >
                      <ChevronDown className="w-3 h-3" />
                    </button>
                    <button 
                      onClick={() => {
                        if (shapeSequence.length <= 1) return;
                        setShapeSequence(prev => prev.filter((_, i) => i !== idx));
                        setActivePreset(-1);
                      }}
                      className="p-1 hover:text-red-400 text-zinc-600 disabled:opacity-0"
                      disabled={shapeSequence.length <= 1}
                    >
                      <Trash2 className="w-3 h-3" />
                    </button>
                  </div>
                </div>
              ))}
            </div>

            {shapeSequence.includes('custom-ascii') && (
              <div className="space-y-2 pt-2 border-t border-zinc-800/50 animate-in fade-in slide-in-from-top-2 duration-300">
                <label className="text-[10px] text-zinc-500 lowercase">Custom ASCII Art</label>
                <textarea 
                  value={customAscii}
                  onChange={(e) => setCustomAscii(e.target.value)}
                  placeholder="[ base ]..."
                  className="w-full h-20 bg-zinc-900 border border-zinc-800 rounded-md p-2 text-[10px] font-mono text-zinc-300 focus:outline-none focus:border-zinc-600 transition-colors resize-none"
                />
              </div>
            )}

            {shapeSequence.includes('custom') && (
              <div className="space-y-2 pt-2 border-t border-zinc-800/50 animate-in fade-in slide-in-from-top-2 duration-300">
                <label className="text-[10px] text-zinc-500 lowercase">Custom SVG Path</label>
                <textarea 
                  value={customSvg}
                  onChange={(e) => setCustomSvg(e.target.value)}
                  placeholder="M 10 10 L 90 90..."
                  className="w-full h-20 bg-zinc-900 border border-zinc-800 rounded-md p-2 text-[10px] font-mono text-zinc-300 focus:outline-none focus:border-zinc-600 transition-colors resize-none"
                />
              </div>
            )}
          </section>

          <section className="space-y-4">
            <h2 className="text-xs font-semibold text-zinc-500 lowercase tracking-wider">System Color</h2>
            <div className="flex flex-wrap gap-2">
              {['#a3e635', '#84cc16', '#c084fc', '#d8b4fe', '#38bdf8', '#fb7185', '#fbbf24'].map(c => (
                <button
                  key={c}
                  onClick={() => setColor(c)}
                  className={cn(
                    "w-6 h-6 rounded-full border-2 transition-all",
                    color === c ? "border-white scale-110" : "border-transparent opacity-50 hover:opacity-100"
                  )}
                  style={{ backgroundColor: c }}
                />
              ))}
            </div>
          </section>

          <section className="space-y-4">
            <h2 className="text-xs font-semibold text-zinc-500 lowercase tracking-wider">Examples</h2>
            <div className="grid grid-cols-2 gap-2">
              {examples.map((preset, i) => (
                <button
                  key={i}
                  onClick={() => {
                    setActivePreset(i);
                    setRenderStyle(preset.style as any);
                    setShapeSequence(preset.shapes);
                    setColor(preset.color);
                    if (preset.style === 'technical') setBgColor('#050505');
                    else if (preset.style === 'blueprint') setBgColor('#0a1a3a');
                    else if (preset.style === 'complex-ascii') setBgColor('#08080a');
                    else setBgColor('#111113');
                  }}
                  className={cn(
                    "px-3 py-2 rounded-md text-[10px] font-medium border transition-all text-left flex flex-col gap-1",
                    activePreset === i 
                      ? "bg-zinc-800 border-zinc-600 text-white" 
                      : "bg-zinc-900 border-zinc-800 text-zinc-500 hover:border-zinc-700"
                  )}
                >
                  <span className="text-zinc-200">{preset.name}</span>
                  <span className="text-[9px] opacity-50 lowercase">{preset.style}</span>
                </button>
              ))}
            </div>
          </section>
        </aside>

        {/* Main Viewport */}
        <main 
          className="flex-1 p-8 flex flex-col items-center justify-center relative transition-colors duration-500 overflow-hidden"
          style={{ backgroundColor: bgColor === 'transparent' ? '#0c0c0e' : bgColor }}
        >
          {viewMode === 'single' ? (
            <>
            <div className="w-full max-w-4xl aspect-video relative flex items-center justify-center">
              {/* Background layers based on style */}
              {renderStyle === 'technical' && (
                <>
                  {/* Starfield */}
                  <div className="absolute inset-0 opacity-60 pointer-events-none overflow-hidden">
                    {technicalStars.map((star, i) => (
                      <motion.div 
                        key={i}
                        className={cn("absolute bg-white rounded-full shadow-[0_0_3px_rgba(255,255,255,0.5)]", star.size)}
                        initial={{ 
                          left: star.left, 
                          top: star.top,
                          opacity: star.opacity 
                        }}
                        animate={{ 
                          x: [0, star.driftX, 0],
                          y: [0, star.driftY, 0],
                          opacity: [star.opacity, 1, star.opacity]
                        }}
                        transition={{
                          duration: star.duration * 4,
                          repeat: Infinity,
                          ease: "linear"
                        }}
                      />
                    ))}
                  </div>

                  {/* Technical Grid/Markers */}
                  <div className="absolute inset-0 opacity-10 pointer-events-none">
                    <div className="absolute inset-0" style={{ backgroundImage: 'radial-gradient(circle, #fff 1px, transparent 1px)', backgroundSize: '40px 40px' }} />
                    {technicalGridMarkers.map((marker, i) => (
                      <div 
                        key={i} 
                        className="absolute flex flex-col gap-1"
                        style={{ left: marker.left, top: marker.top }}
                      >
                        <div className="w-2 h-2 border-t border-l border-white" />
                        <div className="font-mono text-[8px] text-white/50">{marker.label}</div>
                      </div>
                    ))}
                  </div>
                  {/* Grid */}
                  <div className="absolute inset-0 opacity-10 pointer-events-none" 
                       style={{ backgroundImage: 'linear-gradient(to right, #333 1px, transparent 1px), linear-gradient(to bottom, #333 1px, transparent 1px)', backgroundSize: '40px 40px' }} />
                  
                  {/* Decorative labels */}
                  <div className="absolute top-4 left-4 font-mono text-[10px] text-zinc-600 space-y-1">
                    <div>[ process_0x1a ]</div>
                    <div className="text-zinc-800">audit_log: ok</div>
                  </div>
                  <div className="absolute top-4 right-4 font-mono text-[10px] text-red-900/50">
                    &lt; charging &gt;
                  </div>
                  <div className="absolute bottom-4 left-4 font-mono text-[10px] text-zinc-600">
                    signal: stable
                  </div>
                </>
              )}

              {renderStyle === 'blueprint' && (
                <div className="absolute inset-0 opacity-20 pointer-events-none" 
                     style={{ backgroundImage: 'linear-gradient(to right, #fff 1px, transparent 1px), linear-gradient(to bottom, #fff 1px, transparent 1px)', backgroundSize: '20px 20px' }} />
              )}

              {renderStyle === 'glitch' && (
                <div className="absolute inset-0 opacity-5 pointer-events-none bg-gradient-to-t from-green-500/20 to-transparent z-20" />
              )}

              {(renderStyle === 'technical' || renderStyle === 'glitch') && (
                <div className="absolute inset-0 pointer-events-none z-30 opacity-[0.03]" 
                     style={{ backgroundImage: 'linear-gradient(rgba(18, 16, 16, 0) 50%, rgba(0, 0, 0, 0.25) 50%), linear-gradient(90deg, rgba(255, 0, 0, 0.06), rgba(0, 255, 0, 0.02), rgba(0, 0, 255, 0.06))', backgroundSize: '100% 2px, 3px 100%' }} />
              )}

              {renderStyle === 'complex-ascii' && (
                <>
                  {/* Background Noise / Data Stream */}
                  <div className="absolute inset-0 opacity-[0.08] pointer-events-none font-mono text-[8px] overflow-hidden select-none leading-none">
                    {complexAsciiData.map((row, i) => (
                      <div key={i} className="whitespace-nowrap animate-pulse flex gap-4" style={{ animationDelay: row.delay, animationDuration: row.duration }}>
                        {row.streams.map((stream, j) => (
                          <span key={j} className={stream.color}>
                            {stream.content}
                          </span>
                        ))}
                      </div>
                    ))}
                  </div>
                  {/* Scanlines layer 2 */}
                  <div className="absolute inset-0 pointer-events-none z-20 opacity-[0.05] bg-[linear-gradient(rgba(18,16,16,0)_50%,rgba(0,0,0,0.25)_50%),linear-gradient(90deg,rgba(255,0,0,0.06),rgba(0,255,0,0.02),rgba(0,0,255,0.06))] bg-[length:100%_4px,4px_100%]" />
                  {/* Vignette */}
                  <div className="absolute inset-0 bg-[radial-gradient(circle_at_center,transparent_0%,rgba(0,0,0,0.6)_100%)] pointer-events-none" />
                  {/* Random glitch blocks */}
                  <div className="absolute inset-0 pointer-events-none overflow-hidden">
                    {complexAsciiGlitches.map((glitch, i) => (
                      <div 
                        key={i}
                        className="absolute bg-cyan-500/10 border-l border-cyan-500/30 animate-pulse"
                        style={{
                          top: glitch.top,
                          left: glitch.left,
                          width: glitch.width,
                          height: glitch.height,
                          animationDelay: glitch.delay
                        }}
                      />
                    ))}
                  </div>

                  {/* Metadata Overlay */}
                  <div className="absolute top-8 left-8 z-40 font-mono text-[10px] text-cyan-500/60 lowercase tracking-[0.3em] flex flex-col gap-1 pointer-events-none">
                    <div className="flex items-center gap-2">
                      <div className="w-1.5 h-1.5 bg-cyan-500 animate-pulse" />
                      <span>{shapes.find(s => s.id === shapeSequence[0])?.name || 'unknown'}</span>
                    </div>
                    <div className="text-[8px] text-cyan-900 ml-4 max-w-[150px] leading-tight">
                      { (shapes.find(s => s.id === shapeSequence[0]) as any)?.desc || 'system_idle' }
                    </div>
                  </div>
                </>
              )}

              <svg id="morph-svg" viewBox="0 0 100 100" className={cn(
                "w-full h-full overflow-visible relative z-10",
                renderStyle === 'technical' ? "drop-shadow-[0_0_20px_rgba(255,51,51,0.3)]" : 
                renderStyle === 'complex-ascii' ? "drop-shadow-[0_0_25px_rgba(0,255,255,0.2)]" :
                "drop-shadow-[0_0_30px_rgba(0,0,0,0.5)]"
              )}>
                <defs>
                  <linearGradient id="line-grad" x1="0%" y1="0%" x2="100%" y2="100%">
                    <stop offset="0%" stopColor={color} stopOpacity="0.2" />
                    <stop offset="50%" stopColor={color} stopOpacity="1" />
                    <stop offset="100%" stopColor={color} stopOpacity="0.2" />
                  </linearGradient>
                  <filter id="glow" x="-50%" y="-50%" width="200%" height="200%">
                    <feGaussianBlur stdDeviation="0.8" result="blur" />
                    <feMerge>
                      <feMergeNode in="blur" />
                      <feMergeNode in="SourceGraphic" />
                    </feMerge>
                  </filter>
                  <filter id="heavy-glow" x="-50%" y="-50%" width="200%" height="200%">
                    <feGaussianBlur stdDeviation="1.0" result="blur" />
                    <feColorMatrix type="matrix" values="1 0 0 0 0  0 1 0 0 0  0 0 1 0 0  0 0 0 8 -3" result="glow" />
                    <feMerge>
                      <feMergeNode in="glow" />
                      <feMergeNode in="SourceGraphic" />
                    </feMerge>
                  </filter>
                  <filter id="chromatic" x="-10%" y="-10%" width="120%" height="120%">
                    <feOffset in="SourceGraphic" dx="1" dy="0.5" result="red" />
                    <feOffset in="SourceGraphic" dx="-1" dy="-0.5" result="blue" />
                    <feColorMatrix in="red" type="matrix" values="1 0 0 0 0  0 0 0 0 0  0 0 0 0 0  0 0 0 1 0" result="red-only" />
                    <feColorMatrix in="blue" type="matrix" values="0 0 0 0 0  0 0 0 0 0  0 0 1 0 0  0 0 0 1 0" result="blue-only" />
                    <feMerge>
                      <feMergeNode in="red-only" />
                      <feMergeNode in="blue-only" />
                      <feMergeNode in="SourceGraphic" />
                    </feMerge>
                  </filter>
                  <filter id="soft-blur" x="-50%" y="-50%" width="200%" height="200%">
                    <feGaussianBlur stdDeviation="1.2" result="blur" />
                    <feColorMatrix type="matrix" values="1 0 0 0 0  0 1 0 0 0  0 0 1 0 0  0 0 0 0.8 0" result="glow" />
                    <feMerge>
                      <feMergeNode in="glow" />
                      <feMergeNode in="SourceGraphic" />
                    </feMerge>
                  </filter>
                </defs>

                {renderStyle === 'technical' && (
                  <g opacity="0.1">
                    <circle cx="50" cy="50" r="45" fill="none" stroke={color} strokeWidth="0.05" strokeDasharray="1 2" />
                    <circle cx="50" cy="50" r="35" fill="none" stroke={color} strokeWidth="0.05" strokeDasharray="4 4" />
                  </g>
                )}

                <g filter={
                  renderStyle === 'technical' ? "url(#heavy-glow)" : 
                  renderStyle === 'glitch' ? "url(#chromatic)" : 
                  renderStyle === 'complex-ascii' ? "url(#soft-blur)" :
                  "url(#glow)"
                }>
                  {(renderStyle === 'technical' || renderStyle === 'complex-ascii') && Array.from({ length: renderStyle === 'complex-ascii' ? 1 : 2 }).map((_, ghostIdx) => (
                    <g key={`ghost-${ghostIdx}`} opacity={renderStyle === 'complex-ascii' ? 0.05 : 0.08 - ghostIdx * 0.03} style={{ transform: `scale(${1 + (ghostIdx + 1) * 0.02})`, transformOrigin: 'center', willChange: 'transform' }}>
                      <path
                        d={segmentsToPath(currentSegments)}
                        stroke={color}
                        strokeWidth={renderStyle === 'complex-ascii' ? "0.2" : "0.4"}
                        strokeLinecap="round"
                        fill="none"
                      />
                    </g>
                  ))}

                  <path
                    d={segmentsToPath(currentSegments)}
                    stroke={renderStyle === 'complex-ascii' ? "url(#line-grad)" : currentState.color}
                    strokeWidth={renderStyle === 'blueprint' ? "1" : isNetworkInSequence ? "0.4" : "0.7"}
                    strokeLinecap="round"
                    fill="none"
                    opacity={renderStyle === 'technical' ? 0.9 : renderStyle === 'complex-ascii' ? 1 : 0.7}
                  />
                  
                  {isNetworkInSequence && currentProgress > 0.8 && (
                    <g opacity={(currentProgress - 0.8) * 5}>
                      {[
                        {x: 20, y: 80}, {x: 30, y: 30}, {x: 50, y: 50}, 
                        {x: 70, y: 20}, {x: 80, y: 70}, {x: 40, y: 70},
                        {x: 60, y: 40}, {x: 90, y: 40}, {x: 10, y: 50}
                      ].map((n, i) => (
                        <circle key={`node-${i}`} cx={n.x} cy={n.y} r={1.2} fill={currentState.color} />
                      ))}
                    </g>
                  )}
                </g>

                {renderStyle === 'technical' && (
                  <g>
                    <text 
                      x="50" y="60" 
                      textAnchor="middle" 
                      className="font-mono text-[6px] animate-pulse tracking-[0.5em] font-bold"
                      style={{ opacity: 0.4, fill: color }}
                    >
                      [ {technicalText} ]
                    </text>
                    {/* Progress Indicator */}
                    <rect x="35" y="63" width="30" height="0.5" fill={color} opacity="0.1" />
                    <rect x="35" y="63" width={30 * (currentProgress % 1)} height="0.5" fill={color} opacity="0.4" />
                    <text x="50" y="66" textAnchor="middle" className="font-mono text-[3px]" style={{ fill: color, opacity: 0.3 }}>
                      phase: {Math.floor(currentProgress)} | step: {(currentProgress % 1).toFixed(2)}
                    </text>
                  </g>
                )}
              </svg>
            </div>
            <button
              onClick={(e) => {
                e.stopPropagation();
                setNewExampleName('');
                setSaveError('');
                setIsSaveModalOpen(true);
              }}
              className="mt-4 px-6 py-2 rounded-md text-[11px] font-medium border border-zinc-700 bg-zinc-800 text-zinc-300 hover:border-zinc-600 hover:text-white transition-all z-20 relative"
            >
              Add to Library
            </button>
            </>
          ) : (
            <div className="w-full h-full overflow-y-auto custom-scrollbar p-4">
              <div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5 gap-6">
                {shapes.filter(s => !s.isHidden).map((shape) => {
                  let segments = [];
                  const isCode = shape.id === 'anim-practice' || shape.id === 'anim-personalization';
                  
                  if (shape.isAnimated) {
                    const frames = galleryPreviews[shape.id] || [];
                    if (frames.length > 0) {
                      if (isCode) {
                        segments = frames[frames.length - 1];
                      } else {
                        segments = frames[previewFrame % frames.length];
                      }
                    }
                  } else {
                    segments = galleryPreviews[shape.id] || [];
                  }
                  
                  return (
                    <div 
                      key={shape.id} 
                      className={cn(
                        "group relative border rounded-xl p-4 flex flex-col items-center gap-3 transition-all cursor-pointer overflow-hidden",
                        renderStyle === 'technical' ? "bg-red-950/5 border-red-900/20 hover:bg-red-950/10 hover:border-red-500/40" :
                        renderStyle === 'complex-ascii' ? "bg-cyan-950/5 border-cyan-900/20 hover:bg-cyan-950/10 hover:border-cyan-500/40" :
                        renderStyle === 'blueprint' ? "bg-white/5 border-white/10 hover:bg-white/10 hover:border-white/30" :
                        "bg-zinc-900/40 border-zinc-800/50 hover:bg-zinc-800/40 hover:border-zinc-700/50"
                      )}
                      onClick={() => {
                        if (shape.isAnimated && shape.frames) {
                          setShapeSequence(shape.frames);
                        } else {
                          setShapeSequence([shape.id]);
                        }
                        setViewMode('single');
                        setActivePreset(-1);
                      }}
                    >
                      {/* Decorative corners for technical styles */}
                      {(renderStyle === 'technical' || renderStyle === 'complex-ascii') && (
                        <>
                          <div className={cn("absolute top-0 left-0 w-2 h-2 border-t border-l", renderStyle === 'technical' ? "border-red-500/40" : "border-cyan-500/40")} />
                          <div className={cn("absolute top-0 right-0 w-2 h-2 border-t border-r", renderStyle === 'technical' ? "border-red-500/40" : "border-cyan-500/40")} />
                          <div className={cn("absolute bottom-0 left-0 w-2 h-2 border-b border-l", renderStyle === 'technical' ? "border-red-500/40" : "border-cyan-500/40")} />
                          <div className={cn("absolute bottom-0 right-0 w-2 h-2 border-b border-r", renderStyle === 'technical' ? "border-red-500/40" : "border-cyan-500/40")} />
                        </>
                      )}

                      <div className="w-full aspect-square relative flex items-center justify-center">
                        {['custom-ascii', 'custom', 'metaphor'].includes(shape.id) ? (
                          <div className="text-center p-4">
                            <span className="text-xs font-bold tracking-widest text-zinc-500 lowercase leading-relaxed">
                              {shape.name.split(' ').map((word, i) => (
                                <span key={i}>
                                  {word}
                                  <br />
                                </span>
                              ))}
                            </span>
                          </div>
                        ) : (
                          <svg viewBox="0 0 100 100" className={cn(
                            "w-full h-full overflow-visible",
                            renderStyle === 'technical' ? "drop-shadow-[0_0_10px_rgba(255,51,51,0.2)]" : 
                            renderStyle === 'complex-ascii' ? "drop-shadow-[0_0_15px_rgba(0,255,255,0.1)]" :
                            "drop-shadow-[0_0_20px_rgba(0,0,0,0.3)]"
                          )}>
                            <defs>
                              <linearGradient id={`grad-${shape.id}`} x1="0%" y1="0%" x2="100%" y2="100%">
                                <stop offset="0%" stopColor={color} stopOpacity="0.4" />
                                <stop offset="100%" stopColor={color} stopOpacity="1" />
                              </linearGradient>
                            </defs>
                            <g 
                              key={isCode ? Math.floor(previewFrame / 5) : undefined}
                              className={cn(
                              renderStyle === 'technical' ? "drop-shadow-[0_0_3px_rgba(255,51,51,0.5)]" : 
                              renderStyle === 'complex-ascii' ? "drop-shadow-[0_0_5px_rgba(0,255,255,0.4)]" :
                              "drop-shadow-[0_0_8px_rgba(0,0,0,0.4)]"
                            )}>
                              {shape.isAnimated ? (
                                segments.map((seg: any, i: number) => {
                                  let delay = '0s';
                                  if (isCode) {
                                    delay = `${i * 0.02}s`;
                                  }
                                  
                                  const cos = Math.cos(seg.angle || 0);
                                  const sin = Math.sin(seg.angle || 0);
                                  const halfLen = (seg.length || 0) / 2;
                                  const x1 = seg.cx !== undefined ? seg.cx - cos * halfLen : seg.x1;
                                  const y1 = seg.cy !== undefined ? seg.cy - sin * halfLen : seg.y1;
                                  const x2 = seg.cx !== undefined ? seg.cx + cos * halfLen : seg.x2;
                                  const y2 = seg.cy !== undefined ? seg.cy + sin * halfLen : seg.y2;

                                  return (
                                    <line 
                                      key={i} 
                                      x1={x1} y1={y1} x2={x2} y2={y2} 
                                      stroke={renderStyle === 'complex-ascii' ? `url(#grad-${shape.id})` : color}
                                      strokeWidth={renderStyle === 'blueprint' ? "1.2" : "0.8"}
                                      strokeLinecap="round"
                                      className={isCode ? "animate-type-in" : "transition-all duration-500"}
                                      style={isCode ? { animationDelay: delay, opacity: 0 } : { opacity: 0.8 }}
                                    />
                                  );
                                })
                              ) : (
                                <path
                                  d={segmentsToPath(segments)}
                                  stroke={renderStyle === 'complex-ascii' ? `url(#grad-${shape.id})` : color}
                                  strokeWidth={renderStyle === 'blueprint' ? "1.2" : "0.8"}
                                  strokeLinecap="round"
                                  fill="none"
                                  opacity="0.8"
                                />
                              )}
                            </g>
                          </svg>
                        )}
                      </div>
                      <div className="flex flex-col items-center gap-1">
                        <div className="text-[10px] font-mono text-zinc-500 lowercase tracking-wider text-center group-hover:text-zinc-300 transition-colors">
                          {shape.name}
                        </div>
                        {renderStyle === 'complex-ascii' && (
                          <>
                            <div className="text-[8px] font-mono text-cyan-900 lowercase tracking-[0.2em]">
                              id: {shape.id.split('-')[1] || '0'}
                            </div>
                            {(shape as any).desc && (
                              <div className="text-[7px] font-mono text-cyan-500/40 lowercase tracking-tight text-center max-w-[80px] leading-tight mt-1">
                                {(shape as any).desc}
                              </div>
                            )}
                          </>
                        )}
                      </div>
                      <div className="absolute top-2 right-2 opacity-0 group-hover:opacity-100 transition-opacity">
                        <div className="bg-zinc-800 p-1 rounded-md">
                          <Plus className="w-3 h-3 text-zinc-400" />
                        </div>
                      </div>
                    </div>
                  );
                })}
              </div>
            </div>
          )}

          {/* Status Bar */}
          <div className={cn(
            "absolute bottom-8 left-1/2 -translate-x-1/2 flex items-center gap-6 px-6 py-3 backdrop-blur-md rounded-full border text-[10px] font-mono lowercase tracking-[0.2em] transition-all",
            renderStyle === 'technical' ? "bg-red-950/20 border-red-900/30 text-red-500/70" : "bg-zinc-900/50 border-zinc-800/50 text-zinc-500"
          )}>
            <div className="flex items-center gap-2">
              <span className={cn("w-1.5 h-1.5 rounded-full animate-pulse", renderStyle === 'technical' ? "bg-red-500" : "bg-emerald-500")} />
              <span>{renderStyle === 'technical' ? 'core active' : 'live system'}</span>
            </div>
            <div className={cn("w-px h-3", renderStyle === 'technical' ? "bg-red-900/30" : "bg-zinc-800")} />
            <span>{complexity} segments</span>
            <div className={cn("w-px h-3", renderStyle === 'technical' ? "bg-red-900/30" : "bg-zinc-800")} />
            <span>{(currentProgress * 100).toFixed(0)}% morph</span>
          </div>
        </main>
      </div>

      {/* Save Modal */}
      {isSaveModalOpen && (
        <div className="fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-sm p-4">
          <div className="bg-zinc-900 border border-zinc-800 p-6 rounded-xl shadow-2xl w-full max-w-sm" onClick={e => e.stopPropagation()}>
            <h3 className="text-white font-medium mb-4 text-sm">Save to Library</h3>
            <input
              type="text"
              value={newExampleName}
              onChange={(e) => setNewExampleName(e.target.value)}
              placeholder="Enter animation name..."
              className="w-full bg-zinc-950 border border-zinc-800 rounded-md px-3 py-2 text-white text-sm focus:outline-none focus:border-zinc-600 mb-2"
              autoFocus
              onKeyDown={async (e) => {
                if (e.key === 'Enter' && newExampleName.trim()) {
                  await handleSaveExample();
                }
              }}
            />
            {saveError && <div className="text-red-400 text-xs mb-4">{saveError}</div>}
            <div className="flex justify-end gap-2 mt-4">
              <button
                onClick={() => setIsSaveModalOpen(false)}
                className="px-4 py-2 rounded-md text-xs font-medium text-zinc-400 hover:text-white transition-colors"
              >
                Cancel
              </button>
              <button
                onClick={async () => await handleSaveExample()}
                disabled={!newExampleName.trim()}
                className="px-4 py-2 rounded-md text-xs font-medium bg-zinc-100 text-zinc-900 hover:bg-white disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
              >
                Save
              </button>
            </div>
          </div>
        </div>
      )}
    </div>
  );

  async function handleSaveExample() {
    if (!newExampleName.trim()) return;
    
    const data = {
      name: newExampleName.trim(),
      shapes: shapeSequence,
      style: renderStyle,
      color: color,
      bgColor: bgColor
    };
    
    try {
      const res = await fetch('/api/examples', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(data)
      });
      if (res.ok) {
        const newExample = await res.json();
        setExamples(prev => [...prev, newExample]);
        setActivePreset(examples.length);
        setIsSaveModalOpen(false);
      } else {
        setSaveError("Failed to save example");
      }
    } catch (err) {
      console.error(err);
      setSaveError("Error saving example");
    }
  }
}
