/* Anatomy Atelier — static cPanel build
   Three.js interactive organ viewer with clickable hotspots. */
import * as THREE from 'three';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';

/* ================= ORGAN DATA ================= */
const ORGANS = {
  heart: {
    name:'Heart', latin:'Cor', emoji:'🫀', tag:'♥ The Heart', system:'Cardiovascular',
    systemIcon:'🫀', subtitle:'The tireless pump',
    desc:'A muscular organ that pumps blood throughout the body, delivering oxygen and nutrients to every cell.',
    facts:[['Size','About the size of your fist'],['Weight','250–350 g'],['Daily','Beats about 100,000 times'],['Location','Behind the sternum, slightly left'],['Blood supply','Left and right coronary arteries'],['Function','Circulates oxygenated blood']],
    med:'Its electrical rhythm coordinates every heartbeat.',
    dyk:'It beats roughly 2.5 billion times in an average lifetime, and starts before you are born.',
    tissue:'Cardiac muscle tissue', compareWith:'brain',
    conditions:['Coronary artery disease','Arrhythmia','Heart valve disorders','Heart failure','Cardiomyopathy','Myocarditis','Atrial fibrillation','Congenital heart defects'],
    hotspots:[
      {p:[0.35,1.15,0.25],c:'#3b6ea5',t:'Aorta',d:'Main artery carrying oxygenated blood to the body.'},
      {p:[-0.55,0.55,0.55],c:'#e0785f',t:'Left Atrium',d:'Receives oxygenated blood from the lungs.'},
      {p:[0.6,0.45,0.5],c:'#7fb3d5',t:'Right Atrium',d:'Receives venous blood from the body.'},
      {p:[-0.45,-0.55,0.55],c:'#c65f47',t:'Left Ventricle',d:'Powerful chamber that pumps blood to the body.'},
      {p:[0.5,-0.5,0.5],c:'#8e6fb8',t:'Right Ventricle',d:'Pumps deoxygenated blood to the lungs.'},
      {p:[-0.1,0.15,0.85],c:'#c9a86a',t:'Mitral Valve',d:'One-way valve preventing backflow.'}
    ],
    anim:'pulse'
  },
  brain: {
    name:'Brain', latin:'Cerebrum', emoji:'🧠', tag:'🧠 The Brain', system:'Nervous System',
    systemIcon:'🧠', subtitle:'The command center',
    desc:'The body’s control center — processing thought, memory, emotion, movement and every sensation you experience.',
    facts:[['Size','About 15 × 17 cm'],['Weight','≈ 1.3–1.4 kg'],['Cells','≈ 86 billion neurons'],['Location','Inside the cranial cavity'],['Blood supply','Internal carotid & vertebral arteries'],['Function','Controls body and mind']],
    med:'Even brief interruption of its blood supply causes stroke within minutes.',
    dyk:'It generates about 20 watts of electrical power — enough to dim a light bulb.',
    tissue:'Neural tissue', compareWith:'heart',
    conditions:['Stroke','Epilepsy','Alzheimer’s disease','Parkinson’s disease','Meningitis','Migraine','Brain tumors','Concussion'],
    hotspots:[
      {p:[0,0.75,0.35],c:'#e0785f',t:'Frontal Lobe',d:'Reasoning, planning and personality.'},
      {p:[-0.8,0.2,0.3],c:'#7fb3d5',t:'Temporal Lobe',d:'Hearing, language and memory.'},
      {p:[0.8,0.25,0.2],c:'#c9a86a',t:'Parietal Lobe',d:'Processes touch and spatial sense.'},
      {p:[0,0.15,-0.85],c:'#8e6fb8',t:'Occipital Lobe',d:'Visual processing center.'},
      {p:[0,-0.55,-0.55],c:'#3b6ea5',t:'Cerebellum',d:'Balance and coordinated movement.'},
      {p:[0,-0.85,-0.1],c:'#c65f47',t:'Brainstem',d:'Controls breathing and heart rate.'}
    ],
    anim:'pulse'
  },
  lungs: {
    name:'Lungs', latin:'Pulmones', emoji:'🫁', tag:'🫁 The Lungs', system:'Respiratory System',
    systemIcon:'🫁', subtitle:'The breath of life',
    desc:'A pair of spongy organs that exchange oxygen and carbon dioxide with every breath you take.',
    facts:[['Size','Right lung larger than left'],['Weight','≈ 1.3 kg together'],['Breaths','≈ 20,000 per day'],['Location','Thoracic cavity, either side of heart'],['Blood supply','Pulmonary arteries & veins'],['Function','Gas exchange — O₂ in, CO₂ out']],
    med:'Their surface area equals roughly half a tennis court.',
    dyk:'You exhale about 17.5 ml of water every hour just by breathing.',
    tissue:'Alveolar tissue', compareWith:'heart',
    conditions:['Pneumonia','Asthma','COPD','Bronchitis','Tuberculosis','Pulmonary embolism','Lung cancer','Pleurisy'],
    hotspots:[
      {p:[0,1.15,0],c:'#7fb3d5',t:'Trachea',d:'Windpipe carrying air to the lungs.'},
      {p:[-0.75,0.35,0.3],c:'#e0785f',t:'Right Lung',d:'Three lobes — slightly larger.'},
      {p:[0.75,0.35,0.3],c:'#c65f47',t:'Left Lung',d:'Two lobes, with a notch for the heart.'},
      {p:[-0.4,-0.15,0.6],c:'#c9a86a',t:'Bronchi',d:'Branching airways inside the lungs.'},
      {p:[0.75,-0.5,0.35],c:'#8e6fb8',t:'Alveoli',d:'Tiny air sacs where gas exchange happens.'}
    ],
    anim:'breathe'
  },
  liver: {
    name:'Liver', latin:'Hepar', emoji:'🟤', tag:'🟤 The Liver', system:'Digestive System',
    systemIcon:'🟤', subtitle:'The chemical factory',
    desc:'The largest internal organ — filtering blood, storing energy and producing bile for digestion.',
    facts:[['Size','≈ 25 cm wide'],['Weight','1.4–1.6 kg'],['Functions','Over 500 identified'],['Location','Upper right abdomen'],['Blood supply','Hepatic artery & portal vein'],['Function','Detoxification & metabolism']],
    med:'It can regenerate up to 70% of its mass after injury.',
    dyk:'It performs over 500 jobs — and you can survive with just half of one.',
    tissue:'Hepatic lobule tissue', compareWith:'kidneys',
    conditions:['Hepatitis','Cirrhosis','Fatty liver disease','Liver cancer','Gallstones','Jaundice','Hemochromatosis'],
    hotspots:[
      {p:[-0.7,0.35,0.4],c:'#c65f47',t:'Right Lobe',d:'The largest of the four lobes.'},
      {p:[0.65,0.25,0.4],c:'#e0785f',t:'Left Lobe',d:'Smaller lobe near the stomach.'},
      {p:[0.35,-0.45,0.55],c:'#3b6ea5',t:'Gallbladder',d:'Stores bile made by the liver.'},
      {p:[-0.1,0.75,0.2],c:'#c9a86a',t:'Hepatic Vein',d:'Returns filtered blood to the heart.'}
    ],
    anim:'pulse'
  },
  kidneys: {
    name:'Kidneys', latin:'Renes', emoji:'🫘', tag:'🫘 The Kidneys', system:'Urinary System',
    systemIcon:'🫘', subtitle:'The master filters',
    desc:'Two bean-shaped organs that filter your entire blood supply many times a day, balancing fluids and minerals.',
    facts:[['Size','≈ 11 cm each'],['Weight','120–170 g each'],['Filtering','≈ 180 L of blood daily'],['Location','Behind the abdomen, below ribs'],['Blood supply','Renal arteries'],['Function','Filter blood, make urine']],
    med:'They regulate blood pressure through the hormone renin.',
    dyk:'All your blood passes through them about 40 times every single day.',
    tissue:'Nephron tissue', compareWith:'liver',
    conditions:['Kidney stones','Chronic kidney disease','Urinary tract infection','Polycystic kidney disease','Glomerulonephritis','Kidney failure'],
    hotspots:[
      {p:[-0.55,0.3,0.3],c:'#c65f47',t:'Right Kidney',d:'Sits slightly lower than the left.'},
      {p:[0.55,0.45,0.3],c:'#e0785f',t:'Left Kidney',d:'Filters blood and balances fluids.'},
      {p:[0.1,-0.15,0.55],c:'#7fb3d5',t:'Ureter',d:'Carries urine to the bladder.'},
      {p:[-0.3,0.05,0.6],c:'#c9a86a',t:'Nephrons',d:'A million tiny filters per kidney.'}
    ],
    anim:'pulse'
  },
  eye: {
    name:'Eye', latin:'Oculus', emoji:'👁️', tag:'👁 The Eye', system:'Sensory System',
    systemIcon:'👁️', subtitle:'The window of the mind',
    desc:'A precision optical instrument that turns light into the rich moving picture you call vision.',
    facts:[['Size','≈ 24 mm across'],['Weight','≈ 7.5 g'],['Resolution','≈ 576 megapixels equivalent'],['Location','Orbital cavity of the skull'],['Blood supply','Ophthalmic artery'],['Function','Converts light into vision']],
    med:'The retina is the only place blood vessels can be seen without surgery.',
    dyk:'Your eyes can distinguish about 10 million different colors.',
    tissue:'Retinal tissue', compareWith:'brain',
    conditions:['Cataract','Glaucoma','Macular degeneration','Conjunctivitis','Retinal detachment','Dry eye syndrome','Astigmatism'],
    hotspots:[
      {p:[0,0.1,0.95],c:'#3b6ea5',t:'Cornea',d:'Clear front window focusing light.'},
      {p:[0.35,0.25,0.8],c:'#c9a86a',t:'Iris',d:'Colored ring controlling pupil size.'},
      {p:[-0.5,-0.1,0.75],c:'#e0785f',t:'Lens',d:'Fine-focuses images onto the retina.'},
      {p:[0,0,-0.9],c:'#c65f47',t:'Retina',d:'Light-sensitive layer at the back.'},
      {p:[0.15,-0.35,-0.85],c:'#8e6fb8',t:'Optic Nerve',d:'Carries visual signals to the brain.'}
    ],
    anim:'blink'
  },
  intestine: {
    name:'Intestine', latin:'Intestinum', emoji:'🌀', tag:'🌀 The Intestine', system:'Digestive System',
    systemIcon:'🌀', subtitle:'The long winding road',
    desc:'A remarkable tube over 7 meters long where digestion finishes and nutrients enter your blood.',
    facts:[['Size','≈ 7.5 m uncoiled'],['Weight','≈ 2 kg'],['Absorption','≈ 90% of nutrients'],['Location','Lower abdominal cavity'],['Blood supply','Mesenteric arteries'],['Function','Digestion & absorption']],
    med:'It hosts trillions of microbes that influence immunity and mood.',
    dyk:'Its inner surface, unfolded, would cover a badminton court.',
    tissue:'Villi & mucosal tissue', compareWith:'liver',
    conditions:['IBS','Crohn’s disease','Ulcerative colitis','Celiac disease','Appendicitis','Diverticulitis','Colorectal cancer'],
    hotspots:[
      {p:[0,0.7,0.4],c:'#e0785f',t:'Duodenum',d:'First section after the stomach.'},
      {p:[-0.5,0,0.5],c:'#c9a86a',t:'Small Intestine',d:'Where most nutrients are absorbed.'},
      {p:[0.55,-0.35,0.35],c:'#c65f47',t:'Large Intestine',d:'Absorbs water, forms stool.'},
      {p:[0,-0.75,0.3],c:'#7fb3d5',t:'Appendix',d:'Small pouch with immune roles.'}
    ],
    anim:'pulse'
  },
  pancreas: {
    name:'Pancreas', latin:'Pancreas', emoji:'🥜', tag:'🥜 The Pancreas', system:'Endocrine System',
    systemIcon:'🥜', subtitle:'The sugar regulator',
    desc:'A dual-purpose gland — digesting food with enzymes while balancing blood sugar with insulin.',
    facts:[['Size','≈ 15–20 cm long'],['Weight','70–100 g'],['Enzymes','≈ 1.5 L juice daily'],['Location','Behind the stomach'],['Blood supply','Pancreaticoduodenal arteries'],['Function','Insulin & digestive enzymes']],
    med:'Failure of its insulin cells causes diabetes mellitus.',
    dyk:'It tastes every meal chemically before your bloodstream does.',
    tissue:'Islet of Langerhans tissue', compareWith:'liver',
    conditions:['Diabetes type 1 & 2','Pancreatitis','Pancreatic cancer','Cystic fibrosis','Insulinoma'],
    hotspots:[
      {p:[-0.7,0.1,0.4],c:'#e0785f',t:'Head',d:'Nestled in the curve of the duodenum.'},
      {p:[0,0.15,0.5],c:'#c9a86a',t:'Body',d:'Main enzyme-producing region.'},
      {p:[0.7,0.2,0.35],c:'#c65f47',t:'Tail',d:'Reaches toward the spleen.'},
      {p:[0.1,-0.25,0.55],c:'#3b6ea5',t:'Islets',d:'Cell clusters that make insulin.'}
    ],
    anim:'pulse'
  },
  skin: {
    name:'Skin', latin:'Cutis', emoji:'🧴', tag:'🧴 The Skin', system:'Integumentary System',
    systemIcon:'🧴', subtitle:'The living envelope',
    desc:'Your largest organ — a waterproof, self-repairing shield packed with sensors and climate control.',
    facts:[['Size','≈ 2 m² of surface'],['Weight','≈ 3.5–4.5 kg'],['Cells shed','≈ 30,000 per minute'],['Location','Everywhere — your outer layer'],['Layers','Epidermis, dermis, hypodermis'],['Function','Protection, sensation, temperature']],
    med:'It renews its entire outer layer roughly every 28 days.',
    dyk:'You shed about 4 kg of skin cells every year — most house dust is you.',
    tissue:'Epidermal tissue', compareWith:'eye',
    conditions:['Eczema','Psoriasis','Acne','Melanoma','Dermatitis','Vitiligo','Rosacea','Burns & infections'],
    hotspots:[
      {p:[0,0.75,0.5],c:'#e0785f',t:'Epidermis',d:'The self-renewing outer barrier.'},
      {p:[0,0.1,0.6],c:'#c9a86a',t:'Dermis',d:'Nerves, vessels and hair roots.'},
      {p:[0,-0.55,0.5],c:'#c65f47',t:'Hypodermis',d:'Insulating fat layer beneath.'},
      {p:[0.45,0.2,0.55],c:'#8e6fb8',t:'Sweat Gland',d:'Cools the body by evaporation.'}
    ],
    anim:'pulse'
  }
};
const ORDER = ['heart','brain','lungs','liver','kidneys','eye','intestine','pancreas','skin'];

