#!/usr/bin/env python3
"""Generate walkthrough-v4.html — the ultimate interactive walkthrough."""

import json
import os

BASE = "/Users/nova/Desktop/walkthrough-output"

# Load data
with open(os.path.join(BASE, "frames_data.json")) as f:
    frames = json.load(f)
with open(os.path.join(BASE, "audio_data.json")) as f:
    audio = json.load(f)

# Step definitions
STEPS = [
    {"id":"0010","audioKey":"01","tag":"SETUP","title":"Layerpath Dashboard","subtitle":"Starting Point",
     "desc":"The Layerpath dashboard at <strong>app.layerpath.com</strong> showing the 'Latest demos' grid. Vinay's workspace (Tier 3) with sidebar: Dashboard, My demos, Playlists, Brandkit, Insights.",
     "narration":"I am recording how I use CleanShot X. Imagine this is my product Layerpath. I take a screenshot using my keyboard shortcut.",
     "callout":{"text":"Keyboard shortcut captures the full screen","x":50,"y":15},
     "hotspots":[{"x":50,"y":50,"label":"Dashboard"}],
     "zoom":None,"primaryHotspot":0},
    {"id":"0030","audioKey":"02","tag":"TOOLS","title":"CleanShot X Toolbar","subtitle":"Annotation Mode",
     "desc":"CleanShot X's <strong>annotation toolbar</strong> appears at the top. Tools: Cursor, Crop, Shapes, Text, Blur, Spotlight, Counter, Pen, Background.",
     "narration":"Here are the features I want — I use four features which are super useful for me.",
     "callout":{"text":"All annotation tools accessible from this toolbar","x":50,"y":10},
     "hotspots":[{"x":38,"y":4,"label":"Blur"},{"x":50,"y":4,"label":"Spotlight"},{"x":56,"y":4,"label":"Counter"},{"x":16,"y":4,"label":"Background"}],
     "zoom":{"x":30,"y":0,"w":40,"h":8},"primaryHotspot":0},
    {"id":"0040","audioKey":"03","tag":"BLUR","title":"Feature 1: Blur / Redact","subtitle":"Highest Priority",
     "desc":"The <strong>blur tool</strong> is selected. A rectangular selection with blue handles defines the blur area. Blur applies instantly.",
     "narration":"The first one I always start with is blurring. If I want to redact this, I apply this — look how easy it is.",
     "callout":{"text":"Drag to select → blur applies instantly","x":30,"y":75},
     "hotspots":[{"x":30,"y":65,"label":"Blur region"},{"x":30,"y":80,"label":"Resize handles"}],
     "zoom":{"x":15,"y":50,"w":40,"h":40},"primaryHotspot":0},
    {"id":"0050","audioKey":"04","tag":"BLUR","title":"Adjustable Blur Intensity","subtitle":"Fine Control",
     "desc":"Blur intensity <strong>adjustable via slider</strong>. Ranges from subtle to heavy pixelation. Multiple regions can stack.",
     "narration":"I can reduce the blurring effect depending on how little or how more I want. This helps me redact anything I don't want people to see.",
     "callout":{"text":"Slider controls blur intensity","x":60,"y":10},
     "hotspots":[{"x":59,"y":4,"label":"Intensity slider"}],
     "zoom":{"x":40,"y":0,"w":35,"h":10},"primaryHotspot":0},
    {"id":"0070","audioKey":"05","tag":"SPOTLIGHT","title":"Feature 2: Spotlight","subtitle":"High Priority",
     "desc":"The <strong>spotlight tool</strong> darkens everything except the highlighted region. Creates instant focal point. Opacity adjustable.",
     "narration":"I want to bring their attention. I highlight — this is my workspace. I can control the opacity.",
     "callout":{"text":"Everything dims except the focus area","x":65,"y":45},
     "hotspots":[{"x":22,"y":22,"label":"Spotlight area"},{"x":60,"y":4,"label":"Opacity"}],
     "zoom":{"x":10,"y":10,"w":30,"h":30},"primaryHotspot":0},
    {"id":"0090","audioKey":"06","tag":"SPOTLIGHT","title":"Opacity Control","subtitle":"Dimming Effect",
     "desc":"Spotlight with <strong>adjustable opacity</strong>. Multiple spotlights can coexist. Option to darken more or keep subtle.",
     "narration":"I want to bring their attention to the latest demo. I have the option of darkening it — this is super useful.",
     "callout":{"text":"Adjustable dimming — subtle to heavy","x":50,"y":30},
     "hotspots":[{"x":60,"y":4,"label":"Opacity slider"}],
     "zoom":None,"primaryHotspot":0},
    {"id":"0110","audioKey":"07","tag":"COUNTER","title":"Feature 3: Counters","subtitle":"High Priority",
     "desc":"The <strong>counter tool</strong> places numbered circular markers that auto-increment. Red color, size 20. Keyboard shortcut: C.",
     "narration":"If I want to bring their attention to the steps — this is my brand color, but I'll select red. Numbering, lettering — I select 20.",
     "callout":{"text":"Tap to place → numbers auto-increment","x":30,"y":15},
     "hotspots":[{"x":28,"y":11,"label":"Counter ①"},{"x":45,"y":4,"label":"Counter tool (C)"}],
     "zoom":{"x":15,"y":0,"w":45,"h":25},"primaryHotspot":0},
    {"id":"0130","audioKey":"08","tag":"COUNTER","title":"Building the Flow","subtitle":"Steps 1→4",
     "desc":"Four counters: <strong>①</strong> Workspace → <strong>②</strong> Dashboard → <strong>③</strong> Demo card → <strong>④</strong> My demos.",
     "narration":"Start with your workspace. Then dashboard. Then select this project. Sometimes go to your demos.",
     "callout":{"text":"Single tap placement — no dragging","x":55,"y":50},
     "hotspots":[{"x":17,"y":18,"label":"① Workspace"},{"x":16,"y":27,"label":"② Dashboard"},{"x":39,"y":27,"label":"③ Demo"},{"x":12,"y":33,"label":"④ My demos"}],
     "zoom":{"x":5,"y":10,"w":50,"h":35},"primaryHotspot":0},
    {"id":"0140","audioKey":"09","tag":"COUNTER","title":"Complete 5-Step Guide","subtitle":"All Steps Placed",
     "desc":"All <strong>5 numbered markers</strong>: ① Workspace → ② Dashboard → ③ Demo → ④ My demos → ⑤ Second row.",
     "narration":"That is if you want to see all the demos. You can also blur things. These are my top three tools.",
     "callout":{"text":"5 steps = complete guided flow","x":60,"y":75},
     "hotspots":[{"x":17,"y":18,"label":"①"},{"x":16,"y":27,"label":"②"},{"x":39,"y":27,"label":"③"},{"x":12,"y":33,"label":"④"},{"x":39,"y":60,"label":"⑤"}],
     "zoom":None,"primaryHotspot":0},
    {"id":"0190","audioKey":"10","tag":"ARROW","title":"Feature 4: Arrows","subtitle":"Occasional Use",
     "desc":"A large <strong>red arrow</strong> directing attention downward. Used occasionally to emphasize flow.",
     "narration":"Sometimes I use arrows — hey, make sure you're looking at this specific thing.",
     "callout":{"text":"Drag start to end → arrow appears","x":60,"y":35},
     "hotspots":[{"x":55,"y":40,"label":"Arrow indicator"}],
     "zoom":{"x":35,"y":20,"w":40,"h":50},"primaryHotspot":0},
    {"id":"0210","audioKey":"11","tag":"BEAUTIFY","title":"Feature 5: Background","subtitle":"Export Preparation",
     "desc":"The <strong>background panel</strong>: Gradients, Wallpapers, Blurred, Colors. Controls: Padding, Inset, Shadow, Corners, Ratio.",
     "narration":"Add background — beautiful! I select the one closest to my branding.",
     "callout":{"text":"Gradient + padding + inset = polished","x":15,"y":25},
     "hotspots":[{"x":13,"y":22,"label":"Gradient"},{"x":8,"y":73,"label":"Padding"},{"x":8,"y":78,"label":"Inset"},{"x":15,"y":83,"label":"Corners"}],
     "zoom":{"x":0,"y":15,"w":22,"h":55},"primaryHotspot":0},
    {"id":"0240","audioKey":"12","tag":"BEAUTIFY","title":"Fine-Tuning","subtitle":"LinkedIn Optimized",
     "desc":"Fine-tuning: <strong>Corners: 3</strong>, Auto-balance ✓, <strong>Ratio: 4:3</strong> for LinkedIn.",
     "narration":"Corners to 3 or 4. For LinkedIn, I select 4:3. Increase padding for feed visibility.",
     "callout":{"text":"4:3 = LinkedIn optimized","x":15,"y":90},
     "hotspots":[{"x":14,"y":90,"label":"4:3 Ratio"},{"x":11,"y":80,"label":"Auto-balance"}],
     "zoom":{"x":0,"y":70,"w":22,"h":28},"primaryHotspot":0},
    {"id":"0260","audioKey":"13","tag":"EXPORT","title":"Final Result","subtitle":"Ready to Share",
     "desc":"The <strong>final output</strong>: blur, spotlight, counters, arrow — all in gradient background with rounded corners.",
     "narration":"I click done. It saves to my desktop. That's my workflow.",
     "callout":{"text":"One click → saved to desktop","x":90,"y":5},
     "hotspots":[{"x":92,"y":4,"label":"Done"}],
     "zoom":{"x":75,"y":0,"w":22,"h":8},"primaryHotspot":0},
    {"id":"0270","audioKey":"14","tag":"COMPLETE","title":"Workflow Complete","subtitle":"Back to Dashboard",
     "desc":"CleanShot X exits. Polished screenshot saved to Desktop. Back to Layerpath dashboard.",
     "narration":"Those are the top features I use. Thank you.",
     "callout":{"text":"Screenshot saved ✓","x":50,"y":50},
     "hotspots":[],
     "zoom":None,"primaryHotspot":0},
]

