#!/usr/bin/env python3
"""
Generate walkthrough-v3.html — Ultimate interactive walkthrough with embedded base64 assets.
Reads frames_data.json (base64 JPEG images) and audio_data.json (base64 MP3 audio),
then builds a single self-contained HTML file.
"""

import json
import os

SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))

# ---------------------------------------------------------------------------
# Load base64 data
# ---------------------------------------------------------------------------
with open(os.path.join(SCRIPT_DIR, "frames_data.json"), "r") as f:
    frames_data = json.load(f)

with open(os.path.join(SCRIPT_DIR, "audio_data.json"), "r") as f:
    audio_data = json.load(f)

# ---------------------------------------------------------------------------
# Step definitions
# ---------------------------------------------------------------------------
STEPS = [
    {
        "id": "0010", "audioKey": "01", "tag": "SETUP", "tagColor": "#3b82f6",
        "title": "Layerpath Dashboard", "subtitle": "Starting Point",
        "desc": "The Layerpath dashboard at app.layerpath.com showing the Latest demos grid. Vinay\u2019s workspace (Tier 3) with sidebar navigation.",
        "narration": "Okay, so I am recording a video of how I use CleanShot X. Imagine this is my product Layerpath.",
        "callout": {"text": "Keyboard shortcut captures the full screen", "x": 50, "y": 15},
        "hotspots": [],
        "zoom": None,
    },
    {
        "id": "0030", "audioKey": "02", "tag": "TOOLS", "tagColor": "#3b82f6",
        "title": "CleanShot X Toolbar", "subtitle": "Annotation Mode",
        "desc": "CleanShot X\u2019s annotation toolbar appears at the top with all tools: Cursor, Crop, Shapes, Text, Blur, Spotlight, Counter, Pen, Background.",
        "narration": "Here are the features I want \u2014 I use four features which are super useful for me.",
        "callout": {"text": "All annotation tools accessible from this toolbar", "x": 50, "y": 12},
        "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": 10, "y": 0, "w": 80, "h": 8},
    },
    {
        "id": "0040", "audioKey": "03", "tag": "BLUR", "tagColor": "#ef4444",
        "title": "Feature 1: Blur / Redact", "subtitle": "Highest Priority",
        "desc": "The blur tool is selected. A rectangular selection with blue corner 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 \u2014 look how easy it is.",
        "callout": {"text": "Drag to select \u2192 blur applies instantly", "x": 30, "y": 75},
        "hotspots": [{"x": 30, "y": 65, "label": "Blur region"}],
        "zoom": {"x": 15, "y": 50, "w": 45, "h": 45},
    },
    {
        "id": "0050", "audioKey": "04", "tag": "BLUR", "tagColor": "#ef4444",
        "title": "Adjustable Blur Intensity", "subtitle": "Fine Control",
        "desc": "Blur intensity adjustable via a slider. Ranges from subtle blur to heavy pixelation. Multiple blur regions can be stacked.",
        "narration": "I can reduce the blurring effect depending on how little or how more I want.",
        "callout": {"text": "Slider controls blur intensity", "x": 60, "y": 12},
        "hotspots": [{"x": 59, "y": 4, "label": "Intensity slider"}],
        "zoom": {"x": 40, "y": 0, "w": 40, "h": 10},
    },
    {
        "id": "0070", "audioKey": "05", "tag": "SPOTLIGHT", "tagColor": "#f59e0b",
        "title": "Feature 2: Spotlight", "subtitle": "High Priority",
        "desc": "The spotlight tool darkens the entire screenshot except the highlighted region. Creates an instant visual focal point.",
        "narration": "I want to bring their attention. I want to highlight \u2014 this is my workspace.",
        "callout": {"text": "Everything dims except the focus area", "x": 65, "y": 50},
        "hotspots": [
            {"x": 22, "y": 20, "label": "Spotlight area"},
            {"x": 60, "y": 4, "label": "Opacity control"},
        ],
        "zoom": None,
    },
    {
        "id": "0090", "audioKey": "06", "tag": "SPOTLIGHT", "tagColor": "#f59e0b",
        "title": "Opacity Control", "subtitle": "Dimming Effect",
        "desc": "Spotlight darkening with adjustable opacity. Multiple spotlights can coexist.",
        "narration": "I have the option of darkening it \u2014 this is super useful.",
        "callout": {"text": "Adjustable dimming \u2014 subtle to heavy", "x": 50, "y": 30},
        "hotspots": [{"x": 60, "y": 4, "label": "Opacity"}],
        "zoom": None,
    },
    {
        "id": "0110", "audioKey": "07", "tag": "COUNTER", "tagColor": "#22c55e",
        "title": "Feature 3: Counters", "subtitle": "High Priority",
        "desc": "Counter tool places numbered circular markers that auto-increment. Red color, size 20.",
        "narration": "This is my brand color, but I\u2019ll select red. You can see numbering, lettering.",
        "callout": {"text": "Tap to place \u2192 numbers auto-increment", "x": 28, "y": 17},
        "hotspots": [
            {"x": 28, "y": 11, "label": "Counter \u2460"},
            {"x": 45, "y": 4, "label": "Counter tool"},
        ],
        "zoom": {"x": 10, "y": 0, "w": 50, "h": 25},
    },
    {
        "id": "0130", "audioKey": "08", "tag": "COUNTER", "tagColor": "#22c55e",
        "title": "Building the Flow", "subtitle": "Steps 1\u21924",
        "desc": "Four counters: \u2460 Workspace \u2192 \u2461 Dashboard \u2192 \u2462 Demo card \u2192 \u2463 My demos.",
        "narration": "Start with your workspace. Then dashboard. Then select this project.",
        "callout": {"text": "Single tap \u2014 no dragging needed", "x": 50, "y": 55},
        "hotspots": [
            {"x": 17, "y": 18, "label": "\u2460 Workspace"},
            {"x": 16, "y": 27, "label": "\u2461 Dashboard"},
            {"x": 39, "y": 27, "label": "\u2462 Demo"},
            {"x": 12, "y": 33, "label": "\u2463 My demos"},
        ],
        "zoom": None,
    },
    {
        "id": "0140", "audioKey": "09", "tag": "COUNTER", "tagColor": "#22c55e",
        "title": "Complete 5-Step Guide", "subtitle": "All Steps Placed",
        "desc": "All 5 markers: \u2460 Workspace \u2192 \u2461 Dashboard \u2192 \u2462 Demo \u2192 \u2463 My demos \u2192 \u2464 Second row.",
        "narration": "These are my top three tools.",
        "callout": {"text": "5 steps = complete guided flow", "x": 60, "y": 78},
        "hotspots": [
            {"x": 17, "y": 18, "label": "\u2460 Workspace"},
            {"x": 16, "y": 27, "label": "\u2461 Dashboard"},
            {"x": 39, "y": 27, "label": "\u2462 Demo"},
            {"x": 12, "y": 33, "label": "\u2463 My demos"},
            {"x": 39, "y": 60, "label": "\u2464 All demos"},
        ],
        "zoom": None,
    },
    {
        "id": "0190", "audioKey": "10", "tag": "ARROW", "tagColor": "#f59e0b",
        "title": "Feature 4: Arrows", "subtitle": "Occasional Use",
        "desc": "A large red arrow directing attention. Used occasionally to emphasize flow.",
        "narration": "Sometimes I use arrows \u2014 make sure you\u2019re looking at this specific thing.",
        "callout": {"text": "Drag start to end \u2192 arrow appears", "x": 60, "y": 35},
        "hotspots": [{"x": 55, "y": 40, "label": "Arrow"}],
        "zoom": None,
    },
    {
        "id": "0210", "audioKey": "11", "tag": "BEAUTIFY", "tagColor": "#a855f7",
        "title": "Feature 5: Background", "subtitle": "Export Prep",
        "desc": "Background panel: Gradients, Wallpapers, Blurred, Colors. Controls for Padding, Inset, Shadow, Corners, Ratio.",
        "narration": "Add background \u2014 beautiful! I select the one closest to my branding.",
        "callout": {"text": "Gradient + padding + inset = polished", "x": 13, "y": 25},
        "hotspots": [
            {"x": 13, "y": 22, "label": "Gradient"},
            {"x": 7, "y": 73, "label": "Padding"},
            {"x": 7, "y": 78, "label": "Inset"},
            {"x": 14, "y": 83, "label": "Corners"},
        ],
        "zoom": {"x": 0, "y": 10, "w": 20, "h": 60},
    },
    {
        "id": "0240", "audioKey": "12", "tag": "BEAUTIFY", "tagColor": "#a855f7",
        "title": "Fine-Tuning", "subtitle": "LinkedIn Optimized",
        "desc": "Corners: 3, Auto-balance \u2713, Ratio: 4:3 for LinkedIn.",
        "narration": "Corners to 3 or 4. For LinkedIn, I select 4:3.",
        "callout": {"text": "4:3 = LinkedIn optimized", "x": 14, "y": 92},
        "hotspots": [
            {"x": 14, "y": 90, "label": "4:3 Ratio"},
            {"x": 11, "y": 80, "label": "Auto-balance"},
        ],
        "zoom": {"x": 0, "y": 70, "w": 20, "h": 30},
    },
    {
        "id": "0260", "audioKey": "13", "tag": "EXPORT", "tagColor": "#22c55e",
        "title": "Final Result", "subtitle": "Ready to Share",
        "desc": "Final output: blur, spotlight, counters, arrow \u2014 all in gradient background with rounded corners.",
        "narration": "I click done. It saves to my desktop. That\u2019s my workflow.",
        "callout": {"text": "One click \u2192 saved to desktop", "x": 92, "y": 4},
        "hotspots": [{"x": 92, "y": 4, "label": "Done"}],
        "zoom": None,
    },
    {
        "id": "0270", "audioKey": "14", "tag": "COMPLETE", "tagColor": "#22c55e",
        "title": "Workflow Complete", "subtitle": "Back to Dashboard",
        "desc": "CleanShot X exits. Polished screenshot saved. Back to Layerpath dashboard.",
        "narration": "Those are the top features I use. Thank you.",
        "callout": {"text": "Screenshot saved \u2713", "x": 50, "y": 50},
        "hotspots": [],
        "zoom": None,
    },
]