/* ================= STATE ================= */
let current = 'heart';
let autoRotate = true, isolated = false, sectionOn = false, layersOn = false, animating = false;
const compareKey = k => ORGANS[k].compareWith;

/* ================= THREE SETUP ================= */
const stage = document.getElementById('stage');
const canvas = document.getElementById('gl');
const renderer = new THREE.WebGLRenderer({canvas, antialias:true, alpha:true});
renderer.setPixelRatio(Math.min(devicePixelRatio,2));
renderer.localClippingEnabled = true;
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(38, 1, .1, 100);
camera.position.set(0, .4, 6.2);

const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true; controls.dampingFactor = .06;
controls.minDistance = 3; controls.maxDistance = 10;
controls.autoRotate = true; controls.autoRotateSpeed = 1.4;

scene.add(new THREE.AmbientLight(0xfff4e0, 1.15));
const key = new THREE.DirectionalLight(0xffffff, 1.6); key.position.set(3,4,5); scene.add(key);
const fill = new THREE.DirectionalLight(0xffd9c4, .7); fill.position.set(-4,1,2); scene.add(fill);
const rim  = new THREE.DirectionalLight(0xc4d8ff, .8); rim.position.set(0,3,-5); scene.add(rim);

// ground shadow disc
const disc = new THREE.Mesh(
  new THREE.CircleGeometry(2.6, 64),
  new THREE.MeshBasicMaterial({color:0xd8c9b2, transparent:true, opacity:.35})
);
disc.rotation.x = -Math.PI/2; disc.position.y = -2.05; scene.add(disc);

