(() => {
  const stages = ["observe","understand","register","guide","verify","trusted-record"];
  const copy = {
    observe:{label:"Observe",metric:"Stable vehicle features detected"},
    understand:{label:"Understand",metric:"Vehicle structure connected",detail:"Detected landmarks are related into one vehicle-relative state."},
    register:{label:"Register",metric:"Vehicle geometry registering",detail:"The observed structure is being resolved into one stable vehicle-relative model."},
    guide:{label:"Guide",metric:"Next observation calculated",fieldLabel:"Current capture"},
    verify:{label:"Verify",metric:"Required coverage confirmed",detail:"Every required capture section has been observed."},
    "trusted-record":{label:"Trusted record",metric:"VIN-linked visual record ready",detail:"The completed perception state becomes a VIN-linked record."}
  };
  const landmarkGroups = [
    ["left-front-bumper-corner","front-grille-left","front-grille-right"],
    ["left-headlight","hood-edge-left","hood-edge-right"],
    ["roof-edge-front-left","a-pillar-bottom","mid-left-a-pillar"],
    ["left-mirror","roof-edge-front-right","b-pillar-top"],
    ["b-pillar-mid","b-pillar-bottom","front-left-wheel"],
    ["rocker-front-left","c-pillar-top","c-pillar-mid"],
    ["rear-left-wheel","rocker-rear-left","roof-edge-rear-left"],
    ["d-pillar-mid","left-rear-tail-light","left-rear-bumper-corner"]
  ];
  const landmarkOrder = landmarkGroups.flat();
  const registerProgressSteps = [0,.18,.36,.58,.8,1];
  const labels = {"a-pillar-bottom":"A-PILLAR","b-pillar-bottom":"B-PILLAR","b-pillar-mid":"B-PILLAR","b-pillar-top":"B-PILLAR","c-pillar-mid":"C-PILLAR","c-pillar-top":"C-PILLAR","d-pillar-mid":"D-PILLAR","front-grille-left":"FRONT GRILLE","front-grille-right":"FRONT GRILLE","front-left-wheel":"FRONT WHEEL","hood-edge-left":"HOOD EDGE","hood-edge-right":"HOOD EDGE","left-front-bumper-corner":"BUMPER CORNER","left-headlight":"FRONT LIGHT","left-mirror":"MIRROR","left-rear-bumper-corner":"REAR BUMPER","left-rear-tail-light":"TAILLIGHT","mid-left-a-pillar":"A-PILLAR","rear-left-wheel":"REAR WHEEL","rocker-front-left":"ROCKER","rocker-rear-left":"ROCKER","roof-edge-front-left":"ROOF EDGE","roof-edge-front-right":"ROOF EDGE","roof-edge-rear-left":"ROOF EDGE"};
  const captures = [
    {key:"front",scan:"front",target:"target_front"},{key:"front-left",scan:"left_front",target:"target_front_left"},{key:"front-left-fender",scan:"left_front_fender",target:"target_front_left_fender"},{key:"front-left-door",scan:"left_driver_door",target:"target_front_left_door"},{key:"rear-left-door",scan:"left_rear_door",target:"target_rear_left_door"},{key:"mid-left-rear",scan:"left_mid_rear",target:"target_mid_left_rear"},{key:"rear-left",scan:"left_rear",target:"target_rear_left"}
  ];
  const frames = [
    {phase:"idle",duration:560},
    ...landmarkGroups.map((_,group)=>({phase:"detect-landmarks",group,duration:280})),
    {phase:"detect-landmarks",group:landmarkGroups.length-1,duration:80},
    {phase:"build-skeleton",skeleton:-1,duration:160},...[0,1,2,3,4].map(skeleton=>({phase:"build-skeleton",skeleton,duration:skeleton===3?260:skeleton===4?180:220})),
    ...[0,.18,.36,.58,.8,1,1].map((progress,index)=>({phase:"resolve-outline",progress,duration:index===0?160:index===5?230:index===6?220:190})),
    {phase:"place-captures",duration:260},
    ...captures.flatMap((_,capture)=>[{phase:"scan-captures",capture,scanMode:"hold",duration:200},{phase:"scan-captures",capture,scanMode:"scan",duration:740},{phase:"scan-captures",capture,scanMode:"complete",duration:150}]),
    {phase:"coverage-complete",duration:200},{phase:"coverage-confirmed",duration:420},{phase:"verification-sweep",duration:600},{phase:"verify",duration:460},{phase:"trusted-record",duration:1500}
  ];
  const stageRanges = {observe:{start:1,end:9},understand:{start:10,end:15},register:{start:16,end:22},guide:{start:23,end:44},verify:{start:45,end:47},"trusted-record":{start:48,end:49}};
  const root = document.querySelector(".demoShell");
  const figure = document.querySelector(".vsiTechnologyDemo");
  const stage = document.querySelector("#vsi-stage");
  const production = document.querySelector("#production-geometry");
  const targetsRoot = document.querySelector("#target-geometry");
  const registerScan = document.querySelector("#register-scan");
  const verifySweep = document.querySelector("#verify-sweep");
  const verified = document.querySelector("#verified");
  const labelRoot = document.querySelector("#landmark-labels");
  const eyebrow = document.querySelector("#panel-eyebrow");
  const metric = document.querySelector("#panel-metric");
  const detail = document.querySelector("#panel-detail");
  const liveField = document.querySelector("#live-field");
  const liveValue = document.querySelector("#live-value");
  const modeButtons = [...document.querySelectorAll("[data-mode]")];
  const stageButtons = [...document.querySelectorAll("#stage-rail [data-stage]")];
  const panelSteps = [...document.querySelectorAll("#panel-sequence [data-stage]")];
  const reducedMotion = window.matchMedia("(prefers-reduced-motion: reduce)");
  let frameIndex = reducedMotion.matches ? frames.length - 1 : 0;
  let mode = reducedMotion.matches ? "present" : "auto";
  let timer;
  let presentEnd = null;
  let registerBounds = {x:30,y:500,width:3820,height:1840};

  function railStage(frame) {
    if (["idle","detect-landmarks"].includes(frame.phase)) return "observe";
    if (frame.phase === "build-skeleton") return "understand";
    if (frame.phase === "resolve-outline") return "register";
    if (["place-captures","scan-captures"].includes(frame.phase)) return "guide";
    if (["coverage-complete","coverage-confirmed","verification-sweep"].includes(frame.phase)) return "verify";
    return "trusted-record";
  }
  function setState(element, state) { element.dataset.vsiState = state; element.dataset.state = state; }
  function centerX(element) {
    const transform = element.transform?.baseVal?.consolidate?.();
    if (element.classList.contains("vsi-landmark") && transform) return transform.matrix.e + 25;
    const bounds = element.getBBox();
    return bounds.x + bounds.width / 2;
  }
  function setLandmarkState(element, state, offset = 0) {
    setState(element,state);
    element.style.setProperty("--vsi-point-delay",`${offset * 55}ms`);
    element.classList.remove("is-detected","is-active","is-faint","is-registering","is-registered");
    if (state !== "hidden") element.classList.add("is-detected");
    if (state !== "hidden" && state !== "detected") element.classList.add(`is-${state}`);
  }
  function setTargetState(element,state) {
    setState(element,state);
    element.classList.remove("is-active","is-complete");
    if (state === "active") element.classList.add("is-active");
    if (state === "complete") element.classList.add("is-complete");
  }
  function setScanState(element,state) {
    setState(element,state);
    element.classList.remove("is-primed","is-active","is-just-complete","is-complete","is-complete-recent","is-complete-old","is-verified","is-confirmed","is-sweeping","is-clearing","is-final","verify-section--fully-confirmed");
    const classes = {
      primed:["is-primed"],active:["is-active"],"just-complete":["is-just-complete"],
      "complete-recent":["is-complete","is-complete-recent"],"complete-old":["is-complete","is-complete-old"],
      verified:["is-verified"],confirmed:["is-confirmed","verify-section--fully-confirmed"],
      sweeping:["is-sweeping","verify-section--fully-confirmed"],clearing:["is-clearing"],final:["is-final"]
    };
    element.classList.add(...(classes[state] || []));
  }
  function updateLabels(frame) {
    labelRoot.replaceChildren();
    if (frame.phase !== "detect-landmarks") return;
    const items = landmarkGroups[frame.group] || [];
    requestAnimationFrame(() => items.forEach((feature,index) => {
      const source = production.querySelector(`#landmark-${feature}`); if (!source) return;
      const sourceRect = source.getBoundingClientRect(); const stageRect = stage.getBoundingClientRect();
      const x = sourceRect.left - stageRect.left + sourceRect.width / 2; const y = sourceRect.top - stageRect.top + sourceRect.height / 2; const leftSide = x > stageRect.width * .62;
      const item = document.createElement("div"); item.className = "landmarkLabel"; item.dataset.direction = leftSide ? "left" : "right"; item.dataset.state = "current";
      item.style.setProperty("--vsi-label-delay",`${index * 55}ms`); item.style.setProperty("--vsi-label-left",`${leftSide ? Math.max(12,x-12) : Math.min(stageRect.width-12,x+12)}px`); item.style.setProperty("--vsi-label-top",`${Math.min(Math.max(18,y),stageRect.height-28)}px`); item.style.setProperty("--vsi-label-offset",`${(index-1)*18}px`);
      item.innerHTML = `<span></span><strong>${labels[feature] || feature.replaceAll("-"," ").toUpperCase()}</strong>`; labelRoot.append(item);
    }));
  }
  function applyFrame() {
    const frame = frames[frameIndex]; const active = railStage(frame); const activeIndex = stages.indexOf(active); const panelCopy = copy[active];
    figure.dataset.phase = frame.phase; eyebrow.textContent = panelCopy.label; metric.textContent = active === "register" && frame.progress >= 1 ? "Vehicle geometry resolved" : panelCopy.metric;
    detail.hidden = !panelCopy.detail; detail.textContent = active === "register" && frame.progress >= 1 ? "The observed structure becomes a stable vehicle-relative model." : panelCopy.detail || "";
    liveField.hidden = active !== "guide"; liveValue.textContent = frame.capture === undefined ? "front" : captures[frame.capture].key.replaceAll("-","_");
    [...stageButtons,...panelSteps].forEach((element) => { const index = stages.indexOf(element.dataset.stage); const state = index < activeIndex ? "complete" : index === activeIndex ? "active" : "pending"; element.dataset.state = state; if (state === "active") element.setAttribute("aria-current","step"); else element.removeAttribute("aria-current"); });
    production.classList.toggle("is-empty",frame.phase === "trusted-record"); targetsRoot.classList.toggle("is-empty",frame.phase === "trusted-record");
    const currentFeatures = landmarkGroups[frame.group] || [];
    const lastDetectedIndex = frame.group === undefined ? -1 : Math.max(...landmarkGroups.slice(0,frame.group+1).flatMap((items)=>items.map((item)=>landmarkOrder.indexOf(item))));
    production.querySelectorAll(".vsi-landmark").forEach((element) => {
      const feature = element.dataset.feature || element.id.replace("landmark-","");
      const index = landmarkOrder.indexOf(feature);
      let state = "hidden";
      if (["coverage-complete","coverage-confirmed","verification-sweep"].includes(frame.phase)) state = ["front-left-wheel","left-mirror","rear-left-wheel"].includes(feature) ? "faint" : "hidden";
      else if (frame.phase === "build-skeleton") state = index >= 0 ? "detected" : "hidden";
      else if (frame.phase === "detect-landmarks") state = index < 0 || index > lastDetectedIndex ? "hidden" : currentFeatures.includes(feature) ? "active" : "detected";
      else if (frame.phase === "resolve-outline" && index >= 0) {
        const progress = frame.progress || 0;
        if (progress >= 1) state = "registered";
        else if (progress <= 0) state = "detected";
        else {
          const position = centerX(element); const step = registerProgressSteps.indexOf(progress); const previous = step > 0 ? registerProgressSteps[step-1] : 0;
          const previousX = registerBounds.x + registerBounds.width * previous; const sweepX = registerBounds.x + registerBounds.width * progress;
          state = position > previousX && position <= sweepX ? "registering" : position <= previousX ? "registered" : "detected";
        }
      }
      setLandmarkState(element,state,Math.max(0,currentFeatures.indexOf(feature)));
    });
    const skeletons = [...production.querySelectorAll(".vsi-skeleton-line")]; skeletons.forEach((element,index) => {
      const group = Math.min(3,Math.floor(index/Math.max(1,skeletons.length)*4)); let state = "hidden";
      if (frame.phase === "build-skeleton") state = group < frame.skeleton ? "complete" : group === frame.skeleton ? "active" : "hidden";
      else if (frame.phase === "resolve-outline") {
        const progress = frame.progress || 0; const position = centerX(element); const sweepX = registerBounds.x + registerBounds.width * progress;
        state = progress <= 0 ? "register-pending" : progress >= 1 ? "register-gone" : position <= sweepX-120 ? "register-gone" : position <= sweepX+120 ? "register-fading" : "register-pending";
      }
      setState(element,state); element.style.setProperty("--vsi-order",String(index));
    });
    const lines = [...production.querySelectorAll(".vsi-line")]; lines.forEach((element,index) => {
      let state = "hidden";
      if (frame.phase === "resolve-outline") {
        const progress = frame.progress || 0; const position = centerX(element); const step = registerProgressSteps.indexOf(progress); const previous = step > 0 ? registerProgressSteps[step-1] : 0;
        const previousX = registerBounds.x + registerBounds.width * previous; const sweepX = registerBounds.x + registerBounds.width * progress;
        state = progress <= 0 ? "hidden" : progress >= 1 ? "registered" : position > previousX && position <= sweepX ? "registering" : position <= previousX ? "registered" : "hidden";
      } else if (["place-captures","verify"].includes(frame.phase)) state = "clearing";
      setState(element,state); element.style.setProperty("--vsi-order",String(index));
    });
    const registerClip = production.querySelector("#vsi-register-reveal-rect");
    if (registerClip) { registerClip.setAttribute("x",String(registerBounds.x)); registerClip.setAttribute("y",String(registerBounds.y)); registerClip.setAttribute("width",String(registerBounds.width * (frame.progress || 0))); registerClip.setAttribute("height",String(registerBounds.height)); }
    production.querySelector("#lines")?.toggleAttribute("clip-path",frame.phase === "resolve-outline");
    if (frame.phase === "resolve-outline") production.querySelector("#lines")?.setAttribute("clip-path","url(#vsi-register-reveal-clip)");
    targetsRoot.querySelectorAll(".vsi-guide-target").forEach((element) => { const index = captures.findIndex((item)=>item.target===element.id); const state = frame.phase === "place-captures" ? "pending" : frame.phase === "scan-captures" ? (index < frame.capture ? "complete" : index === frame.capture ? "active" : "pending") : "hidden"; setTargetState(element,state); });
    targetsRoot.querySelectorAll(".vsi-scan-section").forEach((element) => {
      const index = captures.findIndex((item)=>item.scan===element.id); let state = "hidden";
      if (frame.phase === "scan-captures") state = index < frame.capture ? (index === frame.capture-1 ? "complete-recent" : "complete-old") : index === frame.capture ? (frame.scanMode === "scan" ? "active" : frame.scanMode === "complete" ? "just-complete" : "primed") : "hidden";
      else if (frame.phase === "coverage-complete") state = "verified";
      else if (frame.phase === "coverage-confirmed") state = "confirmed";
      else if (frame.phase === "verification-sweep") state = "sweeping";
      else if (frame.phase === "verify") state = "clearing";
      setScanState(element,state);
    });
    targetsRoot.querySelectorAll(".vsi-scan-context-line").forEach((element)=>element.dataset.vsiState = ["scan-captures","coverage-complete"].includes(frame.phase) ? "visible" : "hidden");
    registerScan.hidden = frame.phase !== "resolve-outline" || !frame.progress || frame.progress >= 1; if (!registerScan.hidden) { registerScan.style.setProperty("--vsi-register-band-left",`${((registerBounds.x + registerBounds.width * frame.progress)/3879)*100}%`); registerScan.style.setProperty("--vsi-register-band-top",`${(registerBounds.y/2736)*100}%`); registerScan.style.setProperty("--vsi-register-band-height",`${(registerBounds.height/2736)*100}%`); }
    verifySweep.hidden = frame.phase !== "verification-sweep"; if (!verifySweep.hidden) { verifySweep.style.setProperty("--vsi-final-sweep-left",`${(registerBounds.x/3879)*100}%`); verifySweep.style.setProperty("--vsi-final-sweep-top",`${(registerBounds.y/2736)*100}%`); verifySweep.style.setProperty("--vsi-final-sweep-height",`${(registerBounds.height/2736)*100}%`); verifySweep.style.setProperty("--vsi-final-sweep-distance",`${(registerBounds.width/3879)*100}%`); }
    verified.hidden = !["verify","trusted-record"].includes(frame.phase); updateLabels(frame);
  }
  function schedule() { window.clearTimeout(timer); applyFrame(); if (reducedMotion.matches) return; if (mode === "auto") timer = window.setTimeout(()=>{frameIndex=(frameIndex+1)%frames.length;schedule();},frames[frameIndex].duration); else if (mode === "present" && presentEnd !== null && frameIndex < presentEnd) timer = window.setTimeout(()=>{frameIndex+=1;schedule();},frames[frameIndex].duration); }
  function prepareProductionSvg(svg) {
    const lines = [...svg.querySelectorAll(".vsi-line")];
    const boxes = lines.map((element)=>element.getBBox()); const padding = 36;
    if (boxes.length) {
      const minX = Math.max(0,Math.min(...boxes.map((box)=>box.x))-padding); const minY = Math.max(0,Math.min(...boxes.map((box)=>box.y))-padding);
      const maxX = Math.min(3879,Math.max(...boxes.map((box)=>box.x+box.width))+padding); const maxY = Math.min(2736,Math.max(...boxes.map((box)=>box.y+box.height))+padding);
      registerBounds = {x:minX,y:minY,width:maxX-minX,height:maxY-minY};
    }
    const namespace = "http://www.w3.org/2000/svg"; const clip = document.createElementNS(namespace,"clipPath"); clip.id = "vsi-register-reveal-clip";
    const rect = document.createElementNS(namespace,"rect"); rect.id = "vsi-register-reveal-rect"; clip.append(rect); svg.querySelector("#vsi-defs")?.append(clip);
  }
  function prepareTargetSvg(svg) {
    const namespace = "http://www.w3.org/2000/svg";
    svg.id = "halo-vsi-target-dots-scans"; svg.setAttribute("preserveAspectRatio","none");
    const gradient = document.createElementNS(namespace,"linearGradient"); gradient.id = "vsi-scan-band-gradient"; gradient.setAttribute("x1","0%"); gradient.setAttribute("x2","100%"); gradient.setAttribute("y1","0%"); gradient.setAttribute("y2","0%");
    [["0%","0.03"],["38%","0.10"],["76%","0.22"],["90%","0.36"],["100%","0.04"]].forEach(([offset,opacity])=>{ const stop=document.createElementNS(namespace,"stop"); stop.setAttribute("offset",offset); stop.setAttribute("stop-color",offset==="90%"?"rgb(255, 156, 220)":"rgb(241, 58, 173)"); stop.setAttribute("stop-opacity",opacity); gradient.append(stop); });
    svg.querySelector("defs")?.append(gradient);
    svg.querySelectorAll('[id^="target_"]').forEach((element,index)=>{element.classList.add("vsi-guide-target");element.dataset.vsiTargetIndex=String(captures.findIndex((item)=>item.target===element.id));});
    captures.forEach((item,index)=>{
      const path = svg.querySelector(`#${item.scan}`); if (!path) return;
      const box = path.getBBox(); const padding=12; const bounds={x:box.x-padding,y:box.y-padding,width:Math.max(1,box.width+padding*2),height:Math.max(1,box.height+padding*2)}; const bandWidth=Math.min(Math.max(bounds.width*.055,64),132); const scanDistance=bounds.width+bandWidth;
      const group=document.createElementNS(namespace,"g"); group.id=item.scan; group.classList.add("vsi-scan-section"); group.dataset.vsiScanIndex=String(index); group.style.setProperty("--vsi-scan-distance",`${scanDistance}px`);
      const clip=document.createElementNS(namespace,"clipPath"); clip.id=`vsi-scan-clip-${item.scan}`; const clipPath=path.cloneNode(); clipPath.removeAttribute("id"); clip.append(clipPath); group.append(clip);
      const outline=path.cloneNode(); outline.removeAttribute("id"); outline.removeAttribute("stroke"); outline.removeAttribute("stroke-width"); outline.classList.add("vsi-scan-outline"); outline.setAttribute("pathLength","1"); outline.setAttribute("vector-effect","non-scaling-stroke"); group.append(outline);
      const mask=document.createElementNS(namespace,"g"); mask.classList.add("vsi-scan-mask"); mask.setAttribute("clip-path",`url(#vsi-scan-clip-${item.scan})`);
      [["vsi-scan-fill",bounds.x,bounds.y,bounds.width,bounds.height],["vsi-scan-band",bounds.x-bandWidth,bounds.y,bandWidth,bounds.height],["vsi-scan-front",bounds.x,bounds.y,3,bounds.height]].forEach(([className,x,y,width,height])=>{const rect=document.createElementNS(namespace,"rect");rect.classList.add(className);rect.setAttribute("x",String(x));rect.setAttribute("y",String(y));rect.setAttribute("width",String(width));rect.setAttribute("height",String(height));mask.append(rect);});
      group.append(mask); path.replaceWith(group);
    });
    svg.querySelector("#neutral-path-0794")?.classList.add("vsi-scan-context-line");
  }
  Promise.all([fetch("./assets/halo-vsi-production-master.svg").then((response)=>response.text()),fetch("./assets/TargetDots_and_scans.svg").then((response)=>response.text()),fetch("./source/vsi-hero-demo.module.css").then((response)=>response.text())]).then(([productionSvg,targetSvg,sourceCss]) => {
    production.innerHTML = productionSvg; targetsRoot.innerHTML = targetSvg; prepareProductionSvg(production.querySelector("svg")); prepareTargetSvg(targetsRoot.querySelector("svg"));
    const style = document.createElement("style"); style.dataset.source = "VsiHeroDemo CSS module adapted only by removing CSS-module :global wrappers"; style.textContent = sourceCss.replace(/:global\(([^)]+)\)/g,"$1"); document.head.prepend(style); schedule();
  });
  modeButtons.forEach((button)=>button.addEventListener("click",()=>{mode=button.dataset.mode;root.dataset.vsiMode=mode;modeButtons.forEach((item)=>item.setAttribute("aria-pressed",String(item===button)));stageButtons.forEach((item)=>item.disabled=mode!=="present");if(mode==="auto"){presentEnd=null;frameIndex=0;schedule();}else{const range=stageRanges.observe;presentEnd=range.end;frameIndex=range.start;schedule();}}));
  stageButtons.forEach((button)=>button.addEventListener("click",()=>{if(mode!=="present")return;const range=stageRanges[button.dataset.stage];presentEnd=range.end;frameIndex=range.start;schedule();}));
  reducedMotion.addEventListener?.("change",(event)=>{if(event.matches){mode="present";frameIndex=frames.length-1;window.clearTimeout(timer);applyFrame();}});
  window.addEventListener("resize",()=>updateLabels(frames[frameIndex]));
})();