# ---------------------------------------------------------------------------
# Build embedded data JS
# ---------------------------------------------------------------------------

def build_steps_js():
    """Build the JavaScript array literal for step data with embedded base64."""
    parts = []
    for i, s in enumerate(STEPS):
        frame_b64 = frames_data.get(s["id"], "")
        audio_b64 = audio_data.get(s["audioKey"], "")
        hotspots_js = json.dumps(s["hotspots"])
        callout_js = json.dumps(s["callout"])
        zoom_js = json.dumps(s["zoom"]) if s["zoom"] else "null"
        parts.append(f"""{{
      idx: {i},
      id: "{s['id']}",
      audioKey: "{s['audioKey']}",
      tag: "{s['tag']}",
      tagColor: "{s['tagColor']}",
      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},
      imgSrc: "data:image/jpeg;base64,{frame_b64}",
      audioSrc: "data:audio/mpeg;base64,{audio_b64}"
    }}""")
    return "[\n    " + ",\n    ".join(parts) + "\n  ]"


# ---------------------------------------------------------------------------
# HTML template
# ---------------------------------------------------------------------------

HTML_TEMPLATE = r"""<!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 rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<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{font-family:'Inter',system-ui,-apple-system,sans-serif;font-size:16px;-webkit-font-smoothing:antialiased;background:#050507;color:#e8e8ed;scroll-behavior:smooth}
body{min-height:100vh;background:#050507;overflow:hidden}
button{font-family:inherit;cursor:pointer;border:none;background:none;color:inherit}
img{display:block;max-width:100%}

/* ===================== BROWSER CHROME ===================== */
.browser-chrome{position:fixed;top:0;left:0;right:0;z-index:1000;background:#1a1a1e;border-bottom:1px solid #2a2a35}
.chrome-titlebar{display:flex;align-items:center;height:42px;padding:0 16px;gap:8px}
.traffic-light{display:flex;gap:7px;align-items:center}
.traffic-dot{width:12px;height:12px;border-radius:50%}
.dot-red{background:#ff5f57}
.dot-yellow{background:#febc2e}
.dot-green{background:#28c840}
.chrome-title{flex:1;text-align:center;font-size:12px;color:#68687a;font-weight:500;letter-spacing:.3px}
.address-bar{display:flex;align-items:center;height:34px;padding:0 16px 8px}
.address-pill{flex:1;max-width:480px;margin:0 auto;background:#0e0e12;border:1px solid #2a2a35;border-radius:8px;height:28px;display:flex;align-items:center;padding:0 12px;gap:6px}
.address-lock{color:#68687a;font-size:11px}
.address-lock svg{width:12px;height:12px;fill:#68687a}
.address-text{font-size:12px;color:#9898a8;font-weight:400}

/* ===================== MODE BAR ===================== */
.mode-bar{position:fixed;top:76px;left:0;right:0;z-index:999;background:rgba(14,14,18,.92);backdrop-filter:blur(16px);-webkit-backdrop-filter:blur(16px);border-bottom:1px solid #2a2a35;display:flex;align-items:center;justify-content:center;height:48px;padding:0 20px;gap:16px}
.mode-toggle{display:flex;background:#16161c;border-radius:10px;padding:3px;border:1px solid #2a2a35}
.mode-btn{padding:6px 18px;border-radius:8px;font-size:13px;font-weight:500;color:#68687a;transition:all .25s}
.mode-btn.active{background:#7c6aef;color:#fff}
.progress-dots{display:flex;gap:5px;align-items:center;margin-left:12px}
.progress-dots.hidden{display:none}
.pdot{width:7px;height:7px;border-radius:50%;background:#2a2a35;transition:all .3s}
.pdot.active{background:#7c6aef;transform:scale(1.3)}
.pdot.done{background:#7c6aef;opacity:.5}

/* ===================== MAIN CONTENT AREA ===================== */
.content-area{position:fixed;top:124px;left:0;right:0;bottom:0;overflow-y:auto;overflow-x:hidden}
.content-area::-webkit-scrollbar{width:6px}
.content-area::-webkit-scrollbar-track{background:transparent}
.content-area::-webkit-scrollbar-thumb{background:#2a2a35;border-radius:3px}

/* ===================== INTRO SCREEN ===================== */
.screen-intro{display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:calc(100vh - 124px);padding:40px 24px;text-align:center;animation:fadeUp .6s ease}
.intro-icon{width:72px;height:72px;border-radius:18px;background:linear-gradient(135deg,#7c6aef 0%,#a855f7 100%);display:flex;align-items:center;justify-content:center;margin-bottom:28px;box-shadow:0 8px 32px rgba(124,106,239,.3)}
.intro-icon svg{width:36px;height:36px;fill:#fff}
.intro-title{font-size:32px;font-weight:800;letter-spacing:-.5px;margin-bottom:8px;background:linear-gradient(135deg,#e8e8ed,#9898a8);-webkit-background-clip:text;-webkit-text-fill-color:transparent}
.intro-subtitle{font-size:15px;color:#9898a8;margin-bottom:24px;font-weight:400}
.intro-badge{display:inline-flex;align-items:center;gap:6px;background:#16161c;border:1px solid #2a2a35;border-radius:20px;padding:6px 16px;font-size:12px;color:#9898a8;margin-bottom:32px}
.intro-badge .dot{width:5px;height:5px;border-radius:50%;background:#7c6aef}
.intro-start-btn{background:linear-gradient(135deg,#7c6aef,#6c5ce7);color:#fff;font-size:15px;font-weight:600;padding:14px 36px;border-radius:12px;transition:all .25s;box-shadow:0 4px 20px rgba(124,106,239,.35)}
.intro-start-btn:hover{transform:translateY(-2px);box-shadow:0 6px 28px rgba(124,106,239,.5)}
.intro-hint{margin-top:20px;font-size:12px;color:#68687a}

/* ===================== TOUR MODE ===================== */
.screen-tour{display:none;padding:0 24px 120px;max-width:900px;margin:0 auto}
.screen-tour.active{display:block}
.tour-step{display:none;animation:fadeUp .4s ease}
.tour-step.active{display:block}
.step-header{display:flex;align-items:center;gap:12px;margin-bottom:16px;padding-top:20px}
.step-num{width:36px;height:36px;border-radius:10px;background:#7c6aef;display:flex;align-items:center;justify-content:center;font-size:15px;font-weight:700;color:#fff;flex-shrink:0}
.step-tag{display:inline-block;padding:3px 10px;border-radius:6px;font-size:11px;font-weight:600;letter-spacing:.5px;color:#fff}
.step-titles{flex:1}
.step-title{font-size:20px;font-weight:700;color:#e8e8ed;line-height:1.3}
.step-subtitle{font-size:13px;color:#68687a;font-weight:400}

/* Image container */
.step-img-wrap{position:relative;border-radius:12px;overflow:hidden;background:#0e0e12;border:1px solid #2a2a35;margin-bottom:16px}
.step-img-inner{position:relative;overflow:hidden;border-radius:11px}
.step-img{width:100%;display:block;transition:transform 1s cubic-bezier(.4,0,.2,1)}
.step-img-inner.zoomed .step-img{transition:none}

/* Hotspots */
.hotspot{position:absolute;transform:translate(-50%,-50%);z-index:10;pointer-events:none}
.hotspot-dot{width:18px;height:18px;border-radius:50%;background:rgba(124,106,239,.9);border:2px solid rgba(255,255,255,.7);position:relative}
.hotspot-dot::after{content:'';position:absolute;inset:-6px;border-radius:50%;border:2px solid rgba(124,106,239,.5);animation:ripple 2s ease-out infinite}
.hotspot-dot::before{content:'';position:absolute;inset:-12px;border-radius:50%;border:1.5px solid rgba(124,106,239,.25);animation:ripple 2s ease-out infinite .5s}
.hotspot-label{position:absolute;left:50%;top:calc(100% + 6px);transform:translateX(-50%);white-space:nowrap;background:rgba(14,14,18,.9);border:1px solid #2a2a35;border-radius:6px;padding:3px 8px;font-size:10px;color:#e8e8ed;font-weight:500;pointer-events:none}

@keyframes ripple{0%{transform:scale(.8);opacity:1}100%{transform:scale(2);opacity:0}}

/* Callout */
.callout-overlay{position:absolute;z-index:8;transform:translate(-50%,-50%);max-width:240px}
.callout-box{background:rgba(14,14,18,.88);backdrop-filter:blur(8px);border:1px solid rgba(124,106,239,.4);border-radius:10px;padding:8px 14px;font-size:12px;color:#e8e8ed;font-weight:500;line-height:1.4;text-align:center;box-shadow:0 4px 16px rgba(0,0,0,.4)}
.callout-box::after{content:'';position:absolute;bottom:-5px;left:50%;transform:translateX(-50%) rotate(45deg);width:10px;height:10px;background:rgba(14,14,18,.88);border-right:1px solid rgba(124,106,239,.4);border-bottom:1px solid rgba(124,106,239,.4)}

/* Zoom button */
.zoom-btn{position:absolute;top:10px;right:10px;z-index:12;background:rgba(14,14,18,.8);border:1px solid #2a2a35;border-radius:8px;width:34px;height:34px;display:flex;align-items:center;justify-content:center;color:#9898a8;transition:all .2s}
.zoom-btn:hover{background:rgba(124,106,239,.3);color:#fff}
.zoom-btn svg{width:16px;height:16px;fill:currentColor}

/* Content cards */
.step-cards{display:grid;grid-template-columns:1fr 1fr;gap:12px;margin-bottom:14px}
@media(max-width:640px){.step-cards{grid-template-columns:1fr}}
.step-card{background:#0e0e12;border:1px solid #2a2a35;border-radius:12px;padding:16px}
.step-card-label{font-size:11px;font-weight:600;color:#7c6aef;text-transform:uppercase;letter-spacing:.5px;margin-bottom:6px}
.step-card-text{font-size:13px;color:#9898a8;line-height:1.6}
.card-narration .step-card-text{font-style:italic;color:#b0b0bd}

/* Audio bar */
.audio-bar{display:flex;align-items:center;gap:10px;background:#0e0e12;border:1px solid #2a2a35;border-radius:10px;padding:10px 14px;margin-bottom:14px}
.audio-play-btn{width:32px;height:32px;border-radius:50%;background:#7c6aef;display:flex;align-items:center;justify-content:center;flex-shrink:0;transition:all .2s}
.audio-play-btn:hover{background:#6c5ce7;transform:scale(1.05)}
.audio-play-btn svg{width:14px;height:14px;fill:#fff}
.audio-progress-wrap{flex:1;height:4px;background:#2a2a35;border-radius:2px;cursor:pointer;position:relative}
.audio-progress-fill{height:100%;background:#7c6aef;border-radius:2px;width:0%;transition:width .1s linear}
.audio-time{font-size:11px;color:#68687a;font-variant-numeric:tabular-nums;min-width:36px;text-align:right}
.audio-mute-btn{width:28px;height:28px;display:flex;align-items:center;justify-content:center;border-radius:6px;color:#68687a;transition:all .2s}
.audio-mute-btn:hover{color:#e8e8ed;background:rgba(255,255,255,.05)}
.audio-mute-btn svg{width:16px;height:16px;fill:currentColor}

/* Timestamp */
.step-timestamp{font-size:11px;color:#68687a;text-align:right}

/* ===================== BOTTOM NAV BAR ===================== */
.bottom-nav{position:fixed;bottom:0;left:0;right:0;z-index:999;background:rgba(14,14,18,.95);backdrop-filter:blur(16px);-webkit-backdrop-filter:blur(16px);border-top:1px solid #2a2a35;display:none;align-items:center;justify-content:space-between;padding:10px 24px;height:60px}
.bottom-nav.active{display:flex}
.nav-btn{display:flex;align-items:center;gap:6px;padding:8px 18px;border-radius:10px;font-size:13px;font-weight:600;transition:all .2s;color:#9898a8;background:#16161c;border:1px solid #2a2a35}
.nav-btn:hover{background:#1e1e26;color:#e8e8ed}
.nav-btn.primary{background:#7c6aef;color:#fff;border-color:#7c6aef}
.nav-btn.primary:hover{background:#6c5ce7}
.nav-btn:disabled{opacity:.3;pointer-events:none}
.nav-btn svg{width:14px;height:14px;fill:currentColor}
.nav-center{display:flex;align-items:center;gap:14px}
.nav-counter{font-size:13px;color:#68687a;font-weight:500}
.autoplay-toggle{display:flex;align-items:center;gap:6px;font-size:12px;color:#68687a;cursor:pointer}
.autoplay-switch{width:34px;height:18px;border-radius:9px;background:#2a2a35;position:relative;transition:background .25s}
.autoplay-switch.on{background:#7c6aef}
.autoplay-switch::after{content:'';position:absolute;top:2px;left:2px;width:14px;height:14px;border-radius:50%;background:#fff;transition:transform .25s}
.autoplay-switch.on::after{transform:translateX(16px)}

/* ===================== GUIDE MODE ===================== */
.screen-guide{display:none;padding:24px 24px 60px;max-width:860px;margin:0 auto}
.screen-guide.active{display:block}
.guide-header{text-align:center;margin-bottom:32px;padding-top:16px}
.guide-header h2{font-size:24px;font-weight:700;color:#e8e8ed;margin-bottom:4px}
.guide-header p{font-size:14px;color:#68687a}
.guide-step{background:#0e0e12;border:1px solid #2a2a35;border-radius:14px;padding:24px;margin-bottom:20px;border-left:4px solid #2a2a35;position:relative;transition:border-color .3s}
.guide-step-num{width:44px;height:44px;border-radius:50%;background:#16161c;border:2px solid #2a2a35;display:flex;align-items:center;justify-content:center;font-size:18px;font-weight:700;color:#7c6aef;margin-bottom:12px}
.guide-step-title{font-size:18px;font-weight:700;color:#e8e8ed;margin-bottom:4px}
.guide-step-subtitle{font-size:13px;color:#68687a;margin-bottom:14px}
.guide-step-img{border-radius:10px;overflow:hidden;margin-bottom:14px;border:1px solid #2a2a35}
.guide-step-img img{width:100%;display:block}
.guide-step-desc{font-size:14px;color:#9898a8;line-height:1.6}

/* ===================== OUTRO ===================== */
.screen-outro{display:none;flex-direction:column;align-items:center;justify-content:center;min-height:calc(100vh - 124px);padding:40px 24px;text-align:center}
.screen-outro.active{display:flex;animation:fadeUp .5s ease}
.outro-check{width:72px;height:72px;border-radius:50%;background:linear-gradient(135deg,#22c55e,#16a34a);display:flex;align-items:center;justify-content:center;margin-bottom:24px;box-shadow:0 8px 32px rgba(34,197,94,.3)}
.outro-check svg{width:36px;height:36px;fill:#fff}
.outro-title{font-size:28px;font-weight:800;color:#e8e8ed;margin-bottom:8px}
.outro-sub{font-size:15px;color:#9898a8;margin-bottom:28px}
.outro-features{display:flex;flex-wrap:wrap;gap:10px;justify-content:center;margin-bottom:32px}
.outro-feat{background:#16161c;border:1px solid #2a2a35;border-radius:10px;padding:8px 16px;font-size:13px;color:#9898a8;font-weight:500}
.outro-actions{display:flex;gap:12px}
.outro-btn{padding:12px 28px;border-radius:10px;font-size:14px;font-weight:600;transition:all .25s}
.outro-btn.primary{background:#7c6aef;color:#fff}
.outro-btn.primary:hover{background:#6c5ce7}
.outro-btn.secondary{background:#16161c;border:1px solid #2a2a35;color:#9898a8}
.outro-btn.secondary:hover{background:#1e1e26;color:#e8e8ed}

/* ===================== ANIMATIONS ===================== */
@keyframes fadeUp{from{opacity:0;transform:translateY(16px)}to{opacity:1;transform:translateY(0)}}
@keyframes fadeIn{from{opacity:0}to{opacity:1}}

/* ===================== RESPONSIVE ===================== */
@media(max-width:640px){
  .intro-title{font-size:24px}
  .step-title{font-size:17px}
  .chrome-titlebar{height:36px}
  .address-bar{height:30px;padding-bottom:6px}
  .mode-bar{top:66px;height:42px}
  .content-area{top:108px}
  .bottom-nav{padding:8px 16px;height:54px}
  .nav-btn{padding:6px 14px;font-size:12px}
  .screen-tour{padding:0 16px 110px}
}
</style>
</head>
<body>

<!-- =================== BROWSER CHROME =================== -->
<div class="browser-chrome">
  <div class="chrome-titlebar">
    <div class="traffic-light">
      <div class="traffic-dot dot-red"></div>
      <div class="traffic-dot dot-yellow"></div>
      <div class="traffic-dot dot-green"></div>
    </div>
    <div class="chrome-title">CleanShot X Annotation Workflow</div>
    <div style="width:52px"></div>
  </div>
  <div class="address-bar">
    <div class="address-pill">
      <span class="address-lock"><svg viewBox="0 0 24 24"><path d="M18 8h-1V6c0-2.76-2.24-5-5-5S7 3.24 7 6v2H6c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V10c0-1.1-.9-2-2-2zm-6 9c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2zm3.1-9H8.9V6c0-1.71 1.39-3.1 3.1-3.1s3.1 1.39 3.1 3.1v2z"/></svg></span>
      <span class="address-text">cleanshot-walkthrough.app</span>
    </div>
  </div>
</div>

<!-- =================== MODE BAR =================== -->
<div class="mode-bar">
  <div class="mode-toggle">
    <button class="mode-btn active" data-mode="tour" onclick="switchMode('tour')">Interactive Tour</button>
    <button class="mode-btn" data-mode="guide" onclick="switchMode('guide')">Step-by-Step Guide</button>
  </div>
  <div class="progress-dots" id="progressDots"></div>
</div>

<!-- =================== CONTENT AREA =================== -->
<div class="content-area" id="contentArea">

  <!-- INTRO -->
  <div class="screen-intro" id="screenIntro">
    <div class="intro-icon">
      <svg viewBox="0 0 24 24"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"/></svg>
    </div>
    <h1 class="intro-title">CleanShot X Annotation Workflow</h1>
    <p class="intro-subtitle">By Vinay &middot; Layerpath.com</p>
    <div class="intro-badge"><span class="dot"></span> 14 steps &middot; ~2 min read</div>
    <button class="intro-start-btn" onclick="startTour()">Start Walkthrough &rarr;</button>
    <p class="intro-hint">Use arrow keys or click to navigate</p>
  </div>

  <!-- TOUR -->
  <div class="screen-tour" id="screenTour"></div>

  <!-- GUIDE -->
  <div class="screen-guide" id="screenGuide"></div>

  <!-- OUTRO -->
  <div class="screen-outro" id="screenOutro">
    <div class="outro-check">
      <svg viewBox="0 0 24 24"><path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41L9 16.17z"/></svg>
    </div>
    <h2 class="outro-title">Walkthrough Complete</h2>
    <p class="outro-sub">You've explored CleanShot X's top annotation features.</p>
    <div class="outro-features">
      <div class="outro-feat">Blur / Redact</div>
      <div class="outro-feat">Spotlight</div>
      <div class="outro-feat">Counters</div>
      <div class="outro-feat">Arrows</div>
      <div class="outro-feat">Background</div>
    </div>
    <div class="outro-actions">
      <button class="outro-btn primary" onclick="restartTour()">Restart Tour</button>
      <button class="outro-btn secondary" onclick="switchMode('guide')">Switch to Guide Mode</button>
    </div>
  </div>

</div>

<!-- =================== BOTTOM NAV =================== -->
<div class="bottom-nav" id="bottomNav">
  <button class="nav-btn" id="btnPrev" onclick="prevStep()">
    <svg viewBox="0 0 24 24"><path d="M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12z"/></svg>
    Prev
  </button>
  <div class="nav-center">
    <span class="nav-counter" id="navCounter">1 / 14</span>
    <div class="autoplay-toggle" onclick="toggleAutoplay()">
      <div class="autoplay-switch" id="autoplaySwitch"></div>
      <span>Autoplay</span>
    </div>
  </div>
  <button class="nav-btn primary" id="btnNext" onclick="nextStep()">
    Next
    <svg viewBox="0 0 24 24"><path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"/></svg>
  </button>
</div>

<script>
// ===================================================================
// DATA
// ===================================================================
const STEPS = %%STEPS_DATA%%;

// ===================================================================
// STATE
// ===================================================================
let currentStep = 0;
let currentMode = 'intro'; // intro | tour | guide | outro
let autoplay = false;
let userInteracted = false;
let isMuted = false;
let audioElements = [];
let currentAudio = null;
let zoomTimers = {};

// ===================================================================
// INIT
// ===================================================================
document.addEventListener('DOMContentLoaded', () => {
  buildProgressDots();
  buildTourSteps();
  buildGuideSteps();
  preloadAudio();
  document.addEventListener('keydown', handleKeyboard);
});

function preloadAudio() {
  STEPS.forEach((s, i) => {
    const a = new Audio(s.audioSrc);
    a.preload = 'auto';
    a.volume = 1;
    audioElements.push(a);
  });
}

// ===================================================================
// BUILD UI
// ===================================================================
function buildProgressDots() {
  const c = document.getElementById('progressDots');
  for (let i = 0; i < STEPS.length; i++) {
    const d = document.createElement('div');
    d.className = 'pdot' + (i === 0 ? ' active' : '');
    d.dataset.idx = i;
    d.title = 'Step ' + (i+1);
    d.style.cursor = 'pointer';
    d.onclick = () => { if (currentMode === 'tour') goToStep(i); };
    c.appendChild(d);
  }
}

function buildTourSteps() {
  const container = document.getElementById('screenTour');
  STEPS.forEach((s, i) => {
    const div = document.createElement('div');
    div.className = 'tour-step' + (i === 0 ? ' active' : '');
    div.id = 'tourStep' + i;
    div.innerHTML = buildStepHTML(s, i);
    container.appendChild(div);
  });
}

function buildStepHTML(s, i) {
  // hotspots
  let hotspotsHTML = '';
  s.hotspots.forEach(h => {
    hotspotsHTML += `<div class="hotspot" style="left:${h.x}%;top:${h.y}%">
      <div class="hotspot-dot"></div>
      <div class="hotspot-label">${escHtml(h.label)}</div>
    </div>`;
  });

  // callout
  let calloutHTML = '';
  if (s.callout) {
    calloutHTML = `<div class="callout-overlay" style="left:${s.callout.x}%;top:${s.callout.y}%">
      <div class="callout-box">${escHtml(s.callout.text)}</div>
    </div>`;
  }

  // zoom button
  let zoomBtnHTML = s.zoom ? `<button class="zoom-btn" onclick="toggleZoom(${i})" title="Toggle zoom">
    <svg viewBox="0 0 24 24"><path d="M15.5 14h-.79l-.28-.27A6.471 6.471 0 0016 9.5 6.5 6.5 0 109.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/></svg>
  </button>` : '';

  // SVG icons
  const playSVG = `<svg viewBox="0 0 24 24"><path d="M8 5v14l11-7z"/></svg>`;
  const pauseSVG = `<svg viewBox="0 0 24 24"><path d="M6 19h4V5H6v14zm8-14v14h4V5h-4z"/></svg>`;
  const volSVG = `<svg viewBox="0 0 24 24"><path d="M3 9v6h4l5 5V4L7 9H3zm13.5 3A4.5 4.5 0 0014 8.5v7a4.49 4.49 0 002.5-3.5zM14 3.23v2.06c2.89.86 5 3.54 5 6.71s-2.11 5.85-5 6.71v2.06c4.01-.91 7-4.49 7-8.77s-2.99-7.86-7-8.77z"/></svg>`;
  const muteSVG = `<svg viewBox="0 0 24 24"><path d="M16.5 12A4.5 4.5 0 0014 8.5v2.09l2.41 2.41c.06-.31.09-.65.09-1zm2.5 0c0 .94-.2 1.82-.54 2.64l1.51 1.51A8.796 8.796 0 0021 12c0-4.28-2.99-7.86-7-8.77v2.06c2.89.86 5 3.54 5 6.71zM4.27 3L3 4.27 7.73 9H3v6h4l5 5v-6.73l4.25 4.25c-.67.52-1.42.93-2.25 1.18v2.06a8.99 8.99 0 003.69-1.81L19.73 21 21 19.73l-9-9L4.27 3zM12 4L9.91 6.09 12 8.18V4z"/></svg>`;

  return `
    <div class="step-header">
      <div class="step-num">${i+1}</div>
      <div class="step-tag" style="background:${s.tagColor}">${s.tag}</div>
      <div class="step-titles">
        <div class="step-title">${escHtml(s.title)}</div>
        <div class="step-subtitle">${escHtml(s.subtitle)}</div>
      </div>
    </div>
    <div class="step-img-wrap">
      <div class="step-img-inner" id="imgInner${i}">
        <img class="step-img" id="stepImg${i}" src="${s.imgSrc}" alt="Step ${i+1}">
        ${hotspotsHTML}
        ${calloutHTML}
      </div>
      ${zoomBtnHTML}
    </div>
    <div class="step-cards">
      <div class="step-card">
        <div class="step-card-label">What's happening</div>
        <div class="step-card-text">${escHtml(s.desc)}</div>
      </div>
      <div class="step-card card-narration">
        <div class="step-card-label">Vinay's Narration</div>
        <div class="step-card-text">"${escHtml(s.narration)}"</div>
      </div>
    </div>
    <div class="audio-bar" id="audioBar${i}">
      <button class="audio-play-btn" id="audioPlayBtn${i}" onclick="toggleAudioPlay(${i})">
        ${playSVG}
      </button>
      <div class="audio-progress-wrap" id="audioProgressWrap${i}" onclick="seekAudio(event, ${i})">
        <div class="audio-progress-fill" id="audioFill${i}"></div>
      </div>
      <span class="audio-time" id="audioTime${i}">0:00</span>
      <button class="audio-mute-btn" id="audioMuteBtn${i}" onclick="toggleMute(${i})">
        ${volSVG}
      </button>
    </div>
    <div class="step-timestamp">Step ${i+1} of ${STEPS.length}</div>
  `;
}

function buildGuideSteps() {
  const container = document.getElementById('screenGuide');
  let html = `<div class="guide-header"><h2>CleanShot X Annotation Workflow</h2><p>Complete step-by-step reference guide</p></div>`;
  STEPS.forEach((s, i) => {
    html += `<div class="guide-step" style="border-left-color:${s.tagColor}">
      <div class="guide-step-num">${i+1}</div>
      <div class="guide-step-title">${escHtml(s.title)}</div>
      <div class="guide-step-subtitle">${escHtml(s.subtitle)}</div>
      <div class="guide-step-img"><img src="${s.imgSrc}" alt="Step ${i+1}" loading="lazy"></div>
      <div class="guide-step-desc">${escHtml(s.desc)}</div>
    </div>`;
  });
  container.innerHTML = html;
}

// ===================================================================
// MODE SWITCHING
// ===================================================================
function switchMode(mode) {
  stopAllAudio();
  // update pills
  document.querySelectorAll('.mode-btn').forEach(b => b.classList.toggle('active', b.dataset.mode === mode));

  const intro = document.getElementById('screenIntro');
  const tour = document.getElementById('screenTour');
  const guide = document.getElementById('screenGuide');
  const outro = document.getElementById('screenOutro');
  const nav = document.getElementById('bottomNav');
  const dots = document.getElementById('progressDots');

  intro.style.display = 'none';
  tour.classList.remove('active');
  guide.classList.remove('active');
  outro.classList.remove('active');
  outro.style.display = 'none';
  nav.classList.remove('active');
  dots.classList.remove('hidden');

  if (mode === 'tour') {
    currentMode = 'tour';
    tour.classList.add('active');
    nav.classList.add('active');
    goToStep(currentStep);
    document.getElementById('contentArea').scrollTop = 0;
  } else if (mode === 'guide') {
    currentMode = 'guide';
    guide.classList.add('active');
    dots.classList.add('hidden');
    document.getElementById('contentArea').scrollTop = 0;
  }
}

function startTour() {
  userInteracted = true;
  document.getElementById('screenIntro').style.display = 'none';
  currentMode = 'tour';
  document.getElementById('screenTour').classList.add('active');
  document.getElementById('bottomNav').classList.add('active');
  goToStep(0);
}

function restartTour() {
  currentStep = 0;
  document.getElementById('screenOutro').classList.remove('active');
  document.getElementById('screenOutro').style.display = 'none';
  switchMode('tour');
}

// ===================================================================
// STEP NAVIGATION
// ===================================================================
function goToStep(idx) {
  if (idx < 0 || idx >= STEPS.length) return;
  stopAllAudio();
  clearAllZoomTimers();

  // hide all steps
  document.querySelectorAll('.tour-step').forEach(el => el.classList.remove('active'));
  // show target
  const el = document.getElementById('tourStep' + idx);
  el.classList.add('active');
  currentStep = idx;

  // update nav
  document.getElementById('navCounter').textContent = (idx+1) + ' / ' + STEPS.length;
  document.getElementById('btnPrev').disabled = idx === 0;
  document.getElementById('btnNext').textContent = idx === STEPS.length - 1 ? 'Finish' : 'Next';
  // re-add svg to Next button
  const nextBtn = document.getElementById('btnNext');
  if (idx === STEPS.length - 1) {
    nextBtn.innerHTML = `Finish <svg viewBox="0 0 24 24" style="width:14px;height:14px;fill:currentColor"><path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41L9 16.17z"/></svg>`;
  } else {
    nextBtn.innerHTML = `Next <svg viewBox="0 0 24 24" style="width:14px;height:14px;fill:currentColor"><path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"/></svg>`;
  }

  // progress dots
  document.querySelectorAll('.pdot').forEach((d, di) => {
    d.className = 'pdot' + (di === idx ? ' active' : di < idx ? ' done' : '');
  });

  // scroll content area to top
  document.getElementById('contentArea').scrollTop = 0;

  // auto-zoom
  applyAutoZoom(idx);

  // play audio
  if (userInteracted) {
    playStepAudio(idx);
  }
}

function nextStep() {
  if (currentStep >= STEPS.length - 1) {
    // show outro
    showOutro();
    return;
  }
  userInteracted = true;
  goToStep(currentStep + 1);
}

function prevStep() {
  if (currentStep > 0) {
    userInteracted = true;
    goToStep(currentStep - 1);
  }
}

function showOutro() {
  stopAllAudio();
  document.querySelectorAll('.tour-step').forEach(el => el.classList.remove('active'));
  document.getElementById('screenTour').classList.remove('active');
  document.getElementById('bottomNav').classList.remove('active');
  const outro = document.getElementById('screenOutro');
  outro.style.display = 'flex';
  outro.classList.add('active');
  currentMode = 'outro';
}

// ===================================================================
// ZOOM
// ===================================================================
function applyAutoZoom(idx) {
  const s = STEPS[idx];
  const img = document.getElementById('stepImg' + idx);
  const inner = document.getElementById('imgInner' + idx);
  if (!s.zoom || !img) { return; }

  // Calculate scale and translate
  const z = s.zoom;
  const scaleX = 100 / z.w;
  const scaleY = 100 / z.h;
  const scale = Math.min(scaleX, scaleY, 4); // cap at 4x
  // center of zoom region
  const cx = z.x + z.w / 2;
  const cy = z.y + z.h / 2;
  // translate to center the zoom region
  const tx = (50 - cx) * scale;
  const ty = (50 - cy) * scale;

  // immediately zoom in
  inner.classList.add('zoomed');
  img.style.transform = `scale(${scale}) translate(${tx / scale}%, ${ty / scale}%)`;
  img.style.transformOrigin = `${cx}% ${cy}%`;

  // after delay, zoom out
  zoomTimers[idx] = setTimeout(() => {
    inner.classList.remove('zoomed');
    img.style.transition = 'transform 1s cubic-bezier(.4,0,.2,1)';
    img.style.transform = 'scale(1) translate(0,0)';
    img.style.transformOrigin = 'center center';
  }, 1500);
}

function clearAllZoomTimers() {
  Object.keys(zoomTimers).forEach(k => {
    clearTimeout(zoomTimers[k]);
    // reset transform
    const img = document.getElementById('stepImg' + k);
    const inner = document.getElementById('imgInner' + k);
    if (img) {
      img.style.transition = 'none';
      img.style.transform = 'scale(1) translate(0,0)';
      img.style.transformOrigin = 'center center';
    }
    if (inner) inner.classList.remove('zoomed');
  });
  zoomTimers = {};
}

function toggleZoom(idx) {
  const img = document.getElementById('stepImg' + idx);
  const inner = document.getElementById('imgInner' + idx);
  const s = STEPS[idx];
  if (!s.zoom || !img) return;

  const isZoomed = img.style.transform && img.style.transform !== 'scale(1) translate(0, 0)' && img.style.transform !== 'scale(1) translate(0,0)';
  if (isZoomed) {
    img.style.transition = 'transform .6s cubic-bezier(.4,0,.2,1)';
    img.style.transform = 'scale(1) translate(0,0)';
  } else {
    const z = s.zoom;
    const scaleX = 100 / z.w;
    const scaleY = 100 / z.h;
    const scale = Math.min(scaleX, scaleY, 4);
    const cx = z.x + z.w / 2;
    const cy = z.y + z.h / 2;
    const tx = (50 - cx) * scale;
    const ty = (50 - cy) * scale;
    img.style.transition = 'transform .6s cubic-bezier(.4,0,.2,1)';
    img.style.transform = `scale(${scale}) translate(${tx / scale}%, ${ty / scale}%)`;
    img.style.transformOrigin = `${cx}% ${cy}%`;
  }
}

// ===================================================================
// AUDIO
// ===================================================================
function playStepAudio(idx) {
  const audio = audioElements[idx];
  if (!audio || isMuted) return;
  currentAudio = audio;
  audio.currentTime = 0;
  audio.play().catch(() => {});
  updateAudioUI(idx);
  // progress updater
  audio._uiInterval = setInterval(() => updateAudioUI(idx), 100);
  audio.onended = () => {
    clearInterval(audio._uiInterval);
    updatePlayBtnIcon(idx, false);
    if (autoplay && currentStep < STEPS.length - 1) {
      setTimeout(() => nextStep(), 600);
    }
  };
}

function stopAllAudio() {
  audioElements.forEach((a, i) => {
    a.pause();
    a.currentTime = 0;
    if (a._uiInterval) clearInterval(a._uiInterval);
    updatePlayBtnIcon(i, false);
    const fill = document.getElementById('audioFill' + i);
    if (fill) fill.style.width = '0%';
    const time = document.getElementById('audioTime' + i);
    if (time) time.textContent = '0:00';
  });
  currentAudio = null;
}

function toggleAudioPlay(idx) {
  userInteracted = true;
  const audio = audioElements[idx];
  if (!audio) return;
  if (audio.paused) {
    // stop others first
    audioElements.forEach((a, i) => {
      if (i !== idx && !a.paused) { a.pause(); a.currentTime = 0; clearInterval(a._uiInterval); updatePlayBtnIcon(i, false); }
    });
    audio.play().catch(() => {});
    currentAudio = audio;
    updatePlayBtnIcon(idx, true);
    audio._uiInterval = setInterval(() => updateAudioUI(idx), 100);
    audio.onended = () => {
      clearInterval(audio._uiInterval);
      updatePlayBtnIcon(idx, false);
      if (autoplay && currentStep < STEPS.length - 1) {
        setTimeout(() => nextStep(), 600);
      }
    };
  } else {
    audio.pause();
    clearInterval(audio._uiInterval);
    updatePlayBtnIcon(idx, false);
  }
}

function updateAudioUI(idx) {
  const audio = audioElements[idx];
  if (!audio) return;
  const fill = document.getElementById('audioFill' + idx);
  const time = document.getElementById('audioTime' + idx);
  if (fill && audio.duration) {
    fill.style.width = ((audio.currentTime / audio.duration) * 100) + '%';
  }
  if (time) {
    time.textContent = formatTime(audio.currentTime);
  }
}

function updatePlayBtnIcon(idx, playing) {
  const btn = document.getElementById('audioPlayBtn' + idx);
  if (!btn) return;
  if (playing) {
    btn.innerHTML = `<svg viewBox="0 0 24 24"><path d="M6 19h4V5H6v14zm8-14v14h4V5h-4z"/></svg>`;
  } else {
    btn.innerHTML = `<svg viewBox="0 0 24 24"><path d="M8 5v14l11-7z"/></svg>`;
  }
}

function seekAudio(e, idx) {
  const audio = audioElements[idx];
  const wrap = document.getElementById('audioProgressWrap' + idx);
  if (!audio || !wrap || !audio.duration) return;
  const rect = wrap.getBoundingClientRect();
  const pct = Math.max(0, Math.min(1, (e.clientX - rect.left) / rect.width));
  audio.currentTime = pct * audio.duration;
  updateAudioUI(idx);
}

function toggleMute(idx) {
  isMuted = !isMuted;
  audioElements.forEach(a => a.muted = isMuted);
  // update all mute buttons
  const icon = isMuted
    ? `<svg viewBox="0 0 24 24"><path d="M16.5 12A4.5 4.5 0 0014 8.5v2.09l2.41 2.41c.06-.31.09-.65.09-1zm2.5 0c0 .94-.2 1.82-.54 2.64l1.51 1.51A8.796 8.796 0 0021 12c0-4.28-2.99-7.86-7-8.77v2.06c2.89.86 5 3.54 5 6.71zM4.27 3L3 4.27 7.73 9H3v6h4l5 5v-6.73l4.25 4.25c-.67.52-1.42.93-2.25 1.18v2.06a8.99 8.99 0 003.69-1.81L19.73 21 21 19.73l-9-9L4.27 3zM12 4L9.91 6.09 12 8.18V4z"/></svg>`
    : `<svg viewBox="0 0 24 24"><path d="M3 9v6h4l5 5V4L7 9H3zm13.5 3A4.5 4.5 0 0014 8.5v7a4.49 4.49 0 002.5-3.5zM14 3.23v2.06c2.89.86 5 3.54 5 6.71s-2.11 5.85-5 6.71v2.06c4.01-.91 7-4.49 7-8.77s-2.99-7.86-7-8.77z"/></svg>`;
  STEPS.forEach((_, i) => {
    const btn = document.getElementById('audioMuteBtn' + i);
    if (btn) btn.innerHTML = icon;
  });
  // if muted, stop current
  if (isMuted && currentAudio && !currentAudio.paused) {
    // keep playing but muted (handled by .muted)
  }
}

function toggleAutoplay() {
  autoplay = !autoplay;
  const sw = document.getElementById('autoplaySwitch');
  sw.classList.toggle('on', autoplay);
}

// ===================================================================
// KEYBOARD
// ===================================================================
function handleKeyboard(e) {
  if (currentMode === 'tour') {
    if (e.key === 'ArrowRight' || e.key === 'ArrowDown') { e.preventDefault(); nextStep(); }
    else if (e.key === 'ArrowLeft' || e.key === 'ArrowUp') { e.preventDefault(); prevStep(); }
    else if (e.key === ' ') { e.preventDefault(); toggleAudioPlay(currentStep); }
  }
  if (currentMode === 'intro' && (e.key === 'Enter' || e.key === ' ')) { e.preventDefault(); startTour(); }
}

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

function escHtml(str) {
  const d = document.createElement('div');
  d.textContent = str;
  return d.innerHTML;
}
</script>
</body>
</html>"""


# ---------------------------------------------------------------------------
# Assemble & write
# ---------------------------------------------------------------------------
steps_js = build_steps_js()
final_html = HTML_TEMPLATE.replace("%%STEPS_DATA%%", steps_js)

out_path = os.path.join(SCRIPT_DIR, "walkthrough-v3.html")
with open(out_path, "w", encoding="utf-8") as f:
    f.write(final_html)

print(f"Written {len(final_html):,} bytes to {out_path}")