const clipPlane = new THREE.Plane(new THREE.Vector3(0,0,-1), 10);

/* ---------- procedural organ models ---------- */
const M = (color, rough=.45) => new THREE.MeshStandardMaterial({color, roughness:rough, metalness:.08});
let organGroup = new THREE.Group(); scene.add(organGroup);
let hotspotsData = [];

function blob(r, color, sx=1, sy=1, sz=1, detail=5, bump=0){
  const g = new THREE.IcosahedronGeometry(r, detail);
  if(bump){
    const pos = g.attributes.position;
    const v = new THREE.Vector3();
    for(let i=0;i<pos.count;i++){
      v.fromBufferAttribute(pos,i);
      const n = Math.sin(v.x*6.3)*Math.cos(v.y*5.1)*Math.sin(v.z*4.7);
      v.multiplyScalar(1 + n*bump);
      pos.setXYZ(i,v.x,v.y,v.z);
    }
    g.computeVertexNormals();
  }
  const m = new THREE.Mesh(g, M(color));
  m.scale.set(sx,sy,sz);
  return m;
}

/* --- realistic anatomical heart with coronary vessels --- */
function heartBody(){
  // Ventricular mass via lathe profile: wide base at top, tapered apex below
  const pts = [];
  const profile = [ // [radius, y]
    [0.00,-1.55],[0.10,-1.52],[0.26,-1.42],[0.40,-1.28],[0.52,-1.10],
    [0.62,-0.90],[0.70,-0.68],[0.76,-0.44],[0.80,-0.18],[0.82, 0.06],
    [0.82, 0.28],[0.80, 0.46],[0.76, 0.60],[0.70, 0.70],[0.60, 0.76],[0.00,0.80]
  ];
  profile.forEach(p=>pts.push(new THREE.Vector2(p[0],p[1])));
  const geo = new THREE.LatheGeometry(pts, 48);
  // organic asymmetry: squash z, bulge one side, wobble
  const pos = geo.attributes.position, v = new THREE.Vector3();
  for(let i=0;i<pos.count;i++){
    v.fromBufferAttribute(pos,i);
    v.z *= 0.92;
    v.x *= 1 + 0.10*Math.sin(v.y*1.6);         // slight S-curve of the wall
    const n = Math.sin(v.x*9.1)*Math.cos(v.y*7.3)*Math.sin(v.z*8.2);
    v.multiplyScalar(1 + n*0.018);             // subtle muscle texture
    pos.setXYZ(i, v.x, v.y, v.z);
  }
  geo.computeVertexNormals();
  const mesh = new THREE.Mesh(geo, M(0xc65a50, .5));
  mesh.rotation.z = 0.28;            // anatomical tilt: apex points down-left
  mesh.position.y = -0.15;
  return mesh;
}
function vessel(points, radius, color){
  const curve = new THREE.CatmullRomCurve3(points.map(p=>new THREE.Vector3(...p)));
  return new THREE.Mesh(new THREE.TubeGeometry(curve, 40, radius, 10, false), M(color, .35));
}
function buildHeart(g){
  g.add(heartBody());

  // --- Atria (auricles) ---
  const la = blob(.34, 0xb85047, 1.15, .8, .9, 4, .08); la.position.set(-.62,.55,.18); la.rotation.z=.4; g.add(la);
  const ra = blob(.34, 0xb85047, 1.15, .8, .9, 4, .08); ra.position.set(.58,.48,.12); ra.rotation.z=-.4; g.add(ra);

  // --- Aorta: ascending + arch + descending, with 3 branches ---
  const aortaCol = 0xb23a32;
  g.add(vessel([[.18,.55,-.05],[.22,1.0,-.02],[.1,1.28,-.05],[-.18,1.34,-.12],[-.42,1.2,-.18],[-.5,.95,-.22]], .13, aortaCol)); // arch
  g.add(vessel([[.1,1.28,-.05],[.02,1.62,-.04]], .05, aortaCol));      // brachiocephalic
  g.add(vessel([[-.08,1.32,-.08],[-.18,1.6,-.08]], .045, aortaCol));   // carotid
  g.add(vessel([[-.26,1.3,-.14],[-.42,1.55,-.16]], .045, aortaCol));   // subclavian

  // --- Pulmonary trunk splitting left/right (blue-ish) ---
  const pulCol = 0x6b5b9e;
  g.add(vessel([[-.05,.5,.35],[-.18,.85,.3],[-.3,1.05,.15]], .11, pulCol));
  g.add(vessel([[-.3,1.02,.15],[-.62,.95,.05],[-.85,.8,0]], .075, pulCol));
  g.add(vessel([[-.28,1.04,.12],[.05,1.0,-.05],[.4,.85,-.12]], .075, pulCol));

  // --- Vena cava (right side, blue) ---
  g.add(vessel([[.62,.62,-.05],[.68,1.05,-.08]], .10, 0x4a5f8e));       // superior
  g.add(vessel([[.55,.3,-.15],[.6,-.25,-.28]], .10, 0x4a5f8e));         // inferior

  // --- Pulmonary veins into left atrium ---
  g.add(vessel([[-.75,.5,-.1],[-1.0,.55,-.2]], .05, 0x8e3a50));
  g.add(vessel([[-.7,.35,-.15],[-.95,.28,-.28]], .05, 0x8e3a50));

  // --- Coronary arteries (dark red, hugging the surface) ---
  const art = 0x8e2620;
  // Left anterior descending artery — down the front midline
  g.add(vessel([[-.1,.5,.72],[-.18,.2,.78],[-.25,-.15,.72],[-.3,-.55,.55],[-.33,-.95,.3]], .032, art));
  g.add(vessel([[-.18,.2,.78],[-.45,.1,.6],[-.6,-.05,.35]], .024, art));           // diagonal branch
  g.add(vessel([[-.25,-.15,.72],[-.55,-.3,.5],[-.7,-.4,.2]], .02, art));           // diagonal branch 2
  // Circumflex artery — around the left side
  g.add(vessel([[-.12,.55,.66],[-.5,.55,.5],[-.75,.45,.1],[-.85,.35,-.3]], .028, art));
  // Right coronary artery — around the right side
  g.add(vessel([[.3,.45,.6],[.62,.35,.4],[.8,.2,0],[.82,.05,-.4]], .028, art));
  g.add(vessel([[.62,.35,.4],[.55,-.05,.55],[.4,-.4,.5],[.3,-.75,.35]], .022, art)); // PDA branch

  // --- Coronary veins (purple, alongside arteries) ---
  const vei = 0x5a3a6e;
  g.add(vessel([[-.04,.5,.74],[-.1,.18,.8],[-.16,-.18,.74],[-.2,-.6,.55],[-.22,-.98,.28]], .026, vei)); // great cardiac vein
  g.add(vessel([[-.05,.62,.6],[-.45,.6,.42],[-.72,.5,.05],[-.8,.42,-.35]], .03, vei));                  // coronary sinus
  g.add(vessel([[.36,.5,.55],[.66,.4,.32],[.82,.22,-.05]], .024, vei));                                 // small cardiac vein

  // --- Epicardial fat hint along the front groove ---
  const fat = vessel([[-.08,.52,.7],[-.14,.15,.76],[-.2,-.2,.7]], .05, 0xd9b47a);
  fat.material.transparent = true; fat.material.opacity = .5; g.add(fat);
}