TAG_COLORS = {
    "SETUP":"#5b9bf5","TOOLS":"#5b9bf5","BLUR":"#ef6b6b","SPOTLIGHT":"#f5b342",
    "COUNTER":"#3dd9a0","ARROW":"#f5b342","BEAUTIFY":"#9b8af5","EXPORT":"#3dd9a0","COMPLETE":"#3dd9a0"
}

def build_steps_js():
    """Build the JS steps array with embedded base64 data."""
    lines = ["const STEPS = ["]
    for i, s in enumerate(STEPS):
        frame_b64 = frames.get(s["id"], "")
        audio_b64 = audio.get(s["audioKey"], "")
        img_src = f"data:image/jpeg;base64,{frame_b64}"
        aud_src = f"data:audio/mpeg;base64,{audio_b64}"

        zoom_js = "null"
        if s["zoom"]:
            z = s["zoom"]
            zoom_js = f'{{x:{z["x"]},y:{z["y"]},w:{z["w"]},h:{z["h"]}}}'

        hotspots_js = json.dumps(s["hotspots"])
        callout_js = json.dumps(s["callout"])
        tag_color = TAG_COLORS.get(s["tag"], "#7c6aef")

        lines.append(f"""  {{
    idx: {i}, id: "{s['id']}", audioKey: "{s['audioKey']}",
    tag: "{s['tag']}", tagColor: "{tag_color}",
    title: {json.dumps(s['title'])}, subtitle: {json.dumps(s['subtitle'])},
    desc: {json.dumps(s['desc'])},
    narration: {json.dumps(s['narration'])},
    callout: {callout_js},
    hotspots: {hotspots_js},
    zoom: {zoom_js}, primaryHotspot: {s['primaryHotspot']},
    imgSrc: "{img_src}",
    audSrc: "{aud_src}"
  }},""")
    lines.append("];")
    return "\n".join(lines)

# Build the intro image source
intro_img = f"data:image/jpeg;base64,{frames.get('0260', '')}"