function buildBrain(g){
  const left = blob(.95, 0xe59a8a, 1, .82, 1.15, 5, .12); left.position.x = -.55; g.add(left);
  const right = blob(.95, 0xe59a8a, 1, .82, 1.15, 5, .12); right.position.x = .55; g.add(right);
  const cereb = blob(.5, 0xd67f6f, 1.1, .7, .9, 4, .1); cereb.position.set(0,-.6,-.55); g.add(cereb);
  const stem = new THREE.Mesh(new THREE.CylinderGeometry(.16,.2,.7,16), M(0xd67f6f));
  stem.position.set(0,-.95,-.1); g.add(stem);
}

function buildLungs(g){
  const trachea = new THREE.Mesh(new THREE.CylinderGeometry(.13,.13,1.3,16), M(0xd9b3a6));
  trachea.position.y = 1.1; g.add(trachea);
  const y = new THREE.Mesh(new THREE.TorusGeometry(.35,.09,12,24,Math.PI), M(0xd9b3a6));
  y.position.y = .5; y.rotation.z = Math.PI; g.add(y);
  const lL = blob(.85, 0xd98a80, .8, 1.25, .7, 5, .05); lL.position.set(-.85,-.1,0); g.add(lL);
  const lR = blob(.85, 0xd98a80, .8, 1.25, .7, 5, .05); lR.position.set(.85,-.1,0); g.add(lR);
  const bL = new THREE.Mesh(new THREE.CylinderGeometry(.07,.07,.8,10), M(0xc9a08e));
  bL.position.set(-.6,.3,.1); bL.rotation.z = .5; g.add(bL);
  const bR = bL.clone(); bR.position.x = .6; bR.rotation.z = -.5; g.add(bR);
}

function buildLiver(g){
  const main = blob(1, 0x8e4438, 1.35, .8, .75, 5, .05); g.add(main);
  const lobe = blob(.55, 0x8e4438, 1.2, .7, .8, 4, .05); lobe.position.set(.85,-.1,.05); g.add(lobe);
  const gb = blob(.28, 0x7a9a5b, .8, 1.2, .8, 4, .02); gb.position.set(.35,-.55,.55); g.add(gb);
  const vein = new THREE.Mesh(new THREE.CylinderGeometry(.08,.08,1,12), M(0x5b7fa6));
  vein.position.set(-.2,.75,-.1); vein.rotation.z = .9; g.add(vein);
}

function buildKidneys(g){
  const mk = (x)=>{
    const k = blob(.62, 0x9a4438, .75, 1.15, .55, 5, .04); k.position.set(x,.2,0);
    const notch = blob(.3, 0xd9c9b8, .8, 1.2, .6); notch.position.set(x*.4,.2,.3);
    g.add(k); g.add(notch);
    const ur = new THREE.Mesh(new THREE.CylinderGeometry(.05,.05,1.2,10), M(0xd9b3a6));
    ur.position.set(x*.4,-.7,.25); ur.rotation.z = x>0? .15:-.15; g.add(ur);
  };
  mk(-.55); mk(.55);
}