html = f'''<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CleanShot X Annotation Workflow — Interactive Walkthrough</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap" rel="stylesheet">
<style>
/* ═══════════════════════════════════════════════════════════════
   RESET & BASE
   ═══════════════════════════════════════════════════════════════ */
*, *::before, *::after {{ box-sizing: border-box; margin: 0; padding: 0; }}
html {{ scroll-behavior: smooth; -webkit-font-smoothing: antialiased; }}
body {{
  font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
  background: #06060a; color: #eeeef2; line-height: 1.6;
  overflow-x: hidden; min-height: 100vh;
}}
::-webkit-scrollbar {{ width: 6px; }}
::-webkit-scrollbar-track {{ background: #0c0c12; }}
::-webkit-scrollbar-thumb {{ background: #2a2a38; border-radius: 3px; }}
::-webkit-scrollbar-thumb:hover {{ background: #3a3a4a; }}

/* ═══════════════════════════════════════════════════════════════
   DESIGN TOKENS
   ═══════════════════════════════════════════════════════════════ */
:root {{
  --bg: #06060a; --s1: #0c0c12; --s2: #121218; --s3: #1a1a22; --s4: #22222c;
  --border: #2a2a38; --border-light: #33334a;
  --t1: #eeeef2; --t2: #9898aa; --t3: #606078;
  --accent: #7c6aef; --accent-light: #9b8af5; --accent-glow: rgba(124,106,239,0.15);
  --accent-dark: #5a4abf;
  --r-sm: 6px; --r-md: 10px; --r-lg: 14px; --r-xl: 20px;
  --shadow: 0 4px 24px rgba(0,0,0,0.4);
  --shadow-lg: 0 8px 48px rgba(0,0,0,0.6);
  --transition: all 0.3s ease;
  --chrome-h: 44px;
  --toggle-h: 56px;
}}

/* ═══════════════════════════════════════════════════════════════
   MODE TOGGLE BAR (top-level, fixed)
   ═══════════════════════════════════════════════════════════════ */
.mode-toggle-bar {{
  position: fixed; top: 0; left: 0; right: 0; z-index: 1000;
  height: var(--toggle-h); background: #0a0a0f;
  border-bottom: 1px solid var(--border);
  display: flex; align-items: center; justify-content: center; gap: 4px;
  backdrop-filter: blur(12px);
}}
.mode-pill {{
  padding: 8px 22px; border-radius: 100px; font-size: 13px; font-weight: 600;
  cursor: pointer; transition: var(--transition); border: none; outline: none;
  letter-spacing: 0.01em; user-select: none;
}}
.mode-pill.active {{
  background: var(--accent); color: #fff;
  box-shadow: 0 2px 16px rgba(124,106,239,0.35);
}}
.mode-pill.inactive {{
  background: transparent; color: var(--t3);
}}
.mode-pill.inactive:hover {{ color: var(--t2); background: var(--s2); }}

/* ═══════════════════════════════════════════════════════════════
   BROWSER CHROME
   ═══════════════════════════════════════════════════════════════ */
.browser-chrome {{
  position: sticky; top: var(--toggle-h); z-index: 100;
  background: #1c1c20; border-bottom: 1px solid var(--border);
  border-radius: 12px 12px 0 0; margin: 0 auto;
  max-width: 1200px;
}}
.chrome-bar {{
  height: var(--chrome-h); display: flex; align-items: center;
  padding: 0 16px; position: relative;
}}
.chrome-dots {{ display: flex; gap: 7px; }}
.chrome-dot {{
  width: 11px; height: 11px; border-radius: 50%;
}}
.chrome-dot.r {{ background: #ff5f57; }}
.chrome-dot.y {{ background: #febc2e; }}
.chrome-dot.g {{ background: #28c840; }}
.chrome-title {{
  position: absolute; left: 50%; transform: translateX(-50%);
  font-size: 12px; color: var(--t3); font-weight: 500;
  display: flex; align-items: center; gap: 6px;
}}
.chrome-lock {{ font-size: 10px; opacity: 0.6; }}

/* ═══════════════════════════════════════════════════════════════
   MAIN WRAPPER
   ═══════════════════════════════════════════════════════════════ */
.main-wrapper {{
  padding-top: var(--toggle-h);
}}
.tour-wrapper {{
  max-width: 1200px; margin: 0 auto;
  background: var(--s1); border-left: 1px solid var(--border);
  border-right: 1px solid var(--border);
  border-bottom: 1px solid var(--border);
  border-radius: 0 0 12px 12px;
  min-height: calc(100vh - var(--toggle-h) - var(--chrome-h));
}}

/* ═══════════════════════════════════════════════════════════════
   INTRO SCREEN
   ═══════════════════════════════════════════════════════════════ */
.intro-screen {{
  display: flex; align-items: center; min-height: calc(100vh - var(--toggle-h) - var(--chrome-h));
  padding: 60px 48px; gap: 48px; animation: fadeUp 0.6s ease;
}}
.intro-left {{
  flex: 0 0 55%; display: flex; align-items: center; justify-content: center;
  position: relative; perspective: 1000px;
}}
.intro-img-wrap {{
  transform: rotateY(-6deg) rotateX(3deg);
  transition: transform 0.6s ease;
  border-radius: var(--r-lg); overflow: hidden;
  box-shadow: var(--shadow-lg), 0 0 80px rgba(124,106,239,0.08);
  max-width: 90%;
}}
.intro-img-wrap:hover {{ transform: rotateY(-2deg) rotateX(1deg); }}
.intro-img-wrap img {{ width: 100%; display: block; }}
.intro-bg-glow {{
  position: absolute; width: 300px; height: 300px; border-radius: 50%;
  background: radial-gradient(circle, rgba(124,106,239,0.15), transparent 70%);
  top: 50%; left: 50%; transform: translate(-50%, -50%);
  pointer-events: none; z-index: -1;
}}
.intro-right {{
  flex: 0 0 45%; padding-left: 12px;
}}
.intro-tag {{
  display: inline-block; padding: 4px 14px; border-radius: 100px;
  background: var(--accent-glow); border: 1px solid rgba(124,106,239,0.25);
  color: var(--accent-light); font-size: 11px; font-weight: 700;
  letter-spacing: 0.12em; text-transform: uppercase; margin-bottom: 16px;
}}
.intro-title {{
  font-size: 36px; font-weight: 800; line-height: 1.15; margin-bottom: 8px;
  background: linear-gradient(135deg, #eeeef2 30%, var(--accent-light));
  -webkit-background-clip: text; -webkit-text-fill-color: transparent;
  background-clip: text;
}}
.intro-author {{
  font-size: 14px; color: var(--t2); margin-bottom: 20px; font-weight: 500;
}}
.intro-desc {{
  font-size: 15px; color: var(--t2); line-height: 1.7; margin-bottom: 24px;
}}
.intro-badge {{
  display: inline-flex; align-items: center; gap: 8px;
  padding: 6px 14px; border-radius: 100px; background: var(--s3);
  border: 1px solid var(--border); font-size: 12px; color: var(--t2);
  margin-bottom: 32px;
}}
.intro-badge svg {{ width: 14px; height: 14px; stroke: var(--accent-light); }}
.start-btn {{
  display: inline-flex; align-items: center; gap: 10px;
  padding: 14px 36px; border-radius: 12px; border: none;
  background: linear-gradient(135deg, var(--accent), var(--accent-dark));
  color: #fff; font-size: 16px; font-weight: 700; cursor: pointer;
  transition: var(--transition); box-shadow: 0 4px 24px rgba(124,106,239,0.3);
  font-family: inherit;
}}
.start-btn:hover {{
  transform: translateY(-2px);
  box-shadow: 0 6px 32px rgba(124,106,239,0.45);
}}
.start-btn:active {{ transform: translateY(0); }}
.intro-hint {{
  display: block; margin-top: 16px; font-size: 12px; color: var(--t3);
}}

/* ═══════════════════════════════════════════════════════════════
   STEP VIEW
   ═══════════════════════════════════════════════════════════════ */
.step-view {{ display: none; flex-direction: column; min-height: calc(100vh - var(--toggle-h) - var(--chrome-h)); }}
.step-view.active {{ display: flex; }}

/* Progress Bar */
.step-topbar {{
  display: flex; align-items: center; justify-content: space-between;
  padding: 12px 24px; border-bottom: 1px solid var(--border);
  background: var(--s1); position: sticky; top: calc(var(--toggle-h) + var(--chrome-h));
  z-index: 50;
}}
.progress-dots {{ display: flex; gap: 6px; align-items: center; }}
.pdot {{
  width: 8px; height: 8px; border-radius: 50%; background: var(--s4);
  transition: var(--transition); cursor: pointer;
}}
.pdot.completed {{ background: var(--accent); }}
.pdot.active {{
  background: var(--accent); box-shadow: 0 0 8px var(--accent-light);
  transform: scale(1.3);
}}
.pdot:hover {{ transform: scale(1.4); }}
.step-counter {{ font-size: 13px; color: var(--t2); font-weight: 600; }}
.topbar-controls {{ display: flex; align-items: center; gap: 12px; }}
.autoplay-wrap {{
  display: flex; align-items: center; gap: 8px; font-size: 12px; color: var(--t3);
}}
.autoplay-toggle {{
  width: 36px; height: 20px; border-radius: 10px; background: var(--s4);
  cursor: pointer; position: relative; transition: var(--transition); border: none;
}}
.autoplay-toggle.on {{ background: var(--accent); }}
.autoplay-toggle::after {{
  content: ''; position: absolute; width: 16px; height: 16px; border-radius: 50%;
  background: #fff; top: 2px; left: 2px; transition: var(--transition);
}}
.autoplay-toggle.on::after {{ left: 18px; }}
.mute-btn {{
  width: 32px; height: 32px; border-radius: 8px; background: transparent;
  border: 1px solid var(--border); color: var(--t2); cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  transition: var(--transition); font-size: 16px;
}}
.mute-btn:hover {{ background: var(--s3); color: var(--t1); }}

/* Step Content Area */
.step-content {{
  flex: 1; padding: 28px 32px 120px; animation: fadeUp 0.4s ease;
}}
.step-header {{
  display: flex; align-items: flex-start; gap: 16px; margin-bottom: 24px;
}}
.step-num-box {{
  width: 52px; height: 52px; border-radius: var(--r-md); flex-shrink: 0;
  background: linear-gradient(135deg, var(--accent), var(--accent-dark));
  display: flex; align-items: center; justify-content: center;
  font-size: 22px; font-weight: 800; color: #fff;
}}
.step-header-text {{ flex: 1; }}
.step-title-row {{
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-bottom: 2px;
}}
.step-title {{ font-size: 22px; font-weight: 700; color: var(--t1); }}
.step-tag {{
  display: inline-block; padding: 3px 10px; border-radius: 100px;
  font-size: 10px; font-weight: 700; letter-spacing: 0.08em;
  text-transform: uppercase;
}}
.step-subtitle {{ font-size: 13px; color: var(--t3); font-weight: 500; }}

/* Screenshot Container */
.screenshot-wrap {{
  position: relative; border-radius: var(--r-lg); overflow: hidden;
  border: 1px solid var(--border); background: #000;
  margin-bottom: 24px; cursor: pointer;
}}
.screenshot-wrap img {{
  width: 100%; display: block; transition: transform 1.2s cubic-bezier(0.25, 0.1, 0.25, 1);
  transform-origin: center center;
}}
.screenshot-wrap img.zoomed-in {{
  transition: none;
}}

/* Hotspots */
.hotspot {{
  position: absolute; width: 22px; height: 22px; border-radius: 50%;
  background: rgba(255,255,255,0.85); border: 2px solid var(--accent);
  transform: translate(-50%, -50%); z-index: 10;
  animation: hotspotPulse 2s ease-in-out infinite;
  pointer-events: none;
}}
.hotspot::after {{
  content: ''; position: absolute; inset: -6px; border-radius: 50%;
  border: 2px solid rgba(124,106,239,0.4);
  animation: hotspotRipple 2s ease-out infinite;
}}
.hotspot-label {{
  position: absolute; z-index: 11; transform: translate(-50%, 0);
  background: rgba(10,10,15,0.88); color: #fff; padding: 3px 10px;
  border-radius: 100px; font-size: 10px; font-weight: 600;
  white-space: nowrap; pointer-events: none; border: 1px solid rgba(124,106,239,0.3);
  backdrop-filter: blur(4px);
}}

/* Callout Overlay */
.callout-overlay {{
  position: absolute; z-index: 12; max-width: 260px;
  background: rgba(10,10,18,0.82); backdrop-filter: blur(12px);
  border-radius: var(--r-md); padding: 12px 16px;
  border-left: 3px solid var(--accent); color: #eee;
  font-size: 13px; font-weight: 500; line-height: 1.5;
  animation: calloutIn 0.5s ease 0.5s both;
  pointer-events: none;
  box-shadow: 0 4px 20px rgba(0,0,0,0.5);
}}

/* Zoom Button */
.zoom-btn {{
  position: absolute; bottom: 12px; right: 12px; z-index: 15;
  width: 36px; height: 36px; border-radius: var(--r-sm);
  background: rgba(10,10,18,0.7); backdrop-filter: blur(8px);
  border: 1px solid var(--border); color: var(--t2);
  cursor: pointer; display: flex; align-items: center; justify-content: center;
  transition: var(--transition); font-size: 16px;
}}
.zoom-btn:hover {{ background: rgba(10,10,18,0.9); color: var(--t1); }}

/* Cards Grid */
.cards-grid {{
  display: grid; grid-template-columns: 1fr 1fr; gap: 16px; margin-bottom: 24px;
}}
.info-card {{
  background: var(--s2); border: 1px solid var(--border); border-radius: var(--r-lg);
  padding: 20px;
}}
.info-card-title {{
  display: flex; align-items: center; gap: 8px; font-size: 12px;
  font-weight: 700; text-transform: uppercase; letter-spacing: 0.06em;
  color: var(--t3); margin-bottom: 10px;
}}
.info-card-title svg {{ width: 16px; height: 16px; }}
.info-card p {{ font-size: 14px; color: var(--t2); line-height: 1.7; }}
.info-card p strong {{ color: var(--t1); font-weight: 600; }}
.narration-card {{
  border-left: 3px solid var(--accent);
}}
.narration-card p {{ font-style: italic; }}

/* Audio Bar */
.audio-bar {{
  display: flex; align-items: center; gap: 12px;
  background: var(--s2); border: 1px solid var(--border);
  border-radius: var(--r-lg); padding: 12px 16px; margin-bottom: 16px;
}}
.audio-play-btn {{
  width: 36px; height: 36px; border-radius: 50%;
  background: var(--accent); border: none; color: #fff;
  cursor: pointer; display: flex; align-items: center; justify-content: center;
  transition: var(--transition); flex-shrink: 0;
}}
.audio-play-btn:hover {{ background: var(--accent-light); }}
.audio-progress-wrap {{
  flex: 1; height: 4px; background: var(--s4); border-radius: 2px;
  cursor: pointer; position: relative;
}}
.audio-progress-fill {{
  height: 100%; background: var(--accent); border-radius: 2px;
  width: 0%; transition: width 0.1s linear;
}}
.audio-time {{ font-size: 12px; color: var(--t3); font-weight: 500; min-width: 80px; text-align: right; }}
.audio-timestamp {{
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 11px; color: var(--t3); background: var(--s2);
  padding: 4px 10px; border-radius: 100px; border: 1px solid var(--border);
}}

/* Bottom Nav */
.bottom-nav {{
  position: fixed; bottom: 0; left: 50%; transform: translateX(-50%);
  width: 100%; max-width: 1200px; z-index: 80;
  background: rgba(12,12,18,0.92); backdrop-filter: blur(16px);
  border-top: 1px solid var(--border);
  padding: 12px 24px; display: flex; align-items: center; justify-content: space-between;
  border-radius: 0 0 12px 12px;
}}
.nav-btn {{
  display: inline-flex; align-items: center; gap: 8px;
  padding: 10px 24px; border-radius: var(--r-md); border: 1px solid var(--border);
  background: var(--s2); color: var(--t1); font-size: 14px; font-weight: 600;
  cursor: pointer; transition: var(--transition); font-family: inherit;
}}
.nav-btn:hover {{ background: var(--s3); border-color: var(--border-light); }}
.nav-btn.primary {{
  background: var(--accent); border-color: var(--accent); color: #fff;
}}
.nav-btn.primary:hover {{ background: var(--accent-light); }}
.nav-btn:disabled {{ opacity: 0.3; cursor: not-allowed; }}
.nav-center {{ font-size: 13px; color: var(--t3); text-align: center; max-width: 300px; }}
.nav-keys {{ font-size: 11px; color: var(--t3); margin-top: 2px; }}
.nav-keys kbd {{
  display: inline-block; padding: 1px 6px; border-radius: 3px;
  background: var(--s4); border: 1px solid var(--border);
  font-size: 10px; font-family: inherit;
}}

/* ═══════════════════════════════════════════════════════════════
   OUTRO SCREEN
   ═══════════════════════════════════════════════════════════════ */
.outro-screen {{
  display: none; flex-direction: column; align-items: center; justify-content: center;
  min-height: calc(100vh - var(--toggle-h) - var(--chrome-h));
  padding: 60px 32px; text-align: center; position: relative; overflow: hidden;
}}
.outro-screen.active {{ display: flex; }}
.outro-check {{
  width: 80px; height: 80px; border-radius: 50%;
  background: linear-gradient(135deg, #28c840, #1ea835);
  display: flex; align-items: center; justify-content: center;
  margin-bottom: 24px; animation: bounceIn 0.6s cubic-bezier(0.34, 1.56, 0.64, 1);
  box-shadow: 0 4px 32px rgba(40,200,64,0.25);
}}
.outro-check svg {{ width: 40px; height: 40px; stroke: #fff; stroke-width: 3; }}
.outro-title {{ font-size: 32px; font-weight: 800; margin-bottom: 8px; }}
.outro-sub {{ font-size: 15px; color: var(--t2); margin-bottom: 32px; }}
.feature-list {{
  display: flex; flex-wrap: wrap; gap: 12px; justify-content: center; margin-bottom: 40px;
}}
.feature-chip {{
  display: inline-flex; align-items: center; gap: 6px;
  padding: 8px 16px; border-radius: 100px; background: var(--s2);
  border: 1px solid var(--border); font-size: 13px; color: var(--t2); font-weight: 500;
}}
.feature-chip .ficon {{
  width: 18px; height: 18px; border-radius: 50%; display: flex;
  align-items: center; justify-content: center; font-size: 10px;
}}
.outro-buttons {{ display: flex; gap: 12px; }}

/* Confetti */
.confetti-container {{ position: absolute; inset: 0; pointer-events: none; overflow: hidden; }}
.confetti {{
  position: absolute; width: 8px; height: 8px; border-radius: 2px;
  top: -10px; animation: confettiFall linear forwards;
}}

/* ═══════════════════════════════════════════════════════════════
   GUIDE MODE
   ═══════════════════════════════════════════════════════════════ */
.guide-mode {{
  display: none; max-width: 860px; margin: 0 auto;
  padding: calc(var(--toggle-h) + 32px) 24px 60px;
}}
.guide-mode.active {{ display: block; }}
.guide-header {{
  margin-bottom: 40px; padding-bottom: 32px;
  border-bottom: 1px solid var(--border);
}}
.guide-header h1 {{
  font-size: 32px; font-weight: 800; margin-bottom: 8px;
}}
.guide-meta {{
  display: flex; gap: 16px; font-size: 13px; color: var(--t3); margin-bottom: 16px;
}}
.guide-desc {{ font-size: 15px; color: var(--t2); line-height: 1.7; max-width: 600px; }}

/* Guide Step Card */
.guide-step {{
  padding: 32px 0; border-bottom: 1px solid rgba(42,42,56,0.5);
}}
.guide-step-header {{
  display: flex; align-items: center; gap: 16px; margin-bottom: 20px;
}}
.guide-step-num {{
  width: 48px; height: 48px; border-radius: 50%; flex-shrink: 0;
  background: linear-gradient(135deg, var(--accent), var(--accent-dark));
  display: flex; align-items: center; justify-content: center;
  font-size: 18px; font-weight: 800; color: #fff;
}}
.guide-step-title {{ font-size: 20px; font-weight: 700; }}
.guide-step-tag {{
  display: inline-block; padding: 3px 10px; border-radius: 100px;
  font-size: 10px; font-weight: 700; letter-spacing: 0.06em; margin-left: 8px;
}}
.guide-img-wrap {{
  position: relative; width: 85%; margin: 0 auto 20px;
  border-radius: var(--r-lg); overflow: hidden;
  border: 1px solid var(--border);
}}
.guide-img-wrap img {{ width: 100%; display: block; }}
.guide-hotspot {{
  position: absolute; width: 24px; height: 24px; border-radius: 50%;
  background: rgba(124,106,239,0.85); border: 2px solid #fff;
  transform: translate(-50%, -50%); display: flex;
  align-items: center; justify-content: center;
  font-size: 10px; font-weight: 800; color: #fff;
}}
.guide-desc-text {{
  font-size: 14px; color: var(--t2); line-height: 1.7; margin-bottom: 12px;
}}
.guide-desc-text strong {{ color: var(--t1); }}
.guide-narration {{
  border-left: 3px solid var(--accent); padding: 8px 16px;
  margin-left: 0; font-style: italic; font-size: 14px; color: var(--t3);
}}
.guide-footer {{
  padding: 40px 0; text-align: center; font-size: 12px; color: var(--t3);
}}

/* ═══════════════════════════════════════════════════════════════
   FULLSCREEN ZOOM MODAL
   ═══════════════════════════════════════════════════════════════ */
.zoom-modal {{
  display: none; position: fixed; inset: 0; z-index: 2000;
  background: rgba(6,6,10,0.95); backdrop-filter: blur(8px);
  align-items: center; justify-content: center; cursor: zoom-out;
}}
.zoom-modal.active {{ display: flex; }}
.zoom-modal img {{
  max-width: 95vw; max-height: 90vh; border-radius: var(--r-lg);
  box-shadow: var(--shadow-lg); animation: fadeUp 0.3s ease;
}}
.zoom-modal-close {{
  position: absolute; top: 20px; right: 20px; width: 40px; height: 40px;
  border-radius: 50%; background: var(--s3); border: 1px solid var(--border);
  color: var(--t1); font-size: 18px; cursor: pointer;
  display: flex; align-items: center; justify-content: center;
}}

/* ═══════════════════════════════════════════════════════════════
   ANIMATIONS
   ═══════════════════════════════════════════════════════════════ */
@keyframes fadeUp {{
  from {{ opacity: 0; transform: translateY(16px); }}
  to {{ opacity: 1; transform: translateY(0); }}
}}
@keyframes hotspotPulse {{
  0%, 100% {{ transform: translate(-50%,-50%) scale(1); }}
  50% {{ transform: translate(-50%,-50%) scale(1.15); }}
}}
@keyframes hotspotRipple {{
  0% {{ transform: scale(0.8); opacity: 1; }}
  100% {{ transform: scale(2.2); opacity: 0; }}
}}
@keyframes calloutIn {{
  from {{ opacity: 0; transform: translateY(8px); }}
  to {{ opacity: 1; transform: translateY(0); }}
}}
@keyframes bounceIn {{
  0% {{ transform: scale(0); }}
  60% {{ transform: scale(1.15); }}
  100% {{ transform: scale(1); }}
}}
@keyframes confettiFall {{
  0% {{ transform: translateY(0) rotate(0deg); opacity: 1; }}
  100% {{ transform: translateY(100vh) rotate(720deg); opacity: 0; }}
}}

/* ═══════════════════════════════════════════════════════════════
   RESPONSIVE
   ═══════════════════════════════════════════════════════════════ */
@media (max-width: 900px) {{
  .intro-screen {{ flex-direction: column; padding: 40px 24px; }}
  .intro-left, .intro-right {{ flex: unset; width: 100%; }}
  .intro-right {{ padding-left: 0; text-align: center; }}
  .cards-grid {{ grid-template-columns: 1fr; }}
  .guide-img-wrap {{ width: 100%; }}
}}
@media (max-width: 640px) {{
  .intro-title {{ font-size: 26px; }}
  .step-num-box {{ width: 40px; height: 40px; font-size: 17px; }}
  .step-title {{ font-size: 18px; }}
  .step-content {{ padding: 20px 16px 120px; }}
  .step-topbar {{ padding: 10px 16px; }}
  .bottom-nav {{ padding: 10px 16px; }}
  .nav-center {{ display: none; }}
  .nav-btn {{ padding: 8px 16px; font-size: 13px; }}
  .audio-bar {{ padding: 8px 12px; }}
  .guide-step-num {{ width: 38px; height: 38px; font-size: 15px; }}
}}

/* ═══════════════════════════════════════════════════════════════
   PRINT
   ═══════════════════════════════════════════════════════════════ */
@media print {{
  .mode-toggle-bar, .browser-chrome, .bottom-nav, .audio-bar,
  .zoom-btn, .hotspot, .hotspot-label, .callout-overlay {{ display: none !important; }}
  body {{ background: #fff; color: #000; }}
  .guide-mode {{ padding-top: 0; }}
  .guide-step {{ break-inside: avoid; }}
}}
</style>
</head>
<body>

<!-- ═══════════ MODE TOGGLE BAR ═══════════ -->
<div class="mode-toggle-bar">
  <button class="mode-pill active" id="modeInteractive" onclick="setMode('interactive')">Interactive Tour</button>
  <button class="mode-pill inactive" id="modeGuide" onclick="setMode('guide')">Step-by-Step Guide</button>
</div>

<!-- ═══════════ INTERACTIVE TOUR ═══════════ -->
<div class="main-wrapper" id="interactiveMode">
  <div class="browser-chrome">
    <div class="chrome-bar">
      <div class="chrome-dots">
        <div class="chrome-dot r"></div>
        <div class="chrome-dot y"></div>
        <div class="chrome-dot g"></div>
      </div>
      <div class="chrome-title">
        <span class="chrome-lock">&#x1f512;</span>
        cleanshot-walkthrough.app
      </div>
    </div>
  </div>
  <div class="tour-wrapper">

    <!-- INTRO -->
    <div class="intro-screen" id="introScreen">
      <div class="intro-left">
        <div class="intro-bg-glow"></div>
        <div class="intro-img-wrap">
          <img src="{intro_img}" alt="Final Screenshot">
        </div>
      </div>
      <div class="intro-right">
        <div class="intro-tag">INTERACTIVE WALKTHROUGH</div>
        <h1 class="intro-title">CleanShot X Annotation Workflow</h1>
        <p class="intro-author">By Vinay &middot; Layerpath.com</p>
        <p class="intro-desc">Discover the 5 key annotation features used daily &mdash; from blur and spotlight to numbered counters and background beautification.</p>
        <div class="intro-badge">
          <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 2v20M2 12h20"/></svg>
          14 steps &middot; ~2 min read
        </div>
        <br>
        <button class="start-btn" onclick="startTour()">
          Start Tour
          <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12h14M12 5l7 7-7 7"/></svg>
        </button>
        <span class="intro-hint">Arrow keys to navigate &middot; Space for autoplay &middot; Esc to close zoom</span>
      </div>
    </div>

    <!-- STEP VIEW -->
    <div class="step-view" id="stepView">
      <div class="step-topbar">
        <div style="display:flex;align-items:center;gap:16px;">
          <div class="progress-dots" id="progressDots"></div>
          <span class="step-counter" id="stepCounter">Step 1 of 14</span>
        </div>
        <div class="topbar-controls">
          <div class="autoplay-wrap">
            <span>Autoplay</span>
            <button class="autoplay-toggle" id="autoplayToggle" onclick="toggleAutoplay()"></button>
          </div>
          <button class="mute-btn" id="muteBtn" onclick="toggleMute()" title="Mute/Unmute">
            <span id="muteIcon">&#x1f50a;</span>
          </button>
        </div>
      </div>
      <div class="step-content" id="stepContent"></div>
    </div>

    <!-- OUTRO -->
    <div class="outro-screen" id="outroScreen">
      <div class="confetti-container" id="confettiContainer"></div>
      <div class="outro-check">
        <svg viewBox="0 0 24 24" fill="none" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6L9 17l-5-5"/></svg>
      </div>
      <h2 class="outro-title">Walkthrough Complete</h2>
      <p class="outro-sub">You explored 5 powerful features:</p>
      <div class="feature-list">
        <div class="feature-chip"><span class="ficon" style="background:#ef6b6b;">B</span> Blur / Redact</div>
        <div class="feature-chip"><span class="ficon" style="background:#f5b342;">S</span> Spotlight</div>
        <div class="feature-chip"><span class="ficon" style="background:#3dd9a0;">C</span> Counters</div>
        <div class="feature-chip"><span class="ficon" style="background:#f5b342;">A</span> Arrows</div>
        <div class="feature-chip"><span class="ficon" style="background:#9b8af5;">B</span> Background Beautify</div>
      </div>
      <div class="outro-buttons">
        <button class="nav-btn" onclick="restartTour()">Restart Tour</button>
        <button class="nav-btn primary" onclick="setMode('guide')">Switch to Guide &rarr;</button>
      </div>
    </div>
  </div>
</div>

<!-- ═══════════ STEP-BY-STEP GUIDE ═══════════ -->
<div class="guide-mode" id="guideMode"></div>

<!-- ═══════════ ZOOM MODAL ═══════════ -->
<div class="zoom-modal" id="zoomModal" onclick="closeZoom()">
  <button class="zoom-modal-close" onclick="closeZoom()">&times;</button>
  <img id="zoomModalImg" src="" alt="Zoomed screenshot">
</div>

<!-- ═══════════ BOTTOM NAV (interactive only) ═══════════ -->
<div class="bottom-nav" id="bottomNav" style="display:none;">
  <button class="nav-btn" id="prevBtn" onclick="prevStep()">
    <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M19 12H5M12 19l-7-7 7-7"/></svg>
    Previous
  </button>
  <div class="nav-center">
    <div id="navTitle"></div>
    <div class="nav-keys"><kbd>&larr;</kbd> <kbd>&rarr;</kbd> navigate &middot; <kbd>Space</kbd> autoplay</div>
  </div>
  <button class="nav-btn primary" id="nextBtn" onclick="nextStep()">
    Next
    <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12h14M12 5l7 7-7 7"/></svg>
  </button>
</div>

<script>
/* ═══════════════════════════════════════════════════════════════
   DATA
   ═══════════════════════════════════════════════════════════════ */
{build_steps_js()}

/* ═══════════════════════════════════════════════════════════════
   STATE
   ═══════════════════════════════════════════════════════════════ */
let currentStep = 0;
let currentMode = 'interactive'; // 'interactive' | 'guide'
let currentScreen = 'intro';     // 'intro' | 'step' | 'outro'
let isAutoplay = false;
let isMuted = false;
let audioElements = {{}};
let currentAudio = null;
let autoAdvanceTimer = null;
let zoomResetTimers = [];
let userHasInteracted = false;

/* ═══════════════════════════════════════════════════════════════
   MODE SWITCHING
   ═══════════════════════════════════════════════════════════════ */
function setMode(mode) {{
  currentMode = mode;
  document.getElementById('modeInteractive').className = mode === 'interactive' ? 'mode-pill active' : 'mode-pill inactive';
  document.getElementById('modeGuide').className = mode === 'guide' ? 'mode-pill active' : 'mode-pill inactive';
  document.getElementById('interactiveMode').style.display = mode === 'interactive' ? 'block' : 'none';
  document.getElementById('guideMode').style.display = mode === 'guide' ? 'block' : 'none';
  document.getElementById('bottomNav').style.display = (mode === 'interactive' && currentScreen === 'step') ? 'flex' : 'none';
  if (mode === 'guide') buildGuide();
  stopAllAudio();
}}

/* ═══════════════════════════════════════════════════════════════
   INTERACTIVE TOUR
   ═══════════════════════════════════════════════════════════════ */
function startTour() {{
  userHasInteracted = true;
  currentScreen = 'step';
  currentStep = 0;
  document.getElementById('introScreen').style.display = 'none';
  document.getElementById('stepView').classList.add('active');
  document.getElementById('outroScreen').classList.remove('active');
  document.getElementById('bottomNav').style.display = 'flex';
  buildProgressDots();
  renderStep();
}}

function buildProgressDots() {{
  const c = document.getElementById('progressDots');
  c.innerHTML = '';
  for (let i = 0; i < STEPS.length; i++) {{
    const d = document.createElement('div');
    d.className = 'pdot';
    d.onclick = () => goToStep(i);
    d.title = STEPS[i].title;
    c.appendChild(d);
  }}
  updateProgressDots();
}}

function updateProgressDots() {{
  const dots = document.querySelectorAll('.pdot');
  dots.forEach((d, i) => {{
    d.className = 'pdot';
    if (i < currentStep) d.classList.add('completed');
    if (i === currentStep) d.classList.add('active');
  }});
  document.getElementById('stepCounter').textContent = `Step ${{currentStep + 1}} of ${{STEPS.length}}`;
}}

function renderStep() {{
  const s = STEPS[currentStep];
  const sc = document.getElementById('stepContent');
  clearTimers();

  // Calculate zoom transform
  let zoomStyle = '';
  let hasZoom = false;
  if (s.zoom) {{
    hasZoom = true;
    const cx = s.zoom.x + s.zoom.w / 2;
    const cy = s.zoom.y + s.zoom.h / 2;
    const tx = -(cx - 50);
    const ty = -(cy - 50);
    zoomStyle = `transform: scale(2.5) translate(${{tx}}%, ${{ty}}%);`;
  }}

  // Hotspots HTML
  let hotspotsHtml = '';
  s.hotspots.forEach((h, i) => {{
    hotspotsHtml += `<div class="hotspot" style="left:${{h.x}}%;top:${{h.y}}%"></div>`;
    const ly = h.y < 15 ? h.y + 4 : h.y - 5;
    hotspotsHtml += `<div class="hotspot-label" style="left:${{h.x}}%;top:${{ly}}%">${{h.label}}</div>`;
  }});

  // Callout
  let calloutHtml = '';
  if (s.callout) {{
    const cl = s.callout.x < 50 ? `left:${{s.callout.x}}%` : `right:${{100 - s.callout.x}}%`;
    const ct = s.callout.y < 50 ? `top:${{s.callout.y}}%` : `bottom:${{100 - s.callout.y}}%`;
    calloutHtml = `<div class="callout-overlay" style="${{cl}};${{ct}}">${{s.callout.text}}</div>`;
  }}

  sc.innerHTML = `
    <div class="step-header" style="animation:fadeUp 0.4s ease">
      <div class="step-num-box">${{currentStep + 1}}</div>
      <div class="step-header-text">
        <div class="step-title-row">
          <span class="step-title">${{s.title}}</span>
          <span class="step-tag" style="background:${{s.tagColor}}22;color:${{s.tagColor}};border:1px solid ${{s.tagColor}}44">${{s.tag}}</span>
        </div>
        <div class="step-subtitle">${{s.subtitle}}</div>
      </div>
    </div>
    <div class="screenshot-wrap" id="screenshotWrap" onclick="openZoom('${{s.imgSrc}}')">
      <img id="stepImg" src="${{s.imgSrc}}" alt="${{s.title}}" class="${{hasZoom ? 'zoomed-in' : ''}}" style="${{zoomStyle}}">
      ${{hotspotsHtml}}
      ${{calloutHtml}}
      <button class="zoom-btn" onclick="event.stopPropagation();openZoom('${{s.imgSrc}}')" title="Zoom">&#x1f50d;</button>
    </div>
    <div class="cards-grid" style="animation:fadeUp 0.4s ease 0.1s both">
      <div class="info-card">
        <div class="info-card-title">
          <svg viewBox="0 0 24 24" fill="none" stroke="var(--accent-light)" stroke-width="2"><circle cx="12" cy="12" r="10"/><path d="M12 16v-4M12 8h.01"/></svg>
          What's Happening
        </div>
        <p>${{s.desc}}</p>
      </div>
      <div class="info-card narration-card">
        <div class="info-card-title">
          <svg viewBox="0 0 24 24" fill="none" stroke="var(--accent-light)" stroke-width="2"><path d="M12 1a3 3 0 00-3 3v8a3 3 0 006 0V4a3 3 0 00-3-3z"/><path d="M19 10v2a7 7 0 01-14 0v-2"/><line x1="12" y1="19" x2="12" y2="23"/></svg>
          Vinay's Narration
        </div>
        <p>"${{s.narration}}"</p>
      </div>
    </div>
    <div class="audio-bar" style="animation:fadeUp 0.4s ease 0.15s both">
      <button class="audio-play-btn" id="audioPlayBtn" onclick="toggleAudioPlay()">
        <svg id="playIcon" width="16" height="16" viewBox="0 0 24 24" fill="white"><polygon points="5,3 19,12 5,21"/></svg>
      </button>
      <div class="audio-progress-wrap" id="audioProgressWrap" onclick="seekAudio(event)">
        <div class="audio-progress-fill" id="audioProgressFill"></div>
      </div>
      <span class="audio-time" id="audioTime">0:00 / 0:00</span>
    </div>
    <div class="audio-timestamp">Step ${{currentStep + 1}} of ${{STEPS.length}}</div>
  `;

  // Zoom animation
  if (hasZoom) {{
    const img = document.getElementById('stepImg');
    const t = setTimeout(() => {{
      img.classList.remove('zoomed-in');
      img.style.transform = 'scale(1) translate(0, 0)';
    }}, 2000);
    zoomResetTimers.push(t);
  }}

  updateProgressDots();
  updateNavButtons();
  playStepAudio();
  window.scrollTo({{ top: 0, behavior: 'smooth' }});
}}

/* ═══════════════════════════════════════════════════════════════
   AUDIO
   ═══════════════════════════════════════════════════════════════ */
function getAudioForStep(idx) {{
  const key = STEPS[idx].audioKey;
  if (!audioElements[key]) {{
    const a = new Audio(STEPS[idx].audSrc);
    a.preload = 'auto';
    audioElements[key] = a;
  }}
  return audioElements[key];
}}

function playStepAudio() {{
  stopAllAudio();
  const a = getAudioForStep(currentStep);
  currentAudio = a;
  a.muted = isMuted;
  a.currentTime = 0;

  a.ontimeupdate = () => {{
    const fill = document.getElementById('audioProgressFill');
    const timeEl = document.getElementById('audioTime');
    if (fill && a.duration) {{
      fill.style.width = (a.currentTime / a.duration * 100) + '%';
    }}
    if (timeEl) {{
      timeEl.textContent = formatTime(a.currentTime) + ' / ' + formatTime(a.duration || 0);
    }}
  }};
  a.onended = () => {{
    updatePlayIcon(false);
    if (isAutoplay && currentStep < STEPS.length - 1) {{
      autoAdvanceTimer = setTimeout(() => nextStep(), 1000);
    }}
  }};

  if (userHasInteracted) {{
    a.play().catch(() => {{}});
    updatePlayIcon(true);
  }}
}}

function stopAllAudio() {{
  if (currentAudio) {{
    currentAudio.pause();
    currentAudio.ontimeupdate = null;
    currentAudio.onended = null;
  }}
  if (autoAdvanceTimer) {{ clearTimeout(autoAdvanceTimer); autoAdvanceTimer = null; }}
}}

function toggleAudioPlay() {{
  if (!currentAudio) return;
  userHasInteracted = true;
  if (currentAudio.paused) {{
    currentAudio.play().catch(() => {{}});
    updatePlayIcon(true);
  }} else {{
    currentAudio.pause();
    updatePlayIcon(false);
  }}
}}

function updatePlayIcon(playing) {{
  const el = document.getElementById('playIcon');
  if (!el) return;
  if (playing) {{
    el.innerHTML = '<rect x="6" y="4" width="4" height="16" fill="white"/><rect x="14" y="4" width="4" height="16" fill="white"/>';
  }} else {{
    el.innerHTML = '<polygon points="5,3 19,12 5,21" fill="white"/>';
  }}
}}

function seekAudio(e) {{
  if (!currentAudio || !currentAudio.duration) return;
  const rect = e.currentTarget.getBoundingClientRect();
  const pct = (e.clientX - rect.left) / rect.width;
  currentAudio.currentTime = pct * currentAudio.duration;
}}

function toggleMute() {{
  isMuted = !isMuted;
  if (currentAudio) currentAudio.muted = isMuted;
  document.getElementById('muteIcon').innerHTML = isMuted ? '&#x1f507;' : '&#x1f50a;';
}}

function toggleAutoplay() {{
  isAutoplay = !isAutoplay;
  const btn = document.getElementById('autoplayToggle');
  btn.classList.toggle('on', isAutoplay);
}}

function formatTime(s) {{
  if (!s || isNaN(s)) return '0:00';
  const m = Math.floor(s / 60);
  const sec = Math.floor(s % 60);
  return m + ':' + (sec < 10 ? '0' : '') + sec;
}}

/* ═══════════════════════════════════════════════════════════════
   NAVIGATION
   ═══════════════════════════════════════════════════════════════ */
function nextStep() {{
  if (currentScreen === 'outro') return;
  if (currentStep < STEPS.length - 1) {{
    currentStep++;
    renderStep();
  }} else {{
    showOutro();
  }}
}}

function prevStep() {{
  if (currentScreen === 'outro') {{
    currentScreen = 'step';
    document.getElementById('outroScreen').classList.remove('active');
    document.getElementById('stepView').classList.add('active');
    document.getElementById('bottomNav').style.display = 'flex';
    renderStep();
    return;
  }}
  if (currentStep > 0) {{
    currentStep--;
    renderStep();
  }}
}}

function goToStep(i) {{
  if (currentScreen === 'intro') {{
    startTour();
  }}
  if (currentScreen === 'outro') {{
    currentScreen = 'step';
    document.getElementById('outroScreen').classList.remove('active');
    document.getElementById('stepView').classList.add('active');
    document.getElementById('bottomNav').style.display = 'flex';
  }}
  currentStep = i;
  renderStep();
}}

function updateNavButtons() {{
  document.getElementById('prevBtn').disabled = (currentStep === 0);
  document.getElementById('navTitle').textContent = STEPS[currentStep].title;
  document.getElementById('nextBtn').textContent = currentStep === STEPS.length - 1 ? 'Finish' : 'Next';
  // Re-add arrow icon
  const nextBtn = document.getElementById('nextBtn');
  if (currentStep === STEPS.length - 1) {{
    nextBtn.innerHTML = 'Finish <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12h14M12 5l7 7-7 7"/></svg>';
  }} else {{
    nextBtn.innerHTML = 'Next <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12h14M12 5l7 7-7 7"/></svg>';
  }}
}}

function restartTour() {{
  currentStep = 0;
  currentScreen = 'step';
  document.getElementById('outroScreen').classList.remove('active');
  document.getElementById('stepView').classList.add('active');
  document.getElementById('bottomNav').style.display = 'flex';
  buildProgressDots();
  renderStep();
}}

/* ═══════════════════════════════════════════════════════════════
   OUTRO
   ═══════════════════════════════════════════════════════════════ */
function showOutro() {{
  stopAllAudio();
  currentScreen = 'outro';
  document.getElementById('stepView').classList.remove('active');
  document.getElementById('outroScreen').classList.add('active');
  document.getElementById('bottomNav').style.display = 'none';
  spawnConfetti();
}}

function spawnConfetti() {{
  const container = document.getElementById('confettiContainer');
  container.innerHTML = '';
  const colors = ['#7c6aef','#ef6b6b','#f5b342','#3dd9a0','#5b9bf5','#9b8af5'];
  for (let i = 0; i < 24; i++) {{
    const c = document.createElement('div');
    c.className = 'confetti';
    c.style.left = Math.random() * 100 + '%';
    c.style.background = colors[i % colors.length];
    c.style.animationDuration = (2.5 + Math.random() * 2) + 's';
    c.style.animationDelay = (Math.random() * 1.5) + 's';
    c.style.width = (6 + Math.random() * 6) + 'px';
    c.style.height = (6 + Math.random() * 6) + 'px';
    container.appendChild(c);
  }}
}}

/* ═══════════════════════════════════════════════════════════════
   ZOOM MODAL
   ═══════════════════════════════════════════════════════════════ */
function openZoom(src) {{
  document.getElementById('zoomModalImg').src = src;
  document.getElementById('zoomModal').classList.add('active');
  document.body.style.overflow = 'hidden';
}}
function closeZoom() {{
  document.getElementById('zoomModal').classList.remove('active');
  document.body.style.overflow = '';
}}

/* ═══════════════════════════════════════════════════════════════
   GUIDE MODE
   ═══════════════════════════════════════════════════════════════ */
function buildGuide() {{
  const g = document.getElementById('guideMode');
  if (g.dataset.built === '1') return;
  g.dataset.built = '1';

  let html = `
    <div class="guide-header">
      <h1>CleanShot X Annotation Workflow</h1>
      <div class="guide-meta">
        <span>By Vinay &middot; Layerpath.com</span>
        <span>Feb 27, 2026</span>
        <span>14 steps</span>
      </div>
      <p class="guide-desc">A step-by-step guide to the 5 key annotation features in CleanShot X &mdash; blur, spotlight, counters, arrows, and background beautification.</p>
    </div>
  `;

  STEPS.forEach((s, i) => {{
    let hotspotMarkers = '';
    s.hotspots.forEach((h, hi) => {{
      hotspotMarkers += `<div class="guide-hotspot" style="left:${{h.x}}%;top:${{h.y}}%">${{hi+1}}</div>`;
    }});

    html += `
      <div class="guide-step" style="border-left:3px solid ${{s.tagColor}}; padding-left:24px;">
        <div class="guide-step-header">
          <div class="guide-step-num">${{i + 1}}</div>
          <div>
            <span class="guide-step-title">${{s.title}}</span>
            <span class="guide-step-tag" style="background:${{s.tagColor}}22;color:${{s.tagColor}};border:1px solid ${{s.tagColor}}44">${{s.tag}}</span>
          </div>
        </div>
        <div class="guide-img-wrap">
          <img src="${{s.imgSrc}}" alt="${{s.title}}" loading="lazy">
          ${{hotspotMarkers}}
        </div>
        <p class="guide-desc-text">${{s.desc}}</p>
        <blockquote class="guide-narration">"${{s.narration}}"</blockquote>
      </div>
    `;
  }});

  html += `<div class="guide-footer">Generated from screen recording &middot; Feb 27, 2026</div>`;
  g.innerHTML = html;
}}

/* ═══════════════════════════════════════════════════════════════
   TIMERS
   ═══════════════════════════════════════════════════════════════ */
function clearTimers() {{
  zoomResetTimers.forEach(t => clearTimeout(t));
  zoomResetTimers = [];
  if (autoAdvanceTimer) {{ clearTimeout(autoAdvanceTimer); autoAdvanceTimer = null; }}
}}

/* ═══════════════════════════════════════════════════════════════
   KEYBOARD
   ═══════════════════════════════════════════════════════════════ */
document.addEventListener('keydown', (e) => {{
  if (currentMode !== 'interactive') return;
  if (e.key === 'ArrowRight' || e.key === 'ArrowDown') {{
    e.preventDefault();
    if (currentScreen === 'intro') startTour();
    else nextStep();
  }}
  if (e.key === 'ArrowLeft' || e.key === 'ArrowUp') {{
    e.preventDefault();
    prevStep();
  }}
  if (e.key === ' ') {{
    e.preventDefault();
    if (currentScreen === 'intro') startTour();
    else toggleAutoplay();
  }}
  if (e.key === 'Escape') closeZoom();
}});

/* ═══════════════════════════════════════════════════════════════
   INIT
   ═══════════════════════════════════════════════════════════════ */
document.addEventListener('click', () => {{ userHasInteracted = true; }}, {{ once: true }});
</script>
</body>
</html>'''

# Write output
output_path = os.path.join(BASE, "walkthrough-v4.html")
with open(output_path, "w", encoding="utf-8") as f:
    f.write(html)

sz = os.path.getsize(output_path)
print(f"Generated: {output_path}")
print(f"File size: {sz:,} bytes ({sz/1024/1024:.1f} MB)")