function buildEye(g){
  const ball = blob(1, 0xf3e4d8, 1, 1, 1, 5, .01); g.add(ball);
  const iris = new THREE.Mesh(new THREE.CircleGeometry(.42, 48), M(0x4a7fae,.3));
  iris.position.set(0,0,.985); g.add(iris);
  const pupil = new THREE.Mesh(new THREE.CircleGeometry(.19, 32), new THREE.MeshBasicMaterial({color:0x1a1210}));
  pupil.position.set(0,0,.99); g.add(pupil);
  const cornea = blob(.5, 0xffffff, 1,1,.45); cornea.material.transparent = true; cornea.material.opacity = .18;
  cornea.position.set(0,0,.75); g.add(cornea);
  const nerve = new THREE.Mesh(new THREE.CylinderGeometry(.12,.16,1,14), M(0xd98a80));
  nerve.position.set(.15,-.2,-1); nerve.rotation.x = Math.PI/2; nerve.rotation.z = .2; g.add(nerve);
}

function buildIntestine(g){
  const pts = [];
  for(let i=0;i<=120;i++){
    const t = i/120 * Math.PI*5;
    const r = .9 - i/120*.45;
    pts.push(new THREE.Vector3(Math.cos(t)*r, .8 - i/120*1.8, Math.sin(t)*r*.6));
  }
  const tube = new THREE.Mesh(new THREE.TubeGeometry(new THREE.CatmullRomCurve3(pts), 200, .16, 12), M(0xe09a8a));
  g.add(tube);
  const colon = new THREE.Mesh(new THREE.TorusGeometry(1,.2,14,40), M(0xc97a68));
  colon.scale.set(1,.85,.6); colon.rotation.x = .3; g.add(colon);
  const apx = new THREE.Mesh(new THREE.CylinderGeometry(.05,.03,.35,10), M(0xc97a68));
  apx.position.set(.35,-.85,.35); apx.rotation.z = .4; g.add(apx);
}

function buildPancreas(g){
  const body = blob(.45, 0xe0b88a, 2.2, .8, .7, 5, .06); g.add(body);
  const head = blob(.5, 0xe0b88a, 1, 1.1, .9, 4, .05); head.position.set(-1.15,-.1,0); g.add(head);
  const tail = blob(.28, 0xe0b88a, 1.2, .8, .7, 4, .04); tail.position.set(1.15,.15,0); g.add(tail);
  const duct = new THREE.Mesh(new THREE.CylinderGeometry(.04,.04,2,10), M(0xc9a86a));
  duct.rotation.z = Math.PI/2; duct.position.z = .3; g.add(duct);
}

function buildSkin(g){
  const epi = new THREE.Mesh(new THREE.BoxGeometry(2.4,.28,1.6,12,1,8), M(0xe8b39a));
  epi.position.y = .75; g.add(epi);
  const dermis = blob(1, 0xd98a80, 1.2, .28, .8, 4, .08); dermis.position.y = .1; g.add(dermis);
  const hypo = blob(1, 0xf2d9a0, 1.2, .3, .8, 4, .04); hypo.position.y = -.55; g.add(hypo);
  for(let i=0;i<4;i++){
    const hair = new THREE.Mesh(new THREE.CylinderGeometry(.02,.03,.6,8), M(0x5a4636));
    hair.position.set(-.8+i*.55, 1, -.4+((i*37)%60)/100); hair.rotation.z = .2-Math.sin(i)*.4; g.add(hair);
    const gl = blob(.09, 0x8e6fb8, 1, 1.3, 1); gl.position.set(-.7+i*.5, -.05, .45); g.add(gl);
  }
}

const BUILDERS = {heart:buildHeart, brain:buildBrain, lungs:buildLungs, liver:buildLiver,
  kidneys:buildKidneys, eye:buildEye, intestine:buildIntestine, pancreas:buildPancreas, skin:buildSkin};

function loadOrgan(key){
  organGroup.clear();
  BUILDERS[key](organGroup);
  organGroup.traverse(o=>{
    if(o.isMesh){ o.material.clippingPlanes = sectionOn ? [clipPlane] : null; o.material.transparent = layersOn; }
  });
  hotspotsData = ORGANS[key].hotspots;
  renderHotspotDots();
}

/* ================= HOTSPOTS ================= */
const hotspotLayer = document.getElementById('hotspots');
const popup = document.getElementById('hotspotPopup');
function renderHotspotDots(){
  hotspotLayer.innerHTML = '';
  hotspotsData.forEach((h,i)=>{
    const d = document.createElement('button');
    d.className = 'hotspot'; d.style.background = h.c; d.dataset.i = i;
    d.addEventListener('click', e=>{
      e.stopPropagation();
      document.getElementById('hpTitle').textContent = h.t;
      document.getElementById('hpText').textContent = h.d;
      const r = d.getBoundingClientRect();
      popup.style.left = Math.min(innerWidth-280, r.right+14)+'px';
      popup.style.top = Math.max(10, r.top-10)+'px';
      popup.classList.remove('hidden');
    });
    hotspotLayer.appendChild(d);
  });
}
document.addEventListener('click', ()=>popup.classList.add('hidden'));

const v3 = new THREE.Vector3();
function projectHotspots(){
  const w = stage.clientWidth, h = stage.clientHeight;
  const dots = hotspotLayer.children;
  hotspotsData.forEach((hs,i)=>{
    v3.set(...hs.p).applyMatrix4(organGroup.matrixWorld).project(camera);
    const d = dots[i]; if(!d) return;
    if(v3.z > 1){ d.style.display='none'; return; }
    d.style.display='block';
    d.style.left = ((v3.x+1)/2*w)+'px';
    d.style.top  = ((-v3.y+1)/2*h)+'px';
    // fade dots facing away
    d.style.opacity = v3.z < .995 ? 1 : .25;
  });
}

/* ================= UI: organ list & info ================= */
const organList = document.getElementById('organList');
ORDER.forEach(k=>{
  const o = ORGANS[k];
  const li = document.createElement('li');
  li.dataset.key = k;
  li.innerHTML = `<div class="organ-emoji">${o.emoji}</div>
    <div class="organ-meta"><b>${o.name}</b><span>${o.system}</span></div>
    <span class="heart-mark">♥</span>`;
  li.addEventListener('click', ()=>selectOrgan(k));
  organList.appendChild(li);
});

function selectOrgan(k){
  current = k;
  const o = ORGANS[k];
  document.querySelectorAll('.organ-list li').forEach(li=>li.classList.toggle('active', li.dataset.key===k));
  document.getElementById('infoTag').textContent = o.tag;
  document.getElementById('organName').textContent = o.name;
  document.getElementById('organThumb').textContent = o.emoji;
  document.getElementById('organSubtitle').textContent = o.subtitle;
  document.getElementById('organDesc').textContent = o.desc;
  document.getElementById('medImportance').textContent = o.med;
  document.getElementById('didYouKnow').textContent = o.dyk;
  document.getElementById('latinName').textContent = o.latin;
  document.getElementById('tissueName').textContent = o.tissue;
  document.getElementById('functionName').textContent = o.facts[o.facts.length-1][1];
  document.getElementById('systemName').textContent = o.system;
  document.getElementById('systemIcon').textContent = o.systemIcon;
  const cw = ORGANS[o.compareWith];
  document.getElementById('compareName').textContent = `${o.name} vs. ${cw.name.toLowerCase()}`;
  document.getElementById('compareIconA').textContent = o.emoji;
  document.getElementById('compareIconB').textContent = cw.emoji;
  document.getElementById('factList').innerHTML = o.facts.map(f=>`<dt>${f[0]}</dt><dd>${f[1]}</dd>`).join('');
  document.getElementById('conditions').innerHTML = o.conditions.slice(0,5).map(c=>`<li>${c}</li>`).join('')
    + (o.conditions.length>5 ? `<li style="color:var(--accent-deep)">See all (${o.conditions.length})</li>` : '');
  popup.classList.add('hidden');
  loadOrgan(k);
}

/* ================= toolbar & buttons ================= */
const toast = document.getElementById('toast');
let toastT;
function showToast(msg){
  toast.textContent = msg; toast.classList.remove('hidden');
  clearTimeout(toastT); toastT = setTimeout(()=>toast.classList.add('hidden'), 2200);
}

document.getElementById('toolbar').addEventListener('click', e=>{
  const b = e.target.closest('button'); if(!b) return;
  const t = b.dataset.tool;
  if(t==='rotate'){ autoRotate=!autoRotate; syncRotate(); b.classList.toggle('on',autoRotate); }
  else if(t==='zoom'){ document.getElementById('zoomBtns').classList.toggle('show'); b.classList.toggle('on'); }
  else if(t==='isolate'){
    isolated=!isolated; b.classList.toggle('on',isolated);
    organGroup.traverse(o=>{ if(o.isMesh){ o.material.opacity = 1; o.material.transparent = layersOn||isolated; }});
    organGroup.children.forEach((c,i)=>{ if(isolated && i>0) c.traverse?.(o=>{ if(o.isMesh) o.material.opacity=.15; }); });
    showToast(isolated?'Isolated primary structure':'Showing full specimen');
  }
  else if(t==='section'){
    sectionOn=!sectionOn; b.classList.toggle('on',sectionOn);
    clipPlane.constant = sectionOn ? 0 : 10;
    organGroup.traverse(o=>{ if(o.isMesh) o.material.clippingPlanes = sectionOn?[clipPlane]:null; });
    showToast(sectionOn?'Cross-section view on':'Cross-section off');
  }
  else if(t==='layers'){
    layersOn=!layersOn; b.classList.toggle('on',layersOn);
    organGroup.children.forEach((c,i)=>c.traverse?.(o=>{ if(o.isMesh){ o.material.transparent=true; o.material.opacity = layersOn && i>1 ? .25 : 1; }}));
    showToast(layersOn?'Layer view: outer structures faded':'Layer view off');
  }
  else if(t==='compare'){
    const other = compareKey(current);
    showToast(`Comparing ${ORGANS[current].name} vs. ${ORGANS[other].name} — loading ${ORGANS[other].name}…`);
    setTimeout(()=>selectOrgan(other), 900);
  }
  else if(t==='reset'){
    camera.position.set(0,.4,6.2); controls.target.set(0,0,0); controls.reset();
    showToast('View reset');
  }
});

const syncRotate = ()=>{
  controls.autoRotate = autoRotate;
  document.getElementById('autoRotate').checked = autoRotate;
};
document.getElementById('autoRotate').addEventListener('change', e=>{ autoRotate=e.target.checked; syncRotate(); });
document.getElementById('zoomIn').onclick = ()=>camera.position.lerp(controls.target,.2);
document.getElementById('zoomOut').onclick = ()=>camera.position.lerp(new THREE.Vector3().copy(camera.position).multiplyScalar(1.4),.3);

document.getElementById('viewAll').onclick = ()=>showToast(`${ORDER.length} organs in the library`);
document.getElementById('lessonBtn').onclick = ()=>showToast(`Lesson: “${ORGANS[current].name} — ${ORGANS[current].subtitle}” (coming soon)`);
document.getElementById('quizBtn').onclick = ()=>showToast('Quiz mode coming soon — explore hotspots to study!');
document.getElementById('compareBtn').onclick = ()=>document.querySelector('[data-tool="compare"]').click();
document.getElementById('openCompare').onclick = ()=>document.querySelector('[data-tool="compare"]').click();
document.querySelectorAll('.nav-btn').forEach(b=>b.addEventListener('click',()=>{
  document.querySelectorAll('.nav-btn').forEach(x=>x.classList.remove('active'));
  b.classList.add('active');
  if(b.dataset.view!=='explore') showToast(`“${b.textContent.trim()}” section coming soon`);
}));
document.getElementById('search').addEventListener('input', e=>{
  const q = e.target.value.toLowerCase();
  document.querySelectorAll('.organ-list li').forEach(li=>{
    li.style.display = ORGANS[li.dataset.key].name.toLowerCase().includes(q) ? '' : 'none';
  });
});

/* animation (pulse / breathe / blink) */
function toggleAnim(){
  animating=!animating;
  showToast(animating ? `Playing: ${ORGANS[current].facts.at(-1)[1]}` : 'Animation stopped');
}
document.getElementById('animateBtn').onclick = toggleAnim;
document.getElementById('playAnim').onclick = toggleAnim;
document.getElementById('playAnim2').onclick = toggleAnim;

/* ================= resize & loop ================= */
function resize(){
  const w = stage.clientWidth, h = stage.clientHeight;
  renderer.setSize(w,h,false);
  camera.aspect = w/h; camera.updateProjectionMatrix();
}
addEventListener('resize', resize);

const clock = new THREE.Clock();
function loop(){
  requestAnimationFrame(loop);
  const t = clock.getElapsedTime();
  if(animating){
    const mode = ORGANS[current].anim;
    if(mode==='pulse'){ const s = 1 + Math.sin(t*5)*.045; organGroup.scale.set(s,s,s); }
    else if(mode==='breathe'){ const s = 1 + Math.sin(t*1.6)*.09; organGroup.scale.set(s,1+Math.sin(t*1.6)*.05,s); }
    else if(mode==='blink'){ const s = Math.abs(Math.sin(t*1.2))>.08?1:.92; organGroup.scale.set(1,s,1); }
  } else organGroup.scale.lerp(new THREE.Vector3(1,1,1),.12);
  organGroup.position.y = Math.sin(t*.9)*.06; // gentle float
  controls.update();
  renderer.render(scene,camera);
  projectHotspots();
}

/* ================= init ================= */
selectOrgan('heart');
syncRotate();
resize();
loop();
