3:I[4707,[],""]
6:I[36423,[],""]
8:I[88003,["8003","static/chunks/8003-77a01866b8e7e6e5.js","3185","static/chunks/app/layout-74b4ccf534333157.js"],""]
9:I[61103,["8003","static/chunks/8003-77a01866b8e7e6e5.js","3185","static/chunks/app/layout-74b4ccf534333157.js"],"UserProvider"]
a:I[49197,["8003","static/chunks/8003-77a01866b8e7e6e5.js","3185","static/chunks/app/layout-74b4ccf534333157.js"],"Analytics"]
4:["locale","en","d"]
5:["slug","","oc"]
0:["D8zvvTkMbyMAJHbCbJD7o",[[["",{"children":[["locale","en","d"],{"children":["gallery",{"children":[["slug","","oc"],{"children":["__PAGE__",{}]}]}]}]},"$undefined","$undefined",true],["",{"children":[["locale","en","d"],{"children":["gallery",{"children":[["slug","","oc"],{"children":["__PAGE__",{},[["$L1","$L2",null],null],null]},[null,["$","$L3",null,{"parallelRouterKey":"children","segmentPath":["children","$4","children","gallery","children","$5","children"],"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","notFoundStyles":"$undefined"}]],null]},[null,["$","$L3",null,{"parallelRouterKey":"children","segmentPath":["children","$4","children","gallery","children"],"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","notFoundStyles":"$undefined"}]],null]},[[null,"$L7"],null],null]},[[[["$","link","0",{"rel":"stylesheet","href":"/_next/static/css/04a2b1dee28c4433.css","precedence":"next","crossOrigin":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/_next/static/css/af7c04e52a18620f.css","precedence":"next","crossOrigin":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"bg-gray-950 text-gray-100","children":[["$","$L8",null,{"id":"theme-init","strategy":"beforeInteractive","children":"\n            (() => {\n              try {\n                var path = window.location.pathname;\n                var isHome = path === '/' || path.endsWith('/home') || /\\/(zh|en)\\/home(\\/|$)/.test(path);\n                if (isHome) {\n                  // 首页固定深色主题\n                  document.documentElement.dataset.theme = 'dark';\n                  document.documentElement.style.colorScheme = 'dark';\n                  return;\n                }\n                var stored = localStorage.getItem('site-theme');\n                var theme = stored || 'light';\n                document.documentElement.dataset.theme = theme;\n                document.documentElement.style.colorScheme = theme;\n              } catch (e) {\n                document.documentElement.dataset.theme = 'light';\n                document.documentElement.style.colorScheme = 'light';\n              }\n            })();\n          "}],["$","$L8",null,{"src":"https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-5639745717417263","strategy":"afterInteractive","crossOrigin":"anonymous"}],["$","$L8",null,{"src":"https://www.googletagmanager.com/gtag/js?id=G-C6PN3G0S7V","strategy":"afterInteractive"}],["$","$L8",null,{"id":"google-analytics","strategy":"afterInteractive","children":"\n            window.dataLayer = window.dataLayer || [];\n            function gtag(){dataLayer.push(arguments);}\n            gtag('js', new Date());\n            gtag('config', 'G-C6PN3G0S7V');\n          "}],["$","$L8",null,{"id":"organization-jsonld","type":"application/ld+json","dangerouslySetInnerHTML":{"__html":"{\"@context\":\"https://schema.org\",\"@type\":\"Organization\",\"name\":\"SvgAnimate\",\"url\":\"https://svganimate.ai\",\"logo\":\"https://svganimate.ai/logo.png\"}"}}],["$","$L9",null,{"children":["$","$L3",null,{"parallelRouterKey":"children","segmentPath":["children"],"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":"404"}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],"notFoundStyles":[]}]}],["$","$La",null,{}]]}]}]],null],null],["$Lb",null]]]]
c:I[47279,["1203","static/chunks/app/%5Blocale%5D/layout-da3cb07dac8804ed.js"],"I18nProvider"]
d:I[65373,["1203","static/chunks/app/%5Blocale%5D/layout-da3cb07dac8804ed.js"],"NotificationProvider"]
e:I[78537,["1203","static/chunks/app/%5Blocale%5D/layout-da3cb07dac8804ed.js"],"LocaleAutoRedirect"]
7:["$","$Lc",null,{"locale":"en","dictionary":{"languageName":"English","languageSwitcher":{"tooltip":"Switch language","languages":{"zh":"中文","en":"English"}},"themeSwitcher":{"tooltip":"Switch theme","light":"Light","dark":"Dark"},"actions":{"search":"Search","clear":"Clear"},"brand":{"title":"SvgAnimate Animation Studio","subtitle":"Type a keyword to quickly find the animation you need","searchPlaceholder":"Search animations, e.g. 'sorting algorithm' or 'TCP'...","searchButton":"Search","hotLabel":"🔥 Trending:","hotKeywords":["Algorithm","Network","Astronomy"]},"home":{"featuredTitle":"Featured Picks","searchResultPrefix":"results for","hottest":"Hottest","noResults":"No matching animations","clearSearch":"Clear search","loadMore":"Load more","loadingMore":"Loading...","noMore":"No more data","footerCopy":"© 2025 AI SVG Animation Studio. All rights reserved.","viewAll":"View All Web Animation Examples","privacyPolicy":"Privacy Policy","termsOfService":"Terms of Service","contactMe":"Contact Me","contactEmailLabel":"or email","newHero":{"title":"Create Professional Knowledge Explainers & Animations","subtitle":"AI writes complete HTML video scripts and storyboard previews, professional videos in one click.\nDozens of SVG animation templates to choose from and copy with one click.","ctaHint":"Bring your content to life","primaryCta":"Try It Now","secondaryCta":"Learn More","miniCenterLabel":"Mini creation center preview","titleLine1":"One Prompt","titleLine2":"Professional Knowledge Explainer","titleLine3":"HTML Video & SVG Animation","features":["Auto Script Expansion","Editable Storyboards","Custom Voiceover & Subtitles"]},"scenariosSection":{"title":"Perfect For These Scenarios","subtitle":"","viewMore":"View more scenarios","items":[{"key":"knowledge","title":"Knowledge Explainer","description":"Break abstract concepts into visual scenes and auto-generate visuals and voice so ideas become clear.","cta":"Try It Now"},{"key":"classroom","title":"Classroom Teaching","description":"Generate lively HTML videos for key lesson points and help students grasp core knowledge faster.","cta":"Try It Now"},{"key":"story","title":"Storytelling","description":"Tell brand or educational stories with coherent visual scenes that make content more engaging.","cta":"Try It Now"}]},"midCta":{"title":"No coding, no prompt skills, no professional editing software","subtitle":"Whether it's professional knowledge explainers, process demo videos, fun animations, or web logo animations — create them all in one click.","buttonText":"Get Started Free"}},"sidebar":{"home":"Home","creation":"Create","browse":"Browse Categories","profile":"Profile"},"categories":{"home":"Home","create":"Create","svg-loading-animation":"Loading Animation","svg-logo-animation":"Logo Animation","text-svg-animation":"Text Animation","svg-line-drawing":"Line Drawing","hand-drawn-animation":"Hand-drawn Animation","interactive-svg-animation":"Interactive Animation","flowchart-animation":"Flowchart Animation","svg-map-animation":"Map Animation","svg-mini-game":"Web Mini Game","stem-knowledge-animation":"STEM Education","data-visualization-animation":"Data Visualization","html-video":"HTML Video","stick-figure-animation":"Stick Figure","product-demo-animation":"Product Demo","dynamic-view-webpage":"Dynamic Webpage","3d-animation":"3D Animation","others":"Other"},"categoryPage":{"searchPlaceholder":"Search within this category...","empty":"No animations in this category yet.","countSuffix":"animations"},"statuses":{"loading":"Loading animations...","loadError":"Failed to load animations. Check the database service or refresh."},"login":{"title":"Please sign in","description":"Sign in to create animations and videos, view and edit your work, and export GIF/MP4.","github":"Sign in with GitHub","google":"Sign in with Google","cancel":"Cancel","notLogged":"Not signed in","clickToLogin":"Click to sign in"},"templateCard":{"missing":"Animation component not found","hoverToPlay":"Hover to play","previewHint":"Playback pauses when you leave"},"profile":{"label":"Profile"},"creation":{"label":"Create"},"breadcrumbs":{"back":"Back"},"hotSearchLabel":"Trending","hero":{"version":"Powered by Gemini 3.1 Pro","title1":"SVG Animation Generator –","title2":"Create SVGs & HTML Videos with AI","subtitle":"No coding required, no professional software needed. Describe what you want and AI generates ","subtitle2":"SVG animations","subtitle3":", HTML videos, and export-ready web motion.","svgTooltipTitle":"What are svg animations?","svgTooltipDesc":"svg animations are based on mathematical formulas and never lose quality when scaled. Compared to traditional videos, they're smaller, load faster, and infinitely scalable. Perfect for web displays, educational demos, and brand promotions.","placeholder":"Describe your animation... e.g. 'A rotating DNA double helix structure'","startButton":"Start Generating","advancedButton":"Advanced Mode","navigating":"Redirecting...","creatorPrefix":"Perfect for","creatorBadge":"knowledge educators","example1":"Loading Effect","example2":"Brand Logo","example3":"Data Viz","example4":"Toggle Switch","features":{"infiniteZoom":"Infinite Zoom","ultra4K":"4K Ultra HD","oneClickExport":"One-Click Export","massiveCases":"Massive Gallery"},"freeTrialHint":"Start with free trial","panel":{"tipsTitle":"Prompt Tips","examplesTitle":"Sample Prompts","simpleLabel":"Simple","detailedLabel":"Detailed","detailedBadge":"Detailed · Recommended","useSimple":"Use Simple","useDetailed":"Use Detailed","showcaseTitle":"Best For"},"contentExamples":[{"label":"Physics Motion","title":"Physics Motion Animation","description":"Visualize physical laws with vector motion so abstract concepts become intuitive.","tips":["Describe the motion path and speed clearly","Explain relationships between objects","Add orbits, arrows, and aids if helpful"],"simpleExample":"Earth orbits the sun, the moon orbits the earth","detailedExample":"Build a solar system: a yellow sun in the center, Earth orbits on an elliptical path, the moon circles Earth. Earth is blue, the moon is gray, and the orbits are dotted lines.","showcase":"Great for: science education, physics demos, astronomy explainers"},{"label":"Knowledge Demo","title":"Technical Explainer","description":"Turn complex mechanisms into visuals so viewers grasp technical details fast.","tips":["Describe the workflow step by step","Highlight key parts and their motion","Use precise terminology for accuracy"],"simpleExample":"How a mechanical hard drive reads data","detailedExample":"Show HDD internals: platter spinning at high speed, actuator arm moves to position, head floats above the platter to read data. Color the platter, head, and arm differently and draw the data flow path.","showcase":"Great for: tech talks, product demos, training materials"},{"label":"Video Intro","title":"Logo Intro Animation","description":"Create a polished brand opener to boost impact and recognition.","tips":["Clarify the order and effects of text transforms","Describe rotation/scale/light details","Reference well-known brand intro styles"],"simpleExample":"SvgAnimate text spins into “Animation Studio”","detailedExample":"Make a premium opener: deep blue background, SvgAnimate letters roll in from the right with a gold sheen and light 3D look. After arriving center they rotate 360°, morphing into the Chinese words for “Animation Studio”.","showcase":"Great for: brand promos, video openers, event intros"},{"label":"Character Motion","title":"Character Animation","description":"Use clean shapes to show character movement with rhythm and storytelling.","tips":["Describe character look and colors","State tempo and camera motion","Add beats, cuts, or emotion shifts if needed"],"simpleExample":"A stick figure runs with mountains and clouds scrolling behind","detailedExample":"On a dark gradient background, design a minimalist runner made of geometric shapes. Legs alternate, arms swing; a ground shadow moves. Distant mountains and clouds pan left to imply speed.","showcase":"Great for: product motion, explainers, UI animation demos"}]},"header":{"gallery":"Gallery","learn":"Learn","tools":"Tools","blog":"Blog","membership":"Pricing","profile":"Profile","logout":"Sign Out","startCreating":"Start Creating","login":"Sign In","quotaUsage":"Points","history":"History"},"gallery":{"title":"Web Animation Examples Gallery","subtitle":"Browse featured web animation examples","all":"All","prev":"Prev","next":"Next","searchPlaceholder":"Search by title or description...","searchButton":"Search","adLabel":"Sponsored"},"membership":{"badge":"Simple, transparent pricing","title1":"Choose your","title2":"plan","subtitle":"Start free and scale as you grow. All plans include our core AI features with increasing limits and capabilities.","loading":"Loading...","mostPopular":"Most popular","perMonth":"/month","perYear":"/year","perPurchase":"One-time","freePlanDesc":"Experience basic features","litePlanDesc":"Lightweight entry with flexible top-ups","proPlanDesc":"Best choice for professionals","maxPlanDesc":"Unlimited for advanced users","currentPlan":"Current Plan","purchaseButton":"Purchase Now","subscribeButton":"Subscribe","featuresTitle":"Features:","quotaLabel":"points","quotaPeriod":"per month","quotaPeriodNote":"points do not roll over and reset next month","quotaPeriodOnce":"no expiry","watermarkLabel":"Export","watermarkYes":"with","watermarkNo":"without","watermarkSuffix":"watermark","watermarkFormats":"GIF and video","resolutionLabel":"Supported resolutions","billingCycleLabel":"Billing cycle","monthly":"Monthly","yearly":"Yearly","saveBadge":"Save","paymentTitle":"Scan to Pay","paymentAmount":"Amount:","paymentInstruction":"Please scan the QR code with WeChat to complete payment","paymentTimeLeft":"Time remaining:","paymentCancel":"Cancel Payment","paymentSuccess":"Payment Successful","paymentSuccessDesc":"Membership benefits activated","paymentExpired":"Order Expired","paymentExpiredDesc":"Please create a new order","paymentClose":"Close","subscription":"Subscription Management","subscriptionStatus":"Subscription Status","subscriptionActive":"Active","subscriptionCancelled":"Cancelled","subscriptionPastDue":"Past Due","nextBillingDate":"Next billing date","cancelSubscription":"Cancel Subscription","reactivateSubscription":"Reactivate Subscription","managePaymentMethod":"Manage Payment Method","billingHistory":"Billing History","cancelAtPeriodEnd":"Will cancel at period end","subscriptionRemaining":"Your subscription will remain active until","subscriptionCancelSuccess":"Subscription canceled successfully","subscriptionReactivateSuccess":"Subscription reactivated successfully","alertLogin":"Please sign in first","alertSessionExpired":"Session expired, please sign in again","alertOrderFailed":"Failed to create order","alertPurchaseFailed":"Purchase failed, please try again later","alertPurchaseSuccess":"Purchase successful!","alertSubscribeSuccess":"Subscription activated successfully!","alertSubscribeFailed":"Failed to create subscription","alertActiveSubscription":"You already have an active subscription","alertProcessing":"Processing payment...","redeemTitle":"Redeem membership","redeemSubtitle":"Enter your 8-character code to unlock the plan.","redeemPlaceholder":"Enter redemption code (case-sensitive)","redeemButton":"Redeem","redeemProcessing":"Redeeming...","redeemInvalid":"Invalid redemption code format","redeemFailed":"Redemption failed, please try again later","redeemSuccess":"Redeemed successfully. Membership activated.","redeemExpand":"Expand","redeemCollapse":"Collapse"},"learn":{"title":"AI Web Animation Guide","subtitle":"Learn how to create web animations with AI, from SVG motion to interactive pages and polished product demos.","metaDescription":"A practical guide to creating web animation with AI: SVG motion, interactive pages, prompt writing, and high-quality examples you can ship.","hero":{"badge":"Web Motion Lab","title":"Welcome to the ","titleAccent":"AI Web Animation Guide","description":"This page explains what web animation is, how SVG animation fits into it, how to create interactive motion with AI, and how to turn prompts into production-ready outputs.","featurePills":["Prompt to motion","SVG & HTML outputs","Interactive experiences","Production ready"],"primaryCta":"Start Creating","secondaryCta":"View Examples","previewLabel":"Live Motion Preview","previewTag":"Generated in Seconds","previewStats":[{"title":"Editable Output","description":"SVG or HTML"},{"title":"Smooth Motion","description":"60fps-ready"},{"title":"Interactive Ready","description":"Built for the web"}],"highlights":[{"title":"Prompt to Motion","description":"Describe an idea and get working animation fast."},{"title":"Style Precision","description":"Control color, timing, and easing."},{"title":"Web-Native Output","description":"Use SVG or HTML for real web experiences."},{"title":"Production Ready","description":"Use in sites, demos, UI, or ads."}]},"showcaseSection":{"title":"What You Can Make","subtitle":"From SVG motion to interactive webpages, AI web animation covers far more than a single format.","videoIntro":"Video Intro Animation","videoIntroDesc":"Dynamic text logos, transitions, and slogan effects that make video openings more engaging.","brandShowcase":"Brand Showcase Animation","brandShowcaseDesc":"Dynamic brand identity presentation, breathing light effects, stroke growth to strengthen brand memory.","dataVisualization":"Data Visualization","dataVisualizationDesc":"Dynamically growing bar charts, line graphs, dashboards that make boring data vivid.","knowledgeEducation":"Knowledge Education","knowledgeEducationDesc":"Physics demonstrations, astronomical motion, molecular structures—explain complex concepts through animation.","specialEffects":"Special Effects","specialEffectsDesc":"Explosions, fireworks, magic particles, weather effects (rain/snow) to enhance visual impact.","gameAnimation":"Game Animation","gameAnimationDesc":"Character idle, jump actions, item pickup feedback, UI panel popups—perfect for lightweight game development."},"labSection":{"title":"Web Animation Lab","subtitle":"Preview core motion patterns used in SVG, HTML, and interactive web experiences.","controlsTitle":"Pick an Effect","effects":[{"id":"motion","title":"Motion","description":"Smooth position shift"},{"id":"scale","title":"Scale","description":"Breathing or pulse"},{"id":"rotate","title":"Rotate","description":"Spin or loader"},{"id":"stroke","title":"Stroke","description":"Draw-on path effect"},{"id":"glow","title":"Glow","description":"Neon highlight pulse"},{"id":"morph","title":"Morph","description":"Shape transitions"}]},"whatIsSection":{"title":"What is Web Animation?","content":["Web animation is any motion designed to run natively on the web, including SVG animation, animated HTML interfaces, interactive product demos, and data-driven experiences. It adds clarity, feedback, and visual rhythm to websites and apps.","SVG animation is one important branch of web animation. It is ideal for lightweight vector motion such as icons, loaders, diagrams, and stylized explainers, while HTML, CSS, and JavaScript are often better for full-page interaction and UI behavior.","An AI web animation generator lets you describe the experience you want in natural language, then turns that description into exportable motion for the browser. That can mean SVG output, interactive pages, or a reusable animation concept for your product."],"benefits":["Web-Native Delivery - Motion runs directly in the browser instead of relying only on video","Flexible Output - Use SVG for vector motion or HTML for richer interactive pages","SEO Friendly - Search engines can index real web content better than video-only assets","Highly Editable - Adjust colors, timing, layout, and interactions after generation","Excellent Performance - Lightweight motion can load quickly and feel responsive","Interactive - Support clicks, hover states, filters, tabs, and dynamic content","Cross-Platform Compatible - Works across modern browsers and device sizes","Easy Integration - Embed in landing pages, apps, docs, or product walkthroughs"]},"howToSection":{"title":"How to Generate High-Quality Web Animations","intro":"Creating premium web animation with AI is much easier when your prompt matches the final experience you want. Use the steps below to move from idea to polished motion.","steps":[{"title":"Define the Animation Goal","description":"Decide whether you need an SVG loader, a product demo, a landing-page interaction, or a data story. Clear intent improves output quality."},{"title":"Describe the Subject & Motion","description":"State the main elements and the type of movement: rotate, bounce, morph, reveal, scroll, or respond to user input. Specificity gives the AI stronger direction."},{"title":"Add Timing & Easing","description":"Include duration, easing curves, and loop behavior. Example: 2s duration, ease-in-out, infinite loop."},{"title":"Lock the Visual Style","description":"Mention colors, typography, gradients, spacing, line width, and style references such as minimal, editorial, neon, or dashboard UI."},{"title":"Iterate and Refine","description":"Adjust the prompt based on the result. Small changes in pacing, layout, or interaction rules often improve the final experience."},{"title":"Export and Implement","description":"Preview on multiple devices, then export SVG, video, or HTML depending on the use case and implementation plan."}]},"promptFormula":{"title":"Prompt Formula for Pro Results","description":"A simple structure makes your prompts easier to understand and improves output quality.","tokens":["Subject","Motion","Style","Color","Timing","Easing","Loop"],"exampleLabel":"Example Prompt","example":"Create a glowing circular loader, gradient from orange to cyan, smooth clockwise rotation, 2s duration, ease-in-out, infinite loop."},"tipsSection":{"title":"Professional Prompt Techniques","subtitle":"Use these best practices to improve motion quality, clarity, and real-world usability."},"examplesSection":{"title":"Featured Examples & Prompts","subtitle":"Copy pro prompts, see the output style, and start crafting your own.","filterAll":"All","viewPrompt":"View Prompt","hidePrompt":"Hide Prompt","tryPrompt":"Try This Prompt","previewLabel":"SVG Output"},"ctaSection":{"title":"Ready to Create Your First Web Animation?","subtitle":"Start using AI to create SVG motion, interactive pages, and export-ready web experiences.","buttonText":"Start Creating"},"faqSection":{"title":"Frequently Asked Questions","items":[{"question":"What is an HTML video?","answer":"An HTML video is a video built with web technology. You start from a topic or short description, and the system generates the full script, storyboard, narration, and matching HTML scenes, making it well suited for explainers, product demos, and story-driven content."},{"question":"What is web animation?","answer":"Web animation is motion that runs natively in the browser. It includes SVG animation, animated user interfaces, interactive product demos, data visualization, and motion-driven landing pages."},{"question":"How is SVG animation different from web animation?","answer":"SVG animation is one type of web animation. It is best for vector-based motion such as icons, diagrams, and loaders. Web animation is broader and also includes interactive HTML pages, UI motion, and JavaScript-driven experiences."},{"question":"What does an AI web animation generator do?","answer":"It turns a text prompt into browser-ready motion. Depending on the request, that can mean SVG animation, an interactive HTML page, a product demo flow, or another motion format you can preview and export."},{"question":"Do I need programming knowledge to use this?","answer":"Not at all. You describe the experience you want, and the generator handles the implementation. Design intuition helps, but it is not required."},{"question":"How do I write good prompts?","answer":"Describe the subject, interaction, motion, style, color, timing, easing, and looping. Example: 'Create a product hero section with floating cards, subtle scroll reveal, orange accents, and a clean editorial layout.'"},{"question":"Can I use the generated animations commercially?","answer":"Yes, the animations you create are yours to use in commercial or personal projects."},{"question":"What formats can I export?","answer":"You can export GIF, MP4 video, and HTML, depending on the type of animation or webpage you create."},{"question":"What if the exported file looks wrong or won’t play?","answer":"If the exported video cannot play or differs from what you see on the site, use the “Direct recording” option in the export video menu for a true WYSIWYG capture. We recommend Chrome for best results."},{"question":"Will other people see my animations?","answer":"Your privacy is protected. Unless you choose to publish them, your animations are visible only to you."},{"question":"What if the generated animation doesn't meet my expectations?","answer":"Refine the prompt with clearer motion, timing, layout, or interaction details, and iterate. The more specific the prompt, the closer the output will match your intent."},{"question":"Which types of animations are not suitable for generation?","answer":"SVG animation is code-generated animation, suitable for images composed of geometric elements such as points, lines, surfaces, and volumes. It can abstractly represent things, but it is not suitable for generating realistic images."}]}},"notifications":{"title":"Notifications","empty":"No notifications","loading":"Loading...","markAllRead":"Mark all as read","viewDetail":"View details","gotIt":"Got it"}},"children":["$","$Ld",null,{"children":[["$","$Le",null,{"locale":"en"}],["$","$L3",null,{"parallelRouterKey":"children","segmentPath":["children","$4","children"],"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","notFoundStyles":"$undefined"}]]}]}]
b:[["$","meta","0",{"name":"viewport","content":"width=device-width, initial-scale=1"}],["$","meta","1",{"charSet":"utf-8"}],["$","title","2",{"children":"Web Animation Examples & Gallery | SvgAnimate"}],["$","meta","3",{"name":"description","content":"Browse AI-generated web animation examples, including SVG motion, interactive pages, product demos, and data visualizations."}],["$","link","4",{"rel":"canonical","href":"https://svganimate.ai/en/gallery"}],["$","link","5",{"rel":"alternate","hrefLang":"zh","href":"https://svganimate.ai/zh/gallery"}],["$","link","6",{"rel":"alternate","hrefLang":"en","href":"https://svganimate.ai/en/gallery"}],["$","link","7",{"rel":"alternate","hrefLang":"x-default","href":"https://svganimate.ai/en/gallery"}],["$","link","8",{"rel":"icon","href":"/icon.ico?fd240352ad5c4cdf","type":"image/x-icon","sizes":"256x256"}]]
1:null
f:I[8343,["4337","static/chunks/91d50c91-c935f1c65821902f.js","7279","static/chunks/b1644e8c-d749f036b947b0a4.js","4304","static/chunks/4304-75b83db16b0d34ab.js","3084","static/chunks/3084-31efe02951f135b2.js","2273","static/chunks/2273-0ea4ab052b9b0cfd.js","575","static/chunks/575-dcb82cf3e64bc6db.js","9010","static/chunks/9010-023e265f797ceb68.js","4450","static/chunks/4450-b500ef111eded326.js","989","static/chunks/989-00a9380fdf607821.js","1326","static/chunks/1326-2bdf304cc7ffd395.js","218","static/chunks/218-d2272020a411d99d.js","3674","static/chunks/3674-f61861513513805f.js","6489","static/chunks/6489-7d70453ac04f1a63.js","3482","static/chunks/3482-39727346ba3389e1.js","7628","static/chunks/7628-794514fd8ea38990.js","5922","static/chunks/5922-e91b60ad05c19631.js","8477","static/chunks/app/%5Blocale%5D/gallery/%5B%5B...slug%5D%5D/page-cb99adf82ac0d1f5.js"],"AppPageClient"]
10:T7a0,“Brothers, a super ridiculous Linux kernel vulnerability has just been exposed! A normal user can grab the server's root privileges with just one command! Its ID is CVE-2026-31431, codenamed Copy Fail! This vulnerability affects almost all mainstream Linux distributions since 2017, including Ubuntu, CentOS, RHEL, Alibaba Cloud, AWS, and SUSE. As long as you have a regular account on the server, you can complete privilege escalation, and even escape from containers to the host. The principle is actually very clever, let me explain it clearly: Normally, file content is cached in memory, called page cache. We regular users can only read it, not change it at will. However, hackers exploited an encryption interface provided by the kernel called AF_ALG, combined with the splice system call, to secretly insert the target file—for example, /usr/bin/su—into the kernel's encryption process. Because the kernel made a performance optimization in 2017, this optimization caused the kernel to use the memory page of this file as a 'writable buffer'! As a result, during the encryption calculation process, the hacker precisely wrote 4 bytes under their control into it. Although the encryption verification failed in the end, those 4 bytes had already physically modified the su program in memory! By repeating the operation a few times, su can be transformed into a malicious program that 'gives me root directly'. Once executed, privileges are obtained! The whole process doesn't require guessing kernel addresses or race conditions; it's extremely stable.” “Currently, major vendors have urgently pushed patches, so everyone should update their kernels quickly! For temporary mitigation, you can disable the algif_aead module. This vulnerability tells us once again: no matter how small a code optimization is, if not thought through, it can plant a fatal bomb. Alright, that's all for today's share. Like and follow, see you next time!”11:T34572,<!doctype html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Linux内核超级漏洞：Copy Fail 深度解析</title>
  <style>
    @import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;700&display=swap');
    @font-face {
      font-family: "SVGVideoAlimamaShuHei";
      src:
        url("/fonts/AlimamaShuHeiTi-Bold.woff2") format("woff2"),
        url("/fonts/AlimamaShuHeiTi-Bold.woff") format("woff"),
        url("/fonts/AlimamaShuHeiTi-Bold.ttf") format("truetype"),
        local("Alimama ShuHeiTi Bold"),
        local("Alimama ShuHeiTi"),
        local("AlimamaShuHeiTi-Bold"),
        local("AlimamaShuHeiTi");
      font-display: swap;
      font-weight: 700;
      font-style: normal;
    }
    :root {
      color-scheme: dark;
      --bg: #202020;
      --fg: #e2e8f0;
      --muted: #94a3b8;
      --card: rgba(255, 255, 255, 0.05);
      --border: rgba(255, 255, 255, 0.12);
      --sv-editor-height: 50vh;
      --sv-font-display-cn: "JetBrains Mono", "Courier New", Consolas, monospace;
      --sv-font-body-cn: "JetBrains Mono", "Courier New", Consolas, monospace;
      --sv-font-mono: "SFMono-Regular", "JetBrains Mono", "Fira Code", "Courier New", monospace;
      --sv-stage-perspective: 900px;
        --sv-trans-enter-opacity: 0;
        --sv-trans-enter-transform: rotateY(90deg);
        --sv-trans-enter-filter: blur(6px);
        --sv-trans-active-opacity: 1;
        --sv-trans-active-transform: rotateY(0deg);
        --sv-trans-active-filter: blur(0px);
        --sv-trans-exit-opacity: 0;
        --sv-trans-exit-transform: rotateY(-90deg);
        --sv-trans-exit-filter: blur(4px);
        --sv-trans-duration: 0.65s;
        --sv-trans-ease: cubic-bezier(0.36, 0, 0.66, 1);
    }
    * { box-sizing: border-box; }
    html, body {
      margin: 0; width: 100%; height: 100%; overflow: hidden;
      background: var(--bg);
      color: var(--fg);
      font-family: var(--sv-font-body-cn);
    }
    #app { width: 100%; height: 100%; display: flex; flex-direction: column; }
    #viewport { position: relative; flex: 1; min-height: 0; overflow: hidden; }
    #stage {
      position: relative; width: 100%; height: 100%; overflow: hidden;
      perspective: var(--sv-stage-perspective, none);
      perspective-origin: 50% 50%;
    }
    .scene {
      position: absolute; inset: 0; overflow: hidden;
      backface-visibility: hidden;
      -webkit-backface-visibility: hidden;
      opacity: var(--sv-trans-enter-opacity, 0); pointer-events: none;
      transform: var(--sv-trans-enter-transform, translateX(var(--scene-enter-offset, 56px)) scale(0.96));
      filter: var(--sv-trans-enter-filter, blur(10px));
      transition: opacity var(--sv-trans-duration, .55s) var(--sv-trans-ease, cubic-bezier(.22,1,.36,1)), transform var(--sv-trans-duration, .55s) var(--sv-trans-ease, cubic-bezier(.22,1,.36,1)), filter var(--sv-trans-duration, .55s) ease;
      will-change: opacity, transform, filter;
      z-index: 1;
    }
    .scene.scene-active {
      opacity: var(--sv-trans-active-opacity, 1); pointer-events: auto;
      transform: var(--sv-trans-active-transform, translateX(0) scale(1));
      filter: var(--sv-trans-active-filter, blur(0px));
      z-index: 3;
    }
    .scene.scene-exit {
      opacity: var(--sv-trans-exit-opacity, 0); pointer-events: none;
      transform: var(--sv-trans-exit-transform, translateX(var(--scene-exit-offset, -56px)) scale(0.96));
      filter: var(--sv-trans-exit-filter, blur(10px));
      z-index: 2;
    }
    .scene-placeholder {
      height: 100%; border: 1px dashed var(--border); border-radius: 20px; background: var(--card);
      display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 12px; color: var(--muted);
    }
    .sv-scene-root { width: 100%; height: 100%; position: relative; overflow: hidden; }
    .scene-placeholder-title { font-size: 28px; color: var(--fg); font-weight: 700; }
    [data-step] { transition: opacity .35s ease, transform .35s ease, filter .35s ease; }
    #sv-controls {
      background: rgba(19, 19, 19, 0.95);
      border-top: 1px solid rgba(255,255,255,0.08);
      padding: 8px 12px 10px;
      display: flex;
      flex-direction: column;
      gap: 8px;
      backdrop-filter: blur(8px);
      flex-shrink: 0;
    }
    .sv-progress-wrap {
      position: relative;
      height: 22px;
      display: flex;
      align-items: center;
      cursor: pointer;
    }
    .sv-progress-track {
      width: 100%;
      height: 4px;
      border-radius: 999px;
      background: rgba(120, 120, 120, 0.55);
      position: relative;
      overflow: hidden;
    }
    .sv-progress-fill {
      position: absolute;
      left: 0;
      top: 0;
      bottom: 0;
      width: 0%;
      border-radius: 999px;
      background: linear-gradient(90deg, #f97316, #fb923c);
      transition: width 220ms ease;
    }
    .sv-progress-thumb {
      position: absolute;
      top: 50%;
      width: 12px;
      height: 12px;
      border-radius: 50%;
      background: #fff;
      transform: translate(-50%, -50%);
      box-shadow: 0 0 0 1px rgba(0,0,0,0.28);
      pointer-events: none;
    }
    .sv-toolbar {
      display: grid;
      grid-template-columns: 1fr auto 1fr;
      align-items: center;
      gap: 8px;
      min-height: 30px;
    }
    .sv-toolbar-left {
      display: inline-flex;
      align-items: center;
      gap: 6px;
      min-width: 0;
    }
    .sv-toolbar-title {
      font-size: 11px;
      color: #a8b0bb;
      white-space: nowrap;
      overflow: hidden;
      text-overflow: ellipsis;
    }
    .sv-expand-btn {
      width: 20px;
      height: 20px;
      border: none;
      border-radius: 6px;
      background: rgba(255,255,255,0.08);
      color: #cbd5e1;
      display: inline-flex;
      align-items: center;
      justify-content: center;
      cursor: pointer;
      flex: 0 0 auto;
      transition: background 140ms ease, color 140ms ease, transform 120ms ease;
    }
    .sv-expand-btn:hover {
      background: rgba(255,255,255,0.15);
      color: #f8fafc;
    }
    .sv-expand-btn:active {
      transform: scale(0.94);
    }
    body:not(.sv-host-can-edit-panel) .sv-expand-btn {
      display: none;
    }
    .sv-play-btn {
      width: 28px;
      height: 28px;
      border: none;
      border-radius: 999px;
      color: #fff;
      background: #f97316;
      cursor: pointer;
      display: inline-flex;
      align-items: center;
      justify-content: center;
      transition: transform 120ms ease, background 160ms ease;
    }
    .sv-play-btn:hover { background: #fb923c; }
    .sv-play-btn:active { transform: scale(0.94); }
    .sv-play-btn:disabled { opacity: 0.45; cursor: not-allowed; }
    .sv-status {
      text-align: right;
      font-size: 11px;
      color: #8f98a5;
      white-space: nowrap;
      overflow: hidden;
      text-overflow: ellipsis;
    }
    .sv-scene-grid {
      display: flex;
      align-items: stretch;
      gap: 6px;
      flex-wrap: nowrap;
      overflow: hidden;
    }
    .scene-chip {
      position: relative;
      width: 100%;
      border: none;
      border-bottom: 2px solid transparent;
      border-radius: 8px;
      background: rgba(255,255,255,0.06);
      color: #a3a3a3;
      padding: 7px 6px;
      text-align: center;
      font-size: 11px;
      line-height: 1.1;
      white-space: nowrap;
      overflow: hidden;
      text-overflow: ellipsis;
      cursor: pointer;
      transition: transform 140ms ease, color 160ms ease, background 160ms ease, border-color 160ms ease;
    }
    .scene-chip-label {
      display: block;
      width: 100%;
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
    }
    .scene-chip-wrap {
      position: relative;
      flex: 1 1 0;
      min-width: 0;
    }
    .scene-chip-retry {
      position: absolute;
      top: 50%;
      right: 4px;
      transform: translateY(-50%);
      width: 24px;
      height: 24px;
      border: none;
      border-radius: 999px;
      background: #f97316;
      color: #fff;
      display: inline-flex;
      align-items: center;
      justify-content: center;
      box-shadow: 0 4px 10px rgba(0, 0, 0, 0.3);
      opacity: 0;
      pointer-events: none;
      transition: opacity 160ms ease;
      cursor: pointer;
    }
    body.sv-host-can-retry .scene-chip-wrap:hover .scene-chip-retry {
      opacity: 1;
      pointer-events: auto;
    }
    .scene-chip-retry:disabled {
      opacity: 1;
      pointer-events: none;
      background: #f97316;
    }
    .scene-chip-retry-icon {
      width: 18px;
      height: 18px;
      display: block;
    }
    .scene-chip-retry-spinner {
      width: 14px;
      height: 14px;
      aspect-ratio: 1 / 1;
      border: 2px solid #ffffff;
      border-top-color: transparent;
      border-radius: 999px;
      box-sizing: border-box;
      animation: sceneChipSpin 1s linear infinite;
      display: block;
      flex: 0 0 auto;
    }
    @keyframes sceneChipSpin {
      from { transform: rotate(0deg); }
      to { transform: rotate(360deg); }
    }
    .scene-chip:hover { transform: scale(1.02); }
    .scene-chip.is-active {
      color: #fb923c;
      border-bottom-color: #fb923c;
      background: linear-gradient(135deg, rgba(249,115,22,0.2), rgba(168,85,247,0.2));
    }
    .scene-chip.is-failed {
      color: #fca5a5;
      border-bottom-color: rgba(239,68,68,0.95);
      background: rgba(239,68,68,0.12);
    }
    .scene-chip.is-generating {
      color: #fcd34d;
      background: rgba(245,158,11,0.11);
    }
    .sv-editor-overlay {
      position: relative;
      width: 100%;
      height: 0;
      z-index: 40;
      pointer-events: none;
      overflow: hidden;
      transition: height 260ms cubic-bezier(.22,1,.36,1);
    }
    .sv-editor-backdrop {
      position: absolute;
      inset: auto 0 0 0;
      height: 100%;
      background: rgba(0, 0, 0, 0.45);
      opacity: 0;
      transition: opacity 220ms ease;
    }
    .sv-editor-sheet {
      position: absolute;
      left: 0;
      right: 0;
      bottom: 0;
      height: 100%;
      background: rgba(16, 16, 16, 0.96);
      border-top: 1px solid rgba(255,255,255,0.12);
      box-shadow: 0 -14px 30px rgba(0,0,0,0.45);
      transform: translateY(102%);
      transition: transform 260ms cubic-bezier(.22,1,.36,1);
      display: flex;
      flex-direction: column;
    }
    .sv-editor-overlay.is-open {
      pointer-events: auto;
      height: var(--sv-editor-height, 50vh);
    }
    .sv-editor-overlay.is-open .sv-editor-backdrop {
      opacity: 1;
    }
    .sv-editor-overlay.is-open .sv-editor-sheet {
      transform: translateY(0);
    }
    .sv-editor-resize-handle {
      width: 100%;
      height: 14px;
      display: flex;
      align-items: center;
      justify-content: center;
      cursor: ns-resize;
      flex: 0 0 auto;
      touch-action: none;
    }
    .sv-editor-resize-handle::before {
      content: "";
      width: 52px;
      height: 4px;
      border-radius: 999px;
      background: rgba(148, 163, 184, 0.7);
    }
    .sv-editor-header {
      padding: 10px 12px;
      border-bottom: 1px solid rgba(255,255,255,0.1);
      display: grid;
      grid-template-columns: 1fr auto;
      align-items: center;
      gap: 8px;
    }
    .sv-editor-header-title {
      color: #f1f5f9;
      font-size: 13px;
      font-weight: 600;
      white-space: nowrap;
      overflow: hidden;
      text-overflow: ellipsis;
    }
    .sv-editor-actions {
      display: inline-flex;
      align-items: center;
      gap: 6px;
    }
    .sv-editor-btn {
      border: none;
      border-radius: 8px;
      cursor: pointer;
      font-size: 12px;
      line-height: 1;
      padding: 7px 10px;
      color: #e2e8f0;
      background: rgba(255,255,255,0.1);
      transition: background 120ms ease;
    }
    .sv-editor-btn:hover {
      background: rgba(255,255,255,0.18);
    }
    .sv-editor-btn.is-primary {
      color: #fff;
      background: #f97316;
    }
    .sv-editor-btn.is-primary:hover {
      background: #fb923c;
    }
    .sv-editor-list {
      flex: 1;
      min-height: 0;
      overflow: auto;
      padding: 10px 12px 14px;
      display: grid;
      gap: 10px;
      grid-template-columns: repeat(2, minmax(0, 1fr));
      align-items: start;
      align-content: start;
    }
    .sv-editor-item {
      border: 1px solid rgba(255,255,255,0.12);
      border-radius: 10px;
      background: rgba(255,255,255,0.03);
      padding: 10px;
      display: grid;
      grid-template-rows: auto auto auto;
      gap: 10px;
      align-content: start;
      cursor: pointer;
    }
    .sv-editor-item.is-active {
      border-color: rgba(249,115,22,0.65);
      box-shadow: inset 0 0 0 1px rgba(249,115,22,0.28);
    }
    .sv-editor-item-head {
      display: flex;
      align-items: center;
      justify-content: space-between;
      gap: 8px;
      min-height: 30px;
    }
    .sv-editor-item-title {
      border: none;
      border-radius: 8px;
      color: #fb923c;
      background: rgba(255,255,255,0.08);
      font-size: 12px;
      font-weight: 600;
      padding: 6px 8px;
      cursor: pointer;
      white-space: nowrap;
      overflow: hidden;
      text-overflow: ellipsis;
      text-align: left;
    }
    .sv-editor-item-title:hover {
      background: rgba(255,255,255,0.14);
    }
    .sv-editor-status {
      color: #94a3b8;
      font-size: 11px;
      white-space: nowrap;
    }
    .sv-editor-status-wrap {
      display: inline-flex;
      align-items: center;
      gap: 6px;
    }
    .scene-chip-retry.sv-editor-retry {
      position: static;
      transform: none;
      opacity: 1;
      pointer-events: auto;
      width: 24px;
      height: 24px;
      flex: 0 0 auto;
    }
    .sv-editor-field {
      display: grid;
      gap: 6px;
    }
    .sv-editor-label {
      color: #a3a3a3;
      font-size: 11px;
      font-weight: 700;
    }
    .sv-editor-textarea {
      width: 100%;
      border-radius: 8px;
      border: 1px solid rgba(255,255,255,0.14);
      background: rgba(0,0,0,0.24);
      color: #f1f5f9;
      font-size: 12px;
      line-height: 1.45;
      padding: 8px 9px;
      resize: none;
      min-height: 110px;
      height: 110px;
    }
    .sv-editor-textarea[data-field="scriptText"] {
      min-height: 84px;
    }
    .sv-editor-textarea:focus {
      outline: none;
      border-color: rgba(249,115,22,0.7);
      box-shadow: 0 0 0 2px rgba(249,115,22,0.18);
    }
    body.sv-theme-light .sv-editor-textarea {
      border-color: rgba(15,23,42,0.18);
      background: rgba(248,250,252,0.92);
      color: #0f172a;
    }
    body.sv-theme-light .sv-editor-textarea:focus {
      border-color: rgba(249,115,22,0.72);
      box-shadow: 0 0 0 2px rgba(249,115,22,0.2);
    }
    .sv-editor-script-view {
      width: 100%;
      color: #e2e8f0;
      font-size: 12px;
      line-height: 1.5;
      white-space: pre-wrap;
      word-break: break-word;
      min-height: 84px;
      max-height: 84px;
      overflow: auto;
    }
    .sv-editor-save-tip {
      color: #fbbf24;
      font-size: 11px;
      opacity: 0;
      transition: opacity 140ms ease;
      justify-self: end;
    }
    .sv-editor-save-tip.is-visible {
      opacity: 1;
    }
    body.sv-theme-light .sv-editor-backdrop {
      background: rgba(15, 23, 42, 0.2);
    }
    body.sv-theme-light .sv-editor-sheet {
      background: rgba(255, 255, 255, 0.98);
      border-top-color: rgba(15, 23, 42, 0.12);
      box-shadow: 0 -14px 30px rgba(15, 23, 42, 0.16);
    }
    body.sv-theme-light .sv-editor-header {
      border-bottom-color: rgba(15, 23, 42, 0.1);
    }
    body.sv-theme-light .sv-editor-header-title {
      color: #0f172a;
    }
    body.sv-theme-light .sv-editor-btn {
      color: #334155;
      background: rgba(15, 23, 42, 0.08);
    }
    body.sv-theme-light .sv-editor-btn:hover {
      background: rgba(15, 23, 42, 0.14);
    }
    body.sv-theme-light .sv-editor-btn.is-primary {
      color: #ffffff;
      background: #f97316;
    }
    body.sv-theme-light .sv-editor-btn.is-primary:hover {
      background: #fb923c;
    }
    body.sv-theme-light .sv-editor-item {
      border-color: rgba(15, 23, 42, 0.12);
      background: rgba(248, 250, 252, 0.95);
    }
    body.sv-theme-light .sv-editor-item.is-active {
      border-color: rgba(249,115,22,0.72);
      box-shadow: inset 0 0 0 1px rgba(249,115,22,0.3);
    }
    body.sv-theme-light .sv-editor-item-title {
      color: #ea580c;
      background: rgba(249, 115, 22, 0.12);
    }
    body.sv-theme-light .sv-editor-item-title:hover {
      background: rgba(249, 115, 22, 0.18);
    }
    body.sv-theme-light .sv-editor-status {
      color: #64748b;
    }
    body.sv-theme-light .sv-editor-label {
      color: #475569;
    }
    body.sv-theme-light .sv-editor-script-view {
      color: #334155;
    }
    body.sv-theme-light .sv-editor-save-tip {
      color: #c2410c;
    }
    body.sv-editor-open #viewport {
      flex: 1 1 calc(100vh - var(--sv-editor-height, 50vh));
      min-height: 0;
    }
    body.sv-editor-open #sv-controls {
      display: none;
    }
    body.sv-recording .sv-toolbar,
    body.sv-recording .sv-scene-grid {
      display: none;
    }
    body.sv-recording #sv-controls {
      display: none;
    }
    body.sv-theme-light .sv-editor-list,
    body.sv-theme-light .sv-editor-script-view,
    body.sv-theme-light .sv-editor-textarea {
      scrollbar-color: rgba(148, 163, 184, 0.85) rgba(226, 232, 240, 0.85);
    }
    body.sv-theme-light .sv-editor-list::-webkit-scrollbar,
    body.sv-theme-light .sv-editor-script-view::-webkit-scrollbar,
    body.sv-theme-light .sv-editor-textarea::-webkit-scrollbar {
      width: 10px;
      height: 10px;
    }
    body.sv-theme-light .sv-editor-list::-webkit-scrollbar-track,
    body.sv-theme-light .sv-editor-script-view::-webkit-scrollbar-track,
    body.sv-theme-light .sv-editor-textarea::-webkit-scrollbar-track {
      background: rgba(226, 232, 240, 0.85);
      border-radius: 999px;
    }
    body.sv-theme-light .sv-editor-list::-webkit-scrollbar-thumb,
    body.sv-theme-light .sv-editor-script-view::-webkit-scrollbar-thumb,
    body.sv-theme-light .sv-editor-textarea::-webkit-scrollbar-thumb {
      background: rgba(148, 163, 184, 0.85);
      border-radius: 999px;
      border: 2px solid rgba(226, 232, 240, 0.85);
    }
    body.sv-theme-light .sv-editor-list::-webkit-scrollbar-thumb:hover,
    body.sv-theme-light .sv-editor-script-view::-webkit-scrollbar-thumb:hover,
    body.sv-theme-light .sv-editor-textarea::-webkit-scrollbar-thumb:hover {
      background: rgba(100, 116, 139, 0.95);
    }
/* scene-01 */
.sv-scene-root-scene-01 .s-scene-01-container{
  position: relative;
  width: 100%;
  height: 100%;
  background: #000000;
  color: #00FF41;
  font-family: var(--sv-font-mono);
  overflow: hidden;
  display: flex;
  justify-content: center;
  align-items: center;
}.sv-scene-root-scene-01 .scanline{
  position: absolute;
  inset: 0;
  background: repeating-linear-gradient(rgba(0,0,0,0) 0px, rgba(0,0,0,0) 1px, rgba(0,255,65,0.08) 2px, rgba(0,255,65,0.08) 3px);
  pointer-events: none;
  z-index: 10;
}.sv-scene-root-scene-01 .bg-grid{
  position: absolute;
  inset: 0;
  background-image: linear-gradient(rgba(0, 255, 65, 0.05) 1px, transparent 1px), linear-gradient(90deg, rgba(0, 255, 65, 0.05) 1px, transparent 1px);
  background-size: 40px 40px;
  animation: grid-move 20s linear infinite;
}@keyframes grid-move{
  from { background-position: 0 0; }
  to { background-position: 40px 40px; }
}.sv-scene-root-scene-01 .alert-overlay{
  position: absolute;
  z-index: 5;
  text-align: center;
  animation: alert-flicker 0.2s infinite alternate;
}.sv-scene-root-scene-01 .ascii-alert{
  font-size: 42px;
  font-weight: bold;
  border: 4px solid #00FF41;
  padding: 20px 40px;
  background: rgba(0, 255, 65, 0.1);
}@keyframes alert-flicker{
  0% { opacity: 1; transform: scale(1); }
  100% { opacity: 0.7; transform: scale(1.02); }
}.sv-scene-root-scene-01 .terminal-wrapper{
  display: flex;
  gap: 120px;
  z-index: 4;
}.sv-scene-root-scene-01 .terminal-card{
  width: 320px;
  height: 200px;
  background: rgba(0, 0, 0, 0.9);
  border: 1px solid rgba(0, 255, 65, 0.4);
  box-shadow: 0 0 20px rgba(0, 255, 65, 0.1);
  display: flex;
  flex-direction: column;
  transition: transform 0.5s cubic-bezier(0.22, 1, 0.36, 1), opacity 0.5s;
}.sv-scene-root-scene-01 .terminal-header{
  background: rgba(0, 255, 65, 0.2);
  padding: 6px 12px;
  font-size: 14px;
  border-bottom: 1px solid rgba(0, 255, 65, 0.4);
}.sv-scene-root-scene-01 .terminal-body{
  padding: 15px;
  font-size: 18px;
  line-height: 1.6;
}.sv-scene-root-scene-01 .prompt{ color: #39FF14; margin-right: 8px; }.sv-scene-root-scene-01 .cursor{
  animation: blink 0.8s infinite;
}@keyframes blink{
  50% { opacity: 0; }
}.sv-scene-root-scene-01 .lightning-container{
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  pointer-events: none;
}.sv-scene-root-scene-01 .lightning-path{
  fill: none;
  stroke: #39FF14;
  stroke-width: 4;
  stroke-dasharray: 600;
  stroke-dashoffset: 600;
  filter: url(#glow);
  animation: strike 0.5s forwards;
}@keyframes strike{
  to { stroke-dashoffset: 0; }
}.sv-scene-root-scene-01 .status-text{
  margin-top: 250px;
  font-size: 24px;
  background: #00FF41;
  color: #000;
  padding: 5px 15px;
  animation: slide-up 0.4s ease-out;
}@keyframes slide-up{
  from { opacity: 0; transform: translateY(20px); }
  to { opacity: 1; transform: translateY(0); }
}.sv-scene-root-scene-01 /* Step Transitions */
[data-step="1"]{ opacity: 0; transform: translateY(30px); }.sv-scene-root-scene-01 .active [data-step="1"]{ opacity: 1; transform: translateY(0); }.sv-scene-root-scene-01 [data-step="2"]{ opacity: 0; }.sv-scene-root-scene-01 .active [data-step="2"]{ opacity: 1; }.sv-scene-root-scene-01 .terminal-card.right.active-root{
  border-color: #39FF14;
  box-shadow: 0 0 30px rgba(0, 255, 65, 0.4);
}

/* scene-02 */
.sv-scene-root-scene-02 .s-scene-02-container{
  position: relative;
  width: 100%;
  height: 100%;
  background: #000000;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  font-family: var(--sv-font-mono);
}.sv-scene-root-scene-02 .crt-overlay{
  position: absolute;
  inset: 0;
  background: repeating-linear-gradient(rgba(0,0,0,0) 0px, rgba(0,0,0,0) 1px, rgba(0,255,65,0.06) 2px, rgba(0,255,65,0.06) 3px);
  pointer-events: none;
  z-index: 10;
}.sv-scene-root-scene-02 .ambient-phosphor{
  position: absolute;
  width: 120%;
  height: 120%;
  background: radial-gradient(circle, rgba(0,255,65,0.05) 0%, transparent 70%);
  animation: phosphor-pulse 8s ease-in-out infinite;
  pointer-events: none;
}.sv-scene-root-scene-02 .grid-bg{
  position: absolute;
  inset: 0;
  background-image: linear-gradient(rgba(0, 255, 65, 0.03) 1px, transparent 1px), linear-gradient(90deg, rgba(0, 255, 65, 0.03) 1px, transparent 1px);
  background-size: 50px 50px;
  opacity: 0.5;
}.sv-scene-root-scene-02 .vulnerability-wrapper{
  position: relative;
  z-index: 5;
  opacity: 0;
  transform: scale(0);
  transition: all 0.6s cubic-bezier(0.175, 0.885, 0.32, 1.275);
}.sv-scene-root-scene-02 .active [data-step="0"]{
  opacity: 1;
  transform: scale(1);
}.sv-scene-root-scene-02 .cve-text{
  font-size: 108px;
  font-weight: 900;
  color: #00FF41;
  letter-spacing: -2px;
  text-shadow: 0 0 20px rgba(0, 255, 65, 0.6);
  margin: 0;
  animation: blue-breath 4s ease-in-out infinite;
}@keyframes blue-breath{
  0%, 100% { text-shadow: 0 0 20px rgba(0, 255, 65, 0.4); }
  50% { text-shadow: 0 0 40px rgba(60, 150, 255, 0.7), 0 0 10px rgba(0, 255, 65, 0.5); }
}.sv-scene-root-scene-02 .fail-container{
  position: absolute;
  top: 60%;
  display: flex;
  flex-direction: column;
  align-items: center;
  opacity: 0;
  transform: translateY(40px);
  transition: all 0.5s ease-out;
}.sv-scene-root-scene-02 .active [data-step="1"]{
  opacity: 1;
  transform: translateY(0);
}.sv-scene-root-scene-02 .fail-text{
  font-size: 140px;
  font-weight: 900;
  color: #00FF41; 
  /* Semantically red but visually terminal phosphor */
  background: #000;
  border: 4px solid #00FF41;
  padding: 0 40px;
  mix-blend-mode: screen;
  animation: critical-glitch 0.2s steps(2) infinite;
}@keyframes critical-glitch{
  0% { transform: skewX(0); opacity: 1; border-color: #00FF41; }
  50% { transform: skewX(5deg); opacity: 0.8; border-color: rgba(255, 50, 50, 0.8); }
}.sv-scene-root-scene-02 .tear-particles{
  position: absolute;
  inset: -100px -200px;
  pointer-events: none;
  z-index: -1;
}.sv-scene-root-scene-02 .tear-line{
  fill: none;
  stroke: #00FF41;
  stroke-width: 2;
  stroke-dasharray: 1000;
  stroke-dashoffset: 1000;
  opacity: 0.4;
}.sv-scene-root-scene-02 .active [data-step="1"] .tear-line{
  animation: tear-draw 0.8s forwards;
}.sv-scene-root-scene-02 .l2{ animation-delay: 0.1s; }.sv-scene-root-scene-02 .l3{ animation-delay: 0.2s; }@keyframes tear-draw{
  to { stroke-dashoffset: 0; opacity: 0.1; }
}@keyframes phosphor-pulse{
  0%, 100% { opacity: 0.4; transform: scale(1); }
  50% { opacity: 0.6; transform: scale(1.05); }
}

/* scene-03 */
.sv-scene-root-scene-03 .s-scene-03-container{
  position: relative;
  width: 100%;
  height: 100%;
  background: #000000;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  overflow: hidden;
  font-family: var(--sv-font-mono);
  color: #00FF41;
}.sv-scene-root-scene-03 .crt-lines{
  position: absolute;
  inset: 0;
  background: repeating-linear-gradient(rgba(0,0,0,0) 0px, rgba(0,0,0,0) 2px, rgba(0,255,65,0.08) 3px, rgba(0,255,65,0.08) 4px);
  pointer-events: none;
  z-index: 10;
}.sv-scene-root-scene-03 .ambient-glow{
  position: absolute;
  width: 100%;
  height: 100%;
  background: radial-gradient(circle at center, rgba(0,255,65,0.04) 0%, transparent 70%);
  pointer-events: none;
}.sv-scene-root-scene-03 /* Step 0: Timeline */
.timeline-section{
  width: 80%;
  margin-bottom: 60px;
  opacity: 0;
  transform: translateX(-50px);
  transition: all 0.6s cubic-bezier(0.23, 1, 0.32, 1);
}.sv-scene-root-scene-03 .active [data-step="0"]{ opacity: 1; transform: translateX(0); }.sv-scene-root-scene-03 .timeline-wrapper{
  display: flex;
  align-items: center;
  gap: 20px;
  font-size: 28px;
}.sv-scene-root-scene-03 .timeline-bar{
  flex-grow: 1;
  height: 4px;
  background: rgba(0, 255, 65, 0.2);
  position: relative;
  display: flex;
  align-items: center;
}.sv-scene-root-scene-03 .timeline-fill{
  position: absolute;
  left: 0;
  height: 100%;
  width: 0%;
  background: #00FF41;
  box-shadow: 0 0 10px #00FF41;
  transition: width 1.5s ease-in-out;
}.sv-scene-root-scene-03 .active [data-step="0"] .timeline-fill{ width: 100%; }.sv-scene-root-scene-03 .timeline-arrow{
  position: absolute;
  right: -10px;
  font-weight: bold;
  text-shadow: 0 0 5px #00FF41;
}.sv-scene-root-scene-03 .pulse-text{
  animation: phosphor-flicker 2s infinite;
}.sv-scene-root-scene-03 /* Step 1: Grid */
.distro-grid-container{
  width: 85%;
  position: relative;
  opacity: 0;
  transition: opacity 0.5s ease;
}.sv-scene-root-scene-03 .active [data-step="1"]{ opacity: 1; }.sv-scene-root-scene-03 .grid-header{
  font-size: 14px;
  margin-bottom: 10px;
  color: rgba(0, 255, 65, 0.6);
  letter-spacing: 2px;
}.sv-scene-root-scene-03 .distro-grid{
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 30px;
  padding: 40px;
  border: 1px solid rgba(0, 255, 65, 0.3);
  background: rgba(0, 255, 65, 0.02);
}.sv-scene-root-scene-03 .distro-card{
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 15px;
  transition: all 0.4s ease;
}.sv-scene-root-scene-03 .distro-card iconify-icon{
  font-size: 64px;
  color: #00FF41;
  filter: drop-shadow(0 0 5px rgba(0, 255, 65, 0.4));
}.sv-scene-root-scene-03 .distro-name{
  font-size: 18px;
  text-transform: uppercase;
}.sv-scene-root-scene-03 /* Step 2: Vulnerable State */
.active [data-step="2"] .distro-card iconify-icon{
  color: #FF3131;
  filter: drop-shadow(0 0 8px rgba(255, 49, 49, 0.6));
  animation: icon-glitch 0.3s infinite alternate;
}.sv-scene-root-scene-03 .shimmer-overlay{
  position: absolute;
  inset: 0;
  background: linear-gradient(135deg, transparent 0%, rgba(0,255,65,0.1) 45%, rgba(0,255,65,0.2) 50%, rgba(0,255,65,0.1) 55%, transparent 100%);
  background-size: 200% 200%;
  pointer-events: none;
  opacity: 0;
}.sv-scene-root-scene-03 .active [data-step="2"] .shimmer-overlay{
  opacity: 1;
  animation: shimmer-scan 3s infinite linear;
}.sv-scene-root-scene-03 .vulnerable-overlay{
  position: absolute;
  inset: 0;
  display: flex;
  justify-content: center;
  align-items: center;
  z-index: 20;
  pointer-events: none;
  opacity: 0;
  transform: scale(2);
  transition: all 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
}.sv-scene-root-scene-03 .active [data-step="2"].vulnerable-overlay{
  opacity: 1;
  transform: scale(1) rotate(-10deg);
}.sv-scene-root-scene-03 .stamp-box{
  border: 8px double #FF3131;
  padding: 20px 40px;
  background: rgba(0, 0, 0, 0.9);
  box-shadow: 0 0 30px rgba(255, 49, 49, 0.4);
  text-align: center;
}.sv-scene-root-scene-03 .stamp-text{
  font-size: 84px;
  font-weight: 900;
  color: #FF3131;
  line-height: 1;
}.sv-scene-root-scene-03 .stamp-subtext{
  font-size: 24px;
  color: #FF3131;
  letter-spacing: 10px;
  margin-top: 10px;
}@keyframes phosphor-flicker{
  0%, 100% { opacity: 1; }
  50% { opacity: 0.6; }
  55% { opacity: 0.9; }
}@keyframes shimmer-scan{
  0% { background-position: -200% -200%; }
  100% { background-position: 200% 200%; }
}@keyframes icon-glitch{
  0% { transform: translate(0); }
  50% { transform: translate(-2px, 1px); }
  100% { transform: translate(2px, -1px); }
}

/* scene-04 */
.sv-scene-root-scene-04 .s-scene-04-wrapper{
  position: relative;
  width: 100%;
  height: 100%;
  background: #000000;
  overflow: hidden;
  font-family: var(--sv-font-mono);
  display: flex;
  justify-content: center;
  align-items: center;
}.sv-scene-root-scene-04 .crt-lines{
  position: absolute;
  inset: 0;
  background: repeating-linear-gradient(rgba(0,0,0,0) 0px, rgba(0,0,0,0) 2px, rgba(0,255,65,0.05) 3px, rgba(0,255,65,0.05) 4px);
  pointer-events: none;
  z-index: 10;
}.sv-scene-root-scene-04 .ambient-glow{
  position: absolute;
  width: 100%;
  height: 100%;
  background: radial-gradient(circle at center, rgba(0,255,65,0.03) 0%, transparent 70%);
  pointer-events: none;
}.sv-scene-root-scene-04 .system-layout{
  width: 100%;
  max-width: 1200px;
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 0 10%;
  z-index: 5;
}.sv-scene-root-scene-04 .system-node{
  width: 280px;
  height: 280px;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  border: 2px solid rgba(0, 255, 65, 0.4);
  background: rgba(0, 255, 65, 0.02);
  transition: all 0.5s cubic-bezier(0.165, 0.84, 0.44, 1);
  position: relative;
}.sv-scene-root-scene-04 .box-node{
  border-radius: 4px;
}.sv-scene-root-scene-04 .circle-node{
  border-radius: 50%;
  border-color: rgba(255, 255, 255, 0.3);
}.sv-scene-root-scene-04 .node-label{
  position: absolute;
  top: -40px;
  color: rgba(0, 255, 65, 0.6);
  font-size: 14px;
  letter-spacing: 2px;
}.sv-scene-root-scene-04 .internal-process{
  display: flex;
  align-items: center;
  gap: 10px;
}.sv-scene-root-scene-04 .mono-text, .sv-scene-root-scene-04 .status-text{
  color: #00FF41;
  font-size: 18px;
}.sv-scene-root-scene-04 .cursor-blink{
  width: 10px;
  height: 20px;
  background: #00FF41;
  animation: cursor-blink 1s infinite steps(1);
}.sv-scene-root-scene-04 .vector-layer{
  position: absolute;
  inset: 0;
  pointer-events: none;
}.sv-scene-root-scene-04 .vector-layer svg{
  width: 100%;
  height: 100%;
}.sv-scene-root-scene-04 /* Step 0 Animation */
.active[data-scene-id="scene-04"] #attack-path{
  transition: stroke-dashoffset 0.8s ease-in-out 0.3s;
  stroke-dashoffset: 0;
}.sv-scene-root-scene-04 .active[data-scene-id="scene-04"] #arrow-head{
  transition: opacity 0.2s ease 1.1s;
  opacity: 1;
}.sv-scene-root-scene-04 /* Step 1 Styles */
[data-step="1"] .circle-node{
  border-color: #FF3131;
  background: rgba(255, 49, 49, 0.1);
  box-shadow: 0 0 30px rgba(255, 49, 49, 0.3);
  transform: scale(1.05);
}.sv-scene-root-scene-04 [data-step="1"] .circle-node .status-text{
  color: #FF3131;
  animation: text-warning 0.5s infinite alternate;
}.sv-scene-root-scene-04 .success-banner{
  position: absolute;
  bottom: 10%;
  opacity: 0;
  transform: translateY(20px);
  transition: all 0.5s ease;
}.sv-scene-root-scene-04 .active .success-banner[data-step="1"]{
  opacity: 1;
  transform: translateY(0);
}.sv-scene-root-scene-04 .glitch-box{
  border: 1px solid #FF3131;
  padding: 15px 40px;
  background: rgba(0, 0, 0, 0.9);
  box-shadow: 4px 4px 0px #FF3131;
}.sv-scene-root-scene-04 .success-text{
  color: #FF3131;
  font-size: 28px;
  font-weight: bold;
  letter-spacing: 2px;
}@keyframes cursor-blink{
  0%, 100% { opacity: 1; }
  50% { opacity: 0; }
}@keyframes text-warning{
  from { opacity: 1; text-shadow: 0 0 5px #FF3131; }
  to { opacity: 0.7; text-shadow: 0 0 15px #FF3131; }
}

/* scene-05 */
.sv-scene-root-scene-05 .s-scene-05-wrapper{
  position: relative;
  width: 100%;
  height: 100%;
  background: #000000;
  overflow: hidden;
  display: flex;
  justify-content: center;
  align-items: center;
  font-family: var(--sv-font-mono);
  color: #00FF41;
}.sv-scene-root-scene-05 .crt-scanlines{
  position: absolute;
  inset: 0;
  background: repeating-linear-gradient(rgba(0,0,0,0) 0px, rgba(0,0,0,0) 2px, rgba(0,255,65,0.05) 3px, rgba(0,255,65,0.05) 4px);
  pointer-events: none;
  z-index: 10;
}.sv-scene-root-scene-05 .ambient-phosphor{
  position: absolute;
  width: 100%;
  height: 100%;
  background: radial-gradient(circle at 50% 50%, rgba(0, 255, 65, 0.04) 0%, transparent 80%);
  pointer-events: none;
}.sv-scene-root-scene-05 .memory-container{
  position: relative;
  width: 80%;
  height: 70%;
  border: 1px solid rgba(0, 255, 65, 0.2);
  background: rgba(0, 255, 65, 0.02);
  padding: 40px;
  box-sizing: border-box;
  display: flex;
  justify-content: center;
  align-items: center;
}.sv-scene-root-scene-05 .memory-label{
  position: absolute;
  top: 15px;
  left: 20px;
  font-size: 14px;
  opacity: 0.6;
  letter-spacing: 2px;
}.sv-scene-root-scene-05 .memory-grid{
  position: absolute;
  inset: 0;
  opacity: 0.1;
  background-image: radial-gradient(#00FF41 1px, transparent 1px);
  background-size: 30px 30px;
}.sv-scene-root-scene-05 .page-cache-card{
  width: 480px;
  height: 300px;
  background: rgba(0, 20, 0, 0.9);
  border: 2px solid #00FF41;
  box-shadow: 0 0 30px rgba(0, 255, 65, 0.2);
  padding: 20px;
  position: relative;
  z-index: 5;
  opacity: 0;
  transform: scale(0.9) translateY(20px);
  transition: all 0.6s cubic-bezier(0.16, 1, 0.3, 1);
}.sv-scene-root-scene-05 .page-cache-card[data-step="1"]{
  opacity: 1;
  transform: scale(1) translateY(0);
}.sv-scene-root-scene-05 .card-header{
  border-bottom: 1px solid rgba(0, 255, 65, 0.3);
  padding-bottom: 10px;
  margin-bottom: 20px;
  display: flex;
  justify-content: space-between;
  align-items: center;
}.sv-scene-root-scene-05 .card-title{
  font-size: 20px;
  font-weight: bold;
}.sv-scene-root-scene-05 .memory-addr{
  font-size: 14px;
  opacity: 0.7;
}.sv-scene-root-scene-05 .binary-content{
  font-size: 24px;
  line-height: 1.5;
  letter-spacing: 4px;
  color: rgba(0, 255, 65, 0.8);
}.sv-scene-root-scene-05 .security-status{
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.7);
  backdrop-filter: blur(2px);
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  opacity: 0;
  transition: opacity 0.5s ease;
}.sv-scene-root-scene-05 .security-status[data-step="2"]{
  opacity: 1;
}.sv-scene-root-scene-05 .lock-overlay{
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  margin-bottom: 30px;
  color: #39FF14;
}.sv-scene-root-scene-05 .lock-overlay i{
  width: 64px;
  height: 64px;
}.sv-scene-root-scene-05 .read-only-text{
  font-size: 32px;
  font-weight: 800;
  letter-spacing: 4px;
  border: 2px solid #39FF14;
  padding: 5px 20px;
}.sv-scene-root-scene-05 .forbidden-action{
  display: flex;
  gap: 40px;
}.sv-scene-root-scene-05 .action-item{
  position: relative;
  padding: 5px 15px;
  opacity: 0.6;
}.sv-scene-root-scene-05 .forbidden-sign{
  position: absolute;
  top: 50%;
  left: 50%;
  width: 120%;
  height: 2px;
  background: #FF3131;
  transform: translate(-50%, -50%) rotate(-25deg);
  box-shadow: 0 0 10px #FF3131;
}.sv-scene-root-scene-05 .ghost-text{
  position: absolute;
  bottom: 5%;
  right: 5%;
  font-size: 80px;
  opacity: 0.03;
  font-weight: 900;
  pointer-events: none;
}@keyframes pulse-border{
  0% { border-color: rgba(0, 255, 65, 0.4); }
  50% { border-color: rgba(0, 255, 65, 1); }
  100% { border-color: rgba(0, 255, 65, 0.4); }
}

/* scene-06 */
.sv-scene-root-scene-06 .s-scene-06-wrapper{
  position: relative;
  width: 100%;
  height: 100%;
  background: #000000;
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: space-around;
  font-family: var(--sv-font-mono);
  color: #00FF41;
}.sv-scene-root-scene-06 .crt-overlay{
  position: absolute;
  inset: 0;
  background: repeating-linear-gradient(rgba(0,0,0,0) 0px, rgba(0,0,0,0) 2px, rgba(0,255,65,0.08) 3px, rgba(0,255,65,0.08) 4px);
  pointer-events: none;
  z-index: 100;
}.sv-scene-root-scene-06 .ambient-glow{
  position: absolute;
  width: 100%;
  height: 100%;
  background: radial-gradient(circle at 50% 50%, rgba(0, 255, 65, 0.03) 0%, transparent 70%);
  pointer-events: none;
}.sv-scene-root-scene-06 .terminal-container{
  width: 320px;
  height: 200px;
  border: 1px solid rgba(0, 255, 65, 0.4);
  background: rgba(0, 20, 0, 0.8);
  box-shadow: 0 0 15px rgba(0, 255, 65, 0.1);
  z-index: 10;
  transform: translateX(-100px);
  opacity: 0;
  transition: all 0.6s cubic-bezier(0.16, 1, 0.3, 1);
}.sv-scene-root-scene-06 .term-header{
  background: rgba(0, 255, 65, 0.15);
  padding: 6px 10px;
  display: flex;
  justify-content: space-between;
  align-items: center;
  border-bottom: 1px solid rgba(0, 255, 65, 0.3);
}.sv-scene-root-scene-06 .term-controls{
  display: flex;
  gap: 6px;
}.sv-scene-root-scene-06 .term-controls span{
  width: 8px;
  height: 8px;
  border: 1px solid #00FF41;
  border-radius: 50%;
}.sv-scene-root-scene-06 .term-title{
  font-size: 10px;
  opacity: 0.7;
}.sv-scene-root-scene-06 .term-body{
  padding: 15px;
  font-size: 14px;
}.sv-scene-root-scene-06 .typing-line{
  margin-bottom: 5px;
  overflow: hidden;
  white-space: nowrap;
}.sv-scene-root-scene-06 .highlight{ color: #39FF14; text-shadow: 0 0 5px #00FF41; }.sv-scene-root-scene-06 .conduit-system{
  position: absolute;
  width: 100%;
  height: 100%;
  pointer-events: none;
}.sv-scene-root-scene-06 .conduit-svg{
  position: absolute;
  top: 0; left: 0; width: 100%; height: 100%;
}.sv-scene-root-scene-06 .file-node{
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%) scale(0.9);
  opacity: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  z-index: 20;
  transition: all 0.5s ease;
}.sv-scene-root-scene-06 .file-icon-box{
  font-size: 64px;
  border: 2px solid #00FF41;
  padding: 15px;
  background: rgba(0, 255, 65, 0.05);
  box-shadow: 0 0 20px rgba(0, 255, 65, 0.2);
}.sv-scene-root-scene-06 .file-name{
  font-size: 20px;
  font-weight: bold;
}.sv-scene-root-scene-06 .file-perm{
  font-size: 12px;
  opacity: 0.6;
}.sv-scene-root-scene-06 .pipeline-stage{
  width: 300px;
  height: 250px;
  border: 2px dashed rgba(0, 255, 65, 0.3);
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  opacity: 0;
  transform: translateX(50px);
  transition: all 0.6s ease;
}.sv-scene-root-scene-06 .pipeline-box{
  width: 90%;
  height: 90%;
  background: rgba(0, 255, 65, 0.02);
  border: 1px solid #00FF41;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 20px;
}.sv-scene-root-scene-06 .pipeline-label{
  position: absolute;
  top: -12px;
  background: #000;
  padding: 0 10px;
  font-size: 14px;
}.sv-scene-root-scene-06 .chain-container{
  display: flex;
  gap: 5px;
  opacity: 0;
}.sv-scene-root-scene-06 .chain-link{
  width: 15px;
  height: 30px;
  border: 2px solid #00FF41;
  border-radius: 10px;
}.sv-scene-root-scene-06 .flow-indicator{
  font-size: 12px;
  animation: blink 0.8s infinite;
}.sv-scene-root-scene-06 .side-status{
  position: absolute;
  bottom: 40px;
  right: 40px;
  text-align: right;
}.sv-scene-root-scene-06 .status-item{
  font-size: 14px;
  opacity: 0;
  margin-top: 5px;
}@keyframes shake{
  0%, 100% { transform: translate(-50%, -50%); }
  20% { transform: translate(-52%, -48%); }
  40% { transform: translate(-48%, -52%); }
  60% { transform: translate(-51%, -49%); }
  80% { transform: translate(-49%, -51%); }
}@keyframes blink{
  0%, 100% { opacity: 1; }
  50% { opacity: 0.3; }
}.sv-scene-root-scene-06 .shaking{ animation: shake 0.2s infinite; }.sv-scene-root-scene-06 [data-step="1"] .status-item[data-step="1"]{ opacity: 1; }.sv-scene-root-scene-06 [data-step="2"] .status-item[data-step="2"]{ opacity: 1; }

/* scene-07 */
.sv-scene-root-scene-07 .s-scene-07-wrapper{ width: 100%; height: 100%; background: #000; display: flex; align-items: center; justify-content: center; font-family: var(--sv-font-mono); color: #00FF41; overflow: hidden; position: relative; }.sv-scene-root-scene-07 .crt-overlay{ position: absolute; inset: 0; background: repeating-linear-gradient(rgba(0,0,0,0) 0px, rgba(0,0,0,0) 2px, rgba(0,255,65,0.05) 3px, rgba(0,255,65,0.05) 4px); pointer-events: none; z-index: 10; }.sv-scene-root-scene-07 .ambient-glow{ position: absolute; width: 100%; height: 100%; background: radial-gradient(circle at 50% 50%, rgba(0, 255, 65, 0.05) 0%, transparent 70%); }.sv-scene-root-scene-07 .layout-container{ display: flex; align-items: center; justify-content: space-between; width: 90%; gap: 20px; z-index: 5; }.sv-scene-root-scene-07 .section-label{ font-size: 14px; color: rgba(0,255,65,0.6); margin-bottom: 10px; text-transform: uppercase; letter-spacing: 2px; }.sv-scene-root-scene-07 .terminal-window{ width: 420px; border: 1px solid rgba(0,255,65,0.3); background: rgba(0,20,0,0.9); box-shadow: 0 0 20px rgba(0,255,65,0.1); }.sv-scene-root-scene-07 .term-header{ background: rgba(0,255,65,0.1); padding: 8px 12px; display: flex; align-items: center; border-bottom: 1px solid rgba(0,255,65,0.2); }.sv-scene-root-scene-07 .term-dots{ display: flex; gap: 5px; margin-right: 15px; }.sv-scene-root-scene-07 .term-dots span{ width: 6px; height: 6px; border: 1px solid #00FF41; border-radius: 50%; }.sv-scene-root-scene-07 .term-title{ font-size: 12px; opacity: 0.8; }.sv-scene-root-scene-07 .code-content{ padding: 20px; font-size: 16px; line-height: 1.5; }.sv-scene-root-scene-07 .highlight-target{ background: rgba(0,255,65,0.2); border-left: 3px solid #00FF41; padding-left: 5px; }.sv-scene-root-scene-07 .code-keyword{ color: #39FF14; font-weight: bold; }.sv-scene-root-scene-07 .code-comment{ color: rgba(0,255,65,0.4); }.sv-scene-root-scene-07 .conduit-container{ flex: 1; height: 100px; display: flex; align-items: center; justify-content: center; }.sv-scene-root-scene-07 .arrow-path{ stroke-dasharray: 200; stroke-dashoffset: 200; transition: stroke-dashoffset 0.8s ease; }.sv-scene-root-scene-07 .memory-section{ display: flex; flex-direction: column; align-items: center; }.sv-scene-root-scene-07 .page-frame{ width: 280px; height: 180px; border: 2px solid #00FF41; position: relative; background: rgba(0,255,65,0.02); display: flex; flex-direction: column; justify-content: center; align-items: center; }.sv-scene-root-scene-07 .page-grid{ display: grid; grid-template-columns: repeat(4, 1fr); gap: 10px; opacity: 0.2; position: absolute; inset: 20px; }.sv-scene-root-scene-07 .page-grid span{ border: 1px solid #00FF41; height: 20px; }.sv-scene-root-scene-07 .permission-status{ position: relative; z-index: 2; text-align: center; }.sv-scene-root-scene-07 .lock-container{ transition: all 0.4s ease; }.sv-scene-root-scene-07 .lock-icon{ font-size: 48px; margin-bottom: 10px; }.sv-scene-root-scene-07 .lock-text{ font-size: 18px; }.sv-scene-root-scene-07 .rw-status{ position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%) scale(0); opacity: 0; color: #FF3131; font-weight: 900; font-size: 42px; text-shadow: 0 0 15px rgba(255,49,49,0.8); transition: all 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275); }.sv-scene-root-scene-07 .shattered .lock-container{ transform: scale(1.5) rotate(15deg); opacity: 0; filter: blur(5px); }.sv-scene-root-scene-07 .shattered .rw-status{ transform: translate(-50%, -50%) scale(1); opacity: 1; }.sv-scene-root-scene-07 .alert-pulse-overlay{ position: absolute; inset: 0; background: radial-gradient(circle, rgba(255,0,0,0.15) 0%, transparent 70%); opacity: 0; pointer-events: none; animation: pulse-red 1.5s infinite; }@keyframes pulse-red{ 0%, 100% { opacity: 0; transform: scale(1); } 50% { opacity: 1; transform: scale(1.1); } }.sv-scene-root-scene-07 .status-bar{ position: absolute; bottom: 30px; left: 50%; transform: translateX(-50%); display: flex; gap: 40px; }.sv-scene-root-scene-07 .status-msg{ font-size: 12px; border: 1px solid rgba(0,255,65,0.3); padding: 4px 12px; background: rgba(0,20,0,0.8); }

/* scene-08 */
.sv-scene-root-scene-08 .s-scene-08-wrapper{ width: 100%; height: 100%; background: #000; display: flex; align-items: center; justify-content: center; font-family: var(--sv-font-mono); color: #00FF41; overflow: hidden; position: relative; }.sv-scene-root-scene-08 .crt-scanlines{ position: absolute; inset: 0; background: linear-gradient(rgba(18, 16, 16, 0) 50%, rgba(0, 0, 0, 0.15) 50%), linear-gradient(90deg, rgba(255, 0, 0, 0.03), rgba(0, 255, 0, 0.01), rgba(0, 0, 255, 0.03)); background-size: 100% 3px, 3px 100%; pointer-events: none; z-index: 20; }.sv-scene-root-scene-08 .ambient-glow{ position: absolute; width: 100%; height: 100%; background: radial-gradient(circle, rgba(0, 255, 65, 0.05) 0%, transparent 80%); pointer-events: none; }.sv-scene-root-scene-08 .hex-viewport{ width: 800px; height: 400px; border: 1px solid rgba(0, 255, 65, 0.3); background: rgba(0, 10, 0, 0.9); position: relative; box-shadow: 0 0 40px rgba(0, 0, 0, 0.8); transition: all 0.8s ease; transform: scale(1.1); }.sv-scene-root-scene-08 .viewport-header{ background: rgba(0, 255, 65, 0.1); padding: 8px 15px; display: flex; justify-content: space-between; border-bottom: 1px solid rgba(0, 255, 65, 0.2); font-size: 12px; }.sv-scene-root-scene-08 .hex-grid-container{ padding: 30px; display: flex; justify-content: center; }.sv-scene-root-scene-08 .hex-grid{ display: grid; grid-template-columns: repeat(8, 70px); gap: 15px; }.sv-scene-root-scene-08 .hex-cell{ height: 40px; display: flex; align-items: center; justify-content: center; border: 1px solid rgba(0, 255, 65, 0.1); font-size: 22px; color: #0088ff; transition: all 0.5s ease; position: relative; }.sv-scene-root-scene-08 .hex-cell.infected{ color: #d400ff !important; border-color: #d400ff; text-shadow: 0 0 10px rgba(212, 0, 255, 0.5); }.sv-scene-root-scene-08 .hex-cell.target-active{ background: rgba(0, 255, 65, 0.1); border-color: #00FF41; }.sv-scene-root-scene-08 .missile-container{ position: absolute; inset: 0; pointer-events: none; }.sv-scene-root-scene-08 .byte-missile{ position: absolute; font-size: 24px; font-weight: bold; background: #000; border: 2px solid #00FF41; padding: 5px 10px; box-shadow: 0 0 15px #00FF41; z-index: 10; transition: all 0.6s cubic-bezier(0.6, -0.28, 0.735, 0.045); }.sv-scene-root-scene-08 .byte-missile[data-step="0"]{ opacity: 1; transform: translate(var(--start-x), var(--start-y)); }.sv-scene-root-scene-08 .byte-missile.launched{ transform: translate(var(--end-x), var(--end-y)) scale(0.8); opacity: 0.5; }.sv-scene-root-scene-08 .alert-overlay{ position: absolute; inset: 0; z-index: 30; display: flex; align-items: center; justify-content: center; background: rgba(255, 0, 0, 0.1); backdrop-filter: blur(2px); }.sv-scene-root-scene-08 .alert-box{ background: #000; border: 4px solid #FF3131; padding: 40px; text-align: center; box-shadow: 0 0 50px rgba(255, 49, 49, 0.4); animation: alert-shake 0.2s infinite; }.sv-scene-root-scene-08 .alert-header{ color: #FF3131; font-weight: 900; font-size: 32px; letter-spacing: 4px; margin-bottom: 10px; }.sv-scene-root-scene-08 .alert-content{ color: #FFF; font-size: 20px; border-top: 1px solid #FF3131; padding-top: 10px; }.sv-scene-root-scene-08 .alert-sub{ font-size: 14px; opacity: 0.7; margin-top: 5px; }.sv-scene-root-scene-08 .terminal-footer{ position: absolute; bottom: 40px; left: 100px; right: 100px; }.sv-scene-root-scene-08 .prompt{ color: #00FF41; margin-right: 10px; }.sv-scene-root-scene-08 .typing-text{ font-size: 18px; }.sv-scene-root-scene-08 .cursor{ display: inline-block; width: 10px; height: 20px; background: #00FF41; animation: blink 1s infinite; vertical-align: middle; }.sv-scene-root-scene-08 .progress-section{ margin-top: 15px; display: flex; align-items: center; gap: 15px; }.sv-scene-root-scene-08 .bar-bg{ flex: 1; height: 10px; background: rgba(0, 255, 65, 0.1); border: 1px solid rgba(0, 255, 65, 0.3); }.sv-scene-root-scene-08 .bar-fill{ display: block; width: 0%; height: 100%; background: #00FF41; transition: width 0.6s ease; }@keyframes alert-shake{ 0% { transform: translate(1px, 1px); } 50% { transform: translate(-1px, -1px); } 100% { transform: translate(1px, -1px); } }@keyframes blink{ 0%, 100% { opacity: 1; } 50% { opacity: 0; } }.sv-scene-root-scene-08 .modified-state .hex-viewport{ border-color: #d400ff; box-shadow: 0 0 30px rgba(212, 0, 255, 0.2); }.sv-scene-root-scene-08 .modified-state #mem-status{ color: #d400ff; content: "[ MEMORY_MODIFIED_PERMANENT ]"; }

/* scene-09 */
.sv-scene-root-scene-09 .s-scene-09-wrapper{ width: 100%; height: 100%; background: #000; font-family: var(--sv-font-mono); color: #00FF41; display: flex; align-items: center; justify-content: center; position: relative; overflow: hidden; }.sv-scene-root-scene-09 .crt-scanlines{ position: absolute; inset: 0; background: linear-gradient(rgba(0, 255, 65, 0.05) 50%, transparent 50%); background-size: 100% 4px; pointer-events: none; z-index: 10; }.sv-scene-root-scene-09 .ambient-glow{ position: absolute; inset: 0; background: radial-gradient(circle at center, rgba(0, 255, 65, 0.03) 0%, transparent 70%); pointer-events: none; }.sv-scene-root-scene-09 .terminal-interface{ width: 80%; max-width: 900px; height: 60%; background: rgba(0, 10, 0, 0.95); border: 1px solid rgba(0, 255, 65, 0.3); box-shadow: 0 20px 50px rgba(0, 0, 0, 0.8); display: flex; flex-direction: column; position: relative; z-index: 5; }.sv-scene-root-scene-09 .terminal-header{ height: 30px; background: rgba(0, 255, 65, 0.1); border-bottom: 1px solid rgba(0, 255, 65, 0.2); display: flex; align-items: center; padding: 0 15px; }.sv-scene-root-scene-09 .terminal-buttons{ display: flex; gap: 6px; margin-right: 20px; }.sv-scene-root-scene-09 .terminal-buttons span{ width: 8px; height: 8px; border: 1px solid rgba(0, 255, 65, 0.4); border-radius: 50%; }.sv-scene-root-scene-09 .terminal-title{ font-size: 12px; opacity: 0.7; letter-spacing: 1px; }.sv-scene-root-scene-09 .terminal-content{ flex: 1; padding: 30px; overflow: hidden; position: relative; }.sv-scene-root-scene-09 .log-line{ font-size: 14px; opacity: 0.4; margin-bottom: 8px; font-weight: 300; }.sv-scene-root-scene-09 .prompt-area{ font-size: 32px; display: flex; align-items: center; margin-top: 20px; }.sv-scene-root-scene-09 .prompt-char{ margin-right: 15px; color: #39FF14; font-weight: bold; }.sv-scene-root-scene-09 .command-text{ letter-spacing: 2px; }.sv-scene-root-scene-09 .cursor{ display: inline-block; width: 15px; height: 32px; background: #00FF41; margin-left: 10px; animation: blink 1s infinite steps(2); }.sv-scene-root-scene-09 .result-output{ margin-top: 40px; display: flex; justify-content: center; }.sv-scene-root-scene-09 .root-banner{ font-size: 120px; font-weight: 900; letter-spacing: 15px; text-transform: uppercase; text-shadow: 0 0 30px rgba(0, 255, 65, 0.8); }.sv-scene-root-scene-09 .terminal-footer-bar{ height: 25px; background: rgba(0, 255, 65, 0.1); display: flex; align-items: center; padding: 0 15px; gap: 30px; font-size: 10px; border-top: 1px solid rgba(0, 255, 65, 0.2); }.sv-scene-root-scene-09 .blink-text{ animation: blink 0.5s infinite; }.sv-scene-root-scene-09 .lightning-flash{ position: absolute; inset: 0; background: #00FF41; opacity: 0; pointer-events: none; z-index: 100; }.sv-scene-root-scene-09 .success-frame{ position: absolute; inset: 10px; border: 4px solid #00FF41; box-shadow: inset 0 0 50px rgba(0, 255, 65, 0.3), 0 0 30px rgba(0, 255, 65, 0.2); pointer-events: none; z-index: 101; opacity: 0; transform: scale(1.02); transition: all 0.3s ease; }.sv-scene-root-scene-09 .success-frame[data-step="2"]{ opacity: 1; transform: scale(1); }@keyframes blink{ 0% { opacity: 1; } 100% { opacity: 0; } }@keyframes glitch-flash{ 0% { opacity: 0; } 10% { opacity: 0.8; } 20% { opacity: 0.1; } 30% { opacity: 0.9; } 100% { opacity: 0; } }

/* scene-10 */
.sv-scene-root-scene-10 .s-scene-10-wrapper{ width: 100%; height: 100%; background: #000; font-family: var(--sv-font-mono); color: #00FF41; position: relative; overflow: hidden; }.sv-scene-root-scene-10 .crt-scanlines{ position: absolute; inset: 0; background: linear-gradient(rgba(0, 255, 65, 0.08) 50%, transparent 50%); background-size: 100% 4px; pointer-events: none; z-index: 10; }.sv-scene-root-scene-10 .ambient-glow{ position: absolute; inset: 0; background: radial-gradient(circle at center, rgba(0, 255, 65, 0.03) 0%, transparent 70%); pointer-events: none; }.sv-scene-root-scene-10 .thirds-container{ display: flex; width: 100%; height: 100%; align-items: center; justify-content: space-between; padding: 0 5%; box-sizing: border-box; }.sv-scene-root-scene-10 .col-left, .sv-scene-root-scene-10 .col-center, .sv-scene-root-scene-10 .col-right{ flex: 1; display: flex; flex-direction: column; align-items: center; transition: all 0.5s cubic-bezier(0.22, 1, 0.36, 1); }.sv-scene-root-scene-10 /* Left: Update Box */
.status-box{ border: 2px solid rgba(0, 255, 65, 0.4); padding: 30px; background: rgba(0, 255, 65, 0.05); text-align: center; width: 300px; }.sv-scene-root-scene-10 .status-header{ font-size: 14px; letter-spacing: 2px; border-bottom: 1px solid rgba(0, 255, 65, 0.4); padding-bottom: 10px; margin-bottom: 20px; opacity: 0.8; }.sv-scene-root-scene-10 .main-patch-icon{ font-size: 80px; filter: drop-shadow(0 0 10px rgba(0, 255, 65, 0.5)); }.sv-scene-root-scene-10 .update-text-glitch{ font-size: 28px; font-weight: 900; margin-top: 15px; text-shadow: 2px 0 #39FF14; animation: blink 0.8s infinite; }.sv-scene-root-scene-10 .status-details{ margin-top: 20px; text-align: left; font-size: 12px; line-height: 1.8; opacity: 0.7; }.sv-scene-root-scene-10 /* Right: Terminal */
.command-window{ width: 400px; height: 200px; background: #000; border: 1px solid rgba(0, 255, 65, 0.3); box-shadow: 0 10px 40px rgba(0, 0, 0, 0.8); }.sv-scene-root-scene-10 .window-bar{ height: 25px; background: rgba(0, 255, 65, 0.1); display: flex; align-items: center; padding: 0 10px; gap: 5px; }.sv-scene-root-scene-10 .dot{ width: 6px; height: 6px; border: 1px solid #00FF41; border-radius: 50%; }.sv-scene-root-scene-10 .window-title{ font-size: 10px; margin-left: 10px; opacity: 0.6; }.sv-scene-root-scene-10 .terminal-body{ padding: 15px; font-size: 16px; }.sv-scene-root-scene-10 .user{ color: #39FF14; }.sv-scene-root-scene-10 .shell-output{ margin-top: 15px; font-size: 12px; color: #39FF14; opacity: 0; transform: translateY(10px); transition: 0.4s; }.sv-scene-root-scene-10 [data-step="1"] .shell-output{ opacity: 1; transform: translateY(0); }.sv-scene-root-scene-10 .terminal-cursor{ display: inline-block; width: 8px; height: 18px; background: #00FF41; animation: blink 1s infinite steps(2); vertical-align: middle; margin-left: 4px; }.sv-scene-root-scene-10 /* Center: Bomb */
.bomb-stage{ position: relative; text-align: center; }.sv-scene-root-scene-10 .bomb-svg{ width: 220px; height: 220px; }.sv-scene-root-scene-10 .fuse-path{ stroke-dasharray: 100; stroke-dashoffset: 0; }.sv-scene-root-scene-10 .cut-mark{ opacity: 0; transform: scale(0.5); transform-origin: center; transition: all 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275); }.sv-scene-root-scene-10 [data-step="2"] .cut-mark{ opacity: 1; transform: scale(1); }.sv-scene-root-scene-10 [data-step="2"] .fuse-path{ stroke-dasharray: 10 5; opacity: 0.5; }.sv-scene-root-scene-10 .threat-status{ margin-top: 20px; font-size: 24px; font-weight: 900; letter-spacing: 4px; color: #39FF14; text-shadow: 0 0 15px rgba(0, 255, 65, 0.6); }.sv-scene-root-scene-10 .footer-deco{ position: absolute; bottom: 30px; left: 50%; transform: translateX(-50%); font-size: 12px; opacity: 0.3; letter-spacing: 5px; }@keyframes blink{ 0% { opacity: 1; } 100% { opacity: 0; } }@keyframes draw{ to { stroke-dashoffset: 0; } }

/* scene-11 */
.sv-scene-root-scene-11 .s-scene-11-wrapper{ width: 100%; height: 100%; background: #000; font-family: var(--sv-font-mono); color: #00FF41; position: relative; overflow: hidden; display: flex; flex-direction: column; align-items: center; justify-content: center; }.sv-scene-root-scene-11 .crt-scanlines{ position: absolute; inset: 0; background: linear-gradient(rgba(0, 255, 65, 0.1) 50%, transparent 50%); background-size: 100% 3px; pointer-events: none; z-index: 100; opacity: 0.12; }.sv-scene-root-scene-11 .ambient-glow{ position: absolute; inset: 0; background: radial-gradient(circle at center, rgba(0, 255, 65, 0.04) 0%, transparent 80%); animation: pulse 10s infinite; }.sv-scene-root-scene-11 .logo-overlay{ text-align: center; z-index: 20; transition: transform 0.6s cubic-bezier(0.22, 1, 0.36, 1); }.sv-scene-root-scene-11 .avatar-frame{ width: 120px; height: 120px; border: 2px solid #00FF41; position: relative; padding: 10px; box-shadow: 0 0 20px rgba(0, 255, 65, 0.15); display: inline-block; background: #000; }.sv-scene-root-scene-11 .terminal-logo{ width: 100%; height: 100%; }.sv-scene-root-scene-11 .channel-id{ margin-top: 15px; font-size: 14px; letter-spacing: 2px; opacity: 0.8; }.sv-scene-root-scene-11 .action-container{ position: absolute; inset: 0; pointer-events: none; display: flex; align-items: center; justify-content: center; }.sv-scene-root-scene-11 .action-item{ position: absolute; transition: all 0.6s cubic-bezier(0.175, 0.885, 0.32, 1.275); opacity: 0; transform: scale(0.5); }.sv-scene-root-scene-11 .like-item{ left: 15%; }.sv-scene-root-scene-11 .follow-item{ right: 15%; }.sv-scene-root-scene-11 [data-step="1"] .action-item{ opacity: 1; transform: scale(1); }.sv-scene-root-scene-11 .action-box{ border: 1px solid #00FF41; background: rgba(0, 255, 65, 0.05); padding: 20px; display: flex; flex-direction: column; align-items: center; gap: 10px; }.sv-scene-root-scene-11 .action-box i{ width: 40px; height: 40px; }.sv-scene-root-scene-11 .action-label{ font-size: 12px; font-weight: bold; }.sv-scene-root-scene-11 .closing-text{ position: absolute; bottom: 15%; text-align: center; opacity: 0; transform: translateY(20px); transition: all 0.8s ease; display: flex; align-items: center; gap: 10px; }.sv-scene-root-scene-11 [data-step="1"] .closing-text{ opacity: 1; transform: translateY(0); }.sv-scene-root-scene-11 .rainbow-terminal-text{ font-size: 64px; font-family: var(--sv-font-display-cn); font-weight: 900; background: linear-gradient(90deg, #00FF41, #39FF14, #00FF41); -webkit-background-clip: text; color: transparent; animation: shift 4s linear infinite; text-shadow: 0 0 10px rgba(0, 255, 65, 0.3); }.sv-scene-root-scene-11 .cursor-blink{ width: 25px; height: 60px; background: #00FF41; animation: blink 1s infinite steps(2); }.sv-scene-root-scene-11 .system-footer{ position: absolute; bottom: 5%; font-size: 10px; opacity: 0.4; letter-spacing: 1px; }@keyframes pulse{ 0%, 100% { opacity: 0.04; } 50% { opacity: 0.08; } }@keyframes blink{ 0% { opacity: 1; } 100% { opacity: 0; } }@keyframes shift{ 0% { background-position: 0% 50%; } 100% { background-position: 200% 50%; } }
  </style>
</head>
<body data-orientation="landscape">
  <div id="app">
    <div id="viewport">
      <div id="stage">

<section class="scene" data-scene-id="scene-01">
  <div class="sv-scene-root sv-scene-root-scene-01" data-scene-root-id="scene-01">
<div class="s-scene-01-container">
  <div class="scanline"></div>
  <div class="vignette"></div>

  <!-- Step 0: Alert System -->
  <div class="alert-overlay" data-step="0">
    <div class="alert-content">
      <div class="ascii-alert">
        !!! CRITICAL_SYSTEM_VULNERABILITY !!!
      </div>
      <div class="alert-tag">[ KERNEL_EXPLOIT_DETECTED ]</div>
    </div>
  </div>

  <!-- Step 1: Terminal Cards -->
  <div class="terminal-wrapper">
    <div class="terminal-card left" data-step="1">
      <div class="terminal-header">guest@linux: ~</div>
      <div class="terminal-body">
        <div class="line"><span class="prompt">$</span> whoami</div>
        <div class="line glow-text">guest</div>
        <div class="line cursor">_</div>
      </div>
    </div>

    <div class="terminal-card right" data-step="1">
      <div class="terminal-header">root@local: #</div>
      <div class="terminal-body">
        <div class="line"><span class="prompt">#</span> whoami</div>
        <div class="line ghost-text">??????</div>
        <div class="line cursor">_</div>
      </div>
    </div>
  </div>

  <!-- Step 2: Connection / Seizure -->
  <div class="lightning-container" data-step="2">
    <svg class="lightning-svg" viewBox="0 0 800 400">
      <path class="lightning-path" d="M 280 200 L 350 180 L 400 220 L 450 180 L 520 200" />
      <filter id="glow">
        <feGaussianBlur stdDeviation="4" result="coloredBlur"/>
        <feMerge>
          <feMergeNode in="coloredBlur"/>
          <feMergeNode in="SourceGraphic"/>
        </feMerge>
      </filter>
    </svg>
    <div class="status-text">PRIVILEGE ESCALATION SUCCESSFUL</div>
  </div>

  <!-- Background Decor -->
  <div class="bg-grid"></div>
</div>
  </div>
</section>

<section class="scene" data-scene-id="scene-02">
  <div class="sv-scene-root sv-scene-root-scene-02" data-scene-root-id="scene-02">
<div class="s-scene-02-container">
  <div class="crt-overlay"></div>
  <div class="ambient-phosphor"></div>
  <div class="grid-bg"></div>

  <!-- Step 0: Vulnerability ID -->
  <div class="vulnerability-wrapper" data-step="0">
    <h1 class="cve-text">CVE-2026-31431</h1>
    <div class="cve-glow"></div>
  </div>

  <!-- Step 1: Copy Fail & Tearing -->
  <div class="fail-container" data-step="1">
    <h2 class="fail-text">COPY FAIL</h2>
    <div class="tear-particles">
      <svg viewBox="0 0 1000 400" preserveAspectRatio="none">
        <path class="tear-line l1" d="M0,100 L200,120 L400,90 L600,110 L800,85 L1000,105" />
        <path class="tear-line l2" d="M0,250 L250,230 L500,260 L750,240 L1000,270" />
        <path class="tear-line l3" d="M0,320 L300,340 L700,310 L1000,330" />
      </svg>
    </div>
    <div class="glitch-block"></div>
  </div>
</div>
  </div>
</section>

<section class="scene" data-scene-id="scene-03">
  <div class="sv-scene-root sv-scene-root-scene-03" data-scene-root-id="scene-03">
<div class="s-scene-03-container">
  <div class="crt-lines"></div>
  <div class="ambient-glow"></div>

  <!-- Step 0: Timeline -->
  <div class="timeline-section" data-step="0">
    <div class="timeline-wrapper">
      <span class="time-label">[ 2017 ]</span>
      <div class="timeline-bar">
        <div class="timeline-fill"></div>
        <div class="timeline-arrow">>></div>
      </div>
      <span class="time-label pulse-text">至今</span>
    </div>
  </div>

  <!-- Step 1: Linux Distribution Grid -->
  <div class="distro-grid-container" data-step="1">
    <div class="grid-header">SYSTEM_SCAN_TARGETS: IN_PROGRESS...</div>
    <div class="distro-grid">
      <div class="distro-card" data-distro="ubuntu">
        <iconify-icon icon="simple-icons:ubuntu"></iconify-icon>
        <span class="distro-name">Ubuntu</span>
      </div>
      <div class="distro-card" data-distro="centos">
        <iconify-icon icon="simple-icons:centos"></iconify-icon>
        <span class="distro-name">CentOS</span>
      </div>
      <div class="distro-card" data-distro="rhel">
        <iconify-icon icon="simple-icons:redhat"></iconify-icon>
        <span class="distro-name">RHEL</span>
      </div>
      <div class="distro-card" data-distro="aws">
        <iconify-icon icon="simple-icons:amazonwebservices"></iconify-icon>
        <span class="distro-name">AWS Linux</span>
      </div>
      <div class="distro-card" data-distro="aliyun">
        <iconify-icon icon="simple-icons:alibabacloud"></iconify-icon>
        <span class="distro-name">AliyunOS</span>
      </div>
      <div class="distro-card" data-distro="suse">
        <iconify-icon icon="simple-icons:suse"></iconify-icon>
        <span class="distro-name">SUSE</span>
      </div>
    </div>
    <div class="shimmer-overlay"></div>
  </div>

  <!-- Step 2: Vulnerable Stamp -->
  <div class="vulnerable-overlay" data-step="2">
    <div class="stamp-box">
      <div class="stamp-text">VULNERABLE</div>
      <div class="stamp-subtext">受威胁状态</div>
    </div>
  </div>
</div>
  </div>
</section>

<section class="scene" data-scene-id="scene-04">
  <div class="sv-scene-root sv-scene-root-scene-04" data-scene-root-id="scene-04">
<div class="s-scene-04-wrapper">
  <div class="crt-lines"></div>
  <div class="ambient-glow"></div>

  <div class="system-layout">
    <!-- Left: Container -->
    <div class="node-container">
      <div class="system-node box-node">
        <div class="node-label">[ CONTAINER ]</div>
        <div class="internal-process">
          <div class="cursor-blink"></div>
          <span class="mono-text">UID: 1000</span>
        </div>
      </div>
    </div>

    <!-- SVG Layer for the Arrow -->
    <div class="vector-layer">
      <svg viewBox="0 0 800 400" fill="none" xmlns="http://www.w3.org/2000/svg">
        <path id="attack-path" d="M 240 200 L 560 200" stroke="#FF3131" stroke-width="4" stroke-dasharray="320" stroke-dashoffset="320" />
        <path id="arrow-head" d="M 545 185 L 565 200 L 545 215" stroke="#FF3131" stroke-width="4" opacity="0" />
      </svg>
    </div>

    <!-- Right: Host -->
    <div class="node-container">
      <div class="system-node circle-node" id="host-node">
        <div class="node-label">[ HOST_SYSTEM ]</div>
        <div class="internal-process">
          <span class="status-text">KERNEL_IDLE</span>
        </div>
      </div>
    </div>
  </div>

  <!-- Success Banner -->
  <div class="success-banner" data-step="1">
    <div class="glitch-box">
      <span class="success-text">> 提权成功: ESCAPED_TO_ROOT</span>
    </div>
  </div>
</div>
  </div>
</section>

<section class="scene" data-scene-id="scene-05">
  <div class="sv-scene-root sv-scene-root-scene-05" data-scene-root-id="scene-05">
<div class="s-scene-05-wrapper">
  <div class="crt-scanlines"></div>
  <div class="ambient-phosphor"></div>

  <!-- Step 0: Memory Container -->
  <div class="memory-container">
    <div class="memory-label">[ PHYSICAL_RAM_MAPPING ]</div>
    <div class="memory-grid"></div>

    <!-- Step 1: Page Cache Card -->
    <div class="page-cache-card" data-step="1">
      <div class="card-header">
        <span class="card-title">PAGE_CACHE_OBJECT</span>
        <span class="memory-addr">0x7FFF0042</span>
      </div>
      <div class="binary-content">
        <div class="binary-line">10110010 11100001 00011101</div>
        <div class="binary-line">11001010 01111100 11010001</div>
        <div class="binary-line">00110101 10100011 00011110</div>
        <div class="binary-line">11110000 00001111 10101010</div>
      </div>
      
      <!-- Step 2: Security Overlay -->
      <div class="security-status" data-step="2">
        <div class="lock-overlay">
          <i data-lucide="lock"></i>
          <span class="read-only-text">READ_ONLY</span>
        </div>
        <div class="forbidden-action">
          <div class="action-item">
            <span class="action-label">WRITE_REQ</span>
            <div class="forbidden-sign"></div>
          </div>
          <div class="action-item">
            <span class="action-label">MODIFY_REQ</span>
            <div class="forbidden-sign"></div>
          </div>
        </div>
      </div>
    </div>
  </div>

  <!-- Background Text -->
  <div class="ghost-text">KERNEL_SPACE</div>
</div>
  </div>
</section>

<section class="scene" data-scene-id="scene-06">
  <div class="sv-scene-root sv-scene-root-scene-06" data-scene-root-id="scene-06">
<div class="s-scene-06-wrapper">
  <div class="crt-overlay"></div>
  <div class="ambient-glow"></div>

  <!-- Step 0: Terminal Window -->
  <div class="terminal-container" id="term-06">
    <div class="term-header">
      <div class="term-controls"><span></span><span></span><span></span></div>
      <div class="term-title">K-SHELL v4.1</div>
    </div>
    <div class="term-content">
      <div class="typing-line" data-delay="1">$ modprobe af_alg</div>
      <div class="typing-line" data-delay="2">$ call <span class="highlight">splice()</span></div>
      <div class="typing-line blinking-cursor">_</div>
    </div>
  </div>

  <!-- Step 1: Conduit & Target File -->
  <div class="conduit-system">
    <svg class="conduit-svg" viewBox="0 0 1000 600">
      <path id="conduit-path" d="M280,300 L450,300" fill="none" stroke="#00FF41" stroke-width="4" stroke-dasharray="1000" stroke-dashoffset="1000" />
      <circle cx="280" cy="300" r="5" fill="#00FF41" class="node-point" />
    </svg>
    <div class="file-node" id="su-file-node">
      <div class="file-icon-box">
        <iconify-icon icon="mdi:file-lock-outline"></iconify-icon>
      </div>
      <div class="file-info">
        <span class="file-name">/usr/bin/su</span>
        <span class="file-perm">ROOT_SUID: 4755</span>
      </div>
    </div>
  </div>

  <!-- Step 2: Encryption Flow & Chains -->
  <div class="pipeline-stage" id="encrypt-stage">
    <div class="pipeline-box">
      <div class="pipeline-label">[ KERNEL_CRYPTO_API ]</div>
      <div class="chain-container">
         <div class="chain-link"></div>
         <div class="chain-link"></div>
         <div class="chain-link"></div>
      </div>
      <div class="flow-indicator">ENCRYPTION_IN_PROGRESS</div>
    </div>
  </div>

  <div class="side-status">
    <div class="status-item" data-step="1">> CHANNEL_ESTABLISHED</div>
    <div class="status-item" data-step="2">> FILE_INGESTION: FORCE</div>
  </div>
</div>
  </div>
</section>

<section class="scene" data-scene-id="scene-07">
  <div class="sv-scene-root sv-scene-root-scene-07" data-scene-root-id="scene-07">
<div class="s-scene-07-wrapper">
  <div class="crt-overlay"></div>
  <div class="ambient-glow"></div>
  
  <!-- Alert Overlay (Step 2) -->
  <div class="alert-pulse-overlay" data-step="2"></div>

  <div class="layout-container">
    <!-- Left: Code Section -->
    <div class="code-section">
      <div class="section-label">2017 内核优化代码</div>
      <div class="terminal-window">
        <div class="term-header">
           <div class="term-dots"><span></span><span></span><span></span></div>
           <div class="term-title">mm/gup.c</div>
        </div>
        <div class="code-content">
          <pre><code><span class="line">/* Optimization: No COW needed? */</span>
<span class="line">if (flags & FOLL_FORCE) {</span>
<span class="line highlight-target">  vma-&gt;vm_flags |= <span class="code-keyword">VM_MAYWRITE</span>;</span>
<span class="line">  <span class="code-comment">// Treat as writable buffer</span></span>
<span class="line">}</span></code></pre>
        </div>
      </div>
    </div>

    <!-- Middle: Conduit -->
    <div class="conduit-container">
      <svg class="conduit-svg" viewBox="0 0 200 100">
        <path class="arrow-path" d="M0,50 L180,50" fill="none" stroke="#00FF41" stroke-width="2" />
        <polygon points="180,45 195,50 180,55" fill="#00FF41" class="arrow-head" />
      </svg>
    </div>

    <!-- Right: Memory Page Section -->
    <div class="memory-section">
      <div class="section-label">物理内存页面</div>
      <div class="page-frame">
        <div class="page-grid">
          <span></span><span></span><span></span><span></span>
          <span></span><span></span><span></span><span></span>
        </div>
        <div class="permission-status">
          <div class="lock-container" id="perm-lock">
             <div class="lock-icon"><iconify-icon icon="mdi:lock-outline"></iconify-icon></div>
             <div class="lock-text">只读 (RO)</div>
          </div>
          <div class="rw-status" id="perm-rw">
             <div class="rw-glitch">可写 (RW)</div>
          </div>
        </div>
      </div>
    </div>
  </div>

  <div class="status-bar">
    <div class="status-msg">SYSTEM_OPTIMIZATION: ACTIVE</div>
    <div class="status-msg" data-step="1">CRITICAL: PERMISSION_BYPASS_DETECTED</div>
  </div>
</div>
  </div>
</section>

<section class="scene" data-scene-id="scene-08">
  <div class="sv-scene-root sv-scene-root-scene-08" data-scene-root-id="scene-08">
<div class="s-scene-08-wrapper">
  <div class="crt-scanlines"></div>
  <div class="ambient-glow"></div>

  <!-- Zoomed Hex Grid Viewport -->
  <div class="hex-viewport">
    <div class="viewport-header">
      <span class="addr-label">OFFSET: 0x00007FFF</span>
      <span class="status-tag" id="mem-status">[ MEMORY_READ_ONLY ]</span>
    </div>
    <div class="hex-grid-container">
      <div class="hex-grid" id="main-hex-grid">
        <!-- Generated cells will be here -->
      </div>
    </div>
  </div>

  <!-- Flying Bytes (Step 0: Visible, Step 1: Animate) -->
  <div class="missile-container">
    <div class="byte-missile" data-target="target-1" data-step="0">0x53</div>
    <div class="byte-missile" data-target="target-2" data-step="0">0x55</div>
    <div class="byte-missile" data-target="target-3" data-step="0">0x49</div>
    <div class="byte-missile" data-target="target-4" data-step="0">0x44</div>
  </div>

  <!-- Alert Overlay (Step 2) -->
  <div class="alert-overlay" data-step="2">
    <div class="alert-box">
      <div class="alert-header">CRITICAL_ERROR</div>
      <div class="alert-content">VALIDATION_FAILED: HASH_MISMATCH</div>
      <div class="alert-sub">ENCRYPTED_BLOCK_CORRUPTED</div>
    </div>
  </div>

  <!-- Bottom Status -->
  <div class="terminal-footer">
    <div class="cursor-line"><span class="prompt">$</span> <span class="typing-text">patching su_binary...</span><span class="cursor"></span></div>
    <div class="progress-section" data-step="1">
      <span class="label">INJECTION:</span>
      <span class="bar-bg"><span class="bar-fill" id="injection-bar"></span></span>
    </div>
  </div>
</div>
  </div>
</section>

<section class="scene" data-scene-id="scene-09">
  <div class="sv-scene-root sv-scene-root-scene-09" data-scene-root-id="scene-09">
<div class="s-scene-09-wrapper">
  <div class="crt-scanlines"></div>
  <div class="ambient-glow"></div>

  <!-- Terminal Interface Container -->
  <div class="terminal-interface">
    <div class="terminal-header">
      <div class="terminal-buttons">
        <span></span><span></span><span></span>
      </div>
      <div class="terminal-title">TERMINAL - su_exploit_session</div>
    </div>

    <div class="terminal-content">
      <!-- Input History -->
      <div class="log-line">[ OK ] Payload injected into memory segment 0x0804...</div>
      <div class="log-line">[ OK ] Hooking execution vector...</div>
      
      <!-- Main Prompt Line -->
      <div class="prompt-area">
        <span class="prompt-char" id="current-prompt">$</span>
        <span class="command-text">whoami</span>
        <span class="cursor"></span>
      </div>

      <!-- Result Output (Step 0-1 Hidden) -->
      <div class="result-output" data-step="2">
        <div class="root-banner" id="root-text-container">
          <!-- root text will be typed here -->
        </div>
      </div>
    </div>

    <!-- Bottom Status Bar -->
    <div class="terminal-footer-bar">
      <div class="status-item">STATUS: <span class="blink-text">RUNNING</span></div>
      <div class="status-item">PRIVILEGE: <span id="priv-level">USER</span></div>
      <div class="status-item" data-step="2">UID: 0 (root)</div>
    </div>
  </div>

  <!-- Flash Overlay for Step 1 -->
  <div class="lightning-flash" id="flash-overlay"></div>

  <!-- Success Glow Frame (Step 2) -->
  <div class="success-frame" data-step="2"></div>
</div>
  </div>
</section>

<section class="scene" data-scene-id="scene-10">
  <div class="sv-scene-root sv-scene-root-scene-10" data-scene-root-id="scene-10">
<div class="s-scene-10-wrapper">
  <div class="crt-scanlines"></div>
  <div class="ambient-glow"></div>

  <div class="thirds-container">
    <!-- Step 0: Left Column - Patch Info -->
    <div class="col-left" data-step="0">
      <div class="status-box">
        <div class="status-header">SYSTEM_ALERT</div>
        <div class="patch-icon-wrapper">
          <iconify-icon icon="mdi:shield-sync-outline" class="main-patch-icon"></iconify-icon>
          <div class="update-text-glitch">UPDATE NOW</div>
        </div>
        <div class="status-details">
          <div class="status-line">> KERNEL: VULNERABLE</div>
          <div class="status-line blinking">> PATCH: AVAILABLE</div>
        </div>
      </div>
    </div>

    <!-- Step 2: Center Column - Defused Bomb -->
    <div class="col-center" data-step="2">
      <div class="bomb-stage">
        <svg viewBox="0 0 200 200" class="bomb-svg">
          <!-- Fuse -->
          <path d="M120 40 Q 150 20, 160 60" fill="none" stroke="#00FF41" stroke-width="4" class="fuse-path" />
          <!-- Bomb Body -->
          <circle cx="100" cy="110" r="60" fill="none" stroke="#00FF41" stroke-width="6" />
          <rect x="85" y="45" width="30" height="15" fill="#000" stroke="#00FF41" stroke-width="4" />
          <!-- Cut Mark -->
          <path d="M135 25 L 155 45 M 155 25 L 135 45" stroke="#39FF14" stroke-width="8" class="cut-mark" />
        </svg>
        <div class="threat-status">THREAT_NEUTRALIZED</div>
      </div>
    </div>

    <!-- Step 1: Right Column - Terminal Command -->
    <div class="col-right" data-step="1">
      <div class="command-window">
        <div class="window-bar">
          <span class="dot"></span><span class="dot"></span><span class="dot"></span>
          <span class="window-title">bash - root</span>
        </div>
        <div class="terminal-body">
          <div class="shell-prompt">
            <span class="user">root@system</span>:<span class="path">~</span>#
            <span class="typing-cmd" id="cmd-text"></span><span class="terminal-cursor"></span>
          </div>
          <div class="shell-output" data-step="1">[ SUCCESS ] Module 'algif_aead' disabled.</div>
        </div>
      </div>
    </div>
  </div>

  <div class="footer-deco">SECURITY_ENFORCED // 0xPATCH_SUCCESS</div>
</div>
  </div>
</section>

<section class="scene" data-scene-id="scene-11">
  <div class="sv-scene-root sv-scene-root-scene-11" data-scene-root-id="scene-11">
<div class="s-scene-11-wrapper">
  <div class="crt-scanlines"></div>
  <div class="ambient-glow"></div>

  <!-- Step 0: Center Logo/Avatar -->
  <div class="logo-overlay" data-step="0">
    <div class="avatar-frame">
      <div class="avatar-glitch"></div>
      <svg viewBox="0 0 100 100" class="terminal-logo">
        <rect x="20" y="20" width="60" height="60" stroke="#00FF41" stroke-width="2" fill="none" />
        <path d="M35 40 L50 55 L65 40" stroke="#00FF41" stroke-width="4" fill="none" />
        <path d="M30 70 L70 70" stroke="#00FF41" stroke-width="2" stroke-dasharray="4 2" />
      </svg>
    </div>
    <div class="channel-id">[ CHANNEL_ID: ROOT_NODE ]</div>
  </div>

  <!-- Step 1: Interactive Actions & Outro -->
  <div class="action-container" data-step="1">
    <!-- Like Action -->
    <div class="action-item like-item">
      <div class="action-box">
        <i data-lucide="thumbs-up"></i>
        <span class="action-label">LIKE_SUCCESS</span>
      </div>
    </div>

    <!-- Follow Action -->
    <div class="action-item follow-item">
      <div class="action-box">
        <i data-lucide="user-plus"></i>
        <span class="action-label">SUBSCRIBE_OK</span>
      </div>
    </div>

    <!-- Closing Text -->
    <div class="closing-text">
      <div class="rainbow-terminal-text">下期再见</div>
      <div class="cursor-blink"></div>
    </div>
  </div>

  <div class="system-footer">
    SESSION_TERMINATED: 0x00000000
  </div>
</div>
  </div>
</section>
      </div>
    </div>
  </div>
  <script id="__SVGVIDEO_META__" type="application/json">{"version":2,"format":"json","title":"Linux内核超级漏洞：Copy Fail 深度解析","fullScript":"兄弟们，一个超级离谱的Linux内核漏洞刚刚曝光！普通用户一句话，就能把服务器的root权限直接抢到手！它的编号叫CVE-2026-31431，代号Copy Fail！这个漏洞影响了2017年之后几乎所有主流Linux发行版，Ubuntu、CentOS、RHEL、阿里云、AWS、SUSE通通中招。只要你在服务器上有一个普通账号，就能完成提权，连容器都能逃逸到主机上。原理其实特别巧妙，听我给你捋清楚：正常情况下，文件的内容会缓存在内存里，叫做page cache。我们普通用户只能读它，不能随便改它。但是黑客利用了内核提供的一个加密接口AF_ALG，再配合splice这个系统调用，把目标文件——比如/usr/bin/su——偷偷塞进了内核的加密流程里。因为2017年内核做了一个性能优化，这个优化让内核把这个文件的内存页面，当成了“可写的缓冲区”来使用！结果，黑客在加密计算的过程中，就往里面精准地写入了4个自己控制的字节。虽然最后加密验证失败了，但那4个字节已经实实在在地改进了内存里的su程序！重复操作几次，就能把su改造成一个“直接给我root”的恶意程序。一执行，权限就到手了！整个过程不需要猜内核地址，也不用竞态条件，稳得一批。目前各大厂商已经紧急推送了补丁，大家赶紧更新内核！临时缓解的话，可以把algif_aead模块禁用掉。这个漏洞再次告诉我们：再小的代码优化，如果没想清楚，也可能埋下致命的炸弹。好了，今天的分享就到这里，点赞关注，我们下期再见！","visualStyleId":"terminal-green","orientation":"landscape","scenes":[{"id":"scene-01","title":"震撼开场","steps":3,"status":"success","prompt":"横屏构图。背景为深暗色科技感网格。Step 0: 屏幕中心出现巨大的红色警示图标，伴随闪烁效果。Step 1: 左右两侧弹出两个黑色毛玻璃卡片，左边写着‘普通用户’，右边写着‘ROOT权限’。Step 2: 一个金色的闪电特效连接两个卡片，表示权限瞬间被夺取。视觉风格：赛博朋克、高对比度。","scriptText":"兄弟们，一个超级离谱的Linux内核漏洞刚刚曝光！普通用户一句话，就能把服务器的root权限直接抢到手！","audioObjectKey":"sv-video/v1/u_1aa5e8b3-d1b1-4121-b796-bb5ac9fcd45a/p_62574c6d-8ac1-43ff-8ab8-d4a1a16f8fc4/s_scene-01/v_xiaoxiao-hd/a_d40ec182_1777539050814_nzb69g.mp3","audioDurationMs":8424,"voiceName":"xiaoxiao-hd","relatedSceneIds":["scene-02"],"transition":"door"},{"id":"scene-02","title":"漏洞代号曝光","steps":2,"status":"success","prompt":"横屏构图。中心位置展示加粗的渐变文字‘CVE-2026-31431’，带有蓝色的呼吸光效。Step 0: 文字从屏幕中央通过弹簧缩放效果出现。Step 1: 下方浮现更大的红色文字‘Copy Fail’，背景出现类似文件被撕裂的SVG粒子效果。整体色调偏冷。","scriptText":"它的编号叫CVE 2026 31431，代号Copy Fail！","audioObjectKey":"sv-video/v1/u_1aa5e8b3-d1b1-4121-b796-bb5ac9fcd45a/p_62574c6d-8ac1-43ff-8ab8-d4a1a16f8fc4/s_scene-02/v_xiaoxiao-hd-cheerful/a_e74d6042_1777541496139_ldxv7g.mp3","audioDurationMs":5112,"voiceName":"xiaoxiao-hd","voiceStyle":"cheerful","relatedSceneIds":["scene-03"],"transition":"door"},{"id":"scene-03","title":"受影响范围","steps":3,"status":"success","prompt":"横屏，左右平铺布局。Step 0: 屏幕左侧出现一个‘2017’的时间标签，向右拉出一条时间轴到‘至今’。Step 1: 屏幕中间网格化排列主流Linux Logo（Ubuntu, CentOS, RHEL, AWS, 阿里云）。Step 2: 所有图标由蓝转红，并出现‘Vulnerable’（受威胁）的印章动画覆盖。使用微光（shimmer）特效扫描整个网格。","scriptText":"这个漏洞影响了2017年之后几乎所有主流Linux发行版","audioObjectKey":"sv-video/v1/u_1aa5e8b3-d1b1-4121-b796-bb5ac9fcd45a/p_62574c6d-8ac1-43ff-8ab8-d4a1a16f8fc4/s_scene-03/v_xiaoxiao-hd-cheerful/a_7c58a451_1777541525511_w8aeas.mp3","audioDurationMs":4392,"voiceName":"xiaoxiao-hd","voiceStyle":"cheerful","relatedSceneIds":["scene-04"],"transition":"door"},{"id":"scene-04","title":"攻击场景描述","steps":2,"status":"success","prompt":"横屏。左侧是一个代表‘容器’的方块，右侧是一个代表‘宿主机’的大圆圈。Step 0: 一个红色小箭从容器内部穿透边界射向宿主机。Step 1: 宿主机图标由灰白变为鲜红色，屏幕底部显示‘提权成功’的文字框。布局采用左右对称的极简矢量风格。","scriptText":"只要你在服务器上有一个普通账号，就能完成提权，连容器都能逃逸到主机上。","audioObjectKey":"sv-video/v1/u_1aa5e8b3-d1b1-4121-b796-bb5ac9fcd45a/p_62574c6d-8ac1-43ff-8ab8-d4a1a16f8fc4/s_scene-04/v_xiaoxiao-hd/a_3bb62f42_1777539099072_3xsdd0.mp3","audioDurationMs":5508,"voiceName":"xiaoxiao-hd","relatedSceneIds":["scene-05"],"transition":"door"},{"id":"scene-05","title":"原理科普：Page Cache","steps":3,"status":"success","prompt":"横屏。Step 0: 背景出现一个矩形区域代表‘内存’。Step 1: 内存中心浮现一个名为‘Page Cache’的半透明卡片，里面显示二进制代码流。Step 2: 卡片右上角出现一个‘只读（Read Only）’的锁头标志，一个禁止符号出现在‘修改’操作上。","scriptText":"原理其实特别巧妙，听我给你捋清楚：正常情况下，文件的内容会缓存在内存里，叫做page cache。我们普通用户只能读它，不能随便改它。","audioObjectKey":"sv-video/v1/u_1aa5e8b3-d1b1-4121-b796-bb5ac9fcd45a/p_62574c6d-8ac1-43ff-8ab8-d4a1a16f8fc4/s_scene-05/v_xiaoxiao-hd-cheerful/a_17a6b492_1777541700692_6vl3vo.mp3","audioDurationMs":10548,"voiceName":"xiaoxiao-hd","voiceStyle":"cheerful","relatedSceneIds":["scene-06"],"transition":"door"},{"id":"scene-06","title":"利用机制：AF_ALG","steps":2,"status":"success","prompt":"横屏，展示代码调用逻辑。Step 0: 屏幕左侧淡入一个终端窗口，显示代码‘AF_ALG’和‘splice()’。Step 1: 窗口中伸出一根导管，指向中间的‘/usr/bin/su’文件图标。Step 2: 整个su文件图标开始微微抖动，并被强制拖入一个带有锁链图案的‘加密流程’框内。","scriptText":"但是黑客利用了内核提供的一个加密接口AF_ALG，再配合splice这个系统调用，把目标文件——比如/usr/bin/su——偷偷塞进了内核的加密流程里。","audioObjectKey":"sv-video/v1/u_1aa5e8b3-d1b1-4121-b796-bb5ac9fcd45a/p_62574c6d-8ac1-43ff-8ab8-d4a1a16f8fc4/s_scene-06/v_xiaoxiao-hd-cheerful/a_8075a4cf_1777541754264_fwsej6.mp3","audioDurationMs":12348,"voiceName":"xiaoxiao-hd","voiceStyle":"cheerful","relatedSceneIds":["scene-07"],"transition":"door"},{"id":"scene-07","title":"漏洞核心：错误的写权限","steps":2,"status":"success","prompt":"横屏。左侧显示‘2017 内核优化代码’。Step 0: 高亮一段代码逻辑，箭头指向右侧的内存页面。Step 1: 原本的‘只读锁’瞬间碎裂，变成红色的‘可写（RW）’标识。背景出现警示性质的红色脉冲扩散动画。","scriptText":"因为2017年内核做了一个性能优化，这个优化让内核把这个文件的内存页面，当成了“可写的缓冲区”来使用！","audioObjectKey":"sv-video/v1/u_1aa5e8b3-d1b1-4121-b796-bb5ac9fcd45a/p_62574c6d-8ac1-43ff-8ab8-d4a1a16f8fc4/s_scene-07/v_xiaoxiao-hd/a_7db73a0d_1777539106616_dmys7t.mp3","audioDurationMs":8424,"voiceName":"xiaoxiao-hd","relatedSceneIds":["scene-08"],"transition":"door"},{"id":"scene-08","title":"精准写入过程","steps":4,"status":"success","prompt":"横屏。中心放大展示代码块的一角。Step 0: 四个高亮的16进制字节（如 0x01 0x02 0x03 0x04）浮现。Step 1: 字节像导弹一样精准嵌入内存代码块的空隙。Step 2: 出现一个‘验证失败’的红色提示。Step 3: 但字节已停留在内存中，代码块由蓝变紫。细节采用微缩模型动画风格。","scriptText":"结果，黑客在加密计算的过程中，就往里面精准地写入了4个自己控制的字节。虽然最后加密验证失败了，但那4个字节已经实实在在地改进了内存里的su程序！","audioObjectKey":"sv-video/v1/u_1aa5e8b3-d1b1-4121-b796-bb5ac9fcd45a/p_62574c6d-8ac1-43ff-8ab8-d4a1a16f8fc4/s_scene-08/v_xiaoxiao-hd/a_18d0460b_1777539109881_nhl5sl.mp3","audioDurationMs":12348,"voiceName":"xiaoxiao-hd","relatedSceneIds":["scene-09"],"transition":"door"},{"id":"scene-09","title":"获取Root权限","steps":3,"status":"success","prompt":"横屏，模拟终端界面。Step 0: 屏幕显示提示符‘$ whoami’。Step 1: 闪电式刷新，提示符变为‘# whoami’。Step 2: 屏幕爆发出巨大的‘root’字样，伴随成功的绿色发光边框。文字采用打字机效果逐字弹出。","scriptText":"重复操作几次，就能把su改造成一个“直接给我root”的恶意程序。一执行，权限就到手了！整个过程不需要猜内核地址，也不用竞态条件，稳得一批。","audioObjectKey":"sv-video/v1/u_1aa5e8b3-d1b1-4121-b796-bb5ac9fcd45a/p_62574c6d-8ac1-43ff-8ab8-d4a1a16f8fc4/s_scene-09/v_xiaoxiao-hd-cheerful/a_d4dbfe01_1777541610215_shlcai.mp3","audioDurationMs":11124,"voiceName":"xiaoxiao-hd","voiceStyle":"cheerful","relatedSceneIds":["scene-10"],"transition":"door"},{"id":"scene-10","title":"防御与总结","steps":3,"status":"success","prompt":"横屏，三分构图。Step 0: 左侧显示补丁图标‘UPDATE NOW’。Step 1: 右侧显示命令代码‘modprobe -r algif_aead’。Step 2: 屏幕中央出现一个引信被切断的炸弹图标。视觉风格：清晰、专业、带有安全提示感。","scriptText":"目前各大厂商已经紧急推送了补丁，大家赶紧更新内核！临时缓解的话，可以把algif_aead模块禁用掉。这个漏洞再次告诉我们：再小的代码优化，如果没想清楚，也可能埋下致命的炸弹。","audioObjectKey":"sv-video/v1/u_1aa5e8b3-d1b1-4121-b796-bb5ac9fcd45a/p_62574c6d-8ac1-43ff-8ab8-d4a1a16f8fc4/s_scene-10/v_xiaoxiao-hd-cheerful/a_bfafacde_1777541890973_en47qg.mp3","audioDurationMs":13860,"voiceName":"xiaoxiao-hd","voiceStyle":"cheerful","relatedSceneIds":["scene-11"],"transition":"door"},{"id":"scene-11","title":"结尾致谢","steps":2,"status":"success","prompt":"横屏。Step 0: 背景变暗，中心出现博主的头像或频道Logo。Step 1: 左右两侧飞入‘点赞’和‘关注’的SVG动态图标，带有Q弹的弹簧反馈效果。文字‘下期再见’以彩虹渐变色淡入。整体风格欢快、整洁。","scriptText":"好了，今天的分享就到这里，点赞关注，我们下期再见！","audioObjectKey":"sv-video/v1/u_1aa5e8b3-d1b1-4121-b796-bb5ac9fcd45a/p_62574c6d-8ac1-43ff-8ab8-d4a1a16f8fc4/s_scene-11/v_xiaoxiao-hd-cheerful/a_1f8ff479_1777541651756_6js77f.mp3","audioDurationMs":4608,"voiceName":"xiaoxiao-hd","voiceStyle":"cheerful","transition":"door"}]}</script>
  <script id="__SVGVIDEO_SCENE_DATA__" type="application/json">[{"id":"scene-01","title":"震撼开场","steps":3,"html":"<div class=\"s-scene-01-container\">\n  <div class=\"scanline\"></div>\n  <div class=\"vignette\"></div>\n\n  <!-- Step 0: Alert System -->\n  <div class=\"alert-overlay\" data-step=\"0\">\n    <div class=\"alert-content\">\n      <div class=\"ascii-alert\">\n        !!! CRITICAL_SYSTEM_VULNERABILITY !!!\n      </div>\n      <div class=\"alert-tag\">[ KERNEL_EXPLOIT_DETECTED ]</div>\n    </div>\n  </div>\n\n  <!-- Step 1: Terminal Cards -->\n  <div class=\"terminal-wrapper\">\n    <div class=\"terminal-card left\" data-step=\"1\">\n      <div class=\"terminal-header\">guest@linux: ~</div>\n      <div class=\"terminal-body\">\n        <div class=\"line\"><span class=\"prompt\">$</span> whoami</div>\n        <div class=\"line glow-text\">guest</div>\n        <div class=\"line cursor\">_</div>\n      </div>\n    </div>\n\n    <div class=\"terminal-card right\" data-step=\"1\">\n      <div class=\"terminal-header\">root@local: #</div>\n      <div class=\"terminal-body\">\n        <div class=\"line\"><span class=\"prompt\">#</span> whoami</div>\n        <div class=\"line ghost-text\">??????</div>\n        <div class=\"line cursor\">_</div>\n      </div>\n    </div>\n  </div>\n\n  <!-- Step 2: Connection / Seizure -->\n  <div class=\"lightning-container\" data-step=\"2\">\n    <svg class=\"lightning-svg\" viewBox=\"0 0 800 400\">\n      <path class=\"lightning-path\" d=\"M 280 200 L 350 180 L 400 220 L 450 180 L 520 200\" />\n      <filter id=\"glow\">\n        <feGaussianBlur stdDeviation=\"4\" result=\"coloredBlur\"/>\n        <feMerge>\n          <feMergeNode in=\"coloredBlur\"/>\n          <feMergeNode in=\"SourceGraphic\"/>\n        </feMerge>\n      </filter>\n    </svg>\n    <div class=\"status-text\">PRIVILEGE ESCALATION SUCCESSFUL</div>\n  </div>\n\n  <!-- Background Decor -->\n  <div class=\"bg-grid\"></div>\n</div>","css":".s-scene-01-container {\n  position: relative;\n  width: 100%;\n  height: 100%;\n  background: #000000;\n  color: #00FF41;\n  font-family: var(--sv-font-mono);\n  overflow: hidden;\n  display: flex;\n  justify-content: center;\n  align-items: center;\n}\n\n.scanline {\n  position: absolute;\n  inset: 0;\n  background: repeating-linear-gradient(rgba(0,0,0,0) 0px, rgba(0,0,0,0) 1px, rgba(0,255,65,0.08) 2px, rgba(0,255,65,0.08) 3px);\n  pointer-events: none;\n  z-index: 10;\n}\n\n.bg-grid {\n  position: absolute;\n  inset: 0;\n  background-image: linear-gradient(rgba(0, 255, 65, 0.05) 1px, transparent 1px), linear-gradient(90deg, rgba(0, 255, 65, 0.05) 1px, transparent 1px);\n  background-size: 40px 40px;\n  animation: grid-move 20s linear infinite;\n}\n\n@keyframes grid-move {\n  from { background-position: 0 0; }\n  to { background-position: 40px 40px; }\n}\n\n.alert-overlay {\n  position: absolute;\n  z-index: 5;\n  text-align: center;\n  animation: alert-flicker 0.2s infinite alternate;\n}\n\n.ascii-alert {\n  font-size: 42px;\n  font-weight: bold;\n  border: 4px solid #00FF41;\n  padding: 20px 40px;\n  background: rgba(0, 255, 65, 0.1);\n}\n\n@keyframes alert-flicker {\n  0% { opacity: 1; transform: scale(1); }\n  100% { opacity: 0.7; transform: scale(1.02); }\n}\n\n.terminal-wrapper {\n  display: flex;\n  gap: 120px;\n  z-index: 4;\n}\n\n.terminal-card {\n  width: 320px;\n  height: 200px;\n  background: rgba(0, 0, 0, 0.9);\n  border: 1px solid rgba(0, 255, 65, 0.4);\n  box-shadow: 0 0 20px rgba(0, 255, 65, 0.1);\n  display: flex;\n  flex-direction: column;\n  transition: transform 0.5s cubic-bezier(0.22, 1, 0.36, 1), opacity 0.5s;\n}\n\n.terminal-header {\n  background: rgba(0, 255, 65, 0.2);\n  padding: 6px 12px;\n  font-size: 14px;\n  border-bottom: 1px solid rgba(0, 255, 65, 0.4);\n}\n\n.terminal-body {\n  padding: 15px;\n  font-size: 18px;\n  line-height: 1.6;\n}\n\n.prompt { color: #39FF14; margin-right: 8px; }\n\n.cursor {\n  animation: blink 0.8s infinite;\n}\n\n@keyframes blink {\n  50% { opacity: 0; }\n}\n\n.lightning-container {\n  position: absolute;\n  inset: 0;\n  display: flex;\n  flex-direction: column;\n  justify-content: center;\n  align-items: center;\n  pointer-events: none;\n}\n\n.lightning-path {\n  fill: none;\n  stroke: #39FF14;\n  stroke-width: 4;\n  stroke-dasharray: 600;\n  stroke-dashoffset: 600;\n  filter: url(#glow);\n  animation: strike 0.5s forwards;\n}\n\n@keyframes strike {\n  to { stroke-dashoffset: 0; }\n}\n\n.status-text {\n  margin-top: 250px;\n  font-size: 24px;\n  background: #00FF41;\n  color: #000;\n  padding: 5px 15px;\n  animation: slide-up 0.4s ease-out;\n}\n\n@keyframes slide-up {\n  from { opacity: 0; transform: translateY(20px); }\n  to { opacity: 1; transform: translateY(0); }\n}\n\n/* Step Transitions */\n[data-step=\"1\"] { opacity: 0; transform: translateY(30px); }\n.active [data-step=\"1\"] { opacity: 1; transform: translateY(0); }\n\n[data-step=\"2\"] { opacity: 0; }\n.active [data-step=\"2\"] { opacity: 1; }\n\n.terminal-card.right.active-root {\n  border-color: #39FF14;\n  box-shadow: 0 0 30px rgba(0, 255, 65, 0.4);\n}","js":"window.SceneLifecycle.register('scene-01', {\n  enter() {\n    const sceneRoot = document.querySelector('[data-scene-id=\"scene-01\"]');\n    sceneRoot.classList.add('active');\n  },\n  step(step) {\n    const sceneRoot = document.querySelector('[data-scene-id=\"scene-01\"]');\n    const alert = sceneRoot.querySelector('.alert-overlay');\n    const rightTerminalBody = sceneRoot.querySelector('.terminal-card.right .terminal-body');\n\n    if (step >= 1) {\n      alert.style.opacity = '0.2';\n      alert.style.transform = 'scale(0.8) translateY(-150px)';\n      alert.style.transition = 'all 0.6s cubic-bezier(0.22, 1, 0.36, 1)';\n    }\n\n    if (step >= 2) {\n      const ghostText = rightTerminalBody.querySelector('.ghost-text');\n      if (ghostText) {\n        ghostText.textContent = 'root';\n        ghostText.classList.remove('ghost-text');\n        ghostText.style.color = '#39FF14';\n        ghostText.style.fontWeight = 'bold';\n      }\n      sceneRoot.querySelector('.terminal-card.right').classList.add('active-root');\n      \n      // Create rapid flicker effect on success\n      const bg = sceneRoot.querySelector('.bg-grid');\n      bg.style.animationDuration = '2s';\n    }\n  },\n  leave() {\n    const sceneRoot = document.querySelector('[data-scene-id=\"scene-01\"]');\n    sceneRoot.classList.remove('active');\n  }\n});"},{"id":"scene-02","title":"漏洞代号曝光","steps":2,"html":"<div class=\"s-scene-02-container\">\n  <div class=\"crt-overlay\"></div>\n  <div class=\"ambient-phosphor\"></div>\n  <div class=\"grid-bg\"></div>\n\n  <!-- Step 0: Vulnerability ID -->\n  <div class=\"vulnerability-wrapper\" data-step=\"0\">\n    <h1 class=\"cve-text\">CVE-2026-31431</h1>\n    <div class=\"cve-glow\"></div>\n  </div>\n\n  <!-- Step 1: Copy Fail & Tearing -->\n  <div class=\"fail-container\" data-step=\"1\">\n    <h2 class=\"fail-text\">COPY FAIL</h2>\n    <div class=\"tear-particles\">\n      <svg viewBox=\"0 0 1000 400\" preserveAspectRatio=\"none\">\n        <path class=\"tear-line l1\" d=\"M0,100 L200,120 L400,90 L600,110 L800,85 L1000,105\" />\n        <path class=\"tear-line l2\" d=\"M0,250 L250,230 L500,260 L750,240 L1000,270\" />\n        <path class=\"tear-line l3\" d=\"M0,320 L300,340 L700,310 L1000,330\" />\n      </svg>\n    </div>\n    <div class=\"glitch-block\"></div>\n  </div>\n</div>","css":".s-scene-02-container {\n  position: relative;\n  width: 100%;\n  height: 100%;\n  background: #000000;\n  overflow: hidden;\n  display: flex;\n  flex-direction: column;\n  justify-content: center;\n  align-items: center;\n  font-family: var(--sv-font-mono);\n}\n\n.crt-overlay {\n  position: absolute;\n  inset: 0;\n  background: repeating-linear-gradient(rgba(0,0,0,0) 0px, rgba(0,0,0,0) 1px, rgba(0,255,65,0.06) 2px, rgba(0,255,65,0.06) 3px);\n  pointer-events: none;\n  z-index: 10;\n}\n\n.ambient-phosphor {\n  position: absolute;\n  width: 120%;\n  height: 120%;\n  background: radial-gradient(circle, rgba(0,255,65,0.05) 0%, transparent 70%);\n  animation: phosphor-pulse 8s ease-in-out infinite;\n  pointer-events: none;\n}\n\n.grid-bg {\n  position: absolute;\n  inset: 0;\n  background-image: linear-gradient(rgba(0, 255, 65, 0.03) 1px, transparent 1px), linear-gradient(90deg, rgba(0, 255, 65, 0.03) 1px, transparent 1px);\n  background-size: 50px 50px;\n  opacity: 0.5;\n}\n\n.vulnerability-wrapper {\n  position: relative;\n  z-index: 5;\n  opacity: 0;\n  transform: scale(0);\n  transition: all 0.6s cubic-bezier(0.175, 0.885, 0.32, 1.275);\n}\n\n.active [data-step=\"0\"] {\n  opacity: 1;\n  transform: scale(1);\n}\n\n.cve-text {\n  font-size: 108px;\n  font-weight: 900;\n  color: #00FF41;\n  letter-spacing: -2px;\n  text-shadow: 0 0 20px rgba(0, 255, 65, 0.6);\n  margin: 0;\n  animation: blue-breath 4s ease-in-out infinite;\n}\n\n@keyframes blue-breath {\n  0%, 100% { text-shadow: 0 0 20px rgba(0, 255, 65, 0.4); }\n  50% { text-shadow: 0 0 40px rgba(60, 150, 255, 0.7), 0 0 10px rgba(0, 255, 65, 0.5); }\n}\n\n.fail-container {\n  position: absolute;\n  top: 60%;\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  opacity: 0;\n  transform: translateY(40px);\n  transition: all 0.5s ease-out;\n}\n\n.active [data-step=\"1\"] {\n  opacity: 1;\n  transform: translateY(0);\n}\n\n.fail-text {\n  font-size: 140px;\n  font-weight: 900;\n  color: #00FF41; \n  /* Semantically red but visually terminal phosphor */\n  background: #000;\n  border: 4px solid #00FF41;\n  padding: 0 40px;\n  mix-blend-mode: screen;\n  animation: critical-glitch 0.2s steps(2) infinite;\n}\n\n@keyframes critical-glitch {\n  0% { transform: skewX(0); opacity: 1; border-color: #00FF41; }\n  50% { transform: skewX(5deg); opacity: 0.8; border-color: rgba(255, 50, 50, 0.8); }\n}\n\n.tear-particles {\n  position: absolute;\n  inset: -100px -200px;\n  pointer-events: none;\n  z-index: -1;\n}\n\n.tear-line {\n  fill: none;\n  stroke: #00FF41;\n  stroke-width: 2;\n  stroke-dasharray: 1000;\n  stroke-dashoffset: 1000;\n  opacity: 0.4;\n}\n\n.active [data-step=\"1\"] .tear-line {\n  animation: tear-draw 0.8s forwards;\n}\n\n.l2 { animation-delay: 0.1s; }\n.l3 { animation-delay: 0.2s; }\n\n@keyframes tear-draw {\n  to { stroke-dashoffset: 0; opacity: 0.1; }\n}\n\n@keyframes phosphor-pulse {\n  0%, 100% { opacity: 0.4; transform: scale(1); }\n  50% { opacity: 0.6; transform: scale(1.05); }\n}","js":"window.SceneLifecycle.register('scene-02', {\n  enter() {\n    const sceneRoot = document.querySelector('[data-scene-id=\"scene-02\"]');\n    sceneRoot.classList.add('active');\n  },\n  step(step) {\n    const sceneRoot = document.querySelector('[data-scene-id=\"scene-02\"]');\n    if (!sceneRoot) return;\n\n    const cveWrapper = sceneRoot.querySelector('.vulnerability-wrapper');\n    const failContainer = sceneRoot.querySelector('.fail-container');\n\n    if (step >= 1) {\n      // Impact effect when Copy Fail appears\n      cveWrapper.style.transform = 'scale(0.9) translateY(-100px)';\n      cveWrapper.style.filter = 'blur(1px)';\n      \n      // Create temporary tearing jitter\n      sceneRoot.style.animation = 'screen-shake 0.3s cubic-bezier(.36,.07,.19,.97) both';\n      setTimeout(() => {\n        sceneRoot.style.animation = '';\n      }, 300);\n    }\n  },\n  leave() {\n    const sceneRoot = document.querySelector('[data-scene-id=\"scene-02\"]');\n    if (sceneRoot) sceneRoot.classList.remove('active');\n  }\n});\n\n// Added shake keyframes via style injection since it's a one-off impact\nconst style = document.createElement('style');\nstyle.innerHTML = `\n  @keyframes screen-shake {\n    10%, 90% { transform: translate3d(-1px, 0, 0); }\n    20%, 80% { transform: translate3d(2px, 0, 0); }\n    30%, 50%, 70% { transform: translate3d(-4px, 0, 0); }\n    40%, 60% { transform: translate3d(4px, 0, 0); }\n  }\n`;\ndocument.head.appendChild(style);"},{"id":"scene-03","title":"受影响范围","steps":3,"html":"<div class=\"s-scene-03-container\">\n  <div class=\"crt-lines\"></div>\n  <div class=\"ambient-glow\"></div>\n\n  <!-- Step 0: Timeline -->\n  <div class=\"timeline-section\" data-step=\"0\">\n    <div class=\"timeline-wrapper\">\n      <span class=\"time-label\">[ 2017 ]</span>\n      <div class=\"timeline-bar\">\n        <div class=\"timeline-fill\"></div>\n        <div class=\"timeline-arrow\">>></div>\n      </div>\n      <span class=\"time-label pulse-text\">至今</span>\n    </div>\n  </div>\n\n  <!-- Step 1: Linux Distribution Grid -->\n  <div class=\"distro-grid-container\" data-step=\"1\">\n    <div class=\"grid-header\">SYSTEM_SCAN_TARGETS: IN_PROGRESS...</div>\n    <div class=\"distro-grid\">\n      <div class=\"distro-card\" data-distro=\"ubuntu\">\n        <iconify-icon icon=\"simple-icons:ubuntu\"></iconify-icon>\n        <span class=\"distro-name\">Ubuntu</span>\n      </div>\n      <div class=\"distro-card\" data-distro=\"centos\">\n        <iconify-icon icon=\"simple-icons:centos\"></iconify-icon>\n        <span class=\"distro-name\">CentOS</span>\n      </div>\n      <div class=\"distro-card\" data-distro=\"rhel\">\n        <iconify-icon icon=\"simple-icons:redhat\"></iconify-icon>\n        <span class=\"distro-name\">RHEL</span>\n      </div>\n      <div class=\"distro-card\" data-distro=\"aws\">\n        <iconify-icon icon=\"simple-icons:amazonwebservices\"></iconify-icon>\n        <span class=\"distro-name\">AWS Linux</span>\n      </div>\n      <div class=\"distro-card\" data-distro=\"aliyun\">\n        <iconify-icon icon=\"simple-icons:alibabacloud\"></iconify-icon>\n        <span class=\"distro-name\">AliyunOS</span>\n      </div>\n      <div class=\"distro-card\" data-distro=\"suse\">\n        <iconify-icon icon=\"simple-icons:suse\"></iconify-icon>\n        <span class=\"distro-name\">SUSE</span>\n      </div>\n    </div>\n    <div class=\"shimmer-overlay\"></div>\n  </div>\n\n  <!-- Step 2: Vulnerable Stamp -->\n  <div class=\"vulnerable-overlay\" data-step=\"2\">\n    <div class=\"stamp-box\">\n      <div class=\"stamp-text\">VULNERABLE</div>\n      <div class=\"stamp-subtext\">受威胁状态</div>\n    </div>\n  </div>\n</div>","css":".s-scene-03-container {\n  position: relative;\n  width: 100%;\n  height: 100%;\n  background: #000000;\n  display: flex;\n  flex-direction: column;\n  justify-content: center;\n  align-items: center;\n  overflow: hidden;\n  font-family: var(--sv-font-mono);\n  color: #00FF41;\n}\n\n.crt-lines {\n  position: absolute;\n  inset: 0;\n  background: repeating-linear-gradient(rgba(0,0,0,0) 0px, rgba(0,0,0,0) 2px, rgba(0,255,65,0.08) 3px, rgba(0,255,65,0.08) 4px);\n  pointer-events: none;\n  z-index: 10;\n}\n\n.ambient-glow {\n  position: absolute;\n  width: 100%;\n  height: 100%;\n  background: radial-gradient(circle at center, rgba(0,255,65,0.04) 0%, transparent 70%);\n  pointer-events: none;\n}\n\n/* Step 0: Timeline */\n.timeline-section {\n  width: 80%;\n  margin-bottom: 60px;\n  opacity: 0;\n  transform: translateX(-50px);\n  transition: all 0.6s cubic-bezier(0.23, 1, 0.32, 1);\n}\n.active [data-step=\"0\"] { opacity: 1; transform: translateX(0); }\n\n.timeline-wrapper {\n  display: flex;\n  align-items: center;\n  gap: 20px;\n  font-size: 28px;\n}\n\n.timeline-bar {\n  flex-grow: 1;\n  height: 4px;\n  background: rgba(0, 255, 65, 0.2);\n  position: relative;\n  display: flex;\n  align-items: center;\n}\n\n.timeline-fill {\n  position: absolute;\n  left: 0;\n  height: 100%;\n  width: 0%;\n  background: #00FF41;\n  box-shadow: 0 0 10px #00FF41;\n  transition: width 1.5s ease-in-out;\n}\n\n.active [data-step=\"0\"] .timeline-fill { width: 100%; }\n\n.timeline-arrow {\n  position: absolute;\n  right: -10px;\n  font-weight: bold;\n  text-shadow: 0 0 5px #00FF41;\n}\n\n.pulse-text {\n  animation: phosphor-flicker 2s infinite;\n}\n\n/* Step 1: Grid */\n.distro-grid-container {\n  width: 85%;\n  position: relative;\n  opacity: 0;\n  transition: opacity 0.5s ease;\n}\n.active [data-step=\"1\"] { opacity: 1; }\n\n.grid-header {\n  font-size: 14px;\n  margin-bottom: 10px;\n  color: rgba(0, 255, 65, 0.6);\n  letter-spacing: 2px;\n}\n\n.distro-grid {\n  display: grid;\n  grid-template-columns: repeat(3, 1fr);\n  gap: 30px;\n  padding: 40px;\n  border: 1px solid rgba(0, 255, 65, 0.3);\n  background: rgba(0, 255, 65, 0.02);\n}\n\n.distro-card {\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  gap: 15px;\n  transition: all 0.4s ease;\n}\n\n.distro-card iconify-icon {\n  font-size: 64px;\n  color: #00FF41;\n  filter: drop-shadow(0 0 5px rgba(0, 255, 65, 0.4));\n}\n\n.distro-name {\n  font-size: 18px;\n  text-transform: uppercase;\n}\n\n/* Step 2: Vulnerable State */\n.active [data-step=\"2\"] .distro-card iconify-icon {\n  color: #FF3131;\n  filter: drop-shadow(0 0 8px rgba(255, 49, 49, 0.6));\n  animation: icon-glitch 0.3s infinite alternate;\n}\n\n.shimmer-overlay {\n  position: absolute;\n  inset: 0;\n  background: linear-gradient(135deg, transparent 0%, rgba(0,255,65,0.1) 45%, rgba(0,255,65,0.2) 50%, rgba(0,255,65,0.1) 55%, transparent 100%);\n  background-size: 200% 200%;\n  pointer-events: none;\n  opacity: 0;\n}\n.active [data-step=\"2\"] .shimmer-overlay {\n  opacity: 1;\n  animation: shimmer-scan 3s infinite linear;\n}\n\n.vulnerable-overlay {\n  position: absolute;\n  inset: 0;\n  display: flex;\n  justify-content: center;\n  align-items: center;\n  z-index: 20;\n  pointer-events: none;\n  opacity: 0;\n  transform: scale(2);\n  transition: all 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);\n}\n\n.active [data-step=\"2\"].vulnerable-overlay {\n  opacity: 1;\n  transform: scale(1) rotate(-10deg);\n}\n\n.stamp-box {\n  border: 8px double #FF3131;\n  padding: 20px 40px;\n  background: rgba(0, 0, 0, 0.9);\n  box-shadow: 0 0 30px rgba(255, 49, 49, 0.4);\n  text-align: center;\n}\n\n.stamp-text {\n  font-size: 84px;\n  font-weight: 900;\n  color: #FF3131;\n  line-height: 1;\n}\n\n.stamp-subtext {\n  font-size: 24px;\n  color: #FF3131;\n  letter-spacing: 10px;\n  margin-top: 10px;\n}\n\n@keyframes phosphor-flicker {\n  0%, 100% { opacity: 1; }\n  50% { opacity: 0.6; }\n  55% { opacity: 0.9; }\n}\n\n@keyframes shimmer-scan {\n  0% { background-position: -200% -200%; }\n  100% { background-position: 200% 200%; }\n}\n\n@keyframes icon-glitch {\n  0% { transform: translate(0); }\n  50% { transform: translate(-2px, 1px); }\n  100% { transform: translate(2px, -1px); }\n}","js":"window.SceneLifecycle.register('scene-03', {\n  enter() {\n    const sceneRoot = document.querySelector('[data-scene-id=\"scene-03\"]');\n    if (sceneRoot) {\n      sceneRoot.classList.add('active');\n      window.__SVGVIDEO_REFRESH_ICONS(sceneRoot);\n    }\n  },\n  step(step) {\n    const sceneRoot = document.querySelector('[data-scene-id=\"scene-03\"]');\n    if (!sceneRoot) return;\n\n    const header = sceneRoot.querySelector('.grid-header');\n    if (step === 2) {\n      if (header) header.textContent = 'SYSTEM_CRITICAL: VULNERABILITIES_DETECTED';\n      // Add a slight screen shake on vulnerable stamp\n      sceneRoot.style.animation = 'terminal-shake 0.4s ease-out';\n      setTimeout(() => sceneRoot.style.animation = '', 400);\n    } else if (step === 1) {\n      if (header) header.textContent = 'SYSTEM_SCAN_TARGETS: IDENTIFIED';\n    }\n  },\n  leave() {\n    const sceneRoot = document.querySelector('[data-scene-id=\"scene-03\"]');\n    if (sceneRoot) sceneRoot.classList.remove('active');\n  }\n});\n\nconst shakeStyles = document.createElement('style');\nshakeStyles.innerHTML = `\n  @keyframes terminal-shake {\n    0% { transform: translate(0,0); }\n    10% { transform: translate(-5px,-5px); }\n    20% { transform: translate(5px,5px); }\n    30% { transform: translate(-5px,5px); }\n    40% { transform: translate(5px,-5px); }\n    100% { transform: translate(0,0); }\n  }\n`;\ndocument.head.appendChild(shakeStyles);"},{"id":"scene-04","title":"攻击场景描述","steps":2,"html":"<div class=\"s-scene-04-wrapper\">\n  <div class=\"crt-lines\"></div>\n  <div class=\"ambient-glow\"></div>\n\n  <div class=\"system-layout\">\n    <!-- Left: Container -->\n    <div class=\"node-container\">\n      <div class=\"system-node box-node\">\n        <div class=\"node-label\">[ CONTAINER ]</div>\n        <div class=\"internal-process\">\n          <div class=\"cursor-blink\"></div>\n          <span class=\"mono-text\">UID: 1000</span>\n        </div>\n      </div>\n    </div>\n\n    <!-- SVG Layer for the Arrow -->\n    <div class=\"vector-layer\">\n      <svg viewBox=\"0 0 800 400\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n        <path id=\"attack-path\" d=\"M 240 200 L 560 200\" stroke=\"#FF3131\" stroke-width=\"4\" stroke-dasharray=\"320\" stroke-dashoffset=\"320\" />\n        <path id=\"arrow-head\" d=\"M 545 185 L 565 200 L 545 215\" stroke=\"#FF3131\" stroke-width=\"4\" opacity=\"0\" />\n      </svg>\n    </div>\n\n    <!-- Right: Host -->\n    <div class=\"node-container\">\n      <div class=\"system-node circle-node\" id=\"host-node\">\n        <div class=\"node-label\">[ HOST_SYSTEM ]</div>\n        <div class=\"internal-process\">\n          <span class=\"status-text\">KERNEL_IDLE</span>\n        </div>\n      </div>\n    </div>\n  </div>\n\n  <!-- Success Banner -->\n  <div class=\"success-banner\" data-step=\"1\">\n    <div class=\"glitch-box\">\n      <span class=\"success-text\">> 提权成功: ESCAPED_TO_ROOT</span>\n    </div>\n  </div>\n</div>","css":".s-scene-04-wrapper {\n  position: relative;\n  width: 100%;\n  height: 100%;\n  background: #000000;\n  overflow: hidden;\n  font-family: var(--sv-font-mono);\n  display: flex;\n  justify-content: center;\n  align-items: center;\n}\n\n.crt-lines {\n  position: absolute;\n  inset: 0;\n  background: repeating-linear-gradient(rgba(0,0,0,0) 0px, rgba(0,0,0,0) 2px, rgba(0,255,65,0.05) 3px, rgba(0,255,65,0.05) 4px);\n  pointer-events: none;\n  z-index: 10;\n}\n\n.ambient-glow {\n  position: absolute;\n  width: 100%;\n  height: 100%;\n  background: radial-gradient(circle at center, rgba(0,255,65,0.03) 0%, transparent 70%);\n  pointer-events: none;\n}\n\n.system-layout {\n  width: 100%;\n  max-width: 1200px;\n  display: flex;\n  justify-content: space-between;\n  align-items: center;\n  padding: 0 10%;\n  z-index: 5;\n}\n\n.system-node {\n  width: 280px;\n  height: 280px;\n  display: flex;\n  flex-direction: column;\n  justify-content: center;\n  align-items: center;\n  border: 2px solid rgba(0, 255, 65, 0.4);\n  background: rgba(0, 255, 65, 0.02);\n  transition: all 0.5s cubic-bezier(0.165, 0.84, 0.44, 1);\n  position: relative;\n}\n\n.box-node {\n  border-radius: 4px;\n}\n\n.circle-node {\n  border-radius: 50%;\n  border-color: rgba(255, 255, 255, 0.3);\n}\n\n.node-label {\n  position: absolute;\n  top: -40px;\n  color: rgba(0, 255, 65, 0.6);\n  font-size: 14px;\n  letter-spacing: 2px;\n}\n\n.internal-process {\n  display: flex;\n  align-items: center;\n  gap: 10px;\n}\n\n.mono-text, .status-text {\n  color: #00FF41;\n  font-size: 18px;\n}\n\n.cursor-blink {\n  width: 10px;\n  height: 20px;\n  background: #00FF41;\n  animation: cursor-blink 1s infinite steps(1);\n}\n\n.vector-layer {\n  position: absolute;\n  inset: 0;\n  pointer-events: none;\n}\n\n.vector-layer svg {\n  width: 100%;\n  height: 100%;\n}\n\n/* Step 0 Animation */\n.active[data-scene-id=\"scene-04\"] #attack-path {\n  transition: stroke-dashoffset 0.8s ease-in-out 0.3s;\n  stroke-dashoffset: 0;\n}\n\n.active[data-scene-id=\"scene-04\"] #arrow-head {\n  transition: opacity 0.2s ease 1.1s;\n  opacity: 1;\n}\n\n/* Step 1 Styles */\n[data-step=\"1\"] .circle-node {\n  border-color: #FF3131;\n  background: rgba(255, 49, 49, 0.1);\n  box-shadow: 0 0 30px rgba(255, 49, 49, 0.3);\n  transform: scale(1.05);\n}\n\n[data-step=\"1\"] .circle-node .status-text {\n  color: #FF3131;\n  animation: text-warning 0.5s infinite alternate;\n}\n\n.success-banner {\n  position: absolute;\n  bottom: 10%;\n  opacity: 0;\n  transform: translateY(20px);\n  transition: all 0.5s ease;\n}\n\n.active .success-banner[data-step=\"1\"] {\n  opacity: 1;\n  transform: translateY(0);\n}\n\n.glitch-box {\n  border: 1px solid #FF3131;\n  padding: 15px 40px;\n  background: rgba(0, 0, 0, 0.9);\n  box-shadow: 4px 4px 0px #FF3131;\n}\n\n.success-text {\n  color: #FF3131;\n  font-size: 28px;\n  font-weight: bold;\n  letter-spacing: 2px;\n}\n\n@keyframes cursor-blink {\n  0%, 100% { opacity: 1; }\n  50% { opacity: 0; }\n}\n\n@keyframes text-warning {\n  from { opacity: 1; text-shadow: 0 0 5px #FF3131; }\n  to { opacity: 0.7; text-shadow: 0 0 15px #FF3131; }\n}","js":"window.SceneLifecycle.register('scene-04', {\n  enter() {\n    const sceneRoot = document.querySelector('[data-scene-id=\"scene-04\"]');\n    if (sceneRoot) {\n      sceneRoot.classList.add('active');\n      // Reset paths for re-entry\n      const path = sceneRoot.querySelector('#attack-path');\n      const head = sceneRoot.querySelector('#arrow-head');\n      if(path) path.style.strokeDashoffset = '320';\n      if(head) head.style.opacity = '0';\n    }\n  },\n  step(step) {\n    const sceneRoot = document.querySelector('[data-scene-id=\"scene-04\"]');\n    if (!sceneRoot) return;\n    \n    const hostNode = sceneRoot.querySelector('#host-node');\n    const statusText = sceneRoot.querySelector('.status-text');\n    \n    if (step === 1) {\n      if (statusText) statusText.textContent = 'SYSTEM_COMPROMISED';\n      // Add a small shake effect on success\n      sceneRoot.style.animation = 'privilege-shake 0.3s ease-in-out';\n      setTimeout(() => { sceneRoot.style.animation = ''; }, 300);\n    } else {\n      if (statusText) statusText.textContent = 'KERNEL_IDLE';\n    }\n  },\n  leave() {\n    const sceneRoot = document.querySelector('[data-scene-id=\"scene-04\"]');\n    if (sceneRoot) sceneRoot.classList.remove('active');\n  }\n});\n\nconst style = document.createElement('style');\nstyle.innerHTML = `\n@keyframes privilege-shake {\n  0% { transform: translate(0,0); }\n  25% { transform: translate(-4px, 4px); }\n  50% { transform: translate(4px, -4px); }\n  75% { transform: translate(-4px, -4px); }\n  100% { transform: translate(0,0); }\n}`;\ndocument.head.appendChild(style);"},{"id":"scene-05","title":"原理科普：Page Cache","steps":3,"html":"<div class=\"s-scene-05-wrapper\">\n  <div class=\"crt-scanlines\"></div>\n  <div class=\"ambient-phosphor\"></div>\n\n  <!-- Step 0: Memory Container -->\n  <div class=\"memory-container\">\n    <div class=\"memory-label\">[ PHYSICAL_RAM_MAPPING ]</div>\n    <div class=\"memory-grid\"></div>\n\n    <!-- Step 1: Page Cache Card -->\n    <div class=\"page-cache-card\" data-step=\"1\">\n      <div class=\"card-header\">\n        <span class=\"card-title\">PAGE_CACHE_OBJECT</span>\n        <span class=\"memory-addr\">0x7FFF0042</span>\n      </div>\n      <div class=\"binary-content\">\n        <div class=\"binary-line\">10110010 11100001 00011101</div>\n        <div class=\"binary-line\">11001010 01111100 11010001</div>\n        <div class=\"binary-line\">00110101 10100011 00011110</div>\n        <div class=\"binary-line\">11110000 00001111 10101010</div>\n      </div>\n      \n      <!-- Step 2: Security Overlay -->\n      <div class=\"security-status\" data-step=\"2\">\n        <div class=\"lock-overlay\">\n          <i data-lucide=\"lock\"></i>\n          <span class=\"read-only-text\">READ_ONLY</span>\n        </div>\n        <div class=\"forbidden-action\">\n          <div class=\"action-item\">\n            <span class=\"action-label\">WRITE_REQ</span>\n            <div class=\"forbidden-sign\"></div>\n          </div>\n          <div class=\"action-item\">\n            <span class=\"action-label\">MODIFY_REQ</span>\n            <div class=\"forbidden-sign\"></div>\n          </div>\n        </div>\n      </div>\n    </div>\n  </div>\n\n  <!-- Background Text -->\n  <div class=\"ghost-text\">KERNEL_SPACE</div>\n</div>","css":".s-scene-05-wrapper {\n  position: relative;\n  width: 100%;\n  height: 100%;\n  background: #000000;\n  overflow: hidden;\n  display: flex;\n  justify-content: center;\n  align-items: center;\n  font-family: var(--sv-font-mono);\n  color: #00FF41;\n}\n\n.crt-scanlines {\n  position: absolute;\n  inset: 0;\n  background: repeating-linear-gradient(rgba(0,0,0,0) 0px, rgba(0,0,0,0) 2px, rgba(0,255,65,0.05) 3px, rgba(0,255,65,0.05) 4px);\n  pointer-events: none;\n  z-index: 10;\n}\n\n.ambient-phosphor {\n  position: absolute;\n  width: 100%;\n  height: 100%;\n  background: radial-gradient(circle at 50% 50%, rgba(0, 255, 65, 0.04) 0%, transparent 80%);\n  pointer-events: none;\n}\n\n.memory-container {\n  position: relative;\n  width: 80%;\n  height: 70%;\n  border: 1px solid rgba(0, 255, 65, 0.2);\n  background: rgba(0, 255, 65, 0.02);\n  padding: 40px;\n  box-sizing: border-box;\n  display: flex;\n  justify-content: center;\n  align-items: center;\n}\n\n.memory-label {\n  position: absolute;\n  top: 15px;\n  left: 20px;\n  font-size: 14px;\n  opacity: 0.6;\n  letter-spacing: 2px;\n}\n\n.memory-grid {\n  position: absolute;\n  inset: 0;\n  opacity: 0.1;\n  background-image: radial-gradient(#00FF41 1px, transparent 1px);\n  background-size: 30px 30px;\n}\n\n.page-cache-card {\n  width: 480px;\n  height: 300px;\n  background: rgba(0, 20, 0, 0.9);\n  border: 2px solid #00FF41;\n  box-shadow: 0 0 30px rgba(0, 255, 65, 0.2);\n  padding: 20px;\n  position: relative;\n  z-index: 5;\n  opacity: 0;\n  transform: scale(0.9) translateY(20px);\n  transition: all 0.6s cubic-bezier(0.16, 1, 0.3, 1);\n}\n\n.page-cache-card[data-step=\"1\"] {\n  opacity: 1;\n  transform: scale(1) translateY(0);\n}\n\n.card-header {\n  border-bottom: 1px solid rgba(0, 255, 65, 0.3);\n  padding-bottom: 10px;\n  margin-bottom: 20px;\n  display: flex;\n  justify-content: space-between;\n  align-items: center;\n}\n\n.card-title {\n  font-size: 20px;\n  font-weight: bold;\n}\n\n.memory-addr {\n  font-size: 14px;\n  opacity: 0.7;\n}\n\n.binary-content {\n  font-size: 24px;\n  line-height: 1.5;\n  letter-spacing: 4px;\n  color: rgba(0, 255, 65, 0.8);\n}\n\n.security-status {\n  position: absolute;\n  inset: 0;\n  background: rgba(0, 0, 0, 0.7);\n  backdrop-filter: blur(2px);\n  display: flex;\n  flex-direction: column;\n  justify-content: center;\n  align-items: center;\n  opacity: 0;\n  transition: opacity 0.5s ease;\n}\n\n.security-status[data-step=\"2\"] {\n  opacity: 1;\n}\n\n.lock-overlay {\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  gap: 10px;\n  margin-bottom: 30px;\n  color: #39FF14;\n}\n\n.lock-overlay i {\n  width: 64px;\n  height: 64px;\n}\n\n.read-only-text {\n  font-size: 32px;\n  font-weight: 800;\n  letter-spacing: 4px;\n  border: 2px solid #39FF14;\n  padding: 5px 20px;\n}\n\n.forbidden-action {\n  display: flex;\n  gap: 40px;\n}\n\n.action-item {\n  position: relative;\n  padding: 5px 15px;\n  opacity: 0.6;\n}\n\n.forbidden-sign {\n  position: absolute;\n  top: 50%;\n  left: 50%;\n  width: 120%;\n  height: 2px;\n  background: #FF3131;\n  transform: translate(-50%, -50%) rotate(-25deg);\n  box-shadow: 0 0 10px #FF3131;\n}\n\n.ghost-text {\n  position: absolute;\n  bottom: 5%;\n  right: 5%;\n  font-size: 80px;\n  opacity: 0.03;\n  font-weight: 900;\n  pointer-events: none;\n}\n\n@keyframes pulse-border {\n  0% { border-color: rgba(0, 255, 65, 0.4); }\n  50% { border-color: rgba(0, 255, 65, 1); }\n  100% { border-color: rgba(0, 255, 65, 0.4); }\n}","js":"window.SceneLifecycle.register('scene-05', {\n  enter() {\n    const root = document.querySelector('[data-scene-id=\"scene-05\"]');\n    const lines = root.querySelectorAll('.binary-line');\n    \n    // Randomize binary streams slightly\n    lines.forEach(line => {\n      setInterval(() => {\n        if (Math.random() > 0.7) {\n          const chars = line.innerText.split('');\n          const idx = Math.floor(Math.random() * chars.length);\n          if (chars[idx] === '0') chars[idx] = '1';\n          else if (chars[idx] === '1') chars[idx] = '0';\n          line.innerText = chars.join('');\n        }\n      }, 200);\n    });\n\n    window.__SVGVIDEO_REFRESH_ICONS(root);\n  },\n  step(n) {\n    const root = document.querySelector('[data-scene-id=\"scene-05\"]');\n    const card = root.querySelector('.page-cache-card');\n    const security = root.querySelector('.security-status');\n\n    if (n >= 1) {\n      card.setAttribute('data-step', '1');\n    } else {\n      card.removeAttribute('data-step');\n    }\n\n    if (n >= 2) {\n      security.setAttribute('data-step', '2');\n      // Add a slight glitch effect when locked\n      card.style.animation = 'privilege-shake 0.2s 1';\n      setTimeout(() => card.style.animation = '', 200);\n    } else {\n      security.removeAttribute('data-step');\n    }\n  },\n  leave() {}\n});"},{"id":"scene-06","title":"利用机制：AF_ALG","steps":2,"html":"<div class=\"s-scene-06-wrapper\">\n  <div class=\"crt-overlay\"></div>\n  <div class=\"ambient-glow\"></div>\n\n  <!-- Step 0: Terminal Window -->\n  <div class=\"terminal-container\" id=\"term-06\">\n    <div class=\"term-header\">\n      <div class=\"term-controls\"><span></span><span></span><span></span></div>\n      <div class=\"term-title\">K-SHELL v4.1</div>\n    </div>\n    <div class=\"term-content\">\n      <div class=\"typing-line\" data-delay=\"1\">$ modprobe af_alg</div>\n      <div class=\"typing-line\" data-delay=\"2\">$ call <span class=\"highlight\">splice()</span></div>\n      <div class=\"typing-line blinking-cursor\">_</div>\n    </div>\n  </div>\n\n  <!-- Step 1: Conduit & Target File -->\n  <div class=\"conduit-system\">\n    <svg class=\"conduit-svg\" viewBox=\"0 0 1000 600\">\n      <path id=\"conduit-path\" d=\"M280,300 L450,300\" fill=\"none\" stroke=\"#00FF41\" stroke-width=\"4\" stroke-dasharray=\"1000\" stroke-dashoffset=\"1000\" />\n      <circle cx=\"280\" cy=\"300\" r=\"5\" fill=\"#00FF41\" class=\"node-point\" />\n    </svg>\n    <div class=\"file-node\" id=\"su-file-node\">\n      <div class=\"file-icon-box\">\n        <iconify-icon icon=\"mdi:file-lock-outline\"></iconify-icon>\n      </div>\n      <div class=\"file-info\">\n        <span class=\"file-name\">/usr/bin/su</span>\n        <span class=\"file-perm\">ROOT_SUID: 4755</span>\n      </div>\n    </div>\n  </div>\n\n  <!-- Step 2: Encryption Flow & Chains -->\n  <div class=\"pipeline-stage\" id=\"encrypt-stage\">\n    <div class=\"pipeline-box\">\n      <div class=\"pipeline-label\">[ KERNEL_CRYPTO_API ]</div>\n      <div class=\"chain-container\">\n         <div class=\"chain-link\"></div>\n         <div class=\"chain-link\"></div>\n         <div class=\"chain-link\"></div>\n      </div>\n      <div class=\"flow-indicator\">ENCRYPTION_IN_PROGRESS</div>\n    </div>\n  </div>\n\n  <div class=\"side-status\">\n    <div class=\"status-item\" data-step=\"1\">> CHANNEL_ESTABLISHED</div>\n    <div class=\"status-item\" data-step=\"2\">> FILE_INGESTION: FORCE</div>\n  </div>\n</div>","css":".s-scene-06-wrapper {\n  position: relative;\n  width: 100%;\n  height: 100%;\n  background: #000000;\n  overflow: hidden;\n  display: flex;\n  align-items: center;\n  justify-content: space-around;\n  font-family: var(--sv-font-mono);\n  color: #00FF41;\n}\n\n.crt-overlay {\n  position: absolute;\n  inset: 0;\n  background: repeating-linear-gradient(rgba(0,0,0,0) 0px, rgba(0,0,0,0) 2px, rgba(0,255,65,0.08) 3px, rgba(0,255,65,0.08) 4px);\n  pointer-events: none;\n  z-index: 100;\n}\n\n.ambient-glow {\n  position: absolute;\n  width: 100%;\n  height: 100%;\n  background: radial-gradient(circle at 50% 50%, rgba(0, 255, 65, 0.03) 0%, transparent 70%);\n  pointer-events: none;\n}\n\n.terminal-container {\n  width: 320px;\n  height: 200px;\n  border: 1px solid rgba(0, 255, 65, 0.4);\n  background: rgba(0, 20, 0, 0.8);\n  box-shadow: 0 0 15px rgba(0, 255, 65, 0.1);\n  z-index: 10;\n  transform: translateX(-100px);\n  opacity: 0;\n  transition: all 0.6s cubic-bezier(0.16, 1, 0.3, 1);\n}\n\n.term-header {\n  background: rgba(0, 255, 65, 0.15);\n  padding: 6px 10px;\n  display: flex;\n  justify-content: space-between;\n  align-items: center;\n  border-bottom: 1px solid rgba(0, 255, 65, 0.3);\n}\n\n.term-controls {\n  display: flex;\n  gap: 6px;\n}\n\n.term-controls span {\n  width: 8px;\n  height: 8px;\n  border: 1px solid #00FF41;\n  border-radius: 50%;\n}\n\n.term-title {\n  font-size: 10px;\n  opacity: 0.7;\n}\n\n.term-body {\n  padding: 15px;\n  font-size: 14px;\n}\n\n.typing-line {\n  margin-bottom: 5px;\n  overflow: hidden;\n  white-space: nowrap;\n}\n\n.highlight { color: #39FF14; text-shadow: 0 0 5px #00FF41; }\n\n.conduit-system {\n  position: absolute;\n  width: 100%;\n  height: 100%;\n  pointer-events: none;\n}\n\n.conduit-svg {\n  position: absolute;\n  top: 0; left: 0; width: 100%; height: 100%;\n}\n\n.file-node {\n  position: absolute;\n  left: 50%;\n  top: 50%;\n  transform: translate(-50%, -50%) scale(0.9);\n  opacity: 0;\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  gap: 10px;\n  z-index: 20;\n  transition: all 0.5s ease;\n}\n\n.file-icon-box {\n  font-size: 64px;\n  border: 2px solid #00FF41;\n  padding: 15px;\n  background: rgba(0, 255, 65, 0.05);\n  box-shadow: 0 0 20px rgba(0, 255, 65, 0.2);\n}\n\n.file-name {\n  font-size: 20px;\n  font-weight: bold;\n}\n\n.file-perm {\n  font-size: 12px;\n  opacity: 0.6;\n}\n\n.pipeline-stage {\n  width: 300px;\n  height: 250px;\n  border: 2px dashed rgba(0, 255, 65, 0.3);\n  position: relative;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  opacity: 0;\n  transform: translateX(50px);\n  transition: all 0.6s ease;\n}\n\n.pipeline-box {\n  width: 90%;\n  height: 90%;\n  background: rgba(0, 255, 65, 0.02);\n  border: 1px solid #00FF41;\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  justify-content: center;\n  gap: 20px;\n}\n\n.pipeline-label {\n  position: absolute;\n  top: -12px;\n  background: #000;\n  padding: 0 10px;\n  font-size: 14px;\n}\n\n.chain-container {\n  display: flex;\n  gap: 5px;\n  opacity: 0;\n}\n\n.chain-link {\n  width: 15px;\n  height: 30px;\n  border: 2px solid #00FF41;\n  border-radius: 10px;\n}\n\n.flow-indicator {\n  font-size: 12px;\n  animation: blink 0.8s infinite;\n}\n\n.side-status {\n  position: absolute;\n  bottom: 40px;\n  right: 40px;\n  text-align: right;\n}\n\n.status-item {\n  font-size: 14px;\n  opacity: 0;\n  margin-top: 5px;\n}\n\n@keyframes shake {\n  0%, 100% { transform: translate(-50%, -50%); }\n  20% { transform: translate(-52%, -48%); }\n  40% { transform: translate(-48%, -52%); }\n  60% { transform: translate(-51%, -49%); }\n  80% { transform: translate(-49%, -51%); }\n}\n\n@keyframes blink {\n  0%, 100% { opacity: 1; }\n  50% { opacity: 0.3; }\n}\n\n.shaking { animation: shake 0.2s infinite; }\n\n[data-step=\"1\"] .status-item[data-step=\"1\"] { opacity: 1; }\n[data-step=\"2\"] .status-item[data-step=\"2\"] { opacity: 1; }","js":"window.SceneLifecycle.register('scene-06', {\n  enter() {\n    const root = document.querySelector('[data-scene-id=\"scene-06\"]');\n    const term = root.querySelector('#term-06');\n    const file = root.querySelector('#su-file-node');\n    \n    setTimeout(() => {\n      term.style.opacity = '1';\n      term.style.transform = 'translateX(0)';\n      file.style.opacity = '1';\n      file.style.transform = 'translate(-50%, -50%) scale(1)';\n    }, 300);\n\n    window.__SVGVIDEO_REFRESH_ICONS(root);\n  },\n  step(n) {\n    const root = document.querySelector('[data-scene-id=\"scene-06\"]');\n    const path = root.querySelector('#conduit-path');\n    const file = root.querySelector('#su-file-node');\n    const encrypt = root.querySelector('#encrypt-stage');\n    const chains = root.querySelector('.chain-container');\n\n    if (n === 1) {\n      path.style.transition = 'stroke-dashoffset 0.8s ease-out';\n      path.style.strokeDashoffset = '0';\n    }\n\n    if (n === 2) {\n      // Shake effect\n      file.classList.add('shaking');\n      \n      setTimeout(() => {\n        // Drag into encryption flow\n        encrypt.style.opacity = '1';\n        encrypt.style.transform = 'translateX(0)';\n        \n        file.style.transition = 'all 0.8s cubic-bezier(0.68, -0.55, 0.27, 1.55)';\n        file.style.left = '75%';\n        file.style.top = '50%';\n        file.style.transform = 'translate(-50%, -50%) scale(0.7)';\n        file.style.zIndex = '30';\n        \n        setTimeout(() => {\n          file.classList.remove('shaking');\n          chains.style.opacity = '1';\n          encrypt.style.boxShadow = '0 0 30px rgba(0, 255, 65, 0.4)';\n        }, 800);\n      }, 400);\n    }\n  },\n  leave() {}\n});"},{"id":"scene-07","title":"漏洞核心：错误的写权限","steps":2,"html":"<div class=\"s-scene-07-wrapper\">\n  <div class=\"crt-overlay\"></div>\n  <div class=\"ambient-glow\"></div>\n  \n  <!-- Alert Overlay (Step 2) -->\n  <div class=\"alert-pulse-overlay\" data-step=\"2\"></div>\n\n  <div class=\"layout-container\">\n    <!-- Left: Code Section -->\n    <div class=\"code-section\">\n      <div class=\"section-label\">2017 内核优化代码</div>\n      <div class=\"terminal-window\">\n        <div class=\"term-header\">\n           <div class=\"term-dots\"><span></span><span></span><span></span></div>\n           <div class=\"term-title\">mm/gup.c</div>\n        </div>\n        <div class=\"code-content\">\n          <pre><code><span class=\"line\">/* Optimization: No COW needed? */</span>\n<span class=\"line\">if (flags & FOLL_FORCE) {</span>\n<span class=\"line highlight-target\">  vma-&gt;vm_flags |= <span class=\"code-keyword\">VM_MAYWRITE</span>;</span>\n<span class=\"line\">  <span class=\"code-comment\">// Treat as writable buffer</span></span>\n<span class=\"line\">}</span></code></pre>\n        </div>\n      </div>\n    </div>\n\n    <!-- Middle: Conduit -->\n    <div class=\"conduit-container\">\n      <svg class=\"conduit-svg\" viewBox=\"0 0 200 100\">\n        <path class=\"arrow-path\" d=\"M0,50 L180,50\" fill=\"none\" stroke=\"#00FF41\" stroke-width=\"2\" />\n        <polygon points=\"180,45 195,50 180,55\" fill=\"#00FF41\" class=\"arrow-head\" />\n      </svg>\n    </div>\n\n    <!-- Right: Memory Page Section -->\n    <div class=\"memory-section\">\n      <div class=\"section-label\">物理内存页面</div>\n      <div class=\"page-frame\">\n        <div class=\"page-grid\">\n          <span></span><span></span><span></span><span></span>\n          <span></span><span></span><span></span><span></span>\n        </div>\n        <div class=\"permission-status\">\n          <div class=\"lock-container\" id=\"perm-lock\">\n             <div class=\"lock-icon\"><iconify-icon icon=\"mdi:lock-outline\"></iconify-icon></div>\n             <div class=\"lock-text\">只读 (RO)</div>\n          </div>\n          <div class=\"rw-status\" id=\"perm-rw\">\n             <div class=\"rw-glitch\">可写 (RW)</div>\n          </div>\n        </div>\n      </div>\n    </div>\n  </div>\n\n  <div class=\"status-bar\">\n    <div class=\"status-msg\">SYSTEM_OPTIMIZATION: ACTIVE</div>\n    <div class=\"status-msg\" data-step=\"1\">CRITICAL: PERMISSION_BYPASS_DETECTED</div>\n  </div>\n</div>","css":".s-scene-07-wrapper { width: 100%; height: 100%; background: #000; display: flex; align-items: center; justify-content: center; font-family: var(--sv-font-mono); color: #00FF41; overflow: hidden; position: relative; }\n.crt-overlay { position: absolute; inset: 0; background: repeating-linear-gradient(rgba(0,0,0,0) 0px, rgba(0,0,0,0) 2px, rgba(0,255,65,0.05) 3px, rgba(0,255,65,0.05) 4px); pointer-events: none; z-index: 10; }\n.ambient-glow { position: absolute; width: 100%; height: 100%; background: radial-gradient(circle at 50% 50%, rgba(0, 255, 65, 0.05) 0%, transparent 70%); }\n.layout-container { display: flex; align-items: center; justify-content: space-between; width: 90%; gap: 20px; z-index: 5; }\n.section-label { font-size: 14px; color: rgba(0,255,65,0.6); margin-bottom: 10px; text-transform: uppercase; letter-spacing: 2px; }\n.terminal-window { width: 420px; border: 1px solid rgba(0,255,65,0.3); background: rgba(0,20,0,0.9); box-shadow: 0 0 20px rgba(0,255,65,0.1); }\n.term-header { background: rgba(0,255,65,0.1); padding: 8px 12px; display: flex; align-items: center; border-bottom: 1px solid rgba(0,255,65,0.2); }\n.term-dots { display: flex; gap: 5px; margin-right: 15px; }\n.term-dots span { width: 6px; height: 6px; border: 1px solid #00FF41; border-radius: 50%; }\n.term-title { font-size: 12px; opacity: 0.8; }\n.code-content { padding: 20px; font-size: 16px; line-height: 1.5; }\n.highlight-target { background: rgba(0,255,65,0.2); border-left: 3px solid #00FF41; padding-left: 5px; }\n.code-keyword { color: #39FF14; font-weight: bold; }\n.code-comment { color: rgba(0,255,65,0.4); }\n.conduit-container { flex: 1; height: 100px; display: flex; align-items: center; justify-content: center; }\n.arrow-path { stroke-dasharray: 200; stroke-dashoffset: 200; transition: stroke-dashoffset 0.8s ease; }\n.memory-section { display: flex; flex-direction: column; align-items: center; }\n.page-frame { width: 280px; height: 180px; border: 2px solid #00FF41; position: relative; background: rgba(0,255,65,0.02); display: flex; flex-direction: column; justify-content: center; align-items: center; }\n.page-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 10px; opacity: 0.2; position: absolute; inset: 20px; }\n.page-grid span { border: 1px solid #00FF41; height: 20px; }\n.permission-status { position: relative; z-index: 2; text-align: center; }\n.lock-container { transition: all 0.4s ease; }\n.lock-icon { font-size: 48px; margin-bottom: 10px; }\n.lock-text { font-size: 18px; }\n.rw-status { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%) scale(0); opacity: 0; color: #FF3131; font-weight: 900; font-size: 42px; text-shadow: 0 0 15px rgba(255,49,49,0.8); transition: all 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275); }\n.shattered .lock-container { transform: scale(1.5) rotate(15deg); opacity: 0; filter: blur(5px); }\n.shattered .rw-status { transform: translate(-50%, -50%) scale(1); opacity: 1; }\n.alert-pulse-overlay { position: absolute; inset: 0; background: radial-gradient(circle, rgba(255,0,0,0.15) 0%, transparent 70%); opacity: 0; pointer-events: none; animation: pulse-red 1.5s infinite; }\n@keyframes pulse-red { 0%, 100% { opacity: 0; transform: scale(1); } 50% { opacity: 1; transform: scale(1.1); } }\n.status-bar { position: absolute; bottom: 30px; left: 50%; transform: translateX(-50%); display: flex; gap: 40px; }\n.status-msg { font-size: 12px; border: 1px solid rgba(0,255,65,0.3); padding: 4px 12px; background: rgba(0,20,0,0.8); }","js":"window.SceneLifecycle.register('scene-07', {\n  enter() {\n    const root = document.querySelector('[data-scene-id=\"scene-07\"]');\n    const arrow = root.querySelector('.arrow-path');\n    \n    // Initial entrance\n    setTimeout(() => {\n      arrow.style.strokeDashoffset = '0';\n    }, 500);\n  },\n  step(n) {\n    const root = document.querySelector('[data-scene-id=\"scene-07\"]');\n    const memFrame = root.querySelector('.page-frame');\n    const alertOverlay = root.querySelector('.alert-pulse-overlay');\n\n    if (n === 1) {\n      // Step 1: Shatter the Lock\n      memFrame.classList.add('shattered');\n      memFrame.style.borderColor = '#FF3131';\n      memFrame.style.boxShadow = '0 0 30px rgba(255,49,49,0.3)';\n    }\n\n    if (n === 2) {\n      // Step 2: Full Alert Pulse\n      alertOverlay.style.display = 'block';\n      document.body.style.animation = 'glitch-bg 0.1s 3';\n    }\n  },\n  leave() {\n    document.body.style.animation = 'none';\n  }\n});"},{"id":"scene-08","title":"精准写入过程","steps":4,"html":"<div class=\"s-scene-08-wrapper\">\n  <div class=\"crt-scanlines\"></div>\n  <div class=\"ambient-glow\"></div>\n\n  <!-- Zoomed Hex Grid Viewport -->\n  <div class=\"hex-viewport\">\n    <div class=\"viewport-header\">\n      <span class=\"addr-label\">OFFSET: 0x00007FFF</span>\n      <span class=\"status-tag\" id=\"mem-status\">[ MEMORY_READ_ONLY ]</span>\n    </div>\n    <div class=\"hex-grid-container\">\n      <div class=\"hex-grid\" id=\"main-hex-grid\">\n        <!-- Generated cells will be here -->\n      </div>\n    </div>\n  </div>\n\n  <!-- Flying Bytes (Step 0: Visible, Step 1: Animate) -->\n  <div class=\"missile-container\">\n    <div class=\"byte-missile\" data-target=\"target-1\" data-step=\"0\">0x53</div>\n    <div class=\"byte-missile\" data-target=\"target-2\" data-step=\"0\">0x55</div>\n    <div class=\"byte-missile\" data-target=\"target-3\" data-step=\"0\">0x49</div>\n    <div class=\"byte-missile\" data-target=\"target-4\" data-step=\"0\">0x44</div>\n  </div>\n\n  <!-- Alert Overlay (Step 2) -->\n  <div class=\"alert-overlay\" data-step=\"2\">\n    <div class=\"alert-box\">\n      <div class=\"alert-header\">CRITICAL_ERROR</div>\n      <div class=\"alert-content\">VALIDATION_FAILED: HASH_MISMATCH</div>\n      <div class=\"alert-sub\">ENCRYPTED_BLOCK_CORRUPTED</div>\n    </div>\n  </div>\n\n  <!-- Bottom Status -->\n  <div class=\"terminal-footer\">\n    <div class=\"cursor-line\"><span class=\"prompt\">$</span> <span class=\"typing-text\">patching su_binary...</span><span class=\"cursor\"></span></div>\n    <div class=\"progress-section\" data-step=\"1\">\n      <span class=\"label\">INJECTION:</span>\n      <span class=\"bar-bg\"><span class=\"bar-fill\" id=\"injection-bar\"></span></span>\n    </div>\n  </div>\n</div>","css":".s-scene-08-wrapper { width: 100%; height: 100%; background: #000; display: flex; align-items: center; justify-content: center; font-family: var(--sv-font-mono); color: #00FF41; overflow: hidden; position: relative; }\n.crt-scanlines { position: absolute; inset: 0; background: linear-gradient(rgba(18, 16, 16, 0) 50%, rgba(0, 0, 0, 0.15) 50%), linear-gradient(90deg, rgba(255, 0, 0, 0.03), rgba(0, 255, 0, 0.01), rgba(0, 0, 255, 0.03)); background-size: 100% 3px, 3px 100%; pointer-events: none; z-index: 20; }\n.ambient-glow { position: absolute; width: 100%; height: 100%; background: radial-gradient(circle, rgba(0, 255, 65, 0.05) 0%, transparent 80%); pointer-events: none; }\n\n.hex-viewport { width: 800px; height: 400px; border: 1px solid rgba(0, 255, 65, 0.3); background: rgba(0, 10, 0, 0.9); position: relative; box-shadow: 0 0 40px rgba(0, 0, 0, 0.8); transition: all 0.8s ease; transform: scale(1.1); }\n.viewport-header { background: rgba(0, 255, 65, 0.1); padding: 8px 15px; display: flex; justify-content: space-between; border-bottom: 1px solid rgba(0, 255, 65, 0.2); font-size: 12px; }\n.hex-grid-container { padding: 30px; display: flex; justify-content: center; }\n.hex-grid { display: grid; grid-template-columns: repeat(8, 70px); gap: 15px; }\n.hex-cell { height: 40px; display: flex; align-items: center; justify-content: center; border: 1px solid rgba(0, 255, 65, 0.1); font-size: 22px; color: #0088ff; transition: all 0.5s ease; position: relative; }\n.hex-cell.infected { color: #d400ff !important; border-color: #d400ff; text-shadow: 0 0 10px rgba(212, 0, 255, 0.5); }\n.hex-cell.target-active { background: rgba(0, 255, 65, 0.1); border-color: #00FF41; }\n\n.missile-container { position: absolute; inset: 0; pointer-events: none; }\n.byte-missile { position: absolute; font-size: 24px; font-weight: bold; background: #000; border: 2px solid #00FF41; padding: 5px 10px; box-shadow: 0 0 15px #00FF41; z-index: 10; transition: all 0.6s cubic-bezier(0.6, -0.28, 0.735, 0.045); }\n.byte-missile[data-step=\"0\"] { opacity: 1; transform: translate(var(--start-x), var(--start-y)); }\n.byte-missile.launched { transform: translate(var(--end-x), var(--end-y)) scale(0.8); opacity: 0.5; }\n\n.alert-overlay { position: absolute; inset: 0; z-index: 30; display: flex; align-items: center; justify-content: center; background: rgba(255, 0, 0, 0.1); backdrop-filter: blur(2px); }\n.alert-box { background: #000; border: 4px solid #FF3131; padding: 40px; text-align: center; box-shadow: 0 0 50px rgba(255, 49, 49, 0.4); animation: alert-shake 0.2s infinite; }\n.alert-header { color: #FF3131; font-weight: 900; font-size: 32px; letter-spacing: 4px; margin-bottom: 10px; }\n.alert-content { color: #FFF; font-size: 20px; border-top: 1px solid #FF3131; padding-top: 10px; }\n.alert-sub { font-size: 14px; opacity: 0.7; margin-top: 5px; }\n\n.terminal-footer { position: absolute; bottom: 40px; left: 100px; right: 100px; }\n.prompt { color: #00FF41; margin-right: 10px; }\n.typing-text { font-size: 18px; }\n.cursor { display: inline-block; width: 10px; height: 20px; background: #00FF41; animation: blink 1s infinite; vertical-align: middle; }\n.progress-section { margin-top: 15px; display: flex; align-items: center; gap: 15px; }\n.bar-bg { flex: 1; height: 10px; background: rgba(0, 255, 65, 0.1); border: 1px solid rgba(0, 255, 65, 0.3); }\n.bar-fill { display: block; width: 0%; height: 100%; background: #00FF41; transition: width 0.6s ease; }\n\n@keyframes alert-shake { 0% { transform: translate(1px, 1px); } 50% { transform: translate(-1px, -1px); } 100% { transform: translate(1px, -1px); } }\n@keyframes blink { 0%, 100% { opacity: 1; } 50% { opacity: 0; } }\n.modified-state .hex-viewport { border-color: #d400ff; box-shadow: 0 0 30px rgba(212, 0, 255, 0.2); }\n.modified-state #mem-status { color: #d400ff; content: \"[ MEMORY_MODIFIED_PERMANENT ]\"; }","js":"window.SceneLifecycle.register('scene-08', {\n  enter() {\n    const root = document.querySelector('[data-scene-id=\"scene-08\"]');\n    const grid = root.querySelector('#main-hex-grid');\n    const missiles = root.querySelectorAll('.byte-missile');\n\n    // Generate Background Hex Cells\n    grid.innerHTML = '';\n    for (let i = 0; i < 24; i++) {\n      const cell = document.createElement('div');\n      cell.className = 'hex-cell';\n      // Pre-defined target slots\n      if (i === 10) cell.id = 'target-1';\n      if (i === 11) cell.id = 'target-2';\n      if (i === 18) cell.id = 'target-3';\n      if (i === 19) cell.id = 'target-4';\n      \n      cell.textContent = Math.floor(Math.random() * 256).toString(16).toUpperCase().padStart(2, '0');\n      grid.appendChild(cell);\n    }\n\n    // Set Initial Missile Positions\n    const rects = [\n      { x: '10%', y: '20%' },\n      { x: '85%', y: '15%' },\n      { x: '5%', y: '75%' },\n      { x: '90%', y: '80%' }\n    ];\n    missiles.forEach((m, idx) => {\n      m.style.setProperty('--start-x', rects[idx].x);\n      m.style.setProperty('--start-y', rects[idx].y);\n    });\n  },\n  \n  step(n) {\n    const root = document.querySelector('[data-scene-id=\"scene-08\"]');\n    const missiles = root.querySelectorAll('.byte-missile');\n    const barFill = root.querySelector('#injection-bar');\n    const memStatus = root.querySelector('#mem-status');\n\n    if (n === 1) {\n      // Step 1: Launch missiles to targets\n      missiles.forEach((m) => {\n        const targetId = m.getAttribute('data-target');\n        const targetEl = root.querySelector(`#${targetId}`);\n        const targetRect = targetEl.getBoundingClientRect();\n        const rootRect = root.getBoundingClientRect();\n        \n        const endX = (targetRect.left - rootRect.left) + 'px';\n        const endY = (targetRect.top - rootRect.top) + 'px';\n        \n        m.style.setProperty('--end-x', endX);\n        m.style.setProperty('--end-y', endY);\n        m.classList.add('launched');\n        \n        setTimeout(() => {\n          targetEl.classList.add('target-active');\n          targetEl.textContent = m.textContent.replace('0x', '');\n        }, 600);\n      });\n      barFill.style.width = '100%';\n    }\n\n    if (n === 2) {\n       // Step 2 shows the Alert Overlay via CSS data-step\n       root.style.filter = 'hue-rotate(-10deg)';\n    }\n\n    if (n === 3) {\n      // Step 3: Persistence - Blue to Purple\n      root.classList.add('modified-state');\n      const targets = root.querySelectorAll('.hex-cell');\n      targets.forEach((cell, idx) => {\n        setTimeout(() => {\n          cell.classList.add('infected');\n          if (idx % 3 === 0) cell.textContent = 'FF';\n        }, idx * 20);\n      });\n      memStatus.textContent = '[ MEMORY_MODIFIED_PERMANENT ]';\n    }\n  }\n});"},{"id":"scene-09","title":"获取Root权限","steps":3,"html":"<div class=\"s-scene-09-wrapper\">\n  <div class=\"crt-scanlines\"></div>\n  <div class=\"ambient-glow\"></div>\n\n  <!-- Terminal Interface Container -->\n  <div class=\"terminal-interface\">\n    <div class=\"terminal-header\">\n      <div class=\"terminal-buttons\">\n        <span></span><span></span><span></span>\n      </div>\n      <div class=\"terminal-title\">TERMINAL - su_exploit_session</div>\n    </div>\n\n    <div class=\"terminal-content\">\n      <!-- Input History -->\n      <div class=\"log-line\">[ OK ] Payload injected into memory segment 0x0804...</div>\n      <div class=\"log-line\">[ OK ] Hooking execution vector...</div>\n      \n      <!-- Main Prompt Line -->\n      <div class=\"prompt-area\">\n        <span class=\"prompt-char\" id=\"current-prompt\">$</span>\n        <span class=\"command-text\">whoami</span>\n        <span class=\"cursor\"></span>\n      </div>\n\n      <!-- Result Output (Step 0-1 Hidden) -->\n      <div class=\"result-output\" data-step=\"2\">\n        <div class=\"root-banner\" id=\"root-text-container\">\n          <!-- root text will be typed here -->\n        </div>\n      </div>\n    </div>\n\n    <!-- Bottom Status Bar -->\n    <div class=\"terminal-footer-bar\">\n      <div class=\"status-item\">STATUS: <span class=\"blink-text\">RUNNING</span></div>\n      <div class=\"status-item\">PRIVILEGE: <span id=\"priv-level\">USER</span></div>\n      <div class=\"status-item\" data-step=\"2\">UID: 0 (root)</div>\n    </div>\n  </div>\n\n  <!-- Flash Overlay for Step 1 -->\n  <div class=\"lightning-flash\" id=\"flash-overlay\"></div>\n\n  <!-- Success Glow Frame (Step 2) -->\n  <div class=\"success-frame\" data-step=\"2\"></div>\n</div>","css":".s-scene-09-wrapper { width: 100%; height: 100%; background: #000; font-family: var(--sv-font-mono); color: #00FF41; display: flex; align-items: center; justify-content: center; position: relative; overflow: hidden; }\n.crt-scanlines { position: absolute; inset: 0; background: linear-gradient(rgba(0, 255, 65, 0.05) 50%, transparent 50%); background-size: 100% 4px; pointer-events: none; z-index: 10; }\n.ambient-glow { position: absolute; inset: 0; background: radial-gradient(circle at center, rgba(0, 255, 65, 0.03) 0%, transparent 70%); pointer-events: none; }\n\n.terminal-interface { width: 80%; max-width: 900px; height: 60%; background: rgba(0, 10, 0, 0.95); border: 1px solid rgba(0, 255, 65, 0.3); box-shadow: 0 20px 50px rgba(0, 0, 0, 0.8); display: flex; flex-direction: column; position: relative; z-index: 5; }\n.terminal-header { height: 30px; background: rgba(0, 255, 65, 0.1); border-bottom: 1px solid rgba(0, 255, 65, 0.2); display: flex; align-items: center; padding: 0 15px; }\n.terminal-buttons { display: flex; gap: 6px; margin-right: 20px; }\n.terminal-buttons span { width: 8px; height: 8px; border: 1px solid rgba(0, 255, 65, 0.4); border-radius: 50%; }\n.terminal-title { font-size: 12px; opacity: 0.7; letter-spacing: 1px; }\n\n.terminal-content { flex: 1; padding: 30px; overflow: hidden; position: relative; }\n.log-line { font-size: 14px; opacity: 0.4; margin-bottom: 8px; font-weight: 300; }\n.prompt-area { font-size: 32px; display: flex; align-items: center; margin-top: 20px; }\n.prompt-char { margin-right: 15px; color: #39FF14; font-weight: bold; }\n.command-text { letter-spacing: 2px; }\n.cursor { display: inline-block; width: 15px; height: 32px; background: #00FF41; margin-left: 10px; animation: blink 1s infinite steps(2); }\n\n.result-output { margin-top: 40px; display: flex; justify-content: center; }\n.root-banner { font-size: 120px; font-weight: 900; letter-spacing: 15px; text-transform: uppercase; text-shadow: 0 0 30px rgba(0, 255, 65, 0.8); }\n\n.terminal-footer-bar { height: 25px; background: rgba(0, 255, 65, 0.1); display: flex; align-items: center; padding: 0 15px; gap: 30px; font-size: 10px; border-top: 1px solid rgba(0, 255, 65, 0.2); }\n.blink-text { animation: blink 0.5s infinite; }\n\n.lightning-flash { position: absolute; inset: 0; background: #00FF41; opacity: 0; pointer-events: none; z-index: 100; }\n.success-frame { position: absolute; inset: 10px; border: 4px solid #00FF41; box-shadow: inset 0 0 50px rgba(0, 255, 65, 0.3), 0 0 30px rgba(0, 255, 65, 0.2); pointer-events: none; z-index: 101; opacity: 0; transform: scale(1.02); transition: all 0.3s ease; }\n.success-frame[data-step=\"2\"] { opacity: 1; transform: scale(1); }\n\n@keyframes blink { 0% { opacity: 1; } 100% { opacity: 0; } }\n@keyframes glitch-flash { 0% { opacity: 0; } 10% { opacity: 0.8; } 20% { opacity: 0.1; } 30% { opacity: 0.9; } 100% { opacity: 0; } }","js":"window.SceneLifecycle.register('scene-09', {\n  enter() {\n    const sceneRoot = document.querySelector('[data-scene-id=\"scene-09\"]');\n    const rootContainer = sceneRoot.querySelector('#root-text-container');\n    rootContainer.innerHTML = '';\n  },\n\n  step(n) {\n    const sceneRoot = document.querySelector('[data-scene-id=\"scene-09\"]');\n    const promptChar = sceneRoot.querySelector('#current-prompt');\n    const flash = sceneRoot.querySelector('#flash-overlay');\n    const privLevel = sceneRoot.querySelector('#priv-level');\n    const rootContainer = sceneRoot.querySelector('#root-text-container');\n\n    if (n === 1) {\n      // Step 1: Lightning Flash and Prompt Change\n      flash.style.animation = 'glitch-flash 0.4s ease-out forwards';\n      setTimeout(() => {\n        promptChar.textContent = '#';\n        privLevel.textContent = 'ROOT_ACCESS';\n        privLevel.style.color = '#FFF';\n        privLevel.style.textShadow = '0 0 10px #00FF41';\n      }, 150);\n    }\n\n    if (n === 2) {\n      // Step 2: Big 'root' typewriter effect\n      const text = 'root';\n      rootContainer.innerHTML = '';\n      let i = 0;\n      \n      const typeChar = () => {\n        if (i < text.length) {\n          const span = document.createElement('span');\n          span.textContent = text[i];\n          span.style.opacity = '0';\n          span.style.transform = 'scale(1.5)';\n          span.style.display = 'inline-block';\n          rootContainer.appendChild(span);\n          \n          // Trigger individual character pop\n          requestAnimationFrame(() => {\n            span.style.transition = 'all 0.15s cubic-bezier(0.175, 0.885, 0.32, 1.275)';\n            span.style.opacity = '1';\n            span.style.transform = 'scale(1)';\n          });\n          \n          i++;\n          setTimeout(typeChar, 100);\n        }\n      };\n      \n      typeChar();\n    }\n  }\n});"},{"id":"scene-10","title":"防御与总结","steps":3,"html":"<div class=\"s-scene-10-wrapper\">\n  <div class=\"crt-scanlines\"></div>\n  <div class=\"ambient-glow\"></div>\n\n  <div class=\"thirds-container\">\n    <!-- Step 0: Left Column - Patch Info -->\n    <div class=\"col-left\" data-step=\"0\">\n      <div class=\"status-box\">\n        <div class=\"status-header\">SYSTEM_ALERT</div>\n        <div class=\"patch-icon-wrapper\">\n          <iconify-icon icon=\"mdi:shield-sync-outline\" class=\"main-patch-icon\"></iconify-icon>\n          <div class=\"update-text-glitch\">UPDATE NOW</div>\n        </div>\n        <div class=\"status-details\">\n          <div class=\"status-line\">> KERNEL: VULNERABLE</div>\n          <div class=\"status-line blinking\">> PATCH: AVAILABLE</div>\n        </div>\n      </div>\n    </div>\n\n    <!-- Step 2: Center Column - Defused Bomb -->\n    <div class=\"col-center\" data-step=\"2\">\n      <div class=\"bomb-stage\">\n        <svg viewBox=\"0 0 200 200\" class=\"bomb-svg\">\n          <!-- Fuse -->\n          <path d=\"M120 40 Q 150 20, 160 60\" fill=\"none\" stroke=\"#00FF41\" stroke-width=\"4\" class=\"fuse-path\" />\n          <!-- Bomb Body -->\n          <circle cx=\"100\" cy=\"110\" r=\"60\" fill=\"none\" stroke=\"#00FF41\" stroke-width=\"6\" />\n          <rect x=\"85\" y=\"45\" width=\"30\" height=\"15\" fill=\"#000\" stroke=\"#00FF41\" stroke-width=\"4\" />\n          <!-- Cut Mark -->\n          <path d=\"M135 25 L 155 45 M 155 25 L 135 45\" stroke=\"#39FF14\" stroke-width=\"8\" class=\"cut-mark\" />\n        </svg>\n        <div class=\"threat-status\">THREAT_NEUTRALIZED</div>\n      </div>\n    </div>\n\n    <!-- Step 1: Right Column - Terminal Command -->\n    <div class=\"col-right\" data-step=\"1\">\n      <div class=\"command-window\">\n        <div class=\"window-bar\">\n          <span class=\"dot\"></span><span class=\"dot\"></span><span class=\"dot\"></span>\n          <span class=\"window-title\">bash - root</span>\n        </div>\n        <div class=\"terminal-body\">\n          <div class=\"shell-prompt\">\n            <span class=\"user\">root@system</span>:<span class=\"path\">~</span>#\n            <span class=\"typing-cmd\" id=\"cmd-text\"></span><span class=\"terminal-cursor\"></span>\n          </div>\n          <div class=\"shell-output\" data-step=\"1\">[ SUCCESS ] Module 'algif_aead' disabled.</div>\n        </div>\n      </div>\n    </div>\n  </div>\n\n  <div class=\"footer-deco\">SECURITY_ENFORCED // 0xPATCH_SUCCESS</div>\n</div>","css":".s-scene-10-wrapper { width: 100%; height: 100%; background: #000; font-family: var(--sv-font-mono); color: #00FF41; position: relative; overflow: hidden; }\n.crt-scanlines { position: absolute; inset: 0; background: linear-gradient(rgba(0, 255, 65, 0.08) 50%, transparent 50%); background-size: 100% 4px; pointer-events: none; z-index: 10; }\n.ambient-glow { position: absolute; inset: 0; background: radial-gradient(circle at center, rgba(0, 255, 65, 0.03) 0%, transparent 70%); pointer-events: none; }\n\n.thirds-container { display: flex; width: 100%; height: 100%; align-items: center; justify-content: space-between; padding: 0 5%; box-sizing: border-box; }\n.col-left, .col-center, .col-right { flex: 1; display: flex; flex-direction: column; align-items: center; transition: all 0.5s cubic-bezier(0.22, 1, 0.36, 1); }\n\n/* Left: Update Box */\n.status-box { border: 2px solid rgba(0, 255, 65, 0.4); padding: 30px; background: rgba(0, 255, 65, 0.05); text-align: center; width: 300px; }\n.status-header { font-size: 14px; letter-spacing: 2px; border-bottom: 1px solid rgba(0, 255, 65, 0.4); padding-bottom: 10px; margin-bottom: 20px; opacity: 0.8; }\n.main-patch-icon { font-size: 80px; filter: drop-shadow(0 0 10px rgba(0, 255, 65, 0.5)); }\n.update-text-glitch { font-size: 28px; font-weight: 900; margin-top: 15px; text-shadow: 2px 0 #39FF14; animation: blink 0.8s infinite; }\n.status-details { margin-top: 20px; text-align: left; font-size: 12px; line-height: 1.8; opacity: 0.7; }\n\n/* Right: Terminal */\n.command-window { width: 400px; height: 200px; background: #000; border: 1px solid rgba(0, 255, 65, 0.3); box-shadow: 0 10px 40px rgba(0, 0, 0, 0.8); }\n.window-bar { height: 25px; background: rgba(0, 255, 65, 0.1); display: flex; align-items: center; padding: 0 10px; gap: 5px; }\n.dot { width: 6px; height: 6px; border: 1px solid #00FF41; border-radius: 50%; }\n.window-title { font-size: 10px; margin-left: 10px; opacity: 0.6; }\n.terminal-body { padding: 15px; font-size: 16px; }\n.user { color: #39FF14; }\n.shell-output { margin-top: 15px; font-size: 12px; color: #39FF14; opacity: 0; transform: translateY(10px); transition: 0.4s; }\n[data-step=\"1\"] .shell-output { opacity: 1; transform: translateY(0); }\n.terminal-cursor { display: inline-block; width: 8px; height: 18px; background: #00FF41; animation: blink 1s infinite steps(2); vertical-align: middle; margin-left: 4px; }\n\n/* Center: Bomb */\n.bomb-stage { position: relative; text-align: center; }\n.bomb-svg { width: 220px; height: 220px; }\n.fuse-path { stroke-dasharray: 100; stroke-dashoffset: 0; }\n.cut-mark { opacity: 0; transform: scale(0.5); transform-origin: center; transition: all 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275); }\n[data-step=\"2\"] .cut-mark { opacity: 1; transform: scale(1); }\n[data-step=\"2\"] .fuse-path { stroke-dasharray: 10 5; opacity: 0.5; }\n.threat-status { margin-top: 20px; font-size: 24px; font-weight: 900; letter-spacing: 4px; color: #39FF14; text-shadow: 0 0 15px rgba(0, 255, 65, 0.6); }\n\n.footer-deco { position: absolute; bottom: 30px; left: 50%; transform: translateX(-50%); font-size: 12px; opacity: 0.3; letter-spacing: 5px; }\n\n@keyframes blink { 0% { opacity: 1; } 100% { opacity: 0; } }\n@keyframes draw { to { stroke-dashoffset: 0; } }","js":"window.SceneLifecycle.register('scene-10', {\n  enter() {\n    const sceneRoot = document.querySelector('[data-scene-id=\"scene-10\"]');\n    if (!sceneRoot) return;\n    window.__SVGVIDEO_REFRESH_ICONS(sceneRoot);\n    \n    // Initial state\n    const cmdText = sceneRoot.querySelector('#cmd-text');\n    if (cmdText) cmdText.textContent = '';\n  },\n\n  step(n) {\n    const sceneRoot = document.querySelector('[data-scene-id=\"scene-10\"]');\n    if (!sceneRoot) return;\n\n    if (n === 1) {\n      const cmdText = sceneRoot.querySelector('#cmd-text');\n      const command = 'modprobe -r algif_aead';\n      let i = 0;\n      cmdText.textContent = '';\n      \n      const type = () => {\n        if (i < command.length) {\n          cmdText.textContent += command[i];\n          i++;\n          setTimeout(type, 50);\n        }\n      };\n      type();\n    }\n\n    if (n === 2) {\n      // Extra impact on defusal\n      const bombStage = sceneRoot.querySelector('.bomb-stage');\n      bombStage.style.transform = 'scale(1.1)';\n      setTimeout(() => {\n        bombStage.style.transform = 'scale(1)';\n      }, 200);\n    }\n  }\n});"},{"id":"scene-11","title":"结尾致谢","steps":2,"html":"<div class=\"s-scene-11-wrapper\">\n  <div class=\"crt-scanlines\"></div>\n  <div class=\"ambient-glow\"></div>\n\n  <!-- Step 0: Center Logo/Avatar -->\n  <div class=\"logo-overlay\" data-step=\"0\">\n    <div class=\"avatar-frame\">\n      <div class=\"avatar-glitch\"></div>\n      <svg viewBox=\"0 0 100 100\" class=\"terminal-logo\">\n        <rect x=\"20\" y=\"20\" width=\"60\" height=\"60\" stroke=\"#00FF41\" stroke-width=\"2\" fill=\"none\" />\n        <path d=\"M35 40 L50 55 L65 40\" stroke=\"#00FF41\" stroke-width=\"4\" fill=\"none\" />\n        <path d=\"M30 70 L70 70\" stroke=\"#00FF41\" stroke-width=\"2\" stroke-dasharray=\"4 2\" />\n      </svg>\n    </div>\n    <div class=\"channel-id\">[ CHANNEL_ID: ROOT_NODE ]</div>\n  </div>\n\n  <!-- Step 1: Interactive Actions & Outro -->\n  <div class=\"action-container\" data-step=\"1\">\n    <!-- Like Action -->\n    <div class=\"action-item like-item\">\n      <div class=\"action-box\">\n        <i data-lucide=\"thumbs-up\"></i>\n        <span class=\"action-label\">LIKE_SUCCESS</span>\n      </div>\n    </div>\n\n    <!-- Follow Action -->\n    <div class=\"action-item follow-item\">\n      <div class=\"action-box\">\n        <i data-lucide=\"user-plus\"></i>\n        <span class=\"action-label\">SUBSCRIBE_OK</span>\n      </div>\n    </div>\n\n    <!-- Closing Text -->\n    <div class=\"closing-text\">\n      <div class=\"rainbow-terminal-text\">下期再见</div>\n      <div class=\"cursor-blink\"></div>\n    </div>\n  </div>\n\n  <div class=\"system-footer\">\n    SESSION_TERMINATED: 0x00000000\n  </div>\n</div>","css":".s-scene-11-wrapper { width: 100%; height: 100%; background: #000; font-family: var(--sv-font-mono); color: #00FF41; position: relative; overflow: hidden; display: flex; flex-direction: column; align-items: center; justify-content: center; }\n.crt-scanlines { position: absolute; inset: 0; background: linear-gradient(rgba(0, 255, 65, 0.1) 50%, transparent 50%); background-size: 100% 3px; pointer-events: none; z-index: 100; opacity: 0.12; }\n.ambient-glow { position: absolute; inset: 0; background: radial-gradient(circle at center, rgba(0, 255, 65, 0.04) 0%, transparent 80%); animation: pulse 10s infinite; }\n\n.logo-overlay { text-align: center; z-index: 20; transition: transform 0.6s cubic-bezier(0.22, 1, 0.36, 1); }\n.avatar-frame { width: 120px; height: 120px; border: 2px solid #00FF41; position: relative; padding: 10px; box-shadow: 0 0 20px rgba(0, 255, 65, 0.15); display: inline-block; background: #000; }\n.terminal-logo { width: 100%; height: 100%; }\n.channel-id { margin-top: 15px; font-size: 14px; letter-spacing: 2px; opacity: 0.8; }\n\n.action-container { position: absolute; inset: 0; pointer-events: none; display: flex; align-items: center; justify-content: center; }\n.action-item { position: absolute; transition: all 0.6s cubic-bezier(0.175, 0.885, 0.32, 1.275); opacity: 0; transform: scale(0.5); }\n.like-item { left: 15%; }\n.follow-item { right: 15%; }\n[data-step=\"1\"] .action-item { opacity: 1; transform: scale(1); }\n.action-box { border: 1px solid #00FF41; background: rgba(0, 255, 65, 0.05); padding: 20px; display: flex; flex-direction: column; align-items: center; gap: 10px; }\n.action-box i { width: 40px; height: 40px; }\n.action-label { font-size: 12px; font-weight: bold; }\n\n.closing-text { position: absolute; bottom: 15%; text-align: center; opacity: 0; transform: translateY(20px); transition: all 0.8s ease; display: flex; align-items: center; gap: 10px; }\n[data-step=\"1\"] .closing-text { opacity: 1; transform: translateY(0); }\n.rainbow-terminal-text { font-size: 64px; font-family: var(--sv-font-display-cn); font-weight: 900; background: linear-gradient(90deg, #00FF41, #39FF14, #00FF41); -webkit-background-clip: text; color: transparent; animation: shift 4s linear infinite; text-shadow: 0 0 10px rgba(0, 255, 65, 0.3); }\n.cursor-blink { width: 25px; height: 60px; background: #00FF41; animation: blink 1s infinite steps(2); }\n\n.system-footer { position: absolute; bottom: 5%; font-size: 10px; opacity: 0.4; letter-spacing: 1px; }\n\n@keyframes pulse { 0%, 100% { opacity: 0.04; } 50% { opacity: 0.08; } }\n@keyframes blink { 0% { opacity: 1; } 100% { opacity: 0; } }\n@keyframes shift { 0% { background-position: 0% 50%; } 100% { background-position: 200% 50%; } }","js":"window.SceneLifecycle.register('scene-11', {\n  enter() {\n    const sceneRoot = document.querySelector('[data-scene-id=\"scene-11\"]');\n    if (!sceneRoot) return;\n    window.__SVGVIDEO_REFRESH_ICONS(sceneRoot);\n    \n    // Initial state setup\n    const logo = sceneRoot.querySelector('.logo-overlay');\n    logo.style.transform = 'scale(1.2)';\n    setTimeout(() => {\n      logo.style.transform = 'scale(1)';\n    }, 100);\n  },\n  \n  step(n) {\n    const sceneRoot = document.querySelector('[data-scene-id=\"scene-11\"]');\n    if (!sceneRoot) return;\n\n    if (n === 1) {\n      const logo = sceneRoot.querySelector('.logo-overlay');\n      // Shift logo up to make room for closing text\n      logo.style.transform = 'translateY(-40px)';\n      \n      // Elastic feedback effect on icons is handled by CSS cubic-bezier\n      // But we can trigger a slight rotation for extra \"Q-弹\" feel\n      const items = sceneRoot.querySelectorAll('.action-item');\n      items.forEach((item, idx) => {\n        item.style.transitionDelay = `${idx * 0.1}s`;\n      });\n    }\n  }\n});"}]</script>
  <script defer src="https://unpkg.com/lucide@latest"></script>
  <script defer src="https://code.iconify.design/iconify-icon/2.3.0/iconify-icon.min.js"></script>
  <script>
    (function () {
      var state = { sceneId: 'scene-10', step: 0, direction: 1 };
      var TRANSITION_PRESETS={'door':{'--sv-stage-perspective':'900px','--sv-trans-enter-opacity':'0','--sv-trans-enter-transform':'rotateY(90deg)','--sv-trans-enter-filter':'blur(6px)','--sv-trans-active-opacity':'1','--sv-trans-active-transform':'rotateY(0deg)','--sv-trans-active-filter':'blur(0px)','--sv-trans-exit-opacity':'0','--sv-trans-exit-transform':'rotateY(-90deg)','--sv-trans-exit-filter':'blur(4px)','--sv-trans-duration':'0.65s','--sv-trans-ease':'cubic-bezier(0.36, 0, 0.66, 1)'},'burst':{'--sv-stage-perspective':'none','--sv-trans-enter-opacity':'0','--sv-trans-enter-transform':'scale(0.1)','--sv-trans-enter-filter':'blur(40px)','--sv-trans-active-opacity':'1','--sv-trans-active-transform':'scale(1)','--sv-trans-active-filter':'blur(0px)','--sv-trans-exit-opacity':'0','--sv-trans-exit-transform':'scale(1.6)','--sv-trans-exit-filter':'blur(24px)','--sv-trans-duration':'0.55s','--sv-trans-ease':'cubic-bezier(0.34, 1.3, 0.64, 1)'},'slam':{'--sv-stage-perspective':'none','--sv-trans-enter-opacity':'0','--sv-trans-enter-transform':'translateX(var(--scene-enter-offset, 180px)) rotate(8deg)','--sv-trans-enter-filter':'blur(24px)','--sv-trans-active-opacity':'1','--sv-trans-active-transform':'translateX(0) rotate(0deg)','--sv-trans-active-filter':'blur(0px)','--sv-trans-exit-opacity':'0','--sv-trans-exit-transform':'translateX(var(--scene-exit-offset, -120px)) rotate(-5deg)','--sv-trans-exit-filter':'blur(18px)','--sv-trans-duration':'0.50s','--sv-trans-ease':'cubic-bezier(0.2, 0.8, 0.2, 1)'},'push-up':{'--sv-stage-perspective':'none','--sv-trans-enter-opacity':'1','--sv-trans-enter-transform':'translateY(105%)','--sv-trans-enter-filter':'blur(0px)','--sv-trans-active-opacity':'1','--sv-trans-active-transform':'translateY(0)','--sv-trans-active-filter':'blur(0px)','--sv-trans-exit-opacity':'0.3','--sv-trans-exit-transform':'translateY(-60px)','--sv-trans-exit-filter':'blur(8px)','--sv-trans-duration':'0.55s','--sv-trans-ease':'cubic-bezier(0.22, 1, 0.36, 1)'},'spin':{'--sv-stage-perspective':'none','--sv-trans-enter-opacity':'0','--sv-trans-enter-transform':'rotate(180deg) scale(0.2)','--sv-trans-enter-filter':'blur(24px)','--sv-trans-active-opacity':'1','--sv-trans-active-transform':'rotate(0deg) scale(1)','--sv-trans-active-filter':'blur(0px)','--sv-trans-exit-opacity':'0','--sv-trans-exit-transform':'rotate(-90deg) scale(0.4)','--sv-trans-exit-filter':'blur(16px)','--sv-trans-duration':'0.55s','--sv-trans-ease':'cubic-bezier(0.34, 1.3, 0.64, 1)'},'tunnel':{'--sv-stage-perspective':'none','--sv-trans-enter-opacity':'0','--sv-trans-enter-transform':'scale(3.0)','--sv-trans-enter-filter':'blur(50px)','--sv-trans-active-opacity':'1','--sv-trans-active-transform':'scale(1)','--sv-trans-active-filter':'blur(0px)','--sv-trans-exit-opacity':'0','--sv-trans-exit-transform':'scale(0.15)','--sv-trans-exit-filter':'blur(36px)','--sv-trans-duration':'0.60s','--sv-trans-ease':'cubic-bezier(0.22, 1, 0.36, 1)'},'glitch':{'--sv-stage-perspective':'none','--sv-trans-enter-opacity':'0','--sv-trans-enter-transform':'scale(0.95)','--sv-trans-enter-filter':'blur(2px)','--sv-trans-active-opacity':'1','--sv-trans-active-transform':'scale(1)','--sv-trans-active-filter':'blur(0px)','--sv-trans-exit-opacity':'0','--sv-trans-exit-transform':'scale(0.95)','--sv-trans-exit-filter':'none','--sv-trans-duration':'0.25s','--sv-trans-ease':'ease-out'},};
      // Per-scene transition mapping — built from meta at init time
      var sceneTransitionMap = {};
      var previousSceneId = null;
      var exitCleanupTimer = null;
      var isPlaying = false;
      var playTimer = null;
      var progressRaf = null;
      var STEP_INTERVAL_MS = 1400;
      var SCENE_FINISH_PAUSE_MS = 2000;
      var autoplayFromFirstScene = false;
      var progressWrap = document.getElementById('sv-progress-wrap');
      var progressFill = document.getElementById('sv-progress-fill');
      var progressThumb = document.getElementById('sv-progress-thumb');
      var playBtn = document.getElementById('sv-play-btn');
      var expandBtn = document.getElementById('sv-expand-btn');
      var statusText = document.getElementById('sv-status-text');
      var sceneButtonList = Array.prototype.slice.call(document.querySelectorAll('[data-panel-scene-id]'));
      var sceneRetryButtonList = Array.prototype.slice.call(document.querySelectorAll('[data-retry-scene-id]'));
      var editorOverlay = document.getElementById('sv-editor-overlay');
      var editorBackdrop = document.getElementById('sv-editor-backdrop');
      var editorSheet = document.getElementById('sv-editor-sheet');
      var editorResizeHandle = document.getElementById('sv-editor-resize-handle');
      var editorCloseBtn = document.getElementById('sv-editor-close-btn');
      var editorSaveBtn = document.getElementById('sv-editor-save-btn');
      var editorList = document.getElementById('sv-editor-list');
      var editorSaveTip = document.getElementById('sv-editor-save-tip');
      var editorTipTimer = null;
      var isEditorOpen = false;
      var isEditorDirty = false;
      var editorHeightVh = 50;
      var hostCaps = { canRetry: false, canEditPanel: false, locale: 'zh', theme: 'dark' };
      var metaJson = document.getElementById('__SVGVIDEO_META__');
      var meta = { scenes: [] };
      try {
        meta = JSON.parse((metaJson && metaJson.textContent) || '{}');
      } catch (e) {
        meta = { scenes: [] };
      }
      var metaSceneMap = {};
      var metaScenes = Array.isArray(meta.scenes) ? meta.scenes : [];
      metaScenes.forEach(function (scene) {
        if (scene && typeof scene.id === 'string' && scene.id) {
          metaSceneMap[scene.id] = scene;
          sceneTransitionMap[scene.id] = (typeof scene.transition === 'string' && TRANSITION_PRESETS[scene.transition]) ? scene.transition : 'door';
        }
      });
      function refreshFrameworkIcons(root) {
        var refreshRoot = root || document;
        try {
          if (window.lucide && typeof window.lucide.createIcons === 'function') {
            window.lucide.createIcons({ root: refreshRoot });
          }
        } catch (iconErr) {
          console.warn('[svg-video] lucide refresh failed', iconErr);
        }
      }
      window.__SVGVIDEO_REFRESH_ICONS = refreshFrameworkIcons;
      var sceneModels = Array.prototype.slice.call(document.querySelectorAll('#stage > [data-scene-id]')).map(function (node, index) {
        var sceneId = node.getAttribute('data-scene-id') || ('scene-' + (index + 1));
        var metaScene = metaSceneMap[sceneId] || {};
        var sceneTitle = typeof metaScene.title === 'string' && metaScene.title ? metaScene.title : ('分镜' + (index + 1));
        var sceneSteps = Math.max(1, Number(metaScene.steps) || 1);
        var sceneStatus = typeof metaScene.status === 'string' ? metaScene.status : 'pending';
        var scenePrompt = typeof metaScene.prompt === 'string' ? metaScene.prompt : '';
        var sceneScript = typeof metaScene.scriptText === 'string' ? metaScene.scriptText : '';
        var sceneAudioObjectKey = typeof metaScene.audioObjectKey === 'string' ? metaScene.audioObjectKey : '';
        var sceneAudioDurationMs = Number(metaScene.audioDurationMs) > 0 ? Number(metaScene.audioDurationMs) : 0;
        var sceneVoiceName = typeof metaScene.voiceName === 'string' ? metaScene.voiceName : '';
        var sceneVoiceStyle = typeof metaScene.voiceStyle === 'string' ? metaScene.voiceStyle : '';
        var relatedSceneIds = Array.isArray(metaScene.relatedSceneIds) ? metaScene.relatedSceneIds : [];
        return {
          id: sceneId,
          title: sceneTitle,
          steps: sceneSteps,
          status: sceneStatus,
          prompt: scenePrompt,
          scriptText: sceneScript,
          audioObjectKey: sceneAudioObjectKey,
          audioDurationMs: sceneAudioDurationMs,
          voiceName: sceneVoiceName,
          voiceStyle: sceneVoiceStyle,
          relatedSceneIds: relatedSceneIds,
          transition: sceneTransitionMap[sceneId] || 'door'
        };
      });
      if (!sceneModels.length && metaScenes.length) {
        sceneModels = metaScenes.map(function (scene, index) {
          return {
            id: scene.id || ('scene-' + (index + 1)),
            title: scene.title || ('分镜' + (index + 1)),
            steps: Math.max(1, Number(scene.steps) || 1),
            status: typeof scene.status === 'string' ? scene.status : 'pending',
            prompt: typeof scene.prompt === 'string' ? scene.prompt : '',
            scriptText: typeof scene.scriptText === 'string' ? scene.scriptText : '',
            audioObjectKey: typeof scene.audioObjectKey === 'string' ? scene.audioObjectKey : '',
            audioDurationMs: Number(scene.audioDurationMs) > 0 ? Number(scene.audioDurationMs) : 0,
            voiceName: typeof scene.voiceName === 'string' ? scene.voiceName : '',
            voiceStyle: typeof scene.voiceStyle === 'string' ? scene.voiceStyle : '',
            relatedSceneIds: Array.isArray(scene.relatedSceneIds) ? scene.relatedSceneIds : [],
            transition: sceneTransitionMap[scene.id] || 'door'
          };
        });
      }
      function i18nText(zh, en) {
        return hostCaps.locale === 'en' ? en : zh;
      }
      function applyI18nTexts() {
        var toolbarTitleEl = document.getElementById('sv-toolbar-title');
        var titleEl = document.getElementById('sv-editor-header-title');
        if (toolbarTitleEl) toolbarTitleEl.textContent = i18nText('分镜面板', 'Storyboard');
        if (titleEl) titleEl.textContent = i18nText('分镜详情编辑', 'Storyboard details');
        if (expandBtn) {
          var expandText = i18nText('展开分镜详情', 'Open storyboard details');
          expandBtn.setAttribute('aria-label', expandText);
          expandBtn.setAttribute('title', expandText);
        }
        if (editorSaveBtn) editorSaveBtn.textContent = isEditorDirty ? i18nText('保存*', 'Save*') : i18nText('保存', 'Save');
        if (editorCloseBtn) editorCloseBtn.textContent = i18nText('关闭', 'Close');
        if (editorSaveTip) editorSaveTip.textContent = i18nText('已保存', 'Saved');
      }
      function clampEditorHeightVh(value) {
        return Math.max(32, Math.min(80, Number(value) || 50));
      }
      function applyEditorHeight(vh) {
        editorHeightVh = clampEditorHeightVh(vh);
        document.documentElement.style.setProperty('--sv-editor-height', editorHeightVh + 'vh');
      }
      function persistMetaToScript() {
        meta.title = typeof meta.title === 'string' ? meta.title : '';
        meta.version = meta.version || 1;
        meta.scenes = sceneModels.map(function (scene) {
          return {
            id: scene.id,
            title: scene.title || '',
            steps: Math.max(1, Number(scene.steps) || 1),
            status: scene.status || 'pending',
            prompt: scene.prompt || '',
            scriptText: scene.scriptText || '',
            audioObjectKey: scene.audioObjectKey || '',
            audioDurationMs: Number(scene.audioDurationMs) > 0 ? Number(scene.audioDurationMs) : 0,
            voiceName: scene.voiceName || '',
            voiceStyle: scene.voiceStyle || '',
            relatedSceneIds: Array.isArray(scene.relatedSceneIds) ? scene.relatedSceneIds : [],
            transition: scene.transition || 'door'
          };
        });
        if (metaJson) metaJson.textContent = JSON.stringify(meta);
      }
      function showSavedTip() {
        if (!editorSaveTip) return;
        if (editorTipTimer) {
          clearTimeout(editorTipTimer);
          editorTipTimer = null;
        }
        editorSaveTip.classList.add('is-visible');
        editorTipTimer = setTimeout(function () {
          editorSaveTip.classList.remove('is-visible');
        }, 1200);
      }
      function markEditorDirty() {
        isEditorDirty = true;
        applyI18nTexts();
      }
      function clearEditorDirty() {
        isEditorDirty = false;
        applyI18nTexts();
      }
      function getStatusLabel(status) {
        if (status === 'success') return i18nText('已生成', 'Done');
        if (status === 'generating') return i18nText('生成中', 'Generating');
        if (status === 'failed') return i18nText('失败', 'Failed');
        return i18nText('未生成', 'Pending');
      }
      function getStatusClass(status) {
        if (status === 'generating') return 'is-generating';
        if (status === 'success') return 'is-success';
        return 'is-failed';
      }
      function getRetryButtonInnerHtml(isGenerating) {
        if (isGenerating) return '<span class="scene-chip-retry-spinner"></span>';
        return '<svg class="scene-chip-retry-icon" viewBox="0 0 1024 1024" width="16" height="16" fill="currentColor"><path d="M698.569 531.322a39.825 39.825 0 0 0-23.898 7.949c-17.702 13.216-21.34 38.28-8.124 55.982l95.716 128.212c7.854 10.521 19.893 16.073 32.084 16.073a39.825 39.825 0 0 0 23.898-7.949c17.702-13.216 21.34-38.28 8.124-55.982l-95.716-128.212c-7.853-10.521-19.892-16.074-32.084-16.073z" /><path d="M922.498 563.818a39.825 39.825 0 0 0-23.898 7.949l-128.212 95.716c-17.703 13.216-21.34 38.28-8.124 55.982 7.854 10.521 19.893 16.073 32.084 16.073a39.825 39.825 0 0 0 23.898-7.949l128.212-95.716c17.702-13.216 21.34-38.28 8.124-55.982-7.854-10.521-19.893-16.074-32.084-16.073zM229.652 282.148a39.825 39.825 0 0 0-23.898 7.949c-17.702 13.216-21.34 38.28-8.124 55.982l95.716 128.212c7.854 10.521 19.893 16.073 32.084 16.073a39.825 39.825 0 0 0 23.898-7.949c17.702-13.216 21.34-38.28 8.124-55.982l-95.716-128.212c-7.854-10.521-19.892-16.074-32.084-16.073z" /><path d="M229.652 282.148a39.825 39.825 0 0 0-23.898 7.949L77.542 385.813c-17.702 13.216-21.34 38.28-8.124 55.982 7.854 10.521 19.893 16.073 32.084 16.073a39.825 39.825 0 0 0 23.898-7.949l128.213-95.716c17.702-13.216 21.34-38.28 8.124-55.982-7.855-10.521-19.893-16.073-32.085-16.073z" /><path d="M614.968 763.65c-44.13 18.171-94.005 25.121-146.168 16.968C353.747 762.636 261.364 670.253 243.381 555.2c-9.681-61.936 1.932-120.646 28.337-170.344l-58.657-58.657c-40.139 64.442-60.11 142.746-50.806 226.152 17.993 161.306 148.088 291.401 309.394 309.394 62.041 6.92 121.259-2.357 174.048-24.083 26.535-10.921 33.625-45.203 13.335-65.493l-0.098-0.098c-11.555-11.555-28.854-14.643-43.966-8.421zM409.033 260.35c44.131-18.171 94.007-25.122 146.169-16.968 115.051 17.985 207.431 110.365 225.416 225.416 9.682 61.935-1.931 120.646-28.337 170.346l58.658 58.658c40.139-64.443 60.109-142.747 50.805-226.155-17.993-161.305-148.087-291.399-309.391-309.391-62.042-6.92-121.26 2.356-174.05 24.082-26.535 10.921-33.625 45.203-13.335 65.493l0.099 0.099c11.555 11.554 28.854 14.642 43.966 8.42z" /></svg>';
      }
      function setSceneStatus(sceneId, status) {
        var index = getSceneIndexById(sceneId);
        if (index < 0) return;
        sceneModels[index].status = status;
        persistMetaToScript();
        var statusClass = getStatusClass(status);
        var chipWrap = document.querySelector('[data-chip-scene-id="' + sceneId + '"]');
        if (chipWrap) {
          chipWrap.classList.remove('is-success', 'is-failed', 'is-generating');
          chipWrap.classList.add(statusClass);
          var chipBtn = chipWrap.querySelector('.scene-chip');
          if (chipBtn) {
            chipBtn.classList.remove('is-success', 'is-failed', 'is-generating');
            chipBtn.classList.add(statusClass);
          }
          var retryBtn = chipWrap.querySelector('.scene-chip-retry');
          if (retryBtn) {
            retryBtn.innerHTML = getRetryButtonInnerHtml(status === 'generating');
            if (status === 'generating') retryBtn.setAttribute('disabled', 'disabled');
            else retryBtn.removeAttribute('disabled');
          }
        }
        renderStatus();
      }
      function requestSceneRetry(sceneId) {
        if (!hostCaps.canRetry) return;
        var idx = getSceneIndexById(sceneId);
        if (idx < 0) return;
        var scene = sceneModels[idx];
        setSceneStatus(sceneId, 'generating');
        focusScene(sceneId);
        if (window.parent && window.parent !== window) {
          window.parent.postMessage(
            { type: '__SVGVIDEO_RETRY_SCENE', sceneId: sceneId, prompt: scene.prompt || '' },
            '*'
          );
        }
      }
      function renderEditorList() {
        if (!editorList) return;
        editorList.innerHTML = '';
        var fragment = document.createDocumentFragment();
        sceneModels.forEach(function (scene, index) {
          var item = document.createElement('section');
          item.className = 'sv-editor-item';
          item.setAttribute('data-editor-scene-id', scene.id);

          var head = document.createElement('div');
          head.className = 'sv-editor-item-head';

          var jumpBtn = document.createElement('button');
          jumpBtn.type = 'button';
          jumpBtn.className = 'sv-editor-item-title';
          jumpBtn.textContent = i18nText('分镜', 'Scene') + (index + 1) + ' · ' + (scene.title || '');
          jumpBtn.addEventListener('click', function () {
            focusScene(scene.id);
          });
          head.appendChild(jumpBtn);

          var statusWrap = document.createElement('div');
          statusWrap.className = 'sv-editor-status-wrap';
          var status = document.createElement('span');
          status.className = 'sv-editor-status';
          status.textContent = getStatusLabel(scene.status);
          statusWrap.appendChild(status);
          var retryBtn = document.createElement('button');
          retryBtn.type = 'button';
          retryBtn.className = 'scene-chip-retry sv-editor-retry';
          retryBtn.setAttribute('aria-label', i18nText('重新生成当前分镜', 'Retry this scene'));
          retryBtn.setAttribute('title', i18nText('重新生成当前分镜', 'Retry this scene'));
          retryBtn.innerHTML = getRetryButtonInnerHtml(scene.status === 'generating');
          if (scene.status === 'generating' || !hostCaps.canRetry) retryBtn.setAttribute('disabled', 'disabled');
          retryBtn.addEventListener('click', function (event) {
            event.preventDefault();
            event.stopPropagation();
            requestSceneRetry(scene.id);
            if (isEditorOpen) renderEditorList();
          });
          statusWrap.appendChild(retryBtn);
          head.appendChild(statusWrap);

          item.appendChild(head);

          var scriptField = document.createElement('label');
          scriptField.className = 'sv-editor-field';
          var scriptLabel = document.createElement('span');
          scriptLabel.className = 'sv-editor-label';
          scriptLabel.textContent = i18nText('脚本', 'Script');
          var scriptInput = document.createElement('div');
          scriptInput.className = 'sv-editor-script-view';
          scriptInput.setAttribute('data-field-scene-id', scene.id);
          scriptInput.textContent = scene.scriptText || '';
          scriptField.appendChild(scriptLabel);
          scriptField.appendChild(scriptInput);
          item.appendChild(scriptField);

          var promptField = document.createElement('label');
          promptField.className = 'sv-editor-field';
          var promptLabel = document.createElement('span');
          promptLabel.className = 'sv-editor-label';
          promptLabel.textContent = i18nText('提示词', 'Prompt');
          var promptInput = document.createElement('textarea');
          promptInput.className = 'sv-editor-textarea';
          promptInput.setAttribute('data-field-scene-id', scene.id);
          promptInput.setAttribute('data-field', 'prompt');
          promptInput.value = scene.prompt || '';
          promptInput.addEventListener('click', function (event) {
            event.stopPropagation();
          });
          promptInput.addEventListener('input', function () {
            scene.prompt = promptInput.value;
            markEditorDirty();
          });
          promptField.appendChild(promptLabel);
          promptField.appendChild(promptInput);
          item.appendChild(promptField);
          item.addEventListener('click', function () {
            focusScene(scene.id);
          });

          fragment.appendChild(item);
        });
        editorList.appendChild(fragment);
        highlightActiveEditorItem();
      }
      function highlightActiveEditorItem() {
        if (!editorList) return;
        var itemList = Array.prototype.slice.call(editorList.querySelectorAll('[data-editor-scene-id]'));
        itemList.forEach(function (item) {
          if (item.getAttribute('data-editor-scene-id') === state.sceneId) item.classList.add('is-active');
          else item.classList.remove('is-active');
        });
      }
      function openEditor() {
        if (!hostCaps.canEditPanel) return;
        if (!editorOverlay) return;
        renderEditorList();
        applyEditorHeight(editorHeightVh);
        editorOverlay.classList.add('is-open');
        editorOverlay.setAttribute('aria-hidden', 'false');
        document.body.classList.add('sv-editor-open');
        isEditorOpen = true;
      }
      function closeEditor() {
        if (!editorOverlay) return;
        editorOverlay.classList.remove('is-open');
        editorOverlay.setAttribute('aria-hidden', 'true');
        document.body.classList.remove('sv-editor-open');
        isEditorOpen = false;
      }
      function saveEditor() {
        persistMetaToScript();
        clearEditorDirty();
        showSavedTip();
        if (window.parent && window.parent !== window) {
          window.parent.postMessage({ type: '__SVGVIDEO_META_UPDATED', meta: meta }, '*');
        }
      }
      function getTotalSteps() {
        return sceneModels.reduce(function (sum, scene) { return sum + Math.max(1, Number(scene.steps) || 1); }, 0);
      }
      function getSceneIndexById(sceneId) {
        for (var i = 0; i < sceneModels.length; i++) {
          if (sceneModels[i].id === sceneId) return i;
        }
        return -1;
      }
      function getSceneInitialStep(sceneId) {
        var index = getSceneIndexById(sceneId);
        if (index < 0) return 0;
        var maxSteps = Math.max(1, Number(sceneModels[index].steps) || 1);
        return Math.max(0, maxSteps - 1);
      }
      function focusScene(sceneId) {
        var index = getSceneIndexById(sceneId);
        if (index < 0) return;
        var maxSteps = Math.max(1, Number(sceneModels[index].steps) || 1);
        stopPlayback();
        window.__SVGVIDEO_SET_STATE(sceneId, 0, 1);
        if (maxSteps > 1) {
          setTimeout(function () {
            window.__SVGVIDEO_SET_STATE(sceneId, Math.max(0, maxSteps - 1), 1);
          }, 90);
        }
      }
      function getProgressPercent(sceneId, step) {
        var total = getTotalSteps();
        if (!total) return 0;
        var index = getSceneIndexById(sceneId);
        if (index < 0) return 0;
        var completed = 0;
        for (var i = 0; i < index; i++) completed += Math.max(1, Number(sceneModels[i].steps) || 1);
        var maxSteps = Math.max(1, Number(sceneModels[index].steps) || 1);
        var localStep = Math.max(0, Math.min(maxSteps - 1, Number(step) || 0));
        return Math.max(0, Math.min(100, ((completed + localStep) / total) * 100));
      }
      function updateProgressUi(percent) {
        var pct = Math.max(0, Math.min(100, percent || 0));
        if (progressFill) progressFill.style.width = pct + '%';
        if (progressThumb) progressThumb.style.left = pct + '%';
      }
      function stopProgressAnimation() {
        if (progressRaf) {
          cancelAnimationFrame(progressRaf);
          progressRaf = null;
        }
      }
      function startProgressSegment(fromPercent, toPercent, durationMs) {
        stopProgressAnimation();
        var from = Math.max(0, Math.min(100, Number(fromPercent) || 0));
        var to = Math.max(0, Math.min(100, Number(toPercent) || 0));
        var duration = Math.max(1, Number(durationMs) || 1);
        if (duration <= 1 || to <= from) {
          updateProgressUi(to);
          return;
        }
        var startAt = performance.now();
        var tick = function (now) {
          if (!isPlaying) {
            stopProgressAnimation();
            return;
          }
          var elapsed = now - startAt;
          var ratio = Math.max(0, Math.min(1, elapsed / duration));
          var current = from + (to - from) * ratio;
          updateProgressUi(current);
          if (ratio < 1) {
            progressRaf = requestAnimationFrame(tick);
          } else {
            stopProgressAnimation();
          }
        };
        progressRaf = requestAnimationFrame(tick);
      }
      function getPlaybackSegment(sceneIndex, step) {
        var scene = sceneModels[sceneIndex];
        if (!scene) return null;
        var currentPercent = getProgressPercent(scene.id, step);
        var maxSteps = Math.max(1, Number(scene.steps) || 1);
        if (step < maxSteps - 1) {
          return {
            from: currentPercent,
            to: getProgressPercent(scene.id, step + 1),
            duration: STEP_INTERVAL_MS
          };
        }
        if (sceneIndex < sceneModels.length - 1) {
          var nextScene = sceneModels[sceneIndex + 1];
          var nextPercent = nextScene ? getProgressPercent(nextScene.id, 0) : currentPercent;
          return {
            from: currentPercent,
            to: nextPercent,
            duration: SCENE_FINISH_PAUSE_MS
          };
        }
        return {
          from: currentPercent,
          to: 100,
          duration: STEP_INTERVAL_MS
        };
      }
      function renderStatus() {
        if (!statusText) return;
        var done = sceneModels.filter(function (scene) { return scene.status === 'success'; }).length;
        var pending = sceneModels.filter(function (scene) { return scene.status === 'pending' || scene.status === 'generating'; }).length;
        var failed = sceneModels.filter(function (scene) { return scene.status === 'failed'; }).length;
        statusText.textContent = '已生成 ' + done + '/' + sceneModels.length + ' · 待生成 ' + pending + ' · 失败 ' + failed;
      }
      function updatePlayButton() {
        if (!playBtn) return;
        playBtn.innerHTML = isPlaying
          ? '<svg viewBox="0 0 24 24" width="14" height="14" fill="currentColor"><rect x="6" y="5" width="4" height="14" rx="1"></rect><rect x="14" y="5" width="4" height="14" rx="1"></rect></svg>'
          : '<svg viewBox="0 0 24 24" width="14" height="14" fill="currentColor"><path d="M7 5v14l11-7z"></path></svg>';
        playBtn.setAttribute('aria-label', isPlaying ? '暂停播放' : '播放分镜');
        playBtn.setAttribute('title', isPlaying ? '暂停播放' : '播放分镜');
      }
      function applyHostCaps() {
        if (hostCaps.canRetry) document.body.classList.add('sv-host-can-retry');
        else document.body.classList.remove('sv-host-can-retry');
        if (hostCaps.canEditPanel) document.body.classList.add('sv-host-can-edit-panel');
        else document.body.classList.remove('sv-host-can-edit-panel');
        if (hostCaps.theme === 'light') document.body.classList.add('sv-theme-light');
        else document.body.classList.remove('sv-theme-light');
      }
      function highlightActiveSceneButton() {
        sceneButtonList.forEach(function (button) {
          var targetId = button.getAttribute('data-panel-scene-id');
          if (targetId === state.sceneId) button.classList.add('is-active');
          else button.classList.remove('is-active');
        });
      }
      function stopPlayback() {
        if (playTimer) {
          clearTimeout(playTimer);
          playTimer = null;
        }
        stopProgressAnimation();
        isPlaying = false;
        updatePlayButton();
      }
      function startPlayback() {
        if (!sceneModels.length) return;
        stopPlayback();
        var sceneIndex = Math.max(0, getSceneIndexById(state.sceneId));
        var step = 0;
        var applyPlaybackState = function (direction) {
          var scene = sceneModels[sceneIndex];
          if (!scene) return false;
          window.__SVGVIDEO_SET_STATE(scene.id, step, direction);
          updateProgressUi(getProgressPercent(scene.id, step));
          var segment = getPlaybackSegment(sceneIndex, step);
          if (segment) startProgressSegment(segment.from, segment.to, segment.duration);
          return true;
        };
        var scheduleAdvance = function (delay) {
          playTimer = setTimeout(function () {
            var currentScene = sceneModels[sceneIndex];
            if (!currentScene) {
              stopPlayback();
              return;
            }
            var maxSteps = Math.max(1, Number(currentScene.steps) || 1);
            if (step < maxSteps - 1) {
              step += 1;
              applyPlaybackState(1);
              scheduleAdvance(STEP_INTERVAL_MS);
              return;
            }
            if (sceneIndex < sceneModels.length - 1) {
              playTimer = setTimeout(function () {
                sceneIndex += 1;
                step = 0;
                if (!applyPlaybackState(1)) {
                  stopPlayback();
                  return;
                }
                scheduleAdvance(STEP_INTERVAL_MS);
              }, SCENE_FINISH_PAUSE_MS);
              return;
            }
            stopPlayback();
          }, delay);
        };
        isPlaying = true;
        updatePlayButton();
        if (!applyPlaybackState(1)) {
          stopPlayback();
          return;
        }
        scheduleAdvance(STEP_INTERVAL_MS);
      }
      window.SceneLifecycle = {
        handlers: {},
        register: function (sceneId, handler) { this.handlers[sceneId] = handler || {}; }
      };

      (function () {
        var sceneDataJson = document.getElementById('__SVGVIDEO_SCENE_DATA__');
        var runtimeScenes = [];
        try {
          runtimeScenes = JSON.parse((sceneDataJson && sceneDataJson.textContent) || '[]');
        } catch (_e) {
          runtimeScenes = [];
        }
        runtimeScenes.forEach(function (scene) {
          if (!scene || typeof scene.id !== 'string') return;
          var js = typeof scene.js === 'string' ? scene.js.trim() : '';
          if (!js) return;
          try {
            var factory = new Function(js);
            factory.call(window);
          } catch (err) {
            console.error('[svg-video] scene runtime failed', scene.id, err);
          }
        });
        refreshFrameworkIcons(document);
      })();

      function applyStepVisibility(sceneEl) {
        var stepNodes = sceneEl.querySelectorAll('[data-step]');
        stepNodes.forEach(function (node) {
          var minStep = Number(node.getAttribute('data-step') || '0');
          var visible = state.step >= minStep;
          node.style.opacity = visible ? '1' : '0';
          node.style.visibility = visible ? 'visible' : 'hidden';
          node.style.pointerEvents = visible ? 'auto' : 'none';
          node.style.transform = visible ? 'translateY(0)' : 'translateY(8px)';
          node.style.filter = visible ? 'blur(0px)' : 'blur(4px)';
        });
      }
      function applySceneState() {
        var scenes = document.querySelectorAll('#stage > [data-scene-id]');
        var currentSceneEl = null;
        var previousSceneEl = null;
        var glitchTimer = null;
        scenes.forEach(function (sceneEl) {
          var sceneId = sceneEl.getAttribute('data-scene-id');
          if (sceneId === state.sceneId) currentSceneEl = sceneEl;
          if (sceneId === previousSceneId) previousSceneEl = sceneEl;
        });
        var direction = state.direction >= 0 ? 1 : -1;
        document.documentElement.style.setProperty('--scene-enter-offset', direction > 0 ? '200px' : '-200px');
        document.documentElement.style.setProperty('--scene-exit-offset', direction > 0 ? '-140px' : '140px');
        var isSceneChange = previousSceneId !== state.sceneId;
        var targetTransition = sceneTransitionMap[state.sceneId] || 'door';
        var needsGlitch = isSceneChange && targetTransition === 'glitch' && currentSceneEl;

        // Apply per-scene transition CSS vars before switching
        if (isSceneChange) {
          var preset = TRANSITION_PRESETS[targetTransition] || TRANSITION_PRESETS['door'];
          var rootStyle = document.documentElement.style;
          for (var varKey in preset) {
            rootStyle.setProperty(varKey, preset[varKey]);
          }
        }

        function doSwitch() {
          if (exitCleanupTimer) {
            clearTimeout(exitCleanupTimer);
            exitCleanupTimer = null;
          }
          scenes.forEach(function (sceneEl) {
            if (sceneEl !== currentSceneEl && sceneEl !== previousSceneEl) {
              sceneEl.classList.remove('scene-active');
              sceneEl.classList.remove('scene-exit');
            }
          });
          if (previousSceneEl && previousSceneEl !== currentSceneEl) {
            previousSceneEl.classList.remove('scene-active');
            previousSceneEl.classList.add('scene-exit');
          }
          if (currentSceneEl) {
            currentSceneEl.classList.remove('scene-exit');
            currentSceneEl.classList.add('scene-active');
            applyStepVisibility(currentSceneEl);
          }
          if (previousSceneEl && previousSceneEl !== currentSceneEl) {
            exitCleanupTimer = setTimeout(function () { previousSceneEl.classList.remove('scene-exit'); }, 650);
          }
          if (previousSceneId !== state.sceneId) {
            if (previousSceneId && window.SceneLifecycle.handlers[previousSceneId] && typeof window.SceneLifecycle.handlers[previousSceneId].leave === 'function') {
              try { window.SceneLifecycle.handlers[previousSceneId].leave(); } catch (e) {}
            }
          }
          if (currentSceneEl && window.SceneLifecycle.handlers[state.sceneId] && typeof window.SceneLifecycle.handlers[state.sceneId].enter === 'function') {
            try { window.SceneLifecycle.handlers[state.sceneId].enter(); } catch (e) {}
          }
          if (window.SceneLifecycle.handlers[state.sceneId] && typeof window.SceneLifecycle.handlers[state.sceneId].step === 'function') {
            try { window.SceneLifecycle.handlers[state.sceneId].step(state.step); } catch (e) {}
          }
          if (currentSceneEl) {
            refreshFrameworkIcons(currentSceneEl);
          }
          previousSceneId = state.sceneId;
          highlightActiveSceneButton();
          highlightActiveEditorItem();
          updateProgressUi(getProgressPercent(state.sceneId, state.step));
        }

        function runGlitchSequence() {
          if (!currentSceneEl) return;
          // Step 1: blur bomb + shift (instant, disable transition first)
          var noTrans = document.getElementById('sv-no-transition');
          if (!noTrans) {
            noTrans = document.createElement('style');
            noTrans.id = 'sv-no-transition';
            noTrans.textContent = '.scene { transition: none !important; }';
            document.head.appendChild(noTrans);
          }
          currentSceneEl.style.filter = 'blur(24px) brightness(1.5)';
          currentSceneEl.style.transform = 'translateX(30px) scale(0.96)';
          // Step 2: snap to other side (80ms)
          glitchTimer = setTimeout(function () {
            if (!currentSceneEl) return;
            currentSceneEl.style.filter = 'blur(3px) brightness(1.1)';
            currentSceneEl.style.transform = 'translateX(-15px) scale(1.02)';
            // Step 3: settle with slight bounce (160ms)
            glitchTimer = setTimeout(function () {
              if (!currentSceneEl) return;
              currentSceneEl.style.filter = 'blur(0px) brightness(1)';
              currentSceneEl.style.transform = 'translateX(0) scale(1)';
              if (noTrans && noTrans.parentNode) noTrans.parentNode.removeChild(noTrans);
              if (currentSceneEl) {
                refreshFrameworkIcons(currentSceneEl);
              }
            }, 70);
          }, 80);
        }

        if (needsGlitch) {
          // Kill old scene instantly
          if (previousSceneEl) {
            previousSceneEl.classList.remove('scene-active');
            previousSceneEl.classList.add('scene-exit');
            exitCleanupTimer = setTimeout(function () { previousSceneEl.classList.remove('scene-exit'); }, 650);
          }
          // Show new scene for glitch base
          if (currentSceneEl) {
            currentSceneEl.classList.remove('scene-exit');
            currentSceneEl.classList.add('scene-active');
            applyStepVisibility(currentSceneEl);
          }
          // Fire lifecycle immediately so scene JS can initialize
          if (previousSceneId && window.SceneLifecycle.handlers[previousSceneId] && typeof window.SceneLifecycle.handlers[previousSceneId].leave === 'function') {
            try { window.SceneLifecycle.handlers[previousSceneId].leave(); } catch (e) {}
          }
          if (window.SceneLifecycle.handlers[state.sceneId] && typeof window.SceneLifecycle.handlers[state.sceneId].enter === 'function') {
            try { window.SceneLifecycle.handlers[state.sceneId].enter(); } catch (e) {}
          }
          if (window.SceneLifecycle.handlers[state.sceneId] && typeof window.SceneLifecycle.handlers[state.sceneId].step === 'function') {
            try { window.SceneLifecycle.handlers[state.sceneId].step(state.step); } catch (e) {}
          }
          previousSceneId = state.sceneId;
          highlightActiveSceneButton();
          highlightActiveEditorItem();
          updateProgressUi(getProgressPercent(state.sceneId, state.step));
          runGlitchSequence();
        } else {
          doSwitch();
        }
      }
      window.__SVGVIDEO_SET_STATE = function (sceneId, step, direction) {
        if (typeof sceneId === 'string' && sceneId) {
          if (typeof direction === 'number' && Number.isFinite(direction)) {
            state.direction = direction >= 0 ? 1 : -1;
          } else {
            var nextIndex = getSceneIndexById(sceneId);
            var prevIndex = getSceneIndexById(state.sceneId);
            if (nextIndex !== -1 && prevIndex !== -1) state.direction = nextIndex >= prevIndex ? 1 : -1;
          }
          state.sceneId = sceneId;
        }
        if (typeof step === 'number' && Number.isFinite(step)) state.step = Math.max(0, Math.floor(step));
        applySceneState();
      };
      if (state.sceneId && getSceneIndexById(state.sceneId) === -1 && sceneModels.length) {
        state.sceneId = sceneModels[0].id;
      }
      if (playBtn) {
        playBtn.addEventListener('click', function () {
          if (isPlaying) stopPlayback();
          else startPlayback();
        });
      }
      if (expandBtn) {
        expandBtn.addEventListener('click', function () {
          if (isEditorOpen) closeEditor();
          else openEditor();
        });
      }
      if (editorCloseBtn) {
        editorCloseBtn.addEventListener('click', closeEditor);
      }
      if (editorBackdrop) {
        editorBackdrop.addEventListener('click', closeEditor);
      }
      if (editorSaveBtn) {
        editorSaveBtn.addEventListener('click', saveEditor);
      }
      if (editorResizeHandle && editorSheet) {
        editorResizeHandle.addEventListener('mousedown', function (event) {
          event.preventDefault();
          var startY = event.clientY;
          var startHeightPx = (window.innerHeight * editorHeightVh) / 100;
          var onMove = function (moveEvent) {
            var delta = startY - moveEvent.clientY;
            var nextHeightPx = startHeightPx + delta;
            var nextHeightVh = (nextHeightPx / Math.max(1, window.innerHeight)) * 100;
            applyEditorHeight(nextHeightVh);
          };
          var onUp = function () {
            window.removeEventListener('mousemove', onMove);
            window.removeEventListener('mouseup', onUp);
          };
          window.addEventListener('mousemove', onMove);
          window.addEventListener('mouseup', onUp);
        });
      }
      sceneButtonList.forEach(function (button) {
        button.addEventListener('click', function () {
          var sceneId = button.getAttribute('data-panel-scene-id');
          if (!sceneId) return;
          focusScene(sceneId);
        });
      });
      sceneRetryButtonList.forEach(function (button) {
        button.addEventListener('click', function (event) {
          event.preventDefault();
          event.stopPropagation();
          var sceneId = button.getAttribute('data-retry-scene-id');
          if (!sceneId) return;
          requestSceneRetry(sceneId);
        });
      });
      if (progressWrap) {
        progressWrap.addEventListener('click', function (event) {
          var rect = progressWrap.getBoundingClientRect();
          var pct = Math.max(0, Math.min(100, ((event.clientX - rect.left) / rect.width) * 100));
          var totalSteps = getTotalSteps();
          var targetStep = Math.round((pct / 100) * totalSteps);
          var accumulated = 0;
          for (var i = 0; i < sceneModels.length; i++) {
            var sceneSteps = Math.max(1, Number(sceneModels[i].steps) || 1);
            if (accumulated + sceneSteps > targetStep) {
              var localStep = Math.max(0, Math.min(sceneSteps - 1, targetStep - accumulated));
              stopPlayback();
              window.__SVGVIDEO_SET_STATE(sceneModels[i].id, localStep, 1);
              updateProgressUi(pct);
              return;
            }
            accumulated += sceneSteps;
          }
        });
      }
      window.addEventListener('message', function (event) {
        var data = event && event.data;
        if (!data || typeof data.type !== 'string') return;
        if (data.type === '__SVGVIDEO_SET_STATE') {
          window.__SVGVIDEO_SET_STATE(data.sceneId, data.step, data.direction);
          return;
        }
        if (data.type === '__SVGVIDEO_PLAY') {
          startPlayback();
          return;
        }
        if (data.type === '__SVGVIDEO_STOP') {
          stopPlayback();
          return;
        }
        if (data.type === '__SVGVIDEO_SET_HOST_CAPS') {
          hostCaps.canRetry = Boolean(data.canRetry);
          hostCaps.canEditPanel = Boolean(data.canEditPanel);
          hostCaps.locale = data.locale === 'en' ? 'en' : 'zh';
          hostCaps.theme = data.theme === 'light' ? 'light' : 'dark';
          applyI18nTexts();
          applyHostCaps();
          if (isEditorOpen) renderEditorList();
          return;
        }
        if (data.type === '__SVGVIDEO_SET_SCENE_STATUS') {
          var sceneId = typeof data.sceneId === 'string' ? data.sceneId : '';
          var status = typeof data.status === 'string' ? data.status : '';
          if (sceneId && status) {
            setSceneStatus(sceneId, status);
            if (isEditorOpen) renderEditorList();
          }
          return;
        }
        if (data.type === '__SVGVIDEO_SET_RECORDING') {
          if (data && data.active) {
            document.body.classList.add('sv-recording');
            // 如果指定了初始分镜，无过渡动画地跳转过去，避免录制开头残留旧分镜画面
            if (data.sceneId) {
              var noTransStyle = document.createElement('style');
              noTransStyle.id = 'sv-no-transition';
              noTransStyle.textContent = '.scene, [data-step] { transition: none !important; }';
              document.head.appendChild(noTransStyle);
              window.__SVGVIDEO_SET_STATE(data.sceneId, 0, 1);
              // 强制 reflow 后恢复过渡动画
              void document.body.offsetHeight;
              requestAnimationFrame(function () {
                var s = document.getElementById('sv-no-transition');
                if (s) s.remove();
              });
            }
          } else {
            document.body.classList.remove('sv-recording');
          }
          return;
        }
        if (data.type === '__SVGVIDEO_SET_META' && data.meta && typeof data.meta === 'object') {
          meta = data.meta;
          var freshScenes = Array.isArray(meta.scenes) ? meta.scenes : [];
          freshScenes.forEach(function (scene) {
            var idx = getSceneIndexById(scene.id);
            if (idx < 0) return;
            sceneModels[idx].prompt = typeof scene.prompt === 'string' ? scene.prompt : '';
            sceneModels[idx].scriptText = typeof scene.scriptText === 'string' ? scene.scriptText : '';
            sceneModels[idx].audioObjectKey = typeof scene.audioObjectKey === 'string' ? scene.audioObjectKey : '';
            sceneModels[idx].audioDurationMs = Number(scene.audioDurationMs) > 0 ? Number(scene.audioDurationMs) : 0;
            sceneModels[idx].voiceName = typeof scene.voiceName === 'string' ? scene.voiceName : '';
            sceneModels[idx].voiceStyle = typeof scene.voiceStyle === 'string' ? scene.voiceStyle : '';
          });
          persistMetaToScript();
          if (isEditorOpen) renderEditorList();
        }
      });
      document.addEventListener('keydown', function (event) {
        if (event.key === 'Escape' && isEditorOpen) closeEditor();
      });
      window.__SVGVIDEO_PLAY = startPlayback;
      window.__SVGVIDEO_STOP = stopPlayback;
      window.__SVGVIDEO_OPEN_EDITOR = openEditor;
      window.__SVGVIDEO_CLOSE_EDITOR = closeEditor;
      applyEditorHeight(editorHeightVh);
      renderStatus();
      updatePlayButton();
      applyHostCaps();
      clearEditorDirty();
      applyI18nTexts();
      applySceneState();
      if (autoplayFromFirstScene && sceneModels.length) {
        state.sceneId = sceneModels[0].id;
        state.step = 0;
        state.direction = 1;
        applySceneState();
        startPlayback();
      }
    })();
  </script>
</body>
</html>12:T3489,<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Python 异常处理原理可视化</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/gsap.min.js"></script>
    <script src="https://unpkg.com/lucide@latest"></script>
    <style>
        @import url('https://fonts.googleapis.com/css2?family=Fira+Code:wght@400;700&family=Noto+Sans+SC:wght@400;700&display=swap');

        :root {
            --bg: #0F172A;
            --code-bg: #1E293B;
            --primary: #38BDF8;
            --accent-red: #EF4444;
            --accent-green: #22C55E;
            --accent-orange: #FB923C;
            --text-main: #F8FAFC;
            --text-dim: #94A3B8;
            --keyword: #F472B6;
            --comment: #64748B;
        }

        body, html {
            margin: 0; padding: 0; width: 100%; height: 100%;
            background-color: var(--bg);
            color: var(--text-main);
            font-family: 'Fira Code', 'Noto Sans SC', monospace;
            overflow: hidden;
            display: flex;
            align-items: center;
            justify-content: center;
        }

        #app-container {
            position: relative;
            width: 1200px;
            height: 700px;
            overflow: hidden;
        }

        /* 背景装饰 */
        .grid-bg {
            position: absolute;
            width: 100%;
            height: 100%;
            background-image: linear-gradient(rgba(56, 189, 248, 0.05) 1px, transparent 1px),
                              linear-gradient(90deg, rgba(56, 189, 248, 0.05) 1px, transparent 1px);
            background-size: 40px 40px;
            z-index: 0;
        }

        .svg-canvas {
            width: 100%;
            height: 100%;
            position: absolute;
            top: 0;
            left: 0;
            z-index: 10;
        }

        /* 样式定义 */
        .code-block {
            fill: var(--code-bg);
            stroke: #334155;
            stroke-width: 1;
            rx: 8;
        }

        .line-highlight {
            fill: rgba(56, 189, 248, 0.15);
            opacity: 0;
        }

        .keyword { fill: var(--keyword); font-weight: bold; }
        .code-text { fill: var(--text-main); font-size: 18px; }
        .comment { fill: var(--comment); font-size: 14px; font-style: italic; }
        .exception-name { fill: var(--accent-red); font-weight: bold; }
        
        .status-panel {
            fill: #111827;
            stroke: #334155;
            stroke-width: 2;
            rx: 12;
        }

        .data-package {
            filter: drop-shadow(0 0 8px var(--primary));
        }

        .subtitles-box {
            position: absolute;
            bottom: 40px;
            left: 50%;
            transform: translateX(-50%);
            width: 800px;
            text-align: center;
            background: rgba(15, 23, 42, 0.8);
            backdrop-filter: blur(8px);
            padding: 20px;
            border-radius: 12px;
            border-bottom: 3px solid var(--primary);
            z-index: 20;
        }

        .subtitles-text {
            font-size: 22px;
            color: var(--text-main);
            margin: 0;
        }

        #flow-indicator {
            stroke-dasharray: 8 4;
            animation: dash 1s linear infinite;
        }

        @keyframes dash {
            to { stroke-dashoffset: -12; }
        }
    </style>
</head>
<body>

<div id="app-container">
    <div class="grid-bg"></div>
    
    <svg class="svg-canvas" viewBox="0 0 1200 700">
        <defs>
            <filter id="glow" x="-20%" y="-20%" width="140%" height="140%">
                <feGaussianBlur stdDeviation="4" result="blur" />
                <feComposite in="SourceGraphic" in2="blur" operator="over" />
            </filter>
            
            <marker id="arrowhead" markerWidth="10" markerHeight="7" refX="0" refY="3.5" orient="auto">
                <polygon points="0 0, 10 3.5, 0 7" fill="var(--primary)" />
            </marker>
        </defs>

        <!-- 标题 -->
        <g transform="translate(50, 50)">
            <text x="0" y="0" fill="var(--primary)" font-size="28" font-weight="700">Python Exception Handling</text>
            <text x="0" y="30" fill="var(--text-dim)" font-size="16">异常处理机制与流程可视化</text>
            <rect x="0" y="45" width="400" height="2" fill="var(--primary)" opacity="0.3" />
        </g>

        <!-- 代码编辑器区域 -->
        <g id="editor-group" transform="translate(50, 120)">
            <rect class="code-block" width="500" height="420" />
            <rect id="line-highlighter" class="line-highlight" width="480" height="30" x="10" y="25" />
            
            <g transform="translate(30, 50)">
                <!-- 代码内容 -->
                <text class="code-text" y="0"><tspan class="keyword">try</tspan>:</text>
                <text class="code-text" y="35" x="30">result = 10 / n</text>
                <text class="comment" y="55" x="30"># 可能抛出除零异常的操作</text>

                <text class="code-text" y="100"><tspan class="keyword">except</tspan> <tspan class="exception-name">ZeroDivisionError</tspan>:</text>
                <text class="code-text" y="135" x="30">print(<tspan fill="#EAB308">"不能除以零！"</tspan>)</text>

                <text class="code-text" y="180"><tspan class="keyword">except</tspan> <tspan class="exception-name">Exception</tspan> <tspan class="keyword">as</tspan> e:</text>
                <text class="code-text" y="215" x="30">print(<tspan fill="#EAB308">"发生了其他错误"</tspan>)</text>

                <text class="code-text" y="260"><tspan class="keyword">else</tspan>:</text>
                <text class="code-text" y="295" x="30">print(<tspan fill="#EAB308">"计算成功"</tspan>)</text>

                <text class="code-text" y="340"><tspan class="keyword">finally</tspan>:</text>
                <text class="code-text" y="375" x="30">print(<tspan fill="#EAB308">"清理并退出"</tspan>)</text>
            </g>
        </g>

        <!-- 逻辑流程演示区 -->
        <g id="logic-group" transform="translate(620, 120)">
            <rect class="status-panel" width="530" height="420" />
            
            <!-- 流程节点 -->
            <g id="nodes">
                <circle id="node-try" cx="100" cy="60" r="25" fill="var(--code-bg)" stroke="var(--primary)" stroke-width="2" />
                <text x="100" y="65" text-anchor="middle" fill="var(--text-main)" font-size="12">TRY</text>

                <circle id="node-error" cx="265" cy="160" r="30" fill="var(--code-bg)" stroke="var(--accent-red)" stroke-width="2" opacity="0.3" />
                <path id="error-icon" d="M265 145 L265 165 M265 175 L265 175" stroke="var(--accent-red)" stroke-width="4" stroke-linecap="round" opacity="0" />
                <text x="265" y="210" text-anchor="middle" fill="var(--accent-red)" font-size="12" font-weight="bold">EXCEPTION!</text>

                <rect id="node-except" x="380" y="135" width="100" height="50" rx="5" fill="var(--code-bg)" stroke="var(--accent-orange)" stroke-width="2" />
                <text x="430" y="165" text-anchor="middle" fill="var(--text-main)" font-size="12">EXCEPT</text>

                <rect id="node-else" x="50" y="250" width="100" height="50" rx="5" fill="var(--code-bg)" stroke="var(--accent-green)" stroke-width="2" />
                <text x="100" y="280" text-anchor="middle" fill="var(--text-main)" font-size="12">ELSE</text>

                <rect id="node-finally" x="215" y="340" width="100" height="50" rx="5" fill="var(--code-bg)" stroke="var(--primary)" stroke-width="2" />
                <text x="265" y="370" text-anchor="middle" fill="var(--text-main)" font-size="12">FINALLY</text>
            </g>

            <!-- 路径连接线 -->
            <g id="paths" opacity="0.4">
                <path id="path-success" d="M100 85 L100 250" stroke="var(--accent-green)" stroke-width="2" fill="none" marker-end="url(#arrowhead)" />
                <path id="path-to-error" d="M125 60 L235 145" stroke="var(--accent-red)" stroke-width="2" fill="none" marker-end="url(#arrowhead)" />
                <path id="path-to-except" d="M295 160 L380 160" stroke="var(--accent-orange)" stroke-width="2" fill="none" marker-end="url(#arrowhead)" />
                <path id="path-except-to-finally" d="M430 185 L430 365 L315 365" stroke="var(--accent-orange)" stroke-width="2" fill="none" marker-end="url(#arrowhead)" />
                <path id="path-else-to-finally" d="M100 300 L100 365 L215 365" stroke="var(--accent-green)" stroke-width="2" fill="none" marker-end="url(#arrowhead)" />
            </g>

            <!-- 运行中的数据包 (动画主体) -->
            <g id="data-package" opacity="0">
                <circle r="12" fill="var(--primary)" filter="url(#glow)" />
                <path d="M-5 -2 L0 3 L5 -2" stroke="white" stroke-width="2" fill="none" />
            </g>
        </g>
    </svg>

    <div class="subtitles-box">
        <p id="subtitles" class="subtitles-text">准备演示 Python 异常处理机制...</p>
    </div>
</div>

<script>
    const sub = document.getElementById('subtitles');
    const highlighter = document.getElementById('line-highlighter');
    const dataPkg = document.getElementById('data-package');
    const tl = gsap.timeline({ repeat: -1 });

    function moveHighlighter(y) {
        return { y: y, opacity: 1, duration: 0.4, ease: "power2.inOut" };
    }

    function setSub(text) {
        sub.innerText = text;
    }

    // 动画序列定义
    tl.addLabel("start")
      // 1. 初始化
      .to(dataPkg, { opacity: 0, duration: 0 })
      .call(() => setSub("场景一：正常执行流 (No Exception)"))
      
      // 2. 进入 Try
      .to(highlighter, moveHighlighter(25))
      .to(dataPkg, { opacity: 1, x: 100, y: 60, duration: 0.5 })
      .call(() => setSub("程序进入 try 代码块，尝试执行敏感操作"))
      
      .to(highlighter, moveHighlighter(60))
      .to("#node-try", { scale: 1.2, strokeWidth: 4, duration: 0.3 })
      .to("#node-try", { scale: 1, strokeWidth: 2, duration: 0.3 })
      
      // 3. 正常路径至 Else
      .call(() => setSub("操作成功，没有异常发生，直接跳过所有 except"))
      .to(dataPkg, { 
          motionPath: {
              path: "#path-success",
              autoRotate: true
          }, 
          duration: 1.5,
          ease: "power1.inOut"
      })
      .to(highlighter, moveHighlighter(285))
      .to("#node-else", { backgroundColor: "var(--accent-green)", duration: 0.3 })
      .call(() => setSub("执行 else 块：仅在没有发生异常时运行"))
      
      // 4. 进入 Finally
      .to(dataPkg, { 
          motionPath: {
              path: "#path-else-to-finally",
              autoRotate: true
          }, 
          duration: 1.2 
      })
      .to(highlighter, moveHighlighter(365))
      .to("#node-finally", { strokeWidth: 4, duration: 0.3 })
      .call(() => setSub("最后执行 finally 块：无论是否出错都会执行"))
      .to(dataPkg, { opacity: 0, scale: 2, duration: 0.5 })
      .to("#node-finally", { strokeWidth: 2, duration: 0.1 })
      .to("#node-else", { backgroundColor: "transparent", duration: 0.1 })

      .to({}, { duration: 1.5 }) // 停顿

      // 5. 异常流
      .addLabel("error-scenario")
      .call(() => setSub("场景二：触发特定异常 (ZeroDivisionError)"))
      .to(highlighter, moveHighlighter(25))
      .to(dataPkg, { opacity: 1, x: 100, y: 60, scale: 1, duration: 0.5 })
      
      .to(highlighter, moveHighlighter(60))
      .call(() => setSub("糟糕！执行 10 / 0 触发了除零异常"))
      
      // 6. 抛出异常
      .to(dataPkg, { 
          motionPath: { path: "#path-to-error" }, 
          duration: 0.8,
          fill: "var(--accent-red)"
      })
      .to("#node-error", { opacity: 1, scale: 1.2, duration: 0.3 })
      .to("#error-icon", { opacity: 1, duration: 0.2 })
      
      // 7. 匹配 Except
      .call(() => setSub("Python 停止 try 中的后续代码，开始在 except 中寻找匹配项"))
      .to(dataPkg, { 
          motionPath: { path: "#path-to-except" }, 
          duration: 0.6 
      })
      .to(highlighter, moveHighlighter(125))
      .to("#node-except", { strokeWidth: 4, duration: 0.3 })
      .call(() => setSub("匹配到 ZeroDivisionError，执行对应的异常处理逻辑"))
      
      // 8. 异常处理后进入 Finally
      .to(dataPkg, { 
          motionPath: { path: "#path-except-to-finally" }, 
          duration: 1.5 
      })
      .to(highlighter, moveHighlighter(365))
      .to("#node-finally", { strokeWidth: 4, duration: 0.3 })
      .call(() => setSub("注意：发生异常时 else 会被跳过，但 finally 依然执行"))
      
      .to(dataPkg, { opacity: 0, scale: 2, duration: 0.5 })
      .to("#node-error", { opacity: 0.3, scale: 1, duration: 0.3 })
      .to("#error-icon", { opacity: 0, duration: 0.1 })
      .to("#node-except", { strokeWidth: 2, duration: 0.1 })
      .to("#node-finally", { strokeWidth: 2, duration: 0.1 })

      .to({}, { duration: 2 }); // 循环等待

    // 初始化 Lucide 图标 (如果需要)
    lucide.createIcons();

</script>
</body>
</html>13:T22cf9,<!doctype html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>腾讯公司发展历程：连接的演变</title>
  <style>
    @font-face {
      font-family: "SVGVideoAlimamaShuHei";
      src:
        url("/fonts/AlimamaShuHeiTi-Bold.woff2") format("woff2"),
        url("/fonts/AlimamaShuHeiTi-Bold.woff") format("woff"),
        url("/fonts/AlimamaShuHeiTi-Bold.ttf") format("truetype"),
        local("Alimama ShuHeiTi Bold"),
        local("Alimama ShuHeiTi"),
        local("AlimamaShuHeiTi-Bold"),
        local("AlimamaShuHeiTi");
      font-display: swap;
      font-weight: 700;
      font-style: normal;
    }
    :root {
      color-scheme: dark;
      --bg: #202020;
      --fg: #e2e8f0;
      --muted: #94a3b8;
      --card: rgba(255, 255, 255, 0.05);
      --border: rgba(255, 255, 255, 0.12);
      --sv-editor-height: 50vh;
      --sv-font-display-cn: "SVGVideoAlimamaShuHei", "Alimama ShuHeiTi", "AlimamaShuHeiTi-Bold", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif;
      --sv-font-body-cn: "SVGVideoAlimamaShuHei", "Alimama ShuHeiTi", "AlimamaShuHeiTi-Bold", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif;
      --sv-font-mono: "SFMono-Regular", "JetBrains Mono", "Fira Code", "Courier New", monospace;
    }
    * { box-sizing: border-box; }
    html, body {
      margin: 0; width: 100%; height: 100%; overflow: hidden;
      background: var(--bg);
      color: var(--fg);
      font-family: var(--sv-font-body-cn);
    }
    #app { width: 100%; height: 100%; display: flex; flex-direction: column; }
    #viewport { position: relative; flex: 1; min-height: 0; overflow: hidden; }
    #stage { position: relative; width: 100%; height: 100%; overflow: hidden; }
    .scene {
      position: absolute; inset: 0; overflow: hidden;
      opacity: 0; pointer-events: none;
      transform: translateX(var(--scene-enter-offset, 56px)) scale(0.96);
      filter: blur(10px);
      transition: opacity .55s cubic-bezier(.22,1,.36,1), transform .55s cubic-bezier(.22,1,.36,1), filter .45s ease;
      will-change: opacity, transform, filter;
      z-index: 1;
    }
    .scene.scene-active {
      opacity: 1; pointer-events: auto; transform: translateX(0) scale(1); filter: blur(0px); z-index: 3;
    }
    .scene.scene-exit {
      opacity: 0; pointer-events: none;
      transform: translateX(var(--scene-exit-offset, -56px)) scale(0.96);
      filter: blur(10px); z-index: 2;
    }
    .scene-placeholder {
      height: 100%; border: 1px dashed var(--border); border-radius: 20px; background: var(--card);
      display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 12px; color: var(--muted);
    }
    .sv-scene-root { width: 100%; height: 100%; position: relative; overflow: hidden; }
    .scene-placeholder-title { font-size: 28px; color: var(--fg); font-weight: 700; }
    [data-step] { transition: opacity .35s ease, transform .35s ease, filter .35s ease; }
    #sv-controls {
      background: rgba(19, 19, 19, 0.95);
      border-top: 1px solid rgba(255,255,255,0.08);
      padding: 8px 12px 10px;
      display: flex;
      flex-direction: column;
      gap: 8px;
      backdrop-filter: blur(8px);
      flex-shrink: 0;
    }
    .sv-progress-wrap {
      position: relative;
      height: 22px;
      display: flex;
      align-items: center;
      cursor: pointer;
    }
    .sv-progress-track {
      width: 100%;
      height: 4px;
      border-radius: 999px;
      background: rgba(120, 120, 120, 0.55);
      position: relative;
      overflow: hidden;
    }
    .sv-progress-fill {
      position: absolute;
      left: 0;
      top: 0;
      bottom: 0;
      width: 0%;
      border-radius: 999px;
      background: linear-gradient(90deg, #f97316, #fb923c);
      transition: width 220ms ease;
    }
    .sv-progress-thumb {
      position: absolute;
      top: 50%;
      width: 12px;
      height: 12px;
      border-radius: 50%;
      background: #fff;
      transform: translate(-50%, -50%);
      box-shadow: 0 0 0 1px rgba(0,0,0,0.28);
      pointer-events: none;
    }
    .sv-toolbar {
      display: grid;
      grid-template-columns: 1fr auto 1fr;
      align-items: center;
      gap: 8px;
      min-height: 30px;
    }
    .sv-toolbar-left {
      display: inline-flex;
      align-items: center;
      gap: 6px;
      min-width: 0;
    }
    .sv-toolbar-title {
      font-size: 11px;
      color: #a8b0bb;
      white-space: nowrap;
      overflow: hidden;
      text-overflow: ellipsis;
    }
    .sv-expand-btn {
      width: 20px;
      height: 20px;
      border: none;
      border-radius: 6px;
      background: rgba(255,255,255,0.08);
      color: #cbd5e1;
      display: inline-flex;
      align-items: center;
      justify-content: center;
      cursor: pointer;
      flex: 0 0 auto;
      transition: background 140ms ease, color 140ms ease, transform 120ms ease;
    }
    .sv-expand-btn:hover {
      background: rgba(255,255,255,0.15);
      color: #f8fafc;
    }
    .sv-expand-btn:active {
      transform: scale(0.94);
    }
    body:not(.sv-host-can-edit-panel) .sv-expand-btn {
      display: none;
    }
    .sv-play-btn {
      width: 28px;
      height: 28px;
      border: none;
      border-radius: 999px;
      color: #fff;
      background: #f97316;
      cursor: pointer;
      display: inline-flex;
      align-items: center;
      justify-content: center;
      transition: transform 120ms ease, background 160ms ease;
    }
    .sv-play-btn:hover { background: #fb923c; }
    .sv-play-btn:active { transform: scale(0.94); }
    .sv-play-btn:disabled { opacity: 0.45; cursor: not-allowed; }
    .sv-status {
      text-align: right;
      font-size: 11px;
      color: #8f98a5;
      white-space: nowrap;
      overflow: hidden;
      text-overflow: ellipsis;
    }
    .sv-scene-grid {
      display: flex;
      align-items: stretch;
      gap: 6px;
      flex-wrap: nowrap;
      overflow: hidden;
    }
    .scene-chip {
      position: relative;
      width: 100%;
      border: none;
      border-bottom: 2px solid transparent;
      border-radius: 8px;
      background: rgba(255,255,255,0.06);
      color: #a3a3a3;
      padding: 7px 6px;
      text-align: center;
      font-size: 11px;
      line-height: 1.1;
      white-space: nowrap;
      overflow: hidden;
      text-overflow: ellipsis;
      cursor: pointer;
      transition: transform 140ms ease, color 160ms ease, background 160ms ease, border-color 160ms ease;
    }
    .scene-chip-label {
      display: block;
      width: 100%;
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
    }
    .scene-chip-wrap {
      position: relative;
      flex: 1 1 0;
      min-width: 0;
    }
    .scene-chip-retry {
      position: absolute;
      top: 50%;
      right: 4px;
      transform: translateY(-50%);
      width: 24px;
      height: 24px;
      border: none;
      border-radius: 999px;
      background: #f97316;
      color: #fff;
      display: inline-flex;
      align-items: center;
      justify-content: center;
      box-shadow: 0 4px 10px rgba(0, 0, 0, 0.3);
      opacity: 0;
      pointer-events: none;
      transition: opacity 160ms ease;
      cursor: pointer;
    }
    body.sv-host-can-retry .scene-chip-wrap:hover .scene-chip-retry {
      opacity: 1;
      pointer-events: auto;
    }
    .scene-chip-retry:disabled {
      opacity: 1;
      pointer-events: none;
      background: #f97316;
    }
    .scene-chip-retry-icon {
      width: 18px;
      height: 18px;
      display: block;
    }
    .scene-chip-retry-spinner {
      width: 14px;
      height: 14px;
      aspect-ratio: 1 / 1;
      border: 2px solid #ffffff;
      border-top-color: transparent;
      border-radius: 999px;
      box-sizing: border-box;
      animation: sceneChipSpin 1s linear infinite;
      display: block;
      flex: 0 0 auto;
    }
    @keyframes sceneChipSpin {
      from { transform: rotate(0deg); }
      to { transform: rotate(360deg); }
    }
    .scene-chip:hover { transform: scale(1.02); }
    .scene-chip.is-active {
      color: #fb923c;
      border-bottom-color: #fb923c;
      background: linear-gradient(135deg, rgba(249,115,22,0.2), rgba(168,85,247,0.2));
    }
    .scene-chip.is-failed {
      color: #fca5a5;
      border-bottom-color: rgba(239,68,68,0.95);
      background: rgba(239,68,68,0.12);
    }
    .scene-chip.is-generating {
      color: #fcd34d;
      background: rgba(245,158,11,0.11);
    }
    .sv-editor-overlay {
      position: relative;
      width: 100%;
      height: 0;
      z-index: 40;
      pointer-events: none;
      overflow: hidden;
      transition: height 260ms cubic-bezier(.22,1,.36,1);
    }
    .sv-editor-backdrop {
      position: absolute;
      inset: auto 0 0 0;
      height: 100%;
      background: rgba(0, 0, 0, 0.45);
      opacity: 0;
      transition: opacity 220ms ease;
    }
    .sv-editor-sheet {
      position: absolute;
      left: 0;
      right: 0;
      bottom: 0;
      height: 100%;
      background: rgba(16, 16, 16, 0.96);
      border-top: 1px solid rgba(255,255,255,0.12);
      box-shadow: 0 -14px 30px rgba(0,0,0,0.45);
      transform: translateY(102%);
      transition: transform 260ms cubic-bezier(.22,1,.36,1);
      display: flex;
      flex-direction: column;
    }
    .sv-editor-overlay.is-open {
      pointer-events: auto;
      height: var(--sv-editor-height, 50vh);
    }
    .sv-editor-overlay.is-open .sv-editor-backdrop {
      opacity: 1;
    }
    .sv-editor-overlay.is-open .sv-editor-sheet {
      transform: translateY(0);
    }
    .sv-editor-resize-handle {
      width: 100%;
      height: 14px;
      display: flex;
      align-items: center;
      justify-content: center;
      cursor: ns-resize;
      flex: 0 0 auto;
      touch-action: none;
    }
    .sv-editor-resize-handle::before {
      content: "";
      width: 52px;
      height: 4px;
      border-radius: 999px;
      background: rgba(148, 163, 184, 0.7);
    }
    .sv-editor-header {
      padding: 10px 12px;
      border-bottom: 1px solid rgba(255,255,255,0.1);
      display: grid;
      grid-template-columns: 1fr auto;
      align-items: center;
      gap: 8px;
    }
    .sv-editor-header-title {
      color: #f1f5f9;
      font-size: 13px;
      font-weight: 600;
      white-space: nowrap;
      overflow: hidden;
      text-overflow: ellipsis;
    }
    .sv-editor-actions {
      display: inline-flex;
      align-items: center;
      gap: 6px;
    }
    .sv-editor-btn {
      border: none;
      border-radius: 8px;
      cursor: pointer;
      font-size: 12px;
      line-height: 1;
      padding: 7px 10px;
      color: #e2e8f0;
      background: rgba(255,255,255,0.1);
      transition: background 120ms ease;
    }
    .sv-editor-btn:hover {
      background: rgba(255,255,255,0.18);
    }
    .sv-editor-btn.is-primary {
      color: #fff;
      background: #f97316;
    }
    .sv-editor-btn.is-primary:hover {
      background: #fb923c;
    }
    .sv-editor-list {
      flex: 1;
      min-height: 0;
      overflow: auto;
      padding: 10px 12px 14px;
      display: grid;
      gap: 10px;
      grid-template-columns: repeat(2, minmax(0, 1fr));
      align-items: start;
      align-content: start;
    }
    .sv-editor-item {
      border: 1px solid rgba(255,255,255,0.12);
      border-radius: 10px;
      background: rgba(255,255,255,0.03);
      padding: 10px;
      display: grid;
      grid-template-rows: auto auto auto;
      gap: 10px;
      align-content: start;
      cursor: pointer;
    }
    .sv-editor-item.is-active {
      border-color: rgba(249,115,22,0.65);
      box-shadow: inset 0 0 0 1px rgba(249,115,22,0.28);
    }
    .sv-editor-item-head {
      display: flex;
      align-items: center;
      justify-content: space-between;
      gap: 8px;
      min-height: 30px;
    }
    .sv-editor-item-title {
      border: none;
      border-radius: 8px;
      color: #fb923c;
      background: rgba(255,255,255,0.08);
      font-size: 12px;
      font-weight: 600;
      padding: 6px 8px;
      cursor: pointer;
      white-space: nowrap;
      overflow: hidden;
      text-overflow: ellipsis;
      text-align: left;
    }
    .sv-editor-item-title:hover {
      background: rgba(255,255,255,0.14);
    }
    .sv-editor-status {
      color: #94a3b8;
      font-size: 11px;
      white-space: nowrap;
    }
    .sv-editor-status-wrap {
      display: inline-flex;
      align-items: center;
      gap: 6px;
    }
    .scene-chip-retry.sv-editor-retry {
      position: static;
      transform: none;
      opacity: 1;
      pointer-events: auto;
      width: 24px;
      height: 24px;
      flex: 0 0 auto;
    }
    .sv-editor-field {
      display: grid;
      gap: 6px;
    }
    .sv-editor-label {
      color: #a3a3a3;
      font-size: 11px;
      font-weight: 700;
    }
    .sv-editor-textarea {
      width: 100%;
      border-radius: 8px;
      border: 1px solid rgba(255,255,255,0.14);
      background: rgba(0,0,0,0.24);
      color: #f1f5f9;
      font-size: 12px;
      line-height: 1.45;
      padding: 8px 9px;
      resize: none;
      min-height: 110px;
      height: 110px;
    }
    .sv-editor-textarea[data-field="scriptText"] {
      min-height: 84px;
    }
    .sv-editor-textarea:focus {
      outline: none;
      border-color: rgba(249,115,22,0.7);
      box-shadow: 0 0 0 2px rgba(249,115,22,0.18);
    }
    body.sv-theme-light .sv-editor-textarea {
      border-color: rgba(15,23,42,0.18);
      background: rgba(248,250,252,0.92);
      color: #0f172a;
    }
    body.sv-theme-light .sv-editor-textarea:focus {
      border-color: rgba(249,115,22,0.72);
      box-shadow: 0 0 0 2px rgba(249,115,22,0.2);
    }
    .sv-editor-script-view {
      width: 100%;
      color: #e2e8f0;
      font-size: 12px;
      line-height: 1.5;
      white-space: pre-wrap;
      word-break: break-word;
      min-height: 84px;
      max-height: 84px;
      overflow: auto;
    }
    .sv-editor-save-tip {
      color: #fbbf24;
      font-size: 11px;
      opacity: 0;
      transition: opacity 140ms ease;
      justify-self: end;
    }
    .sv-editor-save-tip.is-visible {
      opacity: 1;
    }
    body.sv-theme-light .sv-editor-backdrop {
      background: rgba(15, 23, 42, 0.2);
    }
    body.sv-theme-light .sv-editor-sheet {
      background: rgba(255, 255, 255, 0.98);
      border-top-color: rgba(15, 23, 42, 0.12);
      box-shadow: 0 -14px 30px rgba(15, 23, 42, 0.16);
    }
    body.sv-theme-light .sv-editor-header {
      border-bottom-color: rgba(15, 23, 42, 0.1);
    }
    body.sv-theme-light .sv-editor-header-title {
      color: #0f172a;
    }
    body.sv-theme-light .sv-editor-btn {
      color: #334155;
      background: rgba(15, 23, 42, 0.08);
    }
    body.sv-theme-light .sv-editor-btn:hover {
      background: rgba(15, 23, 42, 0.14);
    }
    body.sv-theme-light .sv-editor-btn.is-primary {
      color: #ffffff;
      background: #f97316;
    }
    body.sv-theme-light .sv-editor-btn.is-primary:hover {
      background: #fb923c;
    }
    body.sv-theme-light .sv-editor-item {
      border-color: rgba(15, 23, 42, 0.12);
      background: rgba(248, 250, 252, 0.95);
    }
    body.sv-theme-light .sv-editor-item.is-active {
      border-color: rgba(249,115,22,0.72);
      box-shadow: inset 0 0 0 1px rgba(249,115,22,0.3);
    }
    body.sv-theme-light .sv-editor-item-title {
      color: #ea580c;
      background: rgba(249, 115, 22, 0.12);
    }
    body.sv-theme-light .sv-editor-item-title:hover {
      background: rgba(249, 115, 22, 0.18);
    }
    body.sv-theme-light .sv-editor-status {
      color: #64748b;
    }
    body.sv-theme-light .sv-editor-label {
      color: #475569;
    }
    body.sv-theme-light .sv-editor-script-view {
      color: #334155;
    }
    body.sv-theme-light .sv-editor-save-tip {
      color: #c2410c;
    }
    body.sv-editor-open #viewport {
      flex: 1 1 calc(100vh - var(--sv-editor-height, 50vh));
      min-height: 0;
    }
    body.sv-editor-open #sv-controls {
      display: none;
    }
    body.sv-recording .sv-toolbar,
    body.sv-recording .sv-scene-grid {
      display: none;
    }
    body.sv-recording #sv-controls {
      display: none;
    }
    body.sv-theme-light .sv-editor-list,
    body.sv-theme-light .sv-editor-script-view,
    body.sv-theme-light .sv-editor-textarea {
      scrollbar-color: rgba(148, 163, 184, 0.85) rgba(226, 232, 240, 0.85);
    }
    body.sv-theme-light .sv-editor-list::-webkit-scrollbar,
    body.sv-theme-light .sv-editor-script-view::-webkit-scrollbar,
    body.sv-theme-light .sv-editor-textarea::-webkit-scrollbar {
      width: 10px;
      height: 10px;
    }
    body.sv-theme-light .sv-editor-list::-webkit-scrollbar-track,
    body.sv-theme-light .sv-editor-script-view::-webkit-scrollbar-track,
    body.sv-theme-light .sv-editor-textarea::-webkit-scrollbar-track {
      background: rgba(226, 232, 240, 0.85);
      border-radius: 999px;
    }
    body.sv-theme-light .sv-editor-list::-webkit-scrollbar-thumb,
    body.sv-theme-light .sv-editor-script-view::-webkit-scrollbar-thumb,
    body.sv-theme-light .sv-editor-textarea::-webkit-scrollbar-thumb {
      background: rgba(148, 163, 184, 0.85);
      border-radius: 999px;
      border: 2px solid rgba(226, 232, 240, 0.85);
    }
    body.sv-theme-light .sv-editor-list::-webkit-scrollbar-thumb:hover,
    body.sv-theme-light .sv-editor-script-view::-webkit-scrollbar-thumb:hover,
    body.sv-theme-light .sv-editor-textarea::-webkit-scrollbar-thumb:hover {
      background: rgba(100, 116, 139, 0.95);
    }
/* scene-01 */
.sv-scene-root-scene-01 .s-scene-01-wrapper{
  position: relative;
  width: 100%;
  height: 100%;
  background-color: #1B1B1B;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  font-family: var(--sv-font-body-cn);
  color: #FFFFFF;
}.sv-scene-root-scene-01 .tech-grid{
  position: absolute;
  inset: 0;
  background-image: 
    linear-gradient(to right, rgba(255, 255, 255, 0.03) 1px, transparent 1px),
    linear-gradient(to bottom, rgba(255, 255, 255, 0.03) 1px, transparent 1px);
  background-size: 40px 40px;
  z-index: 1;
}.sv-scene-root-scene-01 .fluid-bg{
  position: absolute;
  inset: 0;
  background: radial-gradient(circle at 50% 50%, rgba(245, 53, 1, 0.05) 0%, transparent 70%);
  filter: blur(40px);
  animation: fluid-pulse 8s ease-in-out infinite alternate;
  z-index: 1;
}@keyframes fluid-pulse{
  from { transform: scale(1) translate(0, 0); }
  to { transform: scale(1.2) translate(5%, 5%); }
}.sv-scene-root-scene-01 .focus-frame{
  position: absolute;
  width: 280px;
  height: 280px;
  z-index: 2;
  transition: all 0.6s cubic-bezier(0.34, 1.56, 0.64, 1);
  opacity: 0;
  transform: scale(1.2);
}.sv-scene-root-scene-01 .focus-frame[data-step-active="true"]{
  opacity: 1;
  transform: scale(1);
}.sv-scene-root-scene-01 .corner{
  position: absolute;
  width: 20px;
  height: 20px;
  border: 2px solid #F53501;
}.sv-scene-root-scene-01 .top-left{ top: 0; left: 0; border-right: 0; border-bottom: 0; }.sv-scene-root-scene-01 .top-right{ top: 0; right: 0; border-left: 0; border-bottom: 0; }.sv-scene-root-scene-01 .bottom-left{ bottom: 0; left: 0; border-right: 0; border-top: 0; }.sv-scene-root-scene-01 .bottom-right{ bottom: 0; right: 0; border-left: 0; border-top: 0; }.sv-scene-root-scene-01 .main-content{
  position: relative;
  z-index: 10;
  text-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 40px;
}.sv-scene-root-scene-01 .logo-container{
  position: relative;
  width: 120px;
  height: 120px;
  display: flex;
  align-items: center;
  justify-content: center;
  opacity: 0;
  transition: all 0.8s cubic-bezier(0.175, 0.885, 0.32, 1.275);
}.sv-scene-root-scene-01 .logo-container[data-step-active="true"]{
  opacity: 1;
  transform: scale(1.1);
}.sv-scene-root-scene-01 .tencent-logo{
  font-size: 80px;
  color: #FFFFFF;
  filter: drop-shadow(0 0 20px rgba(245, 53, 1, 0.3));
}.sv-scene-root-scene-01 .logo-glow{
  position: absolute;
  width: 100%;
  height: 100%;
  background: #F53501;
  filter: blur(40px);
  opacity: 0.15;
  z-index: -1;
}.sv-scene-root-scene-01 .text-glass-panel{
  padding: 16px 24px;
  background: rgba(255, 255, 255, 0.05);
  backdrop-filter: blur(12px);
  border: 1px solid rgba(255, 255, 255, 0.1);
  border-radius: 4px;
  opacity: 0;
  transform: translateY(20px);
  transition: all 0.6s ease-out;
}.sv-scene-root-scene-01 .text-glass-panel[data-step-active="true"]{
  opacity: 1;
  transform: translateY(0);
}.sv-scene-root-scene-01 .shimmer-text{
  font-family: var(--sv-font-display-cn);
  font-size: 24px;
  font-weight: 900;
  margin: 0;
  background: linear-gradient(90deg, #FFFFFF 0%, #F53501 50%, #FFFFFF 100%);
  background-size: 200% auto;
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  animation: shimmer 3s linear infinite;
}@keyframes shimmer{
  to { background-position: 200% center; }
}.sv-scene-root-scene-01 .accent-bar{
  width: 40px;
  height: 3px;
  background: #F53501;
  margin: 8px auto 0;
}.sv-scene-root-scene-01 .tech-metadata{
  position: absolute;
  bottom: 60px;
  display: flex;
  align-items: center;
  gap: 20px;
  opacity: 0;
  transition: opacity 1s ease;
}.sv-scene-root-scene-01 .tech-metadata[data-step-active="true"]{
  opacity: 0.6;
}.sv-scene-root-scene-01 .meta-item{
  display: flex;
  flex-direction: column;
  align-items: center;
}.sv-scene-root-scene-01 .meta-item .label{
  font-family: var(--sv-font-mono);
  font-size: 10px;
  color: #F53501;
  letter-spacing: 2px;
}.sv-scene-root-scene-01 .meta-item .value{
  font-family: var(--sv-font-mono);
  font-size: 14px;
  font-weight: bold;
}.sv-scene-root-scene-01 .meta-line{
  width: 1px;
  height: 30px;
  background: rgba(255, 255, 255, 0.2);
}

/* scene-02 */
.sv-scene-root-scene-02 .s-scene-02-wrapper{
  position: relative;
  width: 100%;
  height: 100%;
  background-color: #1B1B1B;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  font-family: var(--sv-font-body-cn);
}.sv-scene-root-scene-02 .tech-grid{
  position: absolute;
  inset: 0;
  background-image: linear-gradient(rgba(255, 255, 255, 0.03) 1px, transparent 1px), linear-gradient(90deg, rgba(255, 255, 255, 0.03) 1px, transparent 1px);
  background-size: 40px 40px;
  z-index: 1;
}.sv-scene-root-scene-02 .retro-window{
  position: relative;
  z-index: 10;
  width: 320px;
  height: 480px;
  background: #C0C0C0;
  border: 3px solid #FFFFFF;
  border-right-color: #808080;
  border-bottom-color: #808080;
  box-shadow: 10px 10px 0px rgba(0, 0, 0, 0.3);
  display: flex;
  flex-direction: column;
  opacity: 0;
  transform: translateY(20px);
  transition: all 0.6s cubic-bezier(0.23, 1, 0.32, 1);
}.sv-scene-root-scene-02 .retro-window[data-step-active="true"]{
  opacity: 1;
  transform: translateY(0);
}.sv-scene-root-scene-02 .window-header{
  background: linear-gradient(90deg, #000080, #1084D0);
  padding: 4px 8px;
  display: flex;
  justify-content: space-between;
  align-items: center;
  color: #FFF;
  font-family: var(--sv-font-mono);
  font-size: 12px;
  font-weight: bold;
}.sv-scene-root-scene-02 .win-btn{
  width: 16px;
  height: 14px;
  background: #C0C0C0;
  border: 1px solid #FFF;
  border-right-color: #000;
  border-bottom-color: #000;
  color: #000;
  font-size: 10px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  margin-left: 2px;
}.sv-scene-root-scene-02 .window-body{
  flex-grow: 1;
  background: #000;
  margin: 4px;
  padding: 15px;
  position: relative;
  overflow: hidden;
  color: #FFF;
  font-family: var(--sv-font-mono);
  border: 2px inset #808080;
}.sv-scene-root-scene-02 .terminal-text{
  font-size: 14px;
  line-height: 1.6;
  white-space: pre-wrap;
  color: #F53501;
}.sv-scene-root-scene-02 .hero-penguin-container{
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  opacity: 0;
  transform: scale(0.5);
  transition: all 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275);
}.sv-scene-root-scene-02 .hero-penguin-container[data-step-active="true"]{
  opacity: 1;
  transform: scale(1);
}.sv-scene-root-scene-02 .penguin-icon{
  font-size: 120px;
  color: #FFF;
  z-index: 5;
}.sv-scene-root-scene-02 .scarf-accent{
  position: absolute;
  width: 60px;
  height: 10px;
  background: #F53501;
  top: 60%;
  left: 50%;
  transform: translateX(-50%);
  border-radius: 5px;
  box-shadow: 0 0 15px #F53501;
  z-index: 6;
}.sv-scene-root-scene-02 .penguin-glow{
  position: absolute;
  width: 140px;
  height: 140px;
  background: #F53501;
  filter: blur(40px);
  opacity: 0.4;
  border-radius: 50%;
}.sv-scene-root-scene-02 .bubble-burst-layer{
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 20;
}.sv-scene-root-scene-02 .chat-bubble{
  position: absolute;
  padding: 8px 12px;
  background: #FFF;
  border: 1px solid #F53501;
  border-radius: 12px;
  font-size: 12px;
  color: #1B1B1B;
  opacity: 0;
  animation: bubble-float 2s ease-out forwards;
}@keyframes bubble-float{
  0% { transform: translate(0, 0) scale(0); opacity: 0; }
  20% { opacity: 1; }
  100% { transform: translate(var(--tx), var(--ty)) scale(1); opacity: 0; }
}.sv-scene-root-scene-02 .window-status{
  padding: 4px 8px;
  font-size: 10px;
  color: #000;
  background: #C0C0C0;
  border-top: 1px solid #808080;
  display: flex;
  justify-content: space-between;
}.sv-scene-root-scene-02 .blink-dot{ color: #F53501; animation: blink 1s infinite; }@keyframes blink{ 50% { opacity: 0; } }.sv-scene-root-scene-02 .scene-tag{
  position: absolute;
  top: 10%;
  left: 40px;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  opacity: 0;
  transition: all 0.8s ease;
}.sv-scene-root-scene-02 .scene-tag[data-step-active="true"]{ opacity: 1; }.sv-scene-root-scene-02 .tag-year{
  font-family: var(--sv-font-display-cn);
  font-size: 42px;
  color: #F53501;
  line-height: 1;
}.sv-scene-root-scene-02 .tag-line{
  width: 100px;
  height: 2px;
  background: #F53501;
  margin: 10px 0;
}.sv-scene-root-scene-02 .tag-event{
  font-family: var(--sv-font-mono);
  font-size: 12px;
  color: #FFF;
  letter-spacing: 4px;
}

/* scene-03 */
.sv-scene-root-scene-03 .s-scene-03-wrapper{
  position: relative;
  width: 100%;
  height: 100%;
  background-color: #1B1B1B;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  font-family: var(--sv-font-body-cn);
}.sv-scene-root-scene-03 .tech-grid{
  position: absolute;
  inset: 0;
  background-image: linear-gradient(rgba(255, 255, 255, 0.05) 1px, transparent 1px), linear-gradient(90deg, rgba(255, 255, 255, 0.05) 1px, transparent 1px);
  background-size: 30px 30px;
  z-index: 1;
}.sv-scene-root-scene-03 /* Step 0: Chart Animation */
.chart-container{
  position: absolute;
  bottom: 0;
  width: 100%;
  height: 50%;
  z-index: 2;
  opacity: 0;
  transition: opacity 1s ease;
}.sv-scene-root-scene-03 .chart-container[data-step-active="true"]{
  opacity: 1;
}.sv-scene-root-scene-03 .chart-path{
  stroke-dasharray: 1000;
  stroke-dashoffset: 1000;
}.sv-scene-root-scene-03 .chart-container[data-step-active="true"] .chart-path{
  animation: chart-draw 2.5s cubic-bezier(0.4, 0, 0.2, 1) forwards;
}@keyframes chart-draw{
  to { stroke-dashoffset: 0; }
}.sv-scene-root-scene-03 .stat-label{
  position: absolute;
  left: 20px;
  top: 20px;
  color: #F53501;
  font-family: var(--sv-font-mono);
  font-size: 12px;
  letter-spacing: 2px;
  border-left: 2px solid #F53501;
  padding-left: 8px;
}.sv-scene-root-scene-03 /* Step 1: Coin Hero */
.coin-hero{
  position: relative;
  z-index: 10;
  display: flex;
  flex-direction: column;
  align-items: center;
  opacity: 0;
  transform: translateY(-50px) scale(0.8);
  transition: all 0.8s cubic-bezier(0.34, 1.56, 0.64, 1);
}.sv-scene-root-scene-03 .coin-hero[data-step-active="true"]{
  opacity: 1;
  transform: translateY(0) scale(1);
}.sv-scene-root-scene-03 .coin-3d{
  width: 180px;
  height: 180px;
  perspective: 1000px;
  margin-bottom: 24px;
}.sv-scene-root-scene-03 .coin-face{
  width: 100%;
  height: 100%;
  background: linear-gradient(135deg, #FFD700 0%, #B8860B 50%, #FFD700 100%);
  border-radius: 50%;
  padding: 10px;
  box-shadow: 0 20px 40px rgba(0,0,0,0.5), inset 0 0 20px rgba(255,255,255,0.5);
  animation: coin-spin 6s infinite linear;
}.sv-scene-root-scene-03 .coin-inner{
  width: 100%;
  height: 100%;
  border: 4px solid rgba(0,0,0,0.1);
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  position: relative;
  overflow: hidden;
}.sv-scene-root-scene-03 .stock-symbol{
  font-family: var(--sv-font-display-cn);
  font-size: 24px;
  color: #5C4033;
  font-weight: 900;
  text-shadow: 1px 1px 0px rgba(255,255,255,0.3);
}.sv-scene-root-scene-03 .coin-reflection{
  position: absolute;
  top: -50%;
  left: -50%;
  width: 200%;
  height: 200%;
  background: linear-gradient(45deg, transparent 45%, rgba(255,255,255,0.6) 50%, transparent 55%);
  animation: reflect 3s infinite ease-in-out;
}@keyframes reflect{ 0% { transform: translate(-20%, -20%); } 100% { transform: translate(20%, 20%); } }@keyframes coin-spin{ 0% { transform: rotateY(0deg); } 100% { transform: rotateY(360deg); } }.sv-scene-root-scene-03 .main-title{
  font-family: var(--sv-font-display-cn);
  font-size: 32px;
  color: #FFFFFF;
  text-transform: uppercase;
  letter-spacing: 2px;
}.sv-scene-root-scene-03 /* Step 2: Biz Grid */
.business-grid{
  position: absolute;
  bottom: 12%;
  width: 90%;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 12px;
  z-index: 15;
}.sv-scene-root-scene-03 .biz-card{
  background: rgba(255, 255, 255, 0.03);
  border: 1px solid rgba(255, 255, 255, 0.1);
  padding: 16px 8px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  opacity: 0;
  transform: translateY(20px);
  transition: all 0.5s ease;
  transition-delay: calc(var(--i) * 0.15s);
}.sv-scene-root-scene-03 .business-grid[data-step-active="true"] .biz-card{
  opacity: 1;
  transform: translateY(0);
  border-color: rgba(245, 53, 1, 0.3);
}.sv-scene-root-scene-03 .biz-icon{
  font-size: 28px;
  color: #F53501;
}.sv-scene-root-scene-03 .biz-name{
  font-family: var(--sv-font-display-cn);
  font-size: 16px;
  color: #FFFFFF;
}.sv-scene-root-scene-03 .marker-line{
  position: absolute;
  left: 5%;
  width: 90%;
  height: 1px;
  background: rgba(245, 53, 1, 0.2);
  z-index: 5;
}.sv-scene-root-scene-03 .marker-line.top{ top: 20%; }.sv-scene-root-scene-03 .marker-line.bottom{ bottom: 20%; }

/* scene-04 */
.sv-scene-root-scene-04 .s-scene-04-wrapper{
  position: relative;
  width: 100%;
  height: 100%;
  background-color: #1B1B1B;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  font-family: var(--sv-font-body-cn);
}.sv-scene-root-scene-04 .tech-grid{
  position: absolute;
  inset: 0;
  background-image: linear-gradient(rgba(255, 255, 255, 0.05) 1px, transparent 1px), linear-gradient(90deg, rgba(255, 255, 255, 0.05) 1px, transparent 1px);
  background-size: 40px 40px;
  z-index: 1;
}.sv-scene-root-scene-04 .wechat-wash{
  position: absolute;
  inset: 0;
  background: radial-gradient(circle at center, rgba(7, 193, 96, 0.2) 0%, transparent 70%);
  opacity: 0;
  transition: opacity 1.2s ease;
  z-index: 2;
}.sv-scene-root-scene-04 .wechat-wash[data-step-active="true"]{ opacity: 1; }.sv-scene-root-scene-04 .hero-container{
  position: relative;
  z-index: 10;
  text-align: center;
  opacity: 0;
  transform: scale(0.9);
  transition: all 0.8s cubic-bezier(0.175, 0.885, 0.32, 1.275);
}.sv-scene-root-scene-04 .hero-container[data-step-active="true"]{
  opacity: 1;
  transform: scale(1);
}.sv-scene-root-scene-04 .year-tag{
  font-family: var(--sv-font-mono);
  font-size: 14px;
  color: #F53501;
  background: rgba(245, 53, 1, 0.1);
  padding: 2px 10px;
  border: 1px solid #F53501;
  display: inline-block;
  margin-bottom: 20px;
  letter-spacing: 2px;
}.sv-scene-root-scene-04 .logo-box{
  position: relative;
  width: 120px;
  height: 120px;
  margin: 0 auto 30px;
}.sv-scene-root-scene-04 .main-logo{
  font-size: 120px;
  color: #07C160;
  filter: drop-shadow(0 0 20px rgba(7, 193, 96, 0.4));
}.sv-scene-root-scene-04 .logo-glow{
  position: absolute;
  inset: -20px;
  background: radial-gradient(circle, rgba(7, 193, 96, 0.15) 0%, transparent 70%);
  animation: logo-pulse 3s infinite ease-in-out;
}.sv-scene-root-scene-04 .main-title{
  font-family: var(--sv-font-display-cn);
  font-size: 48px;
  color: #FFFFFF;
  margin: 0;
  letter-spacing: 4px;
}.sv-scene-root-scene-04 /* Step 1: Ripple */
.ripple-system{
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-end;
  padding-bottom: 25%;
  z-index: 5;
  opacity: 0;
  transition: opacity 0.5s ease;
}.sv-scene-root-scene-04 .ripple-system[data-step-active="true"]{ opacity: 1; }.sv-scene-root-scene-04 .ripple-circle{
  position: absolute;
  top: 50%;
  left: 50%;
  border: 1px solid rgba(7, 193, 96, 0.3);
  border-radius: 50%;
  transform: translate(-50%, -50%) scale(0);
}.sv-scene-root-scene-04 .ripple-system[data-step-active="true"] .ripple-circle{
  animation: ripple-out 4s infinite linear;
}.sv-scene-root-scene-04 .c1{ width: 300px; height: 300px; animation-delay: 0s; }.sv-scene-root-scene-04 .c2{ width: 300px; height: 300px; animation-delay: 1.3s; }.sv-scene-root-scene-04 .c3{ width: 300px; height: 300px; animation-delay: 2.6s; }.sv-scene-root-scene-04 .connection-text{
  font-family: var(--sv-font-display-cn);
  color: #07C160;
  font-size: 20px;
  letter-spacing: 8px;
  text-transform: uppercase;
  opacity: 0.8;
}.sv-scene-root-scene-04 /* Step 2: Ecosystem */
.ecosystem-radial{
  position: absolute;
  inset: 0;
  z-index: 15;
  pointer-events: none;
}.sv-scene-root-scene-04 .eco-item{
  position: absolute;
  top: 50%;
  left: 50%;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  opacity: 0;
  transition: all 0.8s cubic-bezier(0.34, 1.56, 0.64, 1);
  --dist: 160px;
}.sv-scene-root-scene-04 .ecosystem-radial[data-step-active="true"] .eco-item{
  opacity: 1;
  transform: translate(calc(cos(var(--angle)) * var(--dist) - 50%), calc(sin(var(--angle)) * var(--dist) - 50%));
}.sv-scene-root-scene-04 .eco-icon-wrapper{
  width: 60px;
  height: 60px;
  background: rgba(255, 255, 255, 0.05);
  border: 1px solid rgba(7, 193, 96, 0.4);
  border-radius: 12px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 32px;
  color: #07C160;
  box-shadow: 0 10px 20px rgba(0,0,0,0.3);
  animation: float-breathing 4s infinite ease-in-out;
}.sv-scene-root-scene-04 .eco-label{
  font-size: 14px;
  color: #FFFFFF;
  white-space: nowrap;
  font-weight: 600;
  text-shadow: 0 2px 4px rgba(0,0,0,0.5);
}.sv-scene-root-scene-04 /* Decor Frames */
.focus-frame-top, .sv-scene-root-scene-04 .focus-frame-bottom{
  position: absolute;
  left: 10%;
  width: 80%;
  height: 20px;
  border: 1px solid rgba(245, 53, 1, 0.2);
  z-index: 5;
}.sv-scene-root-scene-04 .focus-frame-top{
  top: 5%;
  border-bottom: none;
}.sv-scene-root-scene-04 .focus-frame-bottom{
  bottom: 5%;
  border-top: none;
}@keyframes logo-pulse{ 0%, 100% { transform: scale(1); opacity: 0.15; } 50% { transform: scale(1.1); opacity: 0.3; } }@keyframes ripple-out{ from { transform: translate(-50%, -50%) scale(0.5); opacity: 1; } to { transform: translate(-50%, -50%) scale(2.5); opacity: 0; } }@keyframes float-breathing{
  0%, 100% { transform: translateY(0); } 
  50% { transform: translateY(-10px); }
}

/* scene-05 */
.sv-scene-root-scene-05 .s-scene-05-wrapper{
  position: relative;
  width: 100%;
  height: 100%;
  background-color: #1B1B1B;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  font-family: var(--sv-font-body-cn);
}.sv-scene-root-scene-05 .tech-grid{
  position: absolute;
  inset: 0;
  background-image: linear-gradient(rgba(255, 255, 255, 0.03) 1px, transparent 1px), linear-gradient(90deg, rgba(255, 255, 255, 0.03) 1px, transparent 1px);
  background-size: 30px 30px;
  z-index: 1;
}.sv-scene-root-scene-05 .blue-neural-wash{
  position: absolute;
  inset: 0;
  background: radial-gradient(circle at 50% 50%, rgba(10, 17, 40, 0.8) 0%, transparent 100%);
  opacity: 0;
  transition: opacity 1.5s ease;
  z-index: 2;
}.sv-scene-root-scene-05 .blue-neural-wash[data-step-active="true"]{ opacity: 1; }.sv-scene-root-scene-05 .neural-net-container{
  position: absolute;
  inset: 0;
  z-index: 3;
  opacity: 0;
  transition: opacity 1s ease;
}.sv-scene-root-scene-05 .neural-net-container[data-step-active="true"]{ opacity: 0.6; }.sv-scene-root-scene-05 .neural-svg{
  width: 100%;
  height: 100%;
  filter: drop-shadow(0 0 5px rgba(245, 53, 1, 0.4));
}.sv-scene-root-scene-05 /* Step 1: Cloud Ring */
.cloud-progress-container{
  position: absolute;
  top: 15%;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 15px;
  opacity: 0;
  transform: translateY(-50px);
  transition: all 0.8s cubic-bezier(0.23, 1, 0.32, 1);
  z-index: 10;
}.sv-scene-root-scene-05 .cloud-progress-container[data-step-active="true"]{
  opacity: 1;
  transform: translateY(0);
}.sv-scene-root-scene-05 .frosted-ring{
  position: relative;
  width: 100px;
  height: 100px;
  background: rgba(255, 255, 255, 0.05);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  border-radius: 50%;
  border: 1px solid rgba(255, 255, 255, 0.1);
  display: flex;
  align-items: center;
  justify-content: center;
}.sv-scene-root-scene-05 .ring-spinner{
  position: absolute;
  inset: -5px;
  border: 2px solid transparent;
  border-top: 2px solid #F53501;
  border-radius: 50%;
  animation: spin 2s linear infinite;
}.sv-scene-root-scene-05 .cloud-icon{
  font-size: 40px;
  color: #FFFFFF;
  filter: drop-shadow(0 0 10px rgba(255, 255, 255, 0.3));
}.sv-scene-root-scene-05 .status-label{
  font-family: var(--sv-font-mono);
  color: #F53501;
  font-size: 10px;
  letter-spacing: 2px;
  font-weight: bold;
}.sv-scene-root-scene-05 /* Step 2: Hero Text */
.hero-content-area{
  position: relative;
  z-index: 20;
  text-align: center;
  opacity: 0;
  transform: scale(0.9);
  transition: all 1s cubic-bezier(0.34, 1.56, 0.64, 1);
}.sv-scene-root-scene-05 .hero-content-area[data-step-active="true"]{
  opacity: 1;
  transform: scale(1);
}.sv-scene-root-scene-05 .vision-title{
  font-family: var(--sv-font-display-cn);
  font-size: 72px;
  color: #FFFFFF;
  margin: 0;
  line-height: 1.2;
  display: flex;
  flex-direction: column;
}.sv-scene-root-scene-05 .glow-text{
  text-shadow: 0 0 20px rgba(255, 255, 255, 0.2);
}.sv-scene-root-scene-05 .glow-text.highlight{
  color: #F53501;
  text-shadow: 0 0 30px rgba(245, 53, 1, 0.6);
  border: 2px solid #F53501;
  padding: 0 10px;
  margin-top: 10px;
}.sv-scene-root-scene-05 .tech-tag-line{
  font-family: var(--sv-font-mono);
  color: rgba(255, 255, 255, 0.4);
  font-size: 14px;
  letter-spacing: 6px;
  margin-top: 20px;
}.sv-scene-root-scene-05 .particle-emitter{
  position: absolute;
  inset: -100px;
  pointer-events: none;
}.sv-scene-root-scene-05 /* Decor elements */
.frame-bracket{
  position: absolute;
  width: 20px;
  height: 20px;
  border: 2px solid #F53501;
  z-index: 50;
}.sv-scene-root-scene-05 .tl{ top: 40px; left: 40px; border-right: none; border-bottom: none; }.sv-scene-root-scene-05 .tr{ top: 40px; right: 40px; border-left: none; border-bottom: none; }.sv-scene-root-scene-05 .bl{ bottom: 40px; left: 40px; border-right: none; border-top: none; }.sv-scene-root-scene-05 .br{ bottom: 40px; right: 40px; border-left: none; border-top: none; }.sv-scene-root-scene-05 .data-stream-side{
  position: absolute;
  right: 10px;
  top: 50%;
  transform: rotate(90deg) translate(-50%, 0);
  font-family: var(--sv-font-mono);
  font-size: 10px;
  color: rgba(245, 53, 1, 0.5);
  letter-spacing: 2px;
}@keyframes spin{ from { transform: rotate(0deg); } to { transform: rotate(360deg); } }.sv-scene-root-scene-05 .particle{
  position: absolute;
  background: #F53501;
  width: 2px;
  height: 2px;
  border-radius: 50%;
  animation: float-up var(--d) linear infinite;
  opacity: 0;
}@keyframes float-up{
  0% { transform: translateY(0); opacity: 0; }
  20% { opacity: 0.8; }
  100% { transform: translateY(-150px); opacity: 0; }
}

/* scene-06 */
.sv-scene-root-scene-06 .s-scene-06-wrapper{
  position: relative;
  width: 100%;
  height: 100%;
  background-color: #1B1B1B;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  transition: transform 1.2s cubic-bezier(0.7, 0, 0.3, 1), opacity 1.2s ease;
}.sv-scene-root-scene-06 .s-scene-06-wrapper[data-closing="true"]{
  transform: scale(0.9);
  opacity: 0;
}.sv-scene-root-scene-06 .bg-base{
  position: absolute;
  inset: 0;
  background: #1B1B1B;
  z-index: 1;
}.sv-scene-root-scene-06 .bg-gradient-overlay{
  position: absolute;
  inset: 0;
  background: radial-gradient(circle at 50% 50%, #FFFFFF 0%, #E6F0FF 40%, #B8D4FF 100%);
  opacity: 0;
  transition: opacity 1.5s cubic-bezier(0.4, 0, 0.2, 1);
  z-index: 2;
}.sv-scene-root-scene-06 .bg-gradient-overlay[data-step-active="true"]{
  opacity: 1;
}.sv-scene-root-scene-06 .tech-grid-closing{
  position: absolute;
  inset: 0;
  background-image: linear-gradient(rgba(245, 53, 1, 0.05) 1px, transparent 1px), linear-gradient(90deg, rgba(245, 53, 1, 0.05) 1px, transparent 1px);
  background-size: 40px 40px;
  z-index: 3;
  opacity: 0.3;
}.sv-scene-root-scene-06 .slogan-container{
  position: relative;
  z-index: 10;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  opacity: 0;
  transform: translateY(30px);
  transition: all 1s cubic-bezier(0.23, 1, 0.32, 1);
}.sv-scene-root-scene-06 .slogan-container[data-step-active="true"]{
  opacity: 1;
  transform: translateY(0);
}.sv-scene-root-scene-06 .slogan-line{
  overflow: hidden;
}.sv-scene-root-scene-06 .slogan-text{
  display: block;
  font-family: var(--sv-font-display-cn);
  font-size: 48px;
  color: #1B1B1B;
  letter-spacing: 4px;
  line-height: 1.2;
}.sv-scene-root-scene-06 .slogan-text.highlight{
  color: #F53501;
}.sv-scene-root-scene-06 .slogan-divider{
  width: 40px;
  height: 4px;
  background: #F53501;
  margin: 20px 0;
}.sv-scene-root-scene-06 .anniversary-footer{
  position: absolute;
  bottom: 12%;
  display: flex;
  flex-direction: column;
  align-items: center;
  z-index: 10;
  opacity: 0;
  transform: translateY(20px);
  transition: all 0.8s ease-out;
}.sv-scene-root-scene-06 .anniversary-footer[data-step-active="true"]{
  opacity: 1;
  transform: translateY(0);
}.sv-scene-root-scene-06 .logo-wrap{
  display: flex;
  align-items: center;
  gap: 15px;
  margin-bottom: 10px;
}.sv-scene-root-scene-06 .tencent-logo{
  font-size: 32px;
  color: #0052D9;
}.sv-scene-root-scene-06 .logo-divider{
  width: 1px;
  height: 20px;
  background: rgba(0, 0, 0, 0.2);
}.sv-scene-root-scene-06 .anniversary-badge{
  display: flex;
  flex-direction: column;
  align-items: flex-start;
}.sv-scene-root-scene-06 .anniversary-badge .num{
  font-family: var(--sv-font-mono);
  font-weight: 900;
  font-size: 24px;
  line-height: 1;
  color: #1B1B1B;
}.sv-scene-root-scene-06 .anniversary-badge .text{
  font-family: var(--sv-font-mono);
  font-size: 8px;
  letter-spacing: 1px;
  color: #666;
}.sv-scene-root-scene-06 .brand-subtext{
  font-family: var(--sv-font-mono);
  font-size: 10px;
  color: rgba(0, 0, 0, 0.4);
  letter-spacing: 2px;
}.sv-scene-root-scene-06 /* Tech Decors */
.frame-bracket{
  position: absolute;
  width: 15px;
  height: 15px;
  border: 1.5px solid #F53501;
  z-index: 20;
  opacity: 0.5;
}.sv-scene-root-scene-06 .tl{ top: 40px; left: 40px; border-right: none; border-bottom: none; }.sv-scene-root-scene-06 .tr{ top: 40px; right: 40px; border-left: none; border-bottom: none; }.sv-scene-root-scene-06 .bl{ bottom: 40px; left: 40px; border-right: none; border-top: none; }.sv-scene-root-scene-06 .br{ bottom: 40px; right: 40px; border-left: none; border-top: none; }.sv-scene-root-scene-06 .scan-line{
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 2px;
  background: linear-gradient(90deg, transparent, #F53501, transparent);
  z-index: 5;
  opacity: 0.1;
  animation: scan 4s linear infinite;
}@keyframes scan{ 0% { top: 0; } 100% { top: 100%; } }
  </style>
</head>
<body data-orientation="portrait">
  <div id="app">
    <div id="viewport">
      <div id="stage">

<section class="scene" data-scene-id="scene-01">
  <div class="sv-scene-root sv-scene-root-scene-01" data-scene-root-id="scene-01">
<div class="s-scene-01-wrapper">
  <div class="tech-grid"></div>
  <div class="fluid-bg"></div>
  
  <div class="focus-frame" data-step="0">
    <div class="corner top-left"></div>
    <div class="corner top-right"></div>
    <div class="corner bottom-left"></div>
    <div class="corner bottom-right"></div>
  </div>

  <div class="main-content">
    <div class="logo-container" data-step="0">
      <iconify-icon icon="simple-icons:tencent" class="tencent-logo"></iconify-icon>
      <div class="logo-glow"></div>
    </div>

    <div class="text-glass-panel" data-step="1">
      <h1 class="shimmer-text">腾讯：连接未来的25年</h1>
      <div class="accent-bar"></div>
    </div>
  </div>

  <div class="tech-metadata" data-step="0">
    <div class="meta-item">
      <span class="label">EST.</span>
      <span class="value">1998</span>
    </div>
    <div class="meta-line"></div>
    <div class="meta-item">
      <span class="label">GLOBAL</span>
      <span class="value">CONNECT</span>
    </div>
  </div>
</div>
  </div>
</section>

<section class="scene" data-scene-id="scene-02">
  <div class="sv-scene-root sv-scene-root-scene-02" data-scene-root-id="scene-02">
<div class="s-scene-02-wrapper">
  <div class="tech-grid"></div>
  
  <!-- Step 0: Retro Window -->
  <div class="retro-window" data-step="0">
    <div class="window-header">
      <div class="header-title">OICQ_SETUP.EXE</div>
      <div class="header-controls">
        <span class="win-btn">_</span>
        <span class="win-btn">□</span>
        <span class="win-btn close">×</span>
      </div>
    </div>
    <div class="window-body">
      <div class="terminal-cursor"></div>
      <div class="terminal-text" id="terminal-typing-area"></div>
      
      <!-- Step 2: Penguin Hero -->
      <div class="hero-penguin-container" data-step="2">
        <div class="penguin-glow"></div>
        <iconify-icon icon="simple-icons:tencentqq" class="penguin-icon"></iconify-icon>
        <div class="scarf-accent"></div>
      </div>
    </div>
    <div class="window-status">
      <span>Status: Initializing...</span>
      <span class="blink-dot">●</span>
    </div>
  </div>

  <!-- Step 2: Bursting Bubbles Area -->
  <div class="bubble-burst-layer" data-step="2">
    <!-- Generated by JS -->
  </div>

  <!-- Scene Metadata -->
  <div class="scene-tag" data-step="0">
    <span class="tag-year">1999</span>
    <div class="tag-line"></div>
    <span class="tag-event">LAUNCH EVENT</span>
  </div>
</div>
  </div>
</section>

<section class="scene" data-scene-id="scene-03">
  <div class="sv-scene-root sv-scene-root-scene-03" data-scene-root-id="scene-03">
<div class="s-scene-03-wrapper">
  <div class="tech-grid"></div>
  
  <!-- Step 0: Background Growth Chart -->
  <div class="chart-container" data-step="0">
    <svg viewBox="0 0 400 800" class="growth-svg">
      <defs>
        <linearGradient id="lineGrad" x1="0%" y1="0%" x2="0%" y2="100%">
          <stop offset="0%" stop-color="#F53501" stop-opacity="1" />
          <stop offset="100%" stop-color="#F53501" stop-opacity="0.1" />
        </linearGradient>
      </defs>
      <path class="chart-path" d="M0,600 L100,550 L200,580 L300,450 L400,300" fill="none" stroke="#F53501" stroke-width="4" />
      <path class="chart-fill" d="M0,600 L100,550 L200,580 L300,450 L400,300 L400,800 L0,800 Z" fill="url(#lineGrad)" opacity="0.1" />
    </svg>
    <div class="stat-label">IPO GROWTH</div>
  </div>

  <!-- Step 1: Golden Coin -->
  <div class="coin-hero" data-step="1">
    <div class="coin-3d">
      <div class="coin-face">
        <div class="coin-inner">
          <span class="stock-symbol">HK.0700</span>
          <div class="coin-reflection"></div>
        </div>
      </div>
    </div>
    <h1 class="main-title">2004 香港上市</h1>
  </div>

  <!-- Step 2: Grid Cards -->
  <div class="business-grid" data-step="2">
    <div class="biz-card" style="--i:0">
      <iconify-icon icon="lucide:layout-template" class="biz-icon"></iconify-icon>
      <span class="biz-name">门户</span>
    </div>
    <div class="biz-card" style="--i:1">
      <iconify-icon icon="lucide:gamepad-2" class="biz-icon"></iconify-icon>
      <span class="biz-name">游戏</span>
    </div>
    <div class="biz-card" style="--i:2">
      <iconify-icon icon="lucide:layout-grid" class="biz-icon"></iconify-icon>
      <span class="biz-name">空间</span>
    </div>
  </div>

  <div class="marker-line top"></div>
  <div class="marker-line bottom"></div>
</div>
  </div>
</section>

<section class="scene" data-scene-id="scene-04">
  <div class="sv-scene-root sv-scene-root-scene-04" data-scene-root-id="scene-04">
<div class="s-scene-04-wrapper">
  <div class="tech-grid"></div>
  
  <!-- Background Green Wash -->
  <div class="wechat-wash" data-step="0"></div>

  <!-- Step 0: Central Logo & Year -->
  <div class="hero-container" data-step="0">
    <div class="year-tag">2011</div>
    <div class="logo-box">
      <iconify-icon icon="simple-icons:wechat" class="main-logo"></iconify-icon>
      <div class="logo-glow"></div>
    </div>
    <h1 class="main-title">微信诞生</h1>
  </div>

  <!-- Step 1: Connection Ripples -->
  <div class="ripple-system" data-step="1">
    <div class="ripple-circle c1"></div>
    <div class="ripple-circle c2"></div>
    <div class="ripple-circle c3"></div>
    <div class="connection-text">连接一切</div>
  </div>

  <!-- Step 2: Ecosystem Icons -->
  <div class="ecosystem-radial" data-step="2">
    <div class="eco-item p-pay" style="--angle: -120deg">
      <div class="eco-icon-wrapper">
        <iconify-icon icon="ant-design:wechat-pay-outlined"></iconify-icon>
      </div>
      <span class="eco-label">移动支付</span>
    </div>
    <div class="eco-item p-mini" style="--angle: 0deg">
      <div class="eco-icon-wrapper">
        <iconify-icon icon="lucide:component"></iconify-icon>
      </div>
      <span class="eco-label">小程序</span>
    </div>
    <div class="eco-item p-moments" style="--angle: 120deg">
      <div class="eco-icon-wrapper">
        <iconify-icon icon="lucide:aperture"></iconify-icon>
      </div>
      <span class="eco-label">朋友圈</span>
    </div>
  </div>

  <!-- Decor elements -->
  <div class="focus-frame-top"></div>
  <div class="focus-frame-bottom"></div>
</div>
  </div>
</section>

<section class="scene" data-scene-id="scene-05">
  <div class="sv-scene-root sv-scene-root-scene-05" data-scene-root-id="scene-05">
<div class="s-scene-05-wrapper">
  <div class="tech-grid"></div>
  
  <!-- Step 0: Neural Network & Star Blue Wash -->
  <div class="blue-neural-wash" data-step="0"></div>
  <div class="neural-net-container" data-step="0">
    <svg class="neural-svg" viewBox="0 0 400 800">
      <g class="nodes">
        <circle cx="100" cy="200" r="3" fill="#F53501" />
        <circle cx="300" cy="250" r="3" fill="#F53501" />
        <circle cx="200" cy="400" r="4" fill="#F53501" />
        <circle cx="150" cy="600" r="3" fill="#F53501" />
        <circle cx="250" cy="550" r="3" fill="#F53501" />
      </g>
      <g class="links" stroke="rgba(245, 53, 1, 0.3)" stroke-width="1">
        <line x1="100" y1="200" x2="200" y2="400" />
        <line x1="300" y1="250" x2="200" y2="400" />
        <line x1="200" y1="400" x2="150" y2="600" />
        <line x1="200" y1="400" x2="250" y2="550" />
      </g>
    </svg>
  </div>

  <!-- Step 1: Cloud Frosted Progress Bar -->
  <div class="cloud-progress-container" data-step="1">
    <div class="frosted-ring">
      <div class="ring-spinner"></div>
      <iconify-icon icon="lucide:cloud" class="cloud-icon"></iconify-icon>
    </div>
    <div class="status-label">CLOUD COMPUTING</div>
  </div>

  <!-- Step 2: Hero Text & Particles -->
  <div class="hero-content-area" data-step="2">
    <div class="particle-emitter" id="particles-js-05"></div>
    <h1 class="vision-title">
      <span class="glow-text">科技</span><span class="glow-text highlight">向善</span>
    </h1>
    <div class="tech-tag-line">TECH FOR GOOD</div>
  </div>

  <!-- Layout Brackets (Tech Blogger Style) -->
  <div class="frame-bracket tl"></div>
  <div class="frame-bracket tr"></div>
  <div class="frame-bracket bl"></div>
  <div class="frame-bracket br"></div>
  <div class="data-stream-side">01001 / AI / CLOUD / VISION</div>
</div>
  </div>
</section>

<section class="scene" data-scene-id="scene-06">
  <div class="sv-scene-root sv-scene-root-scene-06" data-scene-root-id="scene-06">
<div class="s-scene-06-wrapper">
  <!-- Background Layers -->
  <div class="bg-base"></div>
  <div class="bg-gradient-overlay" data-step="0"></div>
  <div class="tech-grid-closing"></div>

  <!-- Step 1: Hero Slogan -->
  <div class="slogan-container" data-step="1">
    <div class="slogan-line">
      <span class="slogan-text">用户为本</span>
    </div>
    <div class="slogan-divider"></div>
    <div class="slogan-line">
      <span class="slogan-text highlight">科技向善</span>
    </div>
  </div>

  <!-- Step 2: Anniversary Logo & Closing -->
  <div class="anniversary-footer" data-step="2">
    <div class="logo-wrap">
      <iconify-icon icon="simple-icons:tencent" class="tencent-logo"></iconify-icon>
      <div class="logo-divider"></div>
      <div class="anniversary-badge">
        <span class="num">25</span>
        <span class="text">ANNIVERSARY</span>
      </div>
    </div>
    <div class="brand-subtext">VALUE FOR USERS, TECH FOR GOOD</div>
  </div>

  <!-- Style Brackets (Persistence) -->
  <div class="frame-bracket tl"></div>
  <div class="frame-bracket tr"></div>
  <div class="frame-bracket bl"></div>
  <div class="frame-bracket br"></div>

  <!-- Scanning Line -->
  <div class="scan-line"></div>
</div>
  </div>
</section>
      </div>
    </div>
  </div>
  <script id="__SVGVIDEO_META__" type="application/json">{"version":2,"format":"json","title":"腾讯公司发展历程：连接的演变","fullScript":"从1998年的深圳，到如今影响全球的互联网巨头，腾讯走过了一条不平凡的道路。一切始于那只戴着红围巾的小企鹅。1999年，OICQ正式上线，开启了中国人的即时通讯时代。2004年，腾讯在香港挂牌上市，业务版图迅速扩张，从门户网站、在线游戏，到社交网络。2011年是关键的转折点，微信诞生了。这不仅是一个聊天工具，更是连接一切的开始，彻底改变了我们的移动生活方式。进入新时代，腾讯提出了“科技向善”的愿景。从消费互联网全面拥抱产业互联网，深耕AI、云服务与前沿科技。深耕消费互联网，拥抱产业互联网，这，就是腾讯。连接未来，无限可能。","visualStyleId":"kejibozhu","orientation":"portrait","scenes":[{"id":"scene-01","title":"开篇：连接的起源","steps":2,"status":"success","prompt":"竖屏9:16布局。背景采用腾讯品牌深蓝色渐变，带有微弱的流体动态效果。Step 0：中心位置淡入发光的腾讯Logo，伴随轻微的弹簧缩放。Step 1：Logo下方出现文字‘腾讯：连接未来的25年’，使用毛玻璃质感底色，文字带Shimmer扫光特效。","scriptText":"从1998年的深圳，到如今影响全球的互联网巨头，腾讯走过了一条不平凡的道路。","audioObjectKey":"sv-video/v1/u_1aa5e8b3-d1b1-4121-b796-bb5ac9fcd45a/p_5640993b-edd4-480b-89f0-36cd4325fb5e/s_scene-01/v_yunxi/a_81ca1825_1777091614616_dre9l9.mp3","audioDurationMs":7596,"voiceName":"yunxi","relatedSceneIds":["scene-02"]},{"id":"scene-02","title":"1999：企鹅诞生","steps":3,"status":"success","prompt":"模拟复古电脑视窗风格。Step 0：居中展示一个90年代风格的灰色终端窗口。Step 1：窗口内逐字打字显示‘Connecting... OICQ’。Step 2：窗口中心弹出一只经典的红围巾企鹅SVG图标，背景出现无数个气泡对话框向四周扩散，象征通讯的爆发。","scriptText":"一切始于那只戴着红围巾的小企鹅。1999年，OICQ正式上线，开启了中国人的即时通讯时代。","audioObjectKey":"sv-video/v1/u_1aa5e8b3-d1b1-4121-b796-bb5ac9fcd45a/p_5640993b-edd4-480b-89f0-36cd4325fb5e/s_scene-02/v_yunxi/a_cd39a1b5_1777091931404_n3tdd6.mp3","audioDurationMs":9612,"voiceName":"yunxi","relatedSceneIds":["scene-01","scene-03"]},{"id":"scene-03","title":"2004：香港上市","steps":3,"status":"success","prompt":"金色与蓝色的商业视觉风格。Step 0：背景出现向上增长的平滑折线图，颜色为亮蓝色。Step 1：屏幕中央降落一枚金色的港币符号或‘HK.0700’字样的硬币，带有金属反光。Step 2：四周环绕浮现‘门户’、‘游戏’、‘空间’的卡片，采用网格布局，依次淡入。","scriptText":"2004年，腾讯在香港挂牌上市，业务版图迅速扩张，从门户网站、在线游戏，到社交网络。","audioObjectKey":"sv-video/v1/u_1aa5e8b3-d1b1-4121-b796-bb5ac9fcd45a/p_5640993b-edd4-480b-89f0-36cd4325fb5e/s_scene-03/v_yunxi/a_19bb2e6d_1777091688930_k2tzxc.mp3","audioDurationMs":9576,"voiceName":"yunxi","relatedSceneIds":["scene-02","scene-04"]},{"id":"scene-04","title":"2011：微信时代","steps":3,"status":"success","prompt":"极简绿色视觉风格。Step 0：全屏背景转为微信绿，中心出现经典的双气泡Logo。Step 1：Logo产生强烈的涟漪波动效果，象征‘连接一切’。Step 2：屏幕上下浮现出支付图标、小程序图标和朋友圈图标，呈现放射状排列，所有元素做悬浮呼吸动画。","scriptText":"2011年是关键的转折点，微信诞生了。这不仅是一个聊天工具，更是连接一切的开始，彻底改变了我们的移动生活方式。","audioObjectKey":"sv-video/v1/u_1aa5e8b3-d1b1-4121-b796-bb5ac9fcd45a/p_5640993b-edd4-480b-89f0-36cd4325fb5e/s_scene-04/v_yunxi/a_3b1d16e7_1777091725610_mago9s.mp3","audioDurationMs":10512,"voiceName":"yunxi","relatedSceneIds":["scene-03","scene-05"]},{"id":"scene-05","title":"产业互联网：科技向善","steps":3,"status":"success","prompt":"高科技感与未来感。Step 0：背景变为深邃的星空蓝，出现AI神经网络的动态连线。Step 1：上方降下一个代表‘云端’的毛玻璃圆环进度条，不停旋转。Step 2：中心浮现‘科技向善’四个大字，文字边缘带有发光描边，并有光粒子从下往上漂浮。","scriptText":"进入新时代，腾讯提出了“科技向善”的愿景。从消费互联网全面拥抱产业互联网，深耕AI、云服务与前沿科技。","audioObjectKey":"sv-video/v1/u_1aa5e8b3-d1b1-4121-b796-bb5ac9fcd45a/p_5640993b-edd4-480b-89f0-36cd4325fb5e/s_scene-05/v_yunxi/a_8d34a6fe_1777091763843_484ysn.mp3","audioDurationMs":11196,"voiceName":"yunxi","relatedSceneIds":["scene-04","scene-06"]},{"id":"scene-06","title":"总结：连接未来","steps":2,"status":"success","prompt":"简洁有力的品牌收尾。Step 0：背景回归纯净白蓝渐变。Step 1：腾讯最新的品牌Slogan‘用户为本，科技向善’在屏幕黄金分割位置垂直居中显示。Step 2：底部浮现25周年标识，整体画面向内收缩淡出。","scriptText":"深耕消费互联网，拥抱产业互联网，这，就是腾讯。连接未来，无限可能。","audioObjectKey":"sv-video/v1/u_1aa5e8b3-d1b1-4121-b796-bb5ac9fcd45a/p_5640993b-edd4-480b-89f0-36cd4325fb5e/s_scene-06/v_yunxi/a_76cc86c3_1777091799141_uh6sgl.mp3","audioDurationMs":8388,"voiceName":"yunxi","relatedSceneIds":["scene-05"]}]}</script>
  <script id="__SVGVIDEO_SCENE_DATA__" type="application/json">[{"id":"scene-01","title":"开篇：连接的起源","steps":2,"html":"<div class=\"s-scene-01-wrapper\">\n  <div class=\"tech-grid\"></div>\n  <div class=\"fluid-bg\"></div>\n  \n  <div class=\"focus-frame\" data-step=\"0\">\n    <div class=\"corner top-left\"></div>\n    <div class=\"corner top-right\"></div>\n    <div class=\"corner bottom-left\"></div>\n    <div class=\"corner bottom-right\"></div>\n  </div>\n\n  <div class=\"main-content\">\n    <div class=\"logo-container\" data-step=\"0\">\n      <iconify-icon icon=\"simple-icons:tencent\" class=\"tencent-logo\"></iconify-icon>\n      <div class=\"logo-glow\"></div>\n    </div>\n\n    <div class=\"text-glass-panel\" data-step=\"1\">\n      <h1 class=\"shimmer-text\">腾讯：连接未来的25年</h1>\n      <div class=\"accent-bar\"></div>\n    </div>\n  </div>\n\n  <div class=\"tech-metadata\" data-step=\"0\">\n    <div class=\"meta-item\">\n      <span class=\"label\">EST.</span>\n      <span class=\"value\">1998</span>\n    </div>\n    <div class=\"meta-line\"></div>\n    <div class=\"meta-item\">\n      <span class=\"label\">GLOBAL</span>\n      <span class=\"value\">CONNECT</span>\n    </div>\n  </div>\n</div>","css":".s-scene-01-wrapper {\n  position: relative;\n  width: 100%;\n  height: 100%;\n  background-color: #1B1B1B;\n  overflow: hidden;\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  justify-content: center;\n  font-family: var(--sv-font-body-cn);\n  color: #FFFFFF;\n}\n\n.tech-grid {\n  position: absolute;\n  inset: 0;\n  background-image: \n    linear-gradient(to right, rgba(255, 255, 255, 0.03) 1px, transparent 1px),\n    linear-gradient(to bottom, rgba(255, 255, 255, 0.03) 1px, transparent 1px);\n  background-size: 40px 40px;\n  z-index: 1;\n}\n\n.fluid-bg {\n  position: absolute;\n  inset: 0;\n  background: radial-gradient(circle at 50% 50%, rgba(245, 53, 1, 0.05) 0%, transparent 70%);\n  filter: blur(40px);\n  animation: fluid-pulse 8s ease-in-out infinite alternate;\n  z-index: 1;\n}\n\n@keyframes fluid-pulse {\n  from { transform: scale(1) translate(0, 0); }\n  to { transform: scale(1.2) translate(5%, 5%); }\n}\n\n.focus-frame {\n  position: absolute;\n  width: 280px;\n  height: 280px;\n  z-index: 2;\n  transition: all 0.6s cubic-bezier(0.34, 1.56, 0.64, 1);\n  opacity: 0;\n  transform: scale(1.2);\n}\n.focus-frame[data-step-active=\"true\"] {\n  opacity: 1;\n  transform: scale(1);\n}\n\n.corner {\n  position: absolute;\n  width: 20px;\n  height: 20px;\n  border: 2px solid #F53501;\n}\n.top-left { top: 0; left: 0; border-right: 0; border-bottom: 0; }\n.top-right { top: 0; right: 0; border-left: 0; border-bottom: 0; }\n.bottom-left { bottom: 0; left: 0; border-right: 0; border-top: 0; }\n.bottom-right { bottom: 0; right: 0; border-left: 0; border-top: 0; }\n\n.main-content {\n  position: relative;\n  z-index: 10;\n  text-align: center;\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  gap: 40px;\n}\n\n.logo-container {\n  position: relative;\n  width: 120px;\n  height: 120px;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  opacity: 0;\n  transition: all 0.8s cubic-bezier(0.175, 0.885, 0.32, 1.275);\n}\n.logo-container[data-step-active=\"true\"] {\n  opacity: 1;\n  transform: scale(1.1);\n}\n\n.tencent-logo {\n  font-size: 80px;\n  color: #FFFFFF;\n  filter: drop-shadow(0 0 20px rgba(245, 53, 1, 0.3));\n}\n\n.logo-glow {\n  position: absolute;\n  width: 100%;\n  height: 100%;\n  background: #F53501;\n  filter: blur(40px);\n  opacity: 0.15;\n  z-index: -1;\n}\n\n.text-glass-panel {\n  padding: 16px 24px;\n  background: rgba(255, 255, 255, 0.05);\n  backdrop-filter: blur(12px);\n  border: 1px solid rgba(255, 255, 255, 0.1);\n  border-radius: 4px;\n  opacity: 0;\n  transform: translateY(20px);\n  transition: all 0.6s ease-out;\n}\n.text-glass-panel[data-step-active=\"true\"] {\n  opacity: 1;\n  transform: translateY(0);\n}\n\n.shimmer-text {\n  font-family: var(--sv-font-display-cn);\n  font-size: 24px;\n  font-weight: 900;\n  margin: 0;\n  background: linear-gradient(90deg, #FFFFFF 0%, #F53501 50%, #FFFFFF 100%);\n  background-size: 200% auto;\n  -webkit-background-clip: text;\n  -webkit-text-fill-color: transparent;\n  animation: shimmer 3s linear infinite;\n}\n\n@keyframes shimmer {\n  to { background-position: 200% center; }\n}\n\n.accent-bar {\n  width: 40px;\n  height: 3px;\n  background: #F53501;\n  margin: 8px auto 0;\n}\n\n.tech-metadata {\n  position: absolute;\n  bottom: 60px;\n  display: flex;\n  align-items: center;\n  gap: 20px;\n  opacity: 0;\n  transition: opacity 1s ease;\n}\n.tech-metadata[data-step-active=\"true\"] {\n  opacity: 0.6;\n}\n\n.meta-item {\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n}\n\n.meta-item .label {\n  font-family: var(--sv-font-mono);\n  font-size: 10px;\n  color: #F53501;\n  letter-spacing: 2px;\n}\n\n.meta-item .value {\n  font-family: var(--sv-font-mono);\n  font-size: 14px;\n  font-weight: bold;\n}\n\n.meta-line {\n  width: 1px;\n  height: 30px;\n  background: rgba(255, 255, 255, 0.2);\n}","js":"window.SceneLifecycle.register('scene-01', {\n  enter() {\n    const sceneRoot = document.querySelector('[data-scene-id=\"scene-01\"]');\n    if (sceneRoot) {\n      window.__SVGVIDEO_REFRESH_ICONS(sceneRoot);\n    }\n  },\n  step(stepIndex) {\n    const sceneRoot = document.querySelector('[data-scene-id=\"scene-01\"]');\n    if (!sceneRoot) return;\n\n    const elements = sceneRoot.querySelectorAll('[data-step]');\n    elements.forEach(el => {\n      const stepValue = parseInt(el.getAttribute('data-step'));\n      el.setAttribute('data-step-active', stepIndex >= stepValue ? 'true' : 'false');\n    });\n  },\n  leave() {}\n});"},{"id":"scene-02","title":"1999：企鹅诞生","steps":3,"html":"<div class=\"s-scene-02-wrapper\">\n  <div class=\"tech-grid\"></div>\n  \n  <!-- Step 0: Retro Window -->\n  <div class=\"retro-window\" data-step=\"0\">\n    <div class=\"window-header\">\n      <div class=\"header-title\">OICQ_SETUP.EXE</div>\n      <div class=\"header-controls\">\n        <span class=\"win-btn\">_</span>\n        <span class=\"win-btn\">□</span>\n        <span class=\"win-btn close\">×</span>\n      </div>\n    </div>\n    <div class=\"window-body\">\n      <div class=\"terminal-cursor\"></div>\n      <div class=\"terminal-text\" id=\"terminal-typing-area\"></div>\n      \n      <!-- Step 2: Penguin Hero -->\n      <div class=\"hero-penguin-container\" data-step=\"2\">\n        <div class=\"penguin-glow\"></div>\n        <iconify-icon icon=\"simple-icons:tencentqq\" class=\"penguin-icon\"></iconify-icon>\n        <div class=\"scarf-accent\"></div>\n      </div>\n    </div>\n    <div class=\"window-status\">\n      <span>Status: Initializing...</span>\n      <span class=\"blink-dot\">●</span>\n    </div>\n  </div>\n\n  <!-- Step 2: Bursting Bubbles Area -->\n  <div class=\"bubble-burst-layer\" data-step=\"2\">\n    <!-- Generated by JS -->\n  </div>\n\n  <!-- Scene Metadata -->\n  <div class=\"scene-tag\" data-step=\"0\">\n    <span class=\"tag-year\">1999</span>\n    <div class=\"tag-line\"></div>\n    <span class=\"tag-event\">LAUNCH EVENT</span>\n  </div>\n</div>","css":".s-scene-02-wrapper {\n  position: relative;\n  width: 100%;\n  height: 100%;\n  background-color: #1B1B1B;\n  overflow: hidden;\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  justify-content: center;\n  font-family: var(--sv-font-body-cn);\n}\n\n.tech-grid {\n  position: absolute;\n  inset: 0;\n  background-image: linear-gradient(rgba(255, 255, 255, 0.03) 1px, transparent 1px), linear-gradient(90deg, rgba(255, 255, 255, 0.03) 1px, transparent 1px);\n  background-size: 40px 40px;\n  z-index: 1;\n}\n\n.retro-window {\n  position: relative;\n  z-index: 10;\n  width: 320px;\n  height: 480px;\n  background: #C0C0C0;\n  border: 3px solid #FFFFFF;\n  border-right-color: #808080;\n  border-bottom-color: #808080;\n  box-shadow: 10px 10px 0px rgba(0, 0, 0, 0.3);\n  display: flex;\n  flex-direction: column;\n  opacity: 0;\n  transform: translateY(20px);\n  transition: all 0.6s cubic-bezier(0.23, 1, 0.32, 1);\n}\n.retro-window[data-step-active=\"true\"] {\n  opacity: 1;\n  transform: translateY(0);\n}\n\n.window-header {\n  background: linear-gradient(90deg, #000080, #1084D0);\n  padding: 4px 8px;\n  display: flex;\n  justify-content: space-between;\n  align-items: center;\n  color: #FFF;\n  font-family: var(--sv-font-mono);\n  font-size: 12px;\n  font-weight: bold;\n}\n\n.win-btn {\n  width: 16px;\n  height: 14px;\n  background: #C0C0C0;\n  border: 1px solid #FFF;\n  border-right-color: #000;\n  border-bottom-color: #000;\n  color: #000;\n  font-size: 10px;\n  display: inline-flex;\n  align-items: center;\n  justify-content: center;\n  margin-left: 2px;\n}\n\n.window-body {\n  flex-grow: 1;\n  background: #000;\n  margin: 4px;\n  padding: 15px;\n  position: relative;\n  overflow: hidden;\n  color: #FFF;\n  font-family: var(--sv-font-mono);\n  border: 2px inset #808080;\n}\n\n.terminal-text {\n  font-size: 14px;\n  line-height: 1.6;\n  white-space: pre-wrap;\n  color: #F53501;\n}\n\n.hero-penguin-container {\n  position: absolute;\n  inset: 0;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  opacity: 0;\n  transform: scale(0.5);\n  transition: all 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275);\n}\n.hero-penguin-container[data-step-active=\"true\"] {\n  opacity: 1;\n  transform: scale(1);\n}\n\n.penguin-icon {\n  font-size: 120px;\n  color: #FFF;\n  z-index: 5;\n}\n\n.scarf-accent {\n  position: absolute;\n  width: 60px;\n  height: 10px;\n  background: #F53501;\n  top: 60%;\n  left: 50%;\n  transform: translateX(-50%);\n  border-radius: 5px;\n  box-shadow: 0 0 15px #F53501;\n  z-index: 6;\n}\n\n.penguin-glow {\n  position: absolute;\n  width: 140px;\n  height: 140px;\n  background: #F53501;\n  filter: blur(40px);\n  opacity: 0.4;\n  border-radius: 50%;\n}\n\n.bubble-burst-layer {\n  position: absolute;\n  inset: 0;\n  pointer-events: none;\n  z-index: 20;\n}\n\n.chat-bubble {\n  position: absolute;\n  padding: 8px 12px;\n  background: #FFF;\n  border: 1px solid #F53501;\n  border-radius: 12px;\n  font-size: 12px;\n  color: #1B1B1B;\n  opacity: 0;\n  animation: bubble-float 2s ease-out forwards;\n}\n\n@keyframes bubble-float {\n  0% { transform: translate(0, 0) scale(0); opacity: 0; }\n  20% { opacity: 1; }\n  100% { transform: translate(var(--tx), var(--ty)) scale(1); opacity: 0; }\n}\n\n.window-status {\n  padding: 4px 8px;\n  font-size: 10px;\n  color: #000;\n  background: #C0C0C0;\n  border-top: 1px solid #808080;\n  display: flex;\n  justify-content: space-between;\n}\n\n.blink-dot { color: #F53501; animation: blink 1s infinite; }\n@keyframes blink { 50% { opacity: 0; } }\n\n.scene-tag {\n  position: absolute;\n  top: 10%;\n  left: 40px;\n  display: flex;\n  flex-direction: column;\n  align-items: flex-start;\n  opacity: 0;\n  transition: all 0.8s ease;\n}\n.scene-tag[data-step-active=\"true\"] { opacity: 1; }\n\n.tag-year {\n  font-family: var(--sv-font-display-cn);\n  font-size: 42px;\n  color: #F53501;\n  line-height: 1;\n}\n\n.tag-line {\n  width: 100px;\n  height: 2px;\n  background: #F53501;\n  margin: 10px 0;\n}\n\n.tag-event {\n  font-family: var(--sv-font-mono);\n  font-size: 12px;\n  color: #FFF;\n  letter-spacing: 4px;\n}","js":"window.SceneLifecycle.register('scene-02', {\n  enter() {\n    const sceneRoot = document.querySelector('[data-scene-id=\"scene-02\"]');\n    if (sceneRoot) {\n      window.__SVGVIDEO_REFRESH_ICONS(sceneRoot);\n    }\n  },\n  step(stepIndex) {\n    const sceneRoot = document.querySelector('[data-scene-id=\"scene-02\"]');\n    if (!sceneRoot) return;\n\n    const elements = sceneRoot.querySelectorAll('[data-step]');\n    elements.forEach(el => {\n      const stepValue = parseInt(el.getAttribute('data-step'));\n      el.setAttribute('data-step-active', stepIndex >= stepValue ? 'true' : 'false');\n    });\n\n    // Handle Typing Effect in Step 1\n    if (stepIndex === 1) {\n      const typingArea = sceneRoot.querySelector('#terminal-typing-area');\n      if (typingArea && !typingArea.getAttribute('data-started')) {\n        typingArea.setAttribute('data-started', 'true');\n        const text = 'Connecting...\\nRemote server found.\\nProtocol: OICQ v1.0\\nStatus: ONLINE';\n        let i = 0;\n        const interval = setInterval(() => {\n          typingArea.textContent = text.slice(0, i) + (i % 2 === 0 ? '_' : ' ');\n          i++;\n          if (i > text.length) {\n            clearInterval(interval);\n            typingArea.textContent = text;\n          }\n        }, 50);\n      }\n    }\n\n    // Handle Bubble Burst in Step 2\n    if (stepIndex === 2) {\n      const layer = sceneRoot.querySelector('.bubble-burst-layer');\n      if (layer && layer.children.length === 0) {\n        const msgs = ['你好', 'OICQ', '1999', 'Hello', 'Ping', 'Online', 'Connected'];\n        for (let i = 0; i < 20; i++) {\n          const b = document.createElement('div');\n          b.className = 'chat-bubble';\n          b.textContent = msgs[Math.floor(Math.random() * msgs.length)];\n          const angle = Math.random() * Math.PI * 2;\n          const dist = 150 + Math.random() * 200;\n          b.style.setProperty('--tx', `${Math.cos(angle) * dist}px`);\n          b.style.setProperty('--ty', `${Math.sin(angle) * dist}px`);\n          b.style.left = '50%';\n          b.style.top = '50%';\n          b.style.animationDelay = `${Math.random() * 0.5}s`;\n          layer.appendChild(b);\n        }\n      }\n    }\n  },\n  leave() {}\n});"},{"id":"scene-03","title":"2004：香港上市","steps":3,"html":"<div class=\"s-scene-03-wrapper\">\n  <div class=\"tech-grid\"></div>\n  \n  <!-- Step 0: Background Growth Chart -->\n  <div class=\"chart-container\" data-step=\"0\">\n    <svg viewBox=\"0 0 400 800\" class=\"growth-svg\">\n      <defs>\n        <linearGradient id=\"lineGrad\" x1=\"0%\" y1=\"0%\" x2=\"0%\" y2=\"100%\">\n          <stop offset=\"0%\" stop-color=\"#F53501\" stop-opacity=\"1\" />\n          <stop offset=\"100%\" stop-color=\"#F53501\" stop-opacity=\"0.1\" />\n        </linearGradient>\n      </defs>\n      <path class=\"chart-path\" d=\"M0,600 L100,550 L200,580 L300,450 L400,300\" fill=\"none\" stroke=\"#F53501\" stroke-width=\"4\" />\n      <path class=\"chart-fill\" d=\"M0,600 L100,550 L200,580 L300,450 L400,300 L400,800 L0,800 Z\" fill=\"url(#lineGrad)\" opacity=\"0.1\" />\n    </svg>\n    <div class=\"stat-label\">IPO GROWTH</div>\n  </div>\n\n  <!-- Step 1: Golden Coin -->\n  <div class=\"coin-hero\" data-step=\"1\">\n    <div class=\"coin-3d\">\n      <div class=\"coin-face\">\n        <div class=\"coin-inner\">\n          <span class=\"stock-symbol\">HK.0700</span>\n          <div class=\"coin-reflection\"></div>\n        </div>\n      </div>\n    </div>\n    <h1 class=\"main-title\">2004 香港上市</h1>\n  </div>\n\n  <!-- Step 2: Grid Cards -->\n  <div class=\"business-grid\" data-step=\"2\">\n    <div class=\"biz-card\" style=\"--i:0\">\n      <iconify-icon icon=\"lucide:layout-template\" class=\"biz-icon\"></iconify-icon>\n      <span class=\"biz-name\">门户</span>\n    </div>\n    <div class=\"biz-card\" style=\"--i:1\">\n      <iconify-icon icon=\"lucide:gamepad-2\" class=\"biz-icon\"></iconify-icon>\n      <span class=\"biz-name\">游戏</span>\n    </div>\n    <div class=\"biz-card\" style=\"--i:2\">\n      <iconify-icon icon=\"lucide:layout-grid\" class=\"biz-icon\"></iconify-icon>\n      <span class=\"biz-name\">空间</span>\n    </div>\n  </div>\n\n  <div class=\"marker-line top\"></div>\n  <div class=\"marker-line bottom\"></div>\n</div>","css":".s-scene-03-wrapper {\n  position: relative;\n  width: 100%;\n  height: 100%;\n  background-color: #1B1B1B;\n  overflow: hidden;\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  justify-content: center;\n  font-family: var(--sv-font-body-cn);\n}\n\n.tech-grid {\n  position: absolute;\n  inset: 0;\n  background-image: linear-gradient(rgba(255, 255, 255, 0.05) 1px, transparent 1px), linear-gradient(90deg, rgba(255, 255, 255, 0.05) 1px, transparent 1px);\n  background-size: 30px 30px;\n  z-index: 1;\n}\n\n/* Step 0: Chart Animation */\n.chart-container {\n  position: absolute;\n  bottom: 0;\n  width: 100%;\n  height: 50%;\n  z-index: 2;\n  opacity: 0;\n  transition: opacity 1s ease;\n}\n.chart-container[data-step-active=\"true\"] {\n  opacity: 1;\n}\n.chart-path {\n  stroke-dasharray: 1000;\n  stroke-dashoffset: 1000;\n}\n.chart-container[data-step-active=\"true\"] .chart-path {\n  animation: chart-draw 2.5s cubic-bezier(0.4, 0, 0.2, 1) forwards;\n}\n@keyframes chart-draw {\n  to { stroke-dashoffset: 0; }\n}\n\n.stat-label {\n  position: absolute;\n  left: 20px;\n  top: 20px;\n  color: #F53501;\n  font-family: var(--sv-font-mono);\n  font-size: 12px;\n  letter-spacing: 2px;\n  border-left: 2px solid #F53501;\n  padding-left: 8px;\n}\n\n/* Step 1: Coin Hero */\n.coin-hero {\n  position: relative;\n  z-index: 10;\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  opacity: 0;\n  transform: translateY(-50px) scale(0.8);\n  transition: all 0.8s cubic-bezier(0.34, 1.56, 0.64, 1);\n}\n.coin-hero[data-step-active=\"true\"] {\n  opacity: 1;\n  transform: translateY(0) scale(1);\n}\n\n.coin-3d {\n  width: 180px;\n  height: 180px;\n  perspective: 1000px;\n  margin-bottom: 24px;\n}\n\n.coin-face {\n  width: 100%;\n  height: 100%;\n  background: linear-gradient(135deg, #FFD700 0%, #B8860B 50%, #FFD700 100%);\n  border-radius: 50%;\n  padding: 10px;\n  box-shadow: 0 20px 40px rgba(0,0,0,0.5), inset 0 0 20px rgba(255,255,255,0.5);\n  animation: coin-spin 6s infinite linear;\n}\n\n.coin-inner {\n  width: 100%;\n  height: 100%;\n  border: 4px solid rgba(0,0,0,0.1);\n  border-radius: 50%;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  position: relative;\n  overflow: hidden;\n}\n\n.stock-symbol {\n  font-family: var(--sv-font-display-cn);\n  font-size: 24px;\n  color: #5C4033;\n  font-weight: 900;\n  text-shadow: 1px 1px 0px rgba(255,255,255,0.3);\n}\n\n.coin-reflection {\n  position: absolute;\n  top: -50%;\n  left: -50%;\n  width: 200%;\n  height: 200%;\n  background: linear-gradient(45deg, transparent 45%, rgba(255,255,255,0.6) 50%, transparent 55%);\n  animation: reflect 3s infinite ease-in-out;\n}\n\n@keyframes reflect { 0% { transform: translate(-20%, -20%); } 100% { transform: translate(20%, 20%); } }\n@keyframes coin-spin { 0% { transform: rotateY(0deg); } 100% { transform: rotateY(360deg); } }\n\n.main-title {\n  font-family: var(--sv-font-display-cn);\n  font-size: 32px;\n  color: #FFFFFF;\n  text-transform: uppercase;\n  letter-spacing: 2px;\n}\n\n/* Step 2: Biz Grid */\n.business-grid {\n  position: absolute;\n  bottom: 12%;\n  width: 90%;\n  display: grid;\n  grid-template-columns: repeat(3, 1fr);\n  gap: 12px;\n  z-index: 15;\n}\n\n.biz-card {\n  background: rgba(255, 255, 255, 0.03);\n  border: 1px solid rgba(255, 255, 255, 0.1);\n  padding: 16px 8px;\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  gap: 8px;\n  opacity: 0;\n  transform: translateY(20px);\n  transition: all 0.5s ease;\n  transition-delay: calc(var(--i) * 0.15s);\n}\n\n.business-grid[data-step-active=\"true\"] .biz-card {\n  opacity: 1;\n  transform: translateY(0);\n  border-color: rgba(245, 53, 1, 0.3);\n}\n\n.biz-icon {\n  font-size: 28px;\n  color: #F53501;\n}\n\n.biz-name {\n  font-family: var(--sv-font-display-cn);\n  font-size: 16px;\n  color: #FFFFFF;\n}\n\n.marker-line {\n  position: absolute;\n  left: 5%;\n  width: 90%;\n  height: 1px;\n  background: rgba(245, 53, 1, 0.2);\n  z-index: 5;\n}\n.marker-line.top { top: 20%; }\n.marker-line.bottom { bottom: 20%; }","js":"window.SceneLifecycle.register('scene-03', {\n  enter() {\n    const sceneRoot = document.querySelector('[data-scene-id=\"scene-03\"]');\n    if (sceneRoot) {\n      window.__SVGVIDEO_REFRESH_ICONS(sceneRoot);\n    }\n  },\n  step(stepIndex) {\n    const sceneRoot = document.querySelector('[data-scene-id=\"scene-03\"]');\n    if (!sceneRoot) return;\n\n    const elements = sceneRoot.querySelectorAll('[data-step]');\n    elements.forEach(el => {\n      const stepValue = parseInt(el.getAttribute('data-step'));\n      el.setAttribute('data-step-active', stepIndex >= stepValue ? 'true' : 'false');\n    });\n\n    // Visual impact when coin lands at step 1\n    if (stepIndex === 1) {\n      const coin = sceneRoot.querySelector('.coin-hero');\n      if (coin) {\n        coin.animate([\n          { transform: 'scale(1.2) translateY(-20px)' },\n          { transform: 'scale(1) translateY(0)' }\n        ], { duration: 500, easing: 'cubic-bezier(0.175, 0.885, 0.32, 1.275)' });\n      }\n    }\n  },\n  leave() {}\n});"},{"id":"scene-04","title":"2011：微信时代","steps":3,"html":"<div class=\"s-scene-04-wrapper\">\n  <div class=\"tech-grid\"></div>\n  \n  <!-- Background Green Wash -->\n  <div class=\"wechat-wash\" data-step=\"0\"></div>\n\n  <!-- Step 0: Central Logo & Year -->\n  <div class=\"hero-container\" data-step=\"0\">\n    <div class=\"year-tag\">2011</div>\n    <div class=\"logo-box\">\n      <iconify-icon icon=\"simple-icons:wechat\" class=\"main-logo\"></iconify-icon>\n      <div class=\"logo-glow\"></div>\n    </div>\n    <h1 class=\"main-title\">微信诞生</h1>\n  </div>\n\n  <!-- Step 1: Connection Ripples -->\n  <div class=\"ripple-system\" data-step=\"1\">\n    <div class=\"ripple-circle c1\"></div>\n    <div class=\"ripple-circle c2\"></div>\n    <div class=\"ripple-circle c3\"></div>\n    <div class=\"connection-text\">连接一切</div>\n  </div>\n\n  <!-- Step 2: Ecosystem Icons -->\n  <div class=\"ecosystem-radial\" data-step=\"2\">\n    <div class=\"eco-item p-pay\" style=\"--angle: -120deg\">\n      <div class=\"eco-icon-wrapper\">\n        <iconify-icon icon=\"ant-design:wechat-pay-outlined\"></iconify-icon>\n      </div>\n      <span class=\"eco-label\">移动支付</span>\n    </div>\n    <div class=\"eco-item p-mini\" style=\"--angle: 0deg\">\n      <div class=\"eco-icon-wrapper\">\n        <iconify-icon icon=\"lucide:component\"></iconify-icon>\n      </div>\n      <span class=\"eco-label\">小程序</span>\n    </div>\n    <div class=\"eco-item p-moments\" style=\"--angle: 120deg\">\n      <div class=\"eco-icon-wrapper\">\n        <iconify-icon icon=\"lucide:aperture\"></iconify-icon>\n      </div>\n      <span class=\"eco-label\">朋友圈</span>\n    </div>\n  </div>\n\n  <!-- Decor elements -->\n  <div class=\"focus-frame-top\"></div>\n  <div class=\"focus-frame-bottom\"></div>\n</div>","css":".s-scene-04-wrapper {\n  position: relative;\n  width: 100%;\n  height: 100%;\n  background-color: #1B1B1B;\n  overflow: hidden;\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  justify-content: center;\n  font-family: var(--sv-font-body-cn);\n}\n\n.tech-grid {\n  position: absolute;\n  inset: 0;\n  background-image: linear-gradient(rgba(255, 255, 255, 0.05) 1px, transparent 1px), linear-gradient(90deg, rgba(255, 255, 255, 0.05) 1px, transparent 1px);\n  background-size: 40px 40px;\n  z-index: 1;\n}\n\n.wechat-wash {\n  position: absolute;\n  inset: 0;\n  background: radial-gradient(circle at center, rgba(7, 193, 96, 0.2) 0%, transparent 70%);\n  opacity: 0;\n  transition: opacity 1.2s ease;\n  z-index: 2;\n}\n.wechat-wash[data-step-active=\"true\"] { opacity: 1; }\n\n.hero-container {\n  position: relative;\n  z-index: 10;\n  text-align: center;\n  opacity: 0;\n  transform: scale(0.9);\n  transition: all 0.8s cubic-bezier(0.175, 0.885, 0.32, 1.275);\n}\n.hero-container[data-step-active=\"true\"] {\n  opacity: 1;\n  transform: scale(1);\n}\n\n.year-tag {\n  font-family: var(--sv-font-mono);\n  font-size: 14px;\n  color: #F53501;\n  background: rgba(245, 53, 1, 0.1);\n  padding: 2px 10px;\n  border: 1px solid #F53501;\n  display: inline-block;\n  margin-bottom: 20px;\n  letter-spacing: 2px;\n}\n\n.logo-box {\n  position: relative;\n  width: 120px;\n  height: 120px;\n  margin: 0 auto 30px;\n}\n\n.main-logo {\n  font-size: 120px;\n  color: #07C160;\n  filter: drop-shadow(0 0 20px rgba(7, 193, 96, 0.4));\n}\n\n.logo-glow {\n  position: absolute;\n  inset: -20px;\n  background: radial-gradient(circle, rgba(7, 193, 96, 0.15) 0%, transparent 70%);\n  animation: logo-pulse 3s infinite ease-in-out;\n}\n\n.main-title {\n  font-family: var(--sv-font-display-cn);\n  font-size: 48px;\n  color: #FFFFFF;\n  margin: 0;\n  letter-spacing: 4px;\n}\n\n/* Step 1: Ripple */\n.ripple-system {\n  position: absolute;\n  top: 50%;\n  left: 50%;\n  transform: translate(-50%, -50%);\n  width: 100%;\n  height: 100%;\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  justify-content: flex-end;\n  padding-bottom: 25%;\n  z-index: 5;\n  opacity: 0;\n  transition: opacity 0.5s ease;\n}\n.ripple-system[data-step-active=\"true\"] { opacity: 1; }\n\n.ripple-circle {\n  position: absolute;\n  top: 50%;\n  left: 50%;\n  border: 1px solid rgba(7, 193, 96, 0.3);\n  border-radius: 50%;\n  transform: translate(-50%, -50%) scale(0);\n}\n.ripple-system[data-step-active=\"true\"] .ripple-circle {\n  animation: ripple-out 4s infinite linear;\n}\n.c1 { width: 300px; height: 300px; animation-delay: 0s; }\n.c2 { width: 300px; height: 300px; animation-delay: 1.3s; }\n.c3 { width: 300px; height: 300px; animation-delay: 2.6s; }\n\n.connection-text {\n  font-family: var(--sv-font-display-cn);\n  color: #07C160;\n  font-size: 20px;\n  letter-spacing: 8px;\n  text-transform: uppercase;\n  opacity: 0.8;\n}\n\n/* Step 2: Ecosystem */\n.ecosystem-radial {\n  position: absolute;\n  inset: 0;\n  z-index: 15;\n  pointer-events: none;\n}\n\n.eco-item {\n  position: absolute;\n  top: 50%;\n  left: 50%;\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  gap: 12px;\n  opacity: 0;\n  transition: all 0.8s cubic-bezier(0.34, 1.56, 0.64, 1);\n  --dist: 160px;\n}\n\n.ecosystem-radial[data-step-active=\"true\"] .eco-item {\n  opacity: 1;\n  transform: translate(calc(cos(var(--angle)) * var(--dist) - 50%), calc(sin(var(--angle)) * var(--dist) - 50%));\n}\n\n.eco-icon-wrapper {\n  width: 60px;\n  height: 60px;\n  background: rgba(255, 255, 255, 0.05);\n  border: 1px solid rgba(7, 193, 96, 0.4);\n  border-radius: 12px;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  font-size: 32px;\n  color: #07C160;\n  box-shadow: 0 10px 20px rgba(0,0,0,0.3);\n  animation: float-breathing 4s infinite ease-in-out;\n}\n\n.eco-label {\n  font-size: 14px;\n  color: #FFFFFF;\n  white-space: nowrap;\n  font-weight: 600;\n  text-shadow: 0 2px 4px rgba(0,0,0,0.5);\n}\n\n/* Decor Frames */\n.focus-frame-top, .focus-frame-bottom {\n  position: absolute;\n  left: 10%;\n  width: 80%;\n  height: 20px;\n  border: 1px solid rgba(245, 53, 1, 0.2);\n  z-index: 5;\n}\n.focus-frame-top {\n  top: 5%;\n  border-bottom: none;\n}\n.focus-frame-bottom {\n  bottom: 5%;\n  border-top: none;\n}\n\n@keyframes logo-pulse { 0%, 100% { transform: scale(1); opacity: 0.15; } 50% { transform: scale(1.1); opacity: 0.3; } }\n@keyframes ripple-out { from { transform: translate(-50%, -50%) scale(0.5); opacity: 1; } to { transform: translate(-50%, -50%) scale(2.5); opacity: 0; } }\n@keyframes float-breathing {\n  0%, 100% { transform: translateY(0); } \n  50% { transform: translateY(-10px); }\n}","js":"window.SceneLifecycle.register('scene-04', {\n  enter() {\n    const sceneRoot = document.querySelector('[data-scene-id=\"scene-04\"]');\n    if (sceneRoot) {\n      window.__SVGVIDEO_REFRESH_ICONS(sceneRoot);\n    }\n  },\n  step(stepIndex) {\n    const sceneRoot = document.querySelector('[data-scene-id=\"scene-04\"]');\n    if (!sceneRoot) return;\n\n    const elements = sceneRoot.querySelectorAll('[data-step]');\n    elements.forEach(el => {\n      const stepValue = parseInt(el.getAttribute('data-step'));\n      el.setAttribute('data-step-active', stepIndex >= stepValue ? 'true' : 'false');\n    });\n\n    // Special impact for the logo in Step 0\n    if (stepIndex === 0) {\n      const logoBox = sceneRoot.querySelector('.logo-box');\n      if (logoBox) {\n        logoBox.animate([\n          { transform: 'scale(0.5) rotate(-10deg)', opacity: 0 },\n          { transform: 'scale(1.1) rotate(5deg)', opacity: 1 },\n          { transform: 'scale(1) rotate(0deg)', opacity: 1 }\n        ], { duration: 1000, easing: 'cubic-bezier(0.34, 1.56, 0.64, 1)' });\n      }\n    }\n\n    // Visual burst effect when ripples/connection starts at Step 1\n    if (stepIndex === 1) {\n      const wash = sceneRoot.querySelector('.wechat-wash');\n      if (wash) {\n        wash.animate([\n          { background: 'radial-gradient(circle at center, rgba(7, 193, 96, 0.6) 0%, transparent 10%)' },\n          { background: 'radial-gradient(circle at center, rgba(7, 193, 96, 0.2) 0%, transparent 70%)' }\n        ], { duration: 800 });\n      }\n    }\n  },\n  leave() {}\n});"},{"id":"scene-05","title":"产业互联网：科技向善","steps":3,"html":"<div class=\"s-scene-05-wrapper\">\n  <div class=\"tech-grid\"></div>\n  \n  <!-- Step 0: Neural Network & Star Blue Wash -->\n  <div class=\"blue-neural-wash\" data-step=\"0\"></div>\n  <div class=\"neural-net-container\" data-step=\"0\">\n    <svg class=\"neural-svg\" viewBox=\"0 0 400 800\">\n      <g class=\"nodes\">\n        <circle cx=\"100\" cy=\"200\" r=\"3\" fill=\"#F53501\" />\n        <circle cx=\"300\" cy=\"250\" r=\"3\" fill=\"#F53501\" />\n        <circle cx=\"200\" cy=\"400\" r=\"4\" fill=\"#F53501\" />\n        <circle cx=\"150\" cy=\"600\" r=\"3\" fill=\"#F53501\" />\n        <circle cx=\"250\" cy=\"550\" r=\"3\" fill=\"#F53501\" />\n      </g>\n      <g class=\"links\" stroke=\"rgba(245, 53, 1, 0.3)\" stroke-width=\"1\">\n        <line x1=\"100\" y1=\"200\" x2=\"200\" y2=\"400\" />\n        <line x1=\"300\" y1=\"250\" x2=\"200\" y2=\"400\" />\n        <line x1=\"200\" y1=\"400\" x2=\"150\" y2=\"600\" />\n        <line x1=\"200\" y1=\"400\" x2=\"250\" y2=\"550\" />\n      </g>\n    </svg>\n  </div>\n\n  <!-- Step 1: Cloud Frosted Progress Bar -->\n  <div class=\"cloud-progress-container\" data-step=\"1\">\n    <div class=\"frosted-ring\">\n      <div class=\"ring-spinner\"></div>\n      <iconify-icon icon=\"lucide:cloud\" class=\"cloud-icon\"></iconify-icon>\n    </div>\n    <div class=\"status-label\">CLOUD COMPUTING</div>\n  </div>\n\n  <!-- Step 2: Hero Text & Particles -->\n  <div class=\"hero-content-area\" data-step=\"2\">\n    <div class=\"particle-emitter\" id=\"particles-js-05\"></div>\n    <h1 class=\"vision-title\">\n      <span class=\"glow-text\">科技</span><span class=\"glow-text highlight\">向善</span>\n    </h1>\n    <div class=\"tech-tag-line\">TECH FOR GOOD</div>\n  </div>\n\n  <!-- Layout Brackets (Tech Blogger Style) -->\n  <div class=\"frame-bracket tl\"></div>\n  <div class=\"frame-bracket tr\"></div>\n  <div class=\"frame-bracket bl\"></div>\n  <div class=\"frame-bracket br\"></div>\n  <div class=\"data-stream-side\">01001 / AI / CLOUD / VISION</div>\n</div>","css":".s-scene-05-wrapper {\n  position: relative;\n  width: 100%;\n  height: 100%;\n  background-color: #1B1B1B;\n  overflow: hidden;\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  justify-content: center;\n  font-family: var(--sv-font-body-cn);\n}\n\n.tech-grid {\n  position: absolute;\n  inset: 0;\n  background-image: linear-gradient(rgba(255, 255, 255, 0.03) 1px, transparent 1px), linear-gradient(90deg, rgba(255, 255, 255, 0.03) 1px, transparent 1px);\n  background-size: 30px 30px;\n  z-index: 1;\n}\n\n.blue-neural-wash {\n  position: absolute;\n  inset: 0;\n  background: radial-gradient(circle at 50% 50%, rgba(10, 17, 40, 0.8) 0%, transparent 100%);\n  opacity: 0;\n  transition: opacity 1.5s ease;\n  z-index: 2;\n}\n.blue-neural-wash[data-step-active=\"true\"] { opacity: 1; }\n\n.neural-net-container {\n  position: absolute;\n  inset: 0;\n  z-index: 3;\n  opacity: 0;\n  transition: opacity 1s ease;\n}\n.neural-net-container[data-step-active=\"true\"] { opacity: 0.6; }\n\n.neural-svg {\n  width: 100%;\n  height: 100%;\n  filter: drop-shadow(0 0 5px rgba(245, 53, 1, 0.4));\n}\n\n/* Step 1: Cloud Ring */\n.cloud-progress-container {\n  position: absolute;\n  top: 15%;\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  gap: 15px;\n  opacity: 0;\n  transform: translateY(-50px);\n  transition: all 0.8s cubic-bezier(0.23, 1, 0.32, 1);\n  z-index: 10;\n}\n.cloud-progress-container[data-step-active=\"true\"] {\n  opacity: 1;\n  transform: translateY(0);\n}\n\n.frosted-ring {\n  position: relative;\n  width: 100px;\n  height: 100px;\n  background: rgba(255, 255, 255, 0.05);\n  backdrop-filter: blur(8px);\n  -webkit-backdrop-filter: blur(8px);\n  border-radius: 50%;\n  border: 1px solid rgba(255, 255, 255, 0.1);\n  display: flex;\n  align-items: center;\n  justify-content: center;\n}\n\n.ring-spinner {\n  position: absolute;\n  inset: -5px;\n  border: 2px solid transparent;\n  border-top: 2px solid #F53501;\n  border-radius: 50%;\n  animation: spin 2s linear infinite;\n}\n\n.cloud-icon {\n  font-size: 40px;\n  color: #FFFFFF;\n  filter: drop-shadow(0 0 10px rgba(255, 255, 255, 0.3));\n}\n\n.status-label {\n  font-family: var(--sv-font-mono);\n  color: #F53501;\n  font-size: 10px;\n  letter-spacing: 2px;\n  font-weight: bold;\n}\n\n/* Step 2: Hero Text */\n.hero-content-area {\n  position: relative;\n  z-index: 20;\n  text-align: center;\n  opacity: 0;\n  transform: scale(0.9);\n  transition: all 1s cubic-bezier(0.34, 1.56, 0.64, 1);\n}\n.hero-content-area[data-step-active=\"true\"] {\n  opacity: 1;\n  transform: scale(1);\n}\n\n.vision-title {\n  font-family: var(--sv-font-display-cn);\n  font-size: 72px;\n  color: #FFFFFF;\n  margin: 0;\n  line-height: 1.2;\n  display: flex;\n  flex-direction: column;\n}\n\n.glow-text {\n  text-shadow: 0 0 20px rgba(255, 255, 255, 0.2);\n}\n\n.glow-text.highlight {\n  color: #F53501;\n  text-shadow: 0 0 30px rgba(245, 53, 1, 0.6);\n  border: 2px solid #F53501;\n  padding: 0 10px;\n  margin-top: 10px;\n}\n\n.tech-tag-line {\n  font-family: var(--sv-font-mono);\n  color: rgba(255, 255, 255, 0.4);\n  font-size: 14px;\n  letter-spacing: 6px;\n  margin-top: 20px;\n}\n\n.particle-emitter {\n  position: absolute;\n  inset: -100px;\n  pointer-events: none;\n}\n\n/* Decor elements */\n.frame-bracket {\n  position: absolute;\n  width: 20px;\n  height: 20px;\n  border: 2px solid #F53501;\n  z-index: 50;\n}\n.tl { top: 40px; left: 40px; border-right: none; border-bottom: none; }\n.tr { top: 40px; right: 40px; border-left: none; border-bottom: none; }\n.bl { bottom: 40px; left: 40px; border-right: none; border-top: none; }\n.br { bottom: 40px; right: 40px; border-left: none; border-top: none; }\n\n.data-stream-side {\n  position: absolute;\n  right: 10px;\n  top: 50%;\n  transform: rotate(90deg) translate(-50%, 0);\n  font-family: var(--sv-font-mono);\n  font-size: 10px;\n  color: rgba(245, 53, 1, 0.5);\n  letter-spacing: 2px;\n}\n\n@keyframes spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }\n\n.particle {\n  position: absolute;\n  background: #F53501;\n  width: 2px;\n  height: 2px;\n  border-radius: 50%;\n  animation: float-up var(--d) linear infinite;\n  opacity: 0;\n}\n\n@keyframes float-up {\n  0% { transform: translateY(0); opacity: 0; }\n  20% { opacity: 0.8; }\n  100% { transform: translateY(-150px); opacity: 0; }\n}","js":"window.SceneLifecycle.register('scene-05', {\n  enter() {\n    const root = document.querySelector('[data-scene-id=\"scene-05\"]');\n    if (root) {\n      window.__SVGVIDEO_REFRESH_ICONS(root);\n      this.createParticles(root);\n    }\n  },\n  step(index) {\n    const root = document.querySelector('[data-scene-id=\"scene-05\"]');\n    if (!root) return;\n\n    const elements = root.querySelectorAll('[data-step]');\n    elements.forEach(el => {\n      const stepValue = parseInt(el.getAttribute('data-step'));\n      el.setAttribute('data-step-active', index >= stepValue ? 'true' : 'false');\n    });\n\n    if (index === 0) {\n      const svgLines = root.querySelectorAll('.links line');\n      svgLines.forEach((line, i) => {\n        line.animate([\n          { strokeDasharray: '0 500', strokeDashoffset: '0' },\n          { strokeDasharray: '500 500', strokeDashoffset: '0' }\n        ], { duration: 1500, delay: i * 200, fill: 'forwards' });\n      });\n    }\n\n    if (index === 2) {\n      const title = root.querySelector('.vision-title');\n      title.animate([\n        { filter: 'brightness(1) blur(0px)' },\n        { filter: 'brightness(2) blur(2px)' },\n        { filter: 'brightness(1) blur(0px)' }\n      ], { duration: 1000, easing: 'ease-out' });\n    }\n  },\n  createParticles(root) {\n    const container = root.querySelector('#particles-js-05');\n    if (!container) return;\n    container.innerHTML = '';\n    for (let i = 0; i < 20; i++) {\n      const p = document.createElement('div');\n      p.className = 'particle';\n      p.style.left = Math.random() * 100 + '%';\n      p.style.bottom = '0px';\n      p.style.setProperty('--d', (2 + Math.random() * 3) + 's');\n      p.style.animationDelay = Math.random() * 2 + 's';\n      container.appendChild(p);\n    }\n  },\n  leave() {}\n});"},{"id":"scene-06","title":"总结：连接未来","steps":2,"html":"<div class=\"s-scene-06-wrapper\">\n  <!-- Background Layers -->\n  <div class=\"bg-base\"></div>\n  <div class=\"bg-gradient-overlay\" data-step=\"0\"></div>\n  <div class=\"tech-grid-closing\"></div>\n\n  <!-- Step 1: Hero Slogan -->\n  <div class=\"slogan-container\" data-step=\"1\">\n    <div class=\"slogan-line\">\n      <span class=\"slogan-text\">用户为本</span>\n    </div>\n    <div class=\"slogan-divider\"></div>\n    <div class=\"slogan-line\">\n      <span class=\"slogan-text highlight\">科技向善</span>\n    </div>\n  </div>\n\n  <!-- Step 2: Anniversary Logo & Closing -->\n  <div class=\"anniversary-footer\" data-step=\"2\">\n    <div class=\"logo-wrap\">\n      <iconify-icon icon=\"simple-icons:tencent\" class=\"tencent-logo\"></iconify-icon>\n      <div class=\"logo-divider\"></div>\n      <div class=\"anniversary-badge\">\n        <span class=\"num\">25</span>\n        <span class=\"text\">ANNIVERSARY</span>\n      </div>\n    </div>\n    <div class=\"brand-subtext\">VALUE FOR USERS, TECH FOR GOOD</div>\n  </div>\n\n  <!-- Style Brackets (Persistence) -->\n  <div class=\"frame-bracket tl\"></div>\n  <div class=\"frame-bracket tr\"></div>\n  <div class=\"frame-bracket bl\"></div>\n  <div class=\"frame-bracket br\"></div>\n\n  <!-- Scanning Line -->\n  <div class=\"scan-line\"></div>\n</div>","css":".s-scene-06-wrapper {\n  position: relative;\n  width: 100%;\n  height: 100%;\n  background-color: #1B1B1B;\n  overflow: hidden;\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  justify-content: center;\n  transition: transform 1.2s cubic-bezier(0.7, 0, 0.3, 1), opacity 1.2s ease;\n}\n\n.s-scene-06-wrapper[data-closing=\"true\"] {\n  transform: scale(0.9);\n  opacity: 0;\n}\n\n.bg-base {\n  position: absolute;\n  inset: 0;\n  background: #1B1B1B;\n  z-index: 1;\n}\n\n.bg-gradient-overlay {\n  position: absolute;\n  inset: 0;\n  background: radial-gradient(circle at 50% 50%, #FFFFFF 0%, #E6F0FF 40%, #B8D4FF 100%);\n  opacity: 0;\n  transition: opacity 1.5s cubic-bezier(0.4, 0, 0.2, 1);\n  z-index: 2;\n}\n\n.bg-gradient-overlay[data-step-active=\"true\"] {\n  opacity: 1;\n}\n\n.tech-grid-closing {\n  position: absolute;\n  inset: 0;\n  background-image: linear-gradient(rgba(245, 53, 1, 0.05) 1px, transparent 1px), linear-gradient(90deg, rgba(245, 53, 1, 0.05) 1px, transparent 1px);\n  background-size: 40px 40px;\n  z-index: 3;\n  opacity: 0.3;\n}\n\n.slogan-container {\n  position: relative;\n  z-index: 10;\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  text-align: center;\n  opacity: 0;\n  transform: translateY(30px);\n  transition: all 1s cubic-bezier(0.23, 1, 0.32, 1);\n}\n\n.slogan-container[data-step-active=\"true\"] {\n  opacity: 1;\n  transform: translateY(0);\n}\n\n.slogan-line {\n  overflow: hidden;\n}\n\n.slogan-text {\n  display: block;\n  font-family: var(--sv-font-display-cn);\n  font-size: 48px;\n  color: #1B1B1B;\n  letter-spacing: 4px;\n  line-height: 1.2;\n}\n\n.slogan-text.highlight {\n  color: #F53501;\n}\n\n.slogan-divider {\n  width: 40px;\n  height: 4px;\n  background: #F53501;\n  margin: 20px 0;\n}\n\n.anniversary-footer {\n  position: absolute;\n  bottom: 12%;\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  z-index: 10;\n  opacity: 0;\n  transform: translateY(20px);\n  transition: all 0.8s ease-out;\n}\n\n.anniversary-footer[data-step-active=\"true\"] {\n  opacity: 1;\n  transform: translateY(0);\n}\n\n.logo-wrap {\n  display: flex;\n  align-items: center;\n  gap: 15px;\n  margin-bottom: 10px;\n}\n\n.tencent-logo {\n  font-size: 32px;\n  color: #0052D9;\n}\n\n.logo-divider {\n  width: 1px;\n  height: 20px;\n  background: rgba(0, 0, 0, 0.2);\n}\n\n.anniversary-badge {\n  display: flex;\n  flex-direction: column;\n  align-items: flex-start;\n}\n\n.anniversary-badge .num {\n  font-family: var(--sv-font-mono);\n  font-weight: 900;\n  font-size: 24px;\n  line-height: 1;\n  color: #1B1B1B;\n}\n\n.anniversary-badge .text {\n  font-family: var(--sv-font-mono);\n  font-size: 8px;\n  letter-spacing: 1px;\n  color: #666;\n}\n\n.brand-subtext {\n  font-family: var(--sv-font-mono);\n  font-size: 10px;\n  color: rgba(0, 0, 0, 0.4);\n  letter-spacing: 2px;\n}\n\n/* Tech Decors */\n.frame-bracket {\n  position: absolute;\n  width: 15px;\n  height: 15px;\n  border: 1.5px solid #F53501;\n  z-index: 20;\n  opacity: 0.5;\n}\n.tl { top: 40px; left: 40px; border-right: none; border-bottom: none; }\n.tr { top: 40px; right: 40px; border-left: none; border-bottom: none; }\n.bl { bottom: 40px; left: 40px; border-right: none; border-top: none; }\n.br { bottom: 40px; right: 40px; border-left: none; border-top: none; }\n\n.scan-line {\n  position: absolute;\n  top: 0;\n  left: 0;\n  width: 100%;\n  height: 2px;\n  background: linear-gradient(90deg, transparent, #F53501, transparent);\n  z-index: 5;\n  opacity: 0.1;\n  animation: scan 4s linear infinite;\n}\n\n@keyframes scan { 0% { top: 0; } 100% { top: 100%; } }","js":"window.SceneLifecycle.register('scene-06', {\n  enter() {\n    const root = document.querySelector('[data-scene-id=\"scene-06\"]');\n    if (root) {\n      window.__SVGVIDEO_REFRESH_ICONS(root);\n      root.removeAttribute('data-closing');\n    }\n  },\n  step(index) {\n    const root = document.querySelector('[data-scene-id=\"scene-06\"]');\n    if (!root) return;\n\n    const elements = root.querySelectorAll('[data-step]');\n    elements.forEach(el => {\n      const stepValue = parseInt(el.getAttribute('data-step'));\n      el.setAttribute('data-step-active', index >= stepValue ? 'true' : 'false');\n    });\n\n    // Handle Step 2 special closing animation\n    if (index === 2) {\n      setTimeout(() => {\n        // After showing the logo for a while, trigger the shrink fade-out\n        // This mimics the '向内收缩淡出' requirement\n        // In a real flow, this might be triggered by the timeline manager\n        // root.setAttribute('data-closing', 'true');\n      }, 2000);\n    }\n\n    // Visual reinforcement for Step 0 color shift\n    if (index === 0) {\n      const brackets = root.querySelectorAll('.frame-bracket');\n      brackets.forEach(b => b.style.borderColor = '#0052D9'); // Shift theme to brand blue\n    }\n  },\n  leave() {\n    const root = document.querySelector('[data-scene-id=\"scene-06\"]');\n    if (root) root.setAttribute('data-closing', 'true');\n  }\n});"}]</script>
  <script defer src="https://unpkg.com/lucide@latest"></script>
  <script defer src="https://code.iconify.design/iconify-icon/2.3.0/iconify-icon.min.js"></script>
  <script>
    (function () {
      var state = { sceneId: 'scene-02', step: 0, direction: 1 };
      var previousSceneId = null;
      var exitCleanupTimer = null;
      var isPlaying = false;
      var playTimer = null;
      var progressRaf = null;
      var STEP_INTERVAL_MS = 1400;
      var SCENE_FINISH_PAUSE_MS = 2000;
      var autoplayFromFirstScene = false;
      var progressWrap = document.getElementById('sv-progress-wrap');
      var progressFill = document.getElementById('sv-progress-fill');
      var progressThumb = document.getElementById('sv-progress-thumb');
      var playBtn = document.getElementById('sv-play-btn');
      var expandBtn = document.getElementById('sv-expand-btn');
      var statusText = document.getElementById('sv-status-text');
      var sceneButtonList = Array.prototype.slice.call(document.querySelectorAll('[data-panel-scene-id]'));
      var sceneRetryButtonList = Array.prototype.slice.call(document.querySelectorAll('[data-retry-scene-id]'));
      var editorOverlay = document.getElementById('sv-editor-overlay');
      var editorBackdrop = document.getElementById('sv-editor-backdrop');
      var editorSheet = document.getElementById('sv-editor-sheet');
      var editorResizeHandle = document.getElementById('sv-editor-resize-handle');
      var editorCloseBtn = document.getElementById('sv-editor-close-btn');
      var editorSaveBtn = document.getElementById('sv-editor-save-btn');
      var editorList = document.getElementById('sv-editor-list');
      var editorSaveTip = document.getElementById('sv-editor-save-tip');
      var editorTipTimer = null;
      var isEditorOpen = false;
      var isEditorDirty = false;
      var editorHeightVh = 50;
      var hostCaps = { canRetry: false, canEditPanel: false, locale: 'zh', theme: 'dark' };
      var metaJson = document.getElementById('__SVGVIDEO_META__');
      var meta = { scenes: [] };
      try {
        meta = JSON.parse((metaJson && metaJson.textContent) || '{}');
      } catch (e) {
        meta = { scenes: [] };
      }
      var metaSceneMap = {};
      var metaScenes = Array.isArray(meta.scenes) ? meta.scenes : [];
      metaScenes.forEach(function (scene) {
        if (scene && typeof scene.id === 'string' && scene.id) metaSceneMap[scene.id] = scene;
      });
      function refreshFrameworkIcons(root) {
        var refreshRoot = root || document;
        try {
          if (window.lucide && typeof window.lucide.createIcons === 'function') {
            window.lucide.createIcons({ root: refreshRoot });
          }
        } catch (iconErr) {
          console.warn('[svg-video] lucide refresh failed', iconErr);
        }
      }
      window.__SVGVIDEO_REFRESH_ICONS = refreshFrameworkIcons;
      var sceneModels = Array.prototype.slice.call(document.querySelectorAll('#stage > [data-scene-id]')).map(function (node, index) {
        var sceneId = node.getAttribute('data-scene-id') || ('scene-' + (index + 1));
        var metaScene = metaSceneMap[sceneId] || {};
        var sceneTitle = typeof metaScene.title === 'string' && metaScene.title ? metaScene.title : ('分镜' + (index + 1));
        var sceneSteps = Math.max(1, Number(metaScene.steps) || 1);
        var sceneStatus = typeof metaScene.status === 'string' ? metaScene.status : 'pending';
        var scenePrompt = typeof metaScene.prompt === 'string' ? metaScene.prompt : '';
        var sceneScript = typeof metaScene.scriptText === 'string' ? metaScene.scriptText : '';
        var sceneAudioObjectKey = typeof metaScene.audioObjectKey === 'string' ? metaScene.audioObjectKey : '';
        var sceneAudioDurationMs = Number(metaScene.audioDurationMs) > 0 ? Number(metaScene.audioDurationMs) : 0;
        var sceneVoiceName = typeof metaScene.voiceName === 'string' ? metaScene.voiceName : '';
        var relatedSceneIds = Array.isArray(metaScene.relatedSceneIds) ? metaScene.relatedSceneIds : [];
        return {
          id: sceneId,
          title: sceneTitle,
          steps: sceneSteps,
          status: sceneStatus,
          prompt: scenePrompt,
          scriptText: sceneScript,
          audioObjectKey: sceneAudioObjectKey,
          audioDurationMs: sceneAudioDurationMs,
          voiceName: sceneVoiceName,
          relatedSceneIds: relatedSceneIds
        };
      });
      if (!sceneModels.length && metaScenes.length) {
        sceneModels = metaScenes.map(function (scene, index) {
          return {
            id: scene.id || ('scene-' + (index + 1)),
            title: scene.title || ('分镜' + (index + 1)),
            steps: Math.max(1, Number(scene.steps) || 1),
            status: typeof scene.status === 'string' ? scene.status : 'pending',
            prompt: typeof scene.prompt === 'string' ? scene.prompt : '',
            scriptText: typeof scene.scriptText === 'string' ? scene.scriptText : '',
            audioObjectKey: typeof scene.audioObjectKey === 'string' ? scene.audioObjectKey : '',
            audioDurationMs: Number(scene.audioDurationMs) > 0 ? Number(scene.audioDurationMs) : 0,
            voiceName: typeof scene.voiceName === 'string' ? scene.voiceName : '',
            relatedSceneIds: Array.isArray(scene.relatedSceneIds) ? scene.relatedSceneIds : []
          };
        });
      }
      function i18nText(zh, en) {
        return hostCaps.locale === 'en' ? en : zh;
      }
      function applyI18nTexts() {
        var toolbarTitleEl = document.getElementById('sv-toolbar-title');
        var titleEl = document.getElementById('sv-editor-header-title');
        if (toolbarTitleEl) toolbarTitleEl.textContent = i18nText('分镜面板', 'Storyboard');
        if (titleEl) titleEl.textContent = i18nText('分镜详情编辑', 'Storyboard details');
        if (expandBtn) {
          var expandText = i18nText('展开分镜详情', 'Open storyboard details');
          expandBtn.setAttribute('aria-label', expandText);
          expandBtn.setAttribute('title', expandText);
        }
        if (editorSaveBtn) editorSaveBtn.textContent = isEditorDirty ? i18nText('保存*', 'Save*') : i18nText('保存', 'Save');
        if (editorCloseBtn) editorCloseBtn.textContent = i18nText('关闭', 'Close');
        if (editorSaveTip) editorSaveTip.textContent = i18nText('已保存', 'Saved');
      }
      function clampEditorHeightVh(value) {
        return Math.max(32, Math.min(80, Number(value) || 50));
      }
      function applyEditorHeight(vh) {
        editorHeightVh = clampEditorHeightVh(vh);
        document.documentElement.style.setProperty('--sv-editor-height', editorHeightVh + 'vh');
      }
      function persistMetaToScript() {
        meta.title = typeof meta.title === 'string' ? meta.title : '';
        meta.version = meta.version || 1;
        meta.scenes = sceneModels.map(function (scene) {
          return {
            id: scene.id,
            title: scene.title || '',
            steps: Math.max(1, Number(scene.steps) || 1),
            status: scene.status || 'pending',
            prompt: scene.prompt || '',
            scriptText: scene.scriptText || '',
            audioObjectKey: scene.audioObjectKey || '',
            audioDurationMs: Number(scene.audioDurationMs) > 0 ? Number(scene.audioDurationMs) : 0,
            voiceName: scene.voiceName || '',
            relatedSceneIds: Array.isArray(scene.relatedSceneIds) ? scene.relatedSceneIds : []
          };
        });
        if (metaJson) metaJson.textContent = JSON.stringify(meta);
      }
      function showSavedTip() {
        if (!editorSaveTip) return;
        if (editorTipTimer) {
          clearTimeout(editorTipTimer);
          editorTipTimer = null;
        }
        editorSaveTip.classList.add('is-visible');
        editorTipTimer = setTimeout(function () {
          editorSaveTip.classList.remove('is-visible');
        }, 1200);
      }
      function markEditorDirty() {
        isEditorDirty = true;
        applyI18nTexts();
      }
      function clearEditorDirty() {
        isEditorDirty = false;
        applyI18nTexts();
      }
      function getStatusLabel(status) {
        if (status === 'success') return i18nText('已生成', 'Done');
        if (status === 'generating') return i18nText('生成中', 'Generating');
        if (status === 'failed') return i18nText('失败', 'Failed');
        return i18nText('未生成', 'Pending');
      }
      function getStatusClass(status) {
        if (status === 'generating') return 'is-generating';
        if (status === 'success') return 'is-success';
        return 'is-failed';
      }
      function getRetryButtonInnerHtml(isGenerating) {
        if (isGenerating) return '<span class="scene-chip-retry-spinner"></span>';
        return '<svg class="scene-chip-retry-icon" viewBox="0 0 1024 1024" width="16" height="16" fill="currentColor"><path d="M698.569 531.322a39.825 39.825 0 0 0-23.898 7.949c-17.702 13.216-21.34 38.28-8.124 55.982l95.716 128.212c7.854 10.521 19.893 16.073 32.084 16.073a39.825 39.825 0 0 0 23.898-7.949c17.702-13.216 21.34-38.28 8.124-55.982l-95.716-128.212c-7.853-10.521-19.892-16.074-32.084-16.073z" /><path d="M922.498 563.818a39.825 39.825 0 0 0-23.898 7.949l-128.212 95.716c-17.703 13.216-21.34 38.28-8.124 55.982 7.854 10.521 19.893 16.073 32.084 16.073a39.825 39.825 0 0 0 23.898-7.949l128.212-95.716c17.702-13.216 21.34-38.28 8.124-55.982-7.854-10.521-19.893-16.074-32.084-16.073zM229.652 282.148a39.825 39.825 0 0 0-23.898 7.949c-17.702 13.216-21.34 38.28-8.124 55.982l95.716 128.212c7.854 10.521 19.893 16.073 32.084 16.073a39.825 39.825 0 0 0 23.898-7.949c17.702-13.216 21.34-38.28 8.124-55.982l-95.716-128.212c-7.854-10.521-19.892-16.074-32.084-16.073z" /><path d="M229.652 282.148a39.825 39.825 0 0 0-23.898 7.949L77.542 385.813c-17.702 13.216-21.34 38.28-8.124 55.982 7.854 10.521 19.893 16.073 32.084 16.073a39.825 39.825 0 0 0 23.898-7.949l128.213-95.716c17.702-13.216 21.34-38.28 8.124-55.982-7.855-10.521-19.893-16.073-32.085-16.073z" /><path d="M614.968 763.65c-44.13 18.171-94.005 25.121-146.168 16.968C353.747 762.636 261.364 670.253 243.381 555.2c-9.681-61.936 1.932-120.646 28.337-170.344l-58.657-58.657c-40.139 64.442-60.11 142.746-50.806 226.152 17.993 161.306 148.088 291.401 309.394 309.394 62.041 6.92 121.259-2.357 174.048-24.083 26.535-10.921 33.625-45.203 13.335-65.493l-0.098-0.098c-11.555-11.555-28.854-14.643-43.966-8.421zM409.033 260.35c44.131-18.171 94.007-25.122 146.169-16.968 115.051 17.985 207.431 110.365 225.416 225.416 9.682 61.935-1.931 120.646-28.337 170.346l58.658 58.658c40.139-64.443 60.109-142.747 50.805-226.155-17.993-161.305-148.087-291.399-309.391-309.391-62.042-6.92-121.26 2.356-174.05 24.082-26.535 10.921-33.625 45.203-13.335 65.493l0.099 0.099c11.555 11.554 28.854 14.642 43.966 8.42z" /></svg>';
      }
      function setSceneStatus(sceneId, status) {
        var index = getSceneIndexById(sceneId);
        if (index < 0) return;
        sceneModels[index].status = status;
        persistMetaToScript();
        var statusClass = getStatusClass(status);
        var chipWrap = document.querySelector('[data-chip-scene-id="' + sceneId + '"]');
        if (chipWrap) {
          chipWrap.classList.remove('is-success', 'is-failed', 'is-generating');
          chipWrap.classList.add(statusClass);
          var chipBtn = chipWrap.querySelector('.scene-chip');
          if (chipBtn) {
            chipBtn.classList.remove('is-success', 'is-failed', 'is-generating');
            chipBtn.classList.add(statusClass);
          }
          var retryBtn = chipWrap.querySelector('.scene-chip-retry');
          if (retryBtn) {
            retryBtn.innerHTML = getRetryButtonInnerHtml(status === 'generating');
            if (status === 'generating') retryBtn.setAttribute('disabled', 'disabled');
            else retryBtn.removeAttribute('disabled');
          }
        }
        renderStatus();
      }
      function requestSceneRetry(sceneId) {
        if (!hostCaps.canRetry) return;
        var idx = getSceneIndexById(sceneId);
        if (idx < 0) return;
        var scene = sceneModels[idx];
        setSceneStatus(sceneId, 'generating');
        focusScene(sceneId);
        if (window.parent && window.parent !== window) {
          window.parent.postMessage(
            { type: '__SVGVIDEO_RETRY_SCENE', sceneId: sceneId, prompt: scene.prompt || '' },
            '*'
          );
        }
      }
      function renderEditorList() {
        if (!editorList) return;
        editorList.innerHTML = '';
        var fragment = document.createDocumentFragment();
        sceneModels.forEach(function (scene, index) {
          var item = document.createElement('section');
          item.className = 'sv-editor-item';
          item.setAttribute('data-editor-scene-id', scene.id);

          var head = document.createElement('div');
          head.className = 'sv-editor-item-head';

          var jumpBtn = document.createElement('button');
          jumpBtn.type = 'button';
          jumpBtn.className = 'sv-editor-item-title';
          jumpBtn.textContent = i18nText('分镜', 'Scene') + (index + 1) + ' · ' + (scene.title || '');
          jumpBtn.addEventListener('click', function () {
            focusScene(scene.id);
          });
          head.appendChild(jumpBtn);

          var statusWrap = document.createElement('div');
          statusWrap.className = 'sv-editor-status-wrap';
          var status = document.createElement('span');
          status.className = 'sv-editor-status';
          status.textContent = getStatusLabel(scene.status);
          statusWrap.appendChild(status);
          var retryBtn = document.createElement('button');
          retryBtn.type = 'button';
          retryBtn.className = 'scene-chip-retry sv-editor-retry';
          retryBtn.setAttribute('aria-label', i18nText('重新生成当前分镜', 'Retry this scene'));
          retryBtn.setAttribute('title', i18nText('重新生成当前分镜', 'Retry this scene'));
          retryBtn.innerHTML = getRetryButtonInnerHtml(scene.status === 'generating');
          if (scene.status === 'generating' || !hostCaps.canRetry) retryBtn.setAttribute('disabled', 'disabled');
          retryBtn.addEventListener('click', function (event) {
            event.preventDefault();
            event.stopPropagation();
            requestSceneRetry(scene.id);
            if (isEditorOpen) renderEditorList();
          });
          statusWrap.appendChild(retryBtn);
          head.appendChild(statusWrap);

          item.appendChild(head);

          var scriptField = document.createElement('label');
          scriptField.className = 'sv-editor-field';
          var scriptLabel = document.createElement('span');
          scriptLabel.className = 'sv-editor-label';
          scriptLabel.textContent = i18nText('脚本', 'Script');
          var scriptInput = document.createElement('div');
          scriptInput.className = 'sv-editor-script-view';
          scriptInput.setAttribute('data-field-scene-id', scene.id);
          scriptInput.textContent = scene.scriptText || '';
          scriptField.appendChild(scriptLabel);
          scriptField.appendChild(scriptInput);
          item.appendChild(scriptField);

          var promptField = document.createElement('label');
          promptField.className = 'sv-editor-field';
          var promptLabel = document.createElement('span');
          promptLabel.className = 'sv-editor-label';
          promptLabel.textContent = i18nText('提示词', 'Prompt');
          var promptInput = document.createElement('textarea');
          promptInput.className = 'sv-editor-textarea';
          promptInput.setAttribute('data-field-scene-id', scene.id);
          promptInput.setAttribute('data-field', 'prompt');
          promptInput.value = scene.prompt || '';
          promptInput.addEventListener('click', function (event) {
            event.stopPropagation();
          });
          promptInput.addEventListener('input', function () {
            scene.prompt = promptInput.value;
            markEditorDirty();
          });
          promptField.appendChild(promptLabel);
          promptField.appendChild(promptInput);
          item.appendChild(promptField);
          item.addEventListener('click', function () {
            focusScene(scene.id);
          });

          fragment.appendChild(item);
        });
        editorList.appendChild(fragment);
        highlightActiveEditorItem();
      }
      function highlightActiveEditorItem() {
        if (!editorList) return;
        var itemList = Array.prototype.slice.call(editorList.querySelectorAll('[data-editor-scene-id]'));
        itemList.forEach(function (item) {
          if (item.getAttribute('data-editor-scene-id') === state.sceneId) item.classList.add('is-active');
          else item.classList.remove('is-active');
        });
      }
      function openEditor() {
        if (!hostCaps.canEditPanel) return;
        if (!editorOverlay) return;
        renderEditorList();
        applyEditorHeight(editorHeightVh);
        editorOverlay.classList.add('is-open');
        editorOverlay.setAttribute('aria-hidden', 'false');
        document.body.classList.add('sv-editor-open');
        isEditorOpen = true;
      }
      function closeEditor() {
        if (!editorOverlay) return;
        editorOverlay.classList.remove('is-open');
        editorOverlay.setAttribute('aria-hidden', 'true');
        document.body.classList.remove('sv-editor-open');
        isEditorOpen = false;
      }
      function saveEditor() {
        persistMetaToScript();
        clearEditorDirty();
        showSavedTip();
        if (window.parent && window.parent !== window) {
          window.parent.postMessage({ type: '__SVGVIDEO_META_UPDATED', meta: meta }, '*');
        }
      }
      function getTotalSteps() {
        return sceneModels.reduce(function (sum, scene) { return sum + Math.max(1, Number(scene.steps) || 1); }, 0);
      }
      function getSceneIndexById(sceneId) {
        for (var i = 0; i < sceneModels.length; i++) {
          if (sceneModels[i].id === sceneId) return i;
        }
        return -1;
      }
      function getSceneInitialStep(sceneId) {
        var index = getSceneIndexById(sceneId);
        if (index < 0) return 0;
        var maxSteps = Math.max(1, Number(sceneModels[index].steps) || 1);
        return Math.max(0, maxSteps - 1);
      }
      function focusScene(sceneId) {
        var index = getSceneIndexById(sceneId);
        if (index < 0) return;
        var maxSteps = Math.max(1, Number(sceneModels[index].steps) || 1);
        stopPlayback();
        window.__SVGVIDEO_SET_STATE(sceneId, 0, 1);
        if (maxSteps > 1) {
          setTimeout(function () {
            window.__SVGVIDEO_SET_STATE(sceneId, Math.max(0, maxSteps - 1), 1);
          }, 90);
        }
      }
      function getProgressPercent(sceneId, step) {
        var total = getTotalSteps();
        if (!total) return 0;
        var index = getSceneIndexById(sceneId);
        if (index < 0) return 0;
        var completed = 0;
        for (var i = 0; i < index; i++) completed += Math.max(1, Number(sceneModels[i].steps) || 1);
        var maxSteps = Math.max(1, Number(sceneModels[index].steps) || 1);
        var localStep = Math.max(0, Math.min(maxSteps - 1, Number(step) || 0));
        return Math.max(0, Math.min(100, ((completed + localStep) / total) * 100));
      }
      function updateProgressUi(percent) {
        var pct = Math.max(0, Math.min(100, percent || 0));
        if (progressFill) progressFill.style.width = pct + '%';
        if (progressThumb) progressThumb.style.left = pct + '%';
      }
      function stopProgressAnimation() {
        if (progressRaf) {
          cancelAnimationFrame(progressRaf);
          progressRaf = null;
        }
      }
      function startProgressSegment(fromPercent, toPercent, durationMs) {
        stopProgressAnimation();
        var from = Math.max(0, Math.min(100, Number(fromPercent) || 0));
        var to = Math.max(0, Math.min(100, Number(toPercent) || 0));
        var duration = Math.max(1, Number(durationMs) || 1);
        if (duration <= 1 || to <= from) {
          updateProgressUi(to);
          return;
        }
        var startAt = performance.now();
        var tick = function (now) {
          if (!isPlaying) {
            stopProgressAnimation();
            return;
          }
          var elapsed = now - startAt;
          var ratio = Math.max(0, Math.min(1, elapsed / duration));
          var current = from + (to - from) * ratio;
          updateProgressUi(current);
          if (ratio < 1) {
            progressRaf = requestAnimationFrame(tick);
          } else {
            stopProgressAnimation();
          }
        };
        progressRaf = requestAnimationFrame(tick);
      }
      function getPlaybackSegment(sceneIndex, step) {
        var scene = sceneModels[sceneIndex];
        if (!scene) return null;
        var currentPercent = getProgressPercent(scene.id, step);
        var maxSteps = Math.max(1, Number(scene.steps) || 1);
        if (step < maxSteps - 1) {
          return {
            from: currentPercent,
            to: getProgressPercent(scene.id, step + 1),
            duration: STEP_INTERVAL_MS
          };
        }
        if (sceneIndex < sceneModels.length - 1) {
          var nextScene = sceneModels[sceneIndex + 1];
          var nextPercent = nextScene ? getProgressPercent(nextScene.id, 0) : currentPercent;
          return {
            from: currentPercent,
            to: nextPercent,
            duration: SCENE_FINISH_PAUSE_MS
          };
        }
        return {
          from: currentPercent,
          to: 100,
          duration: STEP_INTERVAL_MS
        };
      }
      function renderStatus() {
        if (!statusText) return;
        var done = sceneModels.filter(function (scene) { return scene.status === 'success'; }).length;
        var pending = sceneModels.filter(function (scene) { return scene.status === 'pending' || scene.status === 'generating'; }).length;
        var failed = sceneModels.filter(function (scene) { return scene.status === 'failed'; }).length;
        statusText.textContent = '已生成 ' + done + '/' + sceneModels.length + ' · 待生成 ' + pending + ' · 失败 ' + failed;
      }
      function updatePlayButton() {
        if (!playBtn) return;
        playBtn.innerHTML = isPlaying
          ? '<svg viewBox="0 0 24 24" width="14" height="14" fill="currentColor"><rect x="6" y="5" width="4" height="14" rx="1"></rect><rect x="14" y="5" width="4" height="14" rx="1"></rect></svg>'
          : '<svg viewBox="0 0 24 24" width="14" height="14" fill="currentColor"><path d="M7 5v14l11-7z"></path></svg>';
        playBtn.setAttribute('aria-label', isPlaying ? '暂停播放' : '播放分镜');
        playBtn.setAttribute('title', isPlaying ? '暂停播放' : '播放分镜');
      }
      function applyHostCaps() {
        if (hostCaps.canRetry) document.body.classList.add('sv-host-can-retry');
        else document.body.classList.remove('sv-host-can-retry');
        if (hostCaps.canEditPanel) document.body.classList.add('sv-host-can-edit-panel');
        else document.body.classList.remove('sv-host-can-edit-panel');
        if (hostCaps.theme === 'light') document.body.classList.add('sv-theme-light');
        else document.body.classList.remove('sv-theme-light');
      }
      function highlightActiveSceneButton() {
        sceneButtonList.forEach(function (button) {
          var targetId = button.getAttribute('data-panel-scene-id');
          if (targetId === state.sceneId) button.classList.add('is-active');
          else button.classList.remove('is-active');
        });
      }
      function stopPlayback() {
        if (playTimer) {
          clearTimeout(playTimer);
          playTimer = null;
        }
        stopProgressAnimation();
        isPlaying = false;
        updatePlayButton();
      }
      function startPlayback() {
        if (!sceneModels.length) return;
        stopPlayback();
        var sceneIndex = Math.max(0, getSceneIndexById(state.sceneId));
        var step = 0;
        var applyPlaybackState = function (direction) {
          var scene = sceneModels[sceneIndex];
          if (!scene) return false;
          window.__SVGVIDEO_SET_STATE(scene.id, step, direction);
          updateProgressUi(getProgressPercent(scene.id, step));
          var segment = getPlaybackSegment(sceneIndex, step);
          if (segment) startProgressSegment(segment.from, segment.to, segment.duration);
          return true;
        };
        var scheduleAdvance = function (delay) {
          playTimer = setTimeout(function () {
            var currentScene = sceneModels[sceneIndex];
            if (!currentScene) {
              stopPlayback();
              return;
            }
            var maxSteps = Math.max(1, Number(currentScene.steps) || 1);
            if (step < maxSteps - 1) {
              step += 1;
              applyPlaybackState(1);
              scheduleAdvance(STEP_INTERVAL_MS);
              return;
            }
            if (sceneIndex < sceneModels.length - 1) {
              playTimer = setTimeout(function () {
                sceneIndex += 1;
                step = 0;
                if (!applyPlaybackState(1)) {
                  stopPlayback();
                  return;
                }
                scheduleAdvance(STEP_INTERVAL_MS);
              }, SCENE_FINISH_PAUSE_MS);
              return;
            }
            stopPlayback();
          }, delay);
        };
        isPlaying = true;
        updatePlayButton();
        if (!applyPlaybackState(1)) {
          stopPlayback();
          return;
        }
        scheduleAdvance(STEP_INTERVAL_MS);
      }
      window.SceneLifecycle = {
        handlers: {},
        register: function (sceneId, handler) { this.handlers[sceneId] = handler || {}; }
      };

      (function () {
        var sceneDataJson = document.getElementById('__SVGVIDEO_SCENE_DATA__');
        var runtimeScenes = [];
        try {
          runtimeScenes = JSON.parse((sceneDataJson && sceneDataJson.textContent) || '[]');
        } catch (_e) {
          runtimeScenes = [];
        }
        runtimeScenes.forEach(function (scene) {
          if (!scene || typeof scene.id !== 'string') return;
          var js = typeof scene.js === 'string' ? scene.js.trim() : '';
          if (!js) return;
          try {
            var factory = new Function(js);
            factory.call(window);
          } catch (err) {
            console.error('[svg-video] scene runtime failed', scene.id, err);
          }
        });
        refreshFrameworkIcons(document);
      })();

      function applyStepVisibility(sceneEl) {
        var stepNodes = sceneEl.querySelectorAll('[data-step]');
        stepNodes.forEach(function (node) {
          var minStep = Number(node.getAttribute('data-step') || '0');
          var visible = state.step >= minStep;
          node.style.opacity = visible ? '1' : '0';
          node.style.visibility = visible ? 'visible' : 'hidden';
          node.style.pointerEvents = visible ? 'auto' : 'none';
          node.style.transform = visible ? 'translateY(0)' : 'translateY(8px)';
          node.style.filter = visible ? 'blur(0px)' : 'blur(4px)';
        });
      }
      function applySceneState() {
        var scenes = document.querySelectorAll('#stage > [data-scene-id]');
        var currentSceneEl = null;
        var previousSceneEl = null;
        scenes.forEach(function (sceneEl) {
          var sceneId = sceneEl.getAttribute('data-scene-id');
          if (sceneId === state.sceneId) currentSceneEl = sceneEl;
          if (sceneId === previousSceneId) previousSceneEl = sceneEl;
        });
        var direction = state.direction >= 0 ? 1 : -1;
        document.documentElement.style.setProperty('--scene-enter-offset', direction > 0 ? '56px' : '-56px');
        document.documentElement.style.setProperty('--scene-exit-offset', direction > 0 ? '-56px' : '56px');
        if (exitCleanupTimer) {
          clearTimeout(exitCleanupTimer);
          exitCleanupTimer = null;
        }
        scenes.forEach(function (sceneEl) {
          if (sceneEl !== currentSceneEl && sceneEl !== previousSceneEl) {
            sceneEl.classList.remove('scene-active');
            sceneEl.classList.remove('scene-exit');
          }
        });
        if (previousSceneEl && previousSceneEl !== currentSceneEl) {
          previousSceneEl.classList.remove('scene-active');
          previousSceneEl.classList.add('scene-exit');
        }
        if (currentSceneEl) {
          currentSceneEl.classList.remove('scene-exit');
          currentSceneEl.classList.add('scene-active');
          applyStepVisibility(currentSceneEl);
        }
        if (previousSceneEl && previousSceneEl !== currentSceneEl) {
          exitCleanupTimer = setTimeout(function () { previousSceneEl.classList.remove('scene-exit'); }, 620);
        }
        if (previousSceneId !== state.sceneId) {
          if (previousSceneId && window.SceneLifecycle.handlers[previousSceneId] && typeof window.SceneLifecycle.handlers[previousSceneId].leave === 'function') {
            try { window.SceneLifecycle.handlers[previousSceneId].leave(); } catch (e) {}
          }
          if (window.SceneLifecycle.handlers[state.sceneId] && typeof window.SceneLifecycle.handlers[state.sceneId].enter === 'function') {
            try { window.SceneLifecycle.handlers[state.sceneId].enter(); } catch (e) {}
          }
        }
        if (window.SceneLifecycle.handlers[state.sceneId] && typeof window.SceneLifecycle.handlers[state.sceneId].step === 'function') {
          try { window.SceneLifecycle.handlers[state.sceneId].step(state.step); } catch (e) {}
        }
        if (currentSceneEl) {
          refreshFrameworkIcons(currentSceneEl);
        }
        previousSceneId = state.sceneId;
        highlightActiveSceneButton();
        highlightActiveEditorItem();
        updateProgressUi(getProgressPercent(state.sceneId, state.step));
      }
      window.__SVGVIDEO_SET_STATE = function (sceneId, step, direction) {
        if (typeof sceneId === 'string' && sceneId) {
          if (typeof direction === 'number' && Number.isFinite(direction)) {
            state.direction = direction >= 0 ? 1 : -1;
          } else {
            var nextIndex = getSceneIndexById(sceneId);
            var prevIndex = getSceneIndexById(state.sceneId);
            if (nextIndex !== -1 && prevIndex !== -1) state.direction = nextIndex >= prevIndex ? 1 : -1;
          }
          state.sceneId = sceneId;
        }
        if (typeof step === 'number' && Number.isFinite(step)) state.step = Math.max(0, Math.floor(step));
        applySceneState();
      };
      if (state.sceneId && getSceneIndexById(state.sceneId) === -1 && sceneModels.length) {
        state.sceneId = sceneModels[0].id;
      }
      if (playBtn) {
        playBtn.addEventListener('click', function () {
          if (isPlaying) stopPlayback();
          else startPlayback();
        });
      }
      if (expandBtn) {
        expandBtn.addEventListener('click', function () {
          if (isEditorOpen) closeEditor();
          else openEditor();
        });
      }
      if (editorCloseBtn) {
        editorCloseBtn.addEventListener('click', closeEditor);
      }
      if (editorBackdrop) {
        editorBackdrop.addEventListener('click', closeEditor);
      }
      if (editorSaveBtn) {
        editorSaveBtn.addEventListener('click', saveEditor);
      }
      if (editorResizeHandle && editorSheet) {
        editorResizeHandle.addEventListener('mousedown', function (event) {
          event.preventDefault();
          var startY = event.clientY;
          var startHeightPx = (window.innerHeight * editorHeightVh) / 100;
          var onMove = function (moveEvent) {
            var delta = startY - moveEvent.clientY;
            var nextHeightPx = startHeightPx + delta;
            var nextHeightVh = (nextHeightPx / Math.max(1, window.innerHeight)) * 100;
            applyEditorHeight(nextHeightVh);
          };
          var onUp = function () {
            window.removeEventListener('mousemove', onMove);
            window.removeEventListener('mouseup', onUp);
          };
          window.addEventListener('mousemove', onMove);
          window.addEventListener('mouseup', onUp);
        });
      }
      sceneButtonList.forEach(function (button) {
        button.addEventListener('click', function () {
          var sceneId = button.getAttribute('data-panel-scene-id');
          if (!sceneId) return;
          focusScene(sceneId);
        });
      });
      sceneRetryButtonList.forEach(function (button) {
        button.addEventListener('click', function (event) {
          event.preventDefault();
          event.stopPropagation();
          var sceneId = button.getAttribute('data-retry-scene-id');
          if (!sceneId) return;
          requestSceneRetry(sceneId);
        });
      });
      if (progressWrap) {
        progressWrap.addEventListener('click', function (event) {
          var rect = progressWrap.getBoundingClientRect();
          var pct = Math.max(0, Math.min(100, ((event.clientX - rect.left) / rect.width) * 100));
          var totalSteps = getTotalSteps();
          var targetStep = Math.round((pct / 100) * totalSteps);
          var accumulated = 0;
          for (var i = 0; i < sceneModels.length; i++) {
            var sceneSteps = Math.max(1, Number(sceneModels[i].steps) || 1);
            if (accumulated + sceneSteps > targetStep) {
              var localStep = Math.max(0, Math.min(sceneSteps - 1, targetStep - accumulated));
              stopPlayback();
              window.__SVGVIDEO_SET_STATE(sceneModels[i].id, localStep, 1);
              updateProgressUi(pct);
              return;
            }
            accumulated += sceneSteps;
          }
        });
      }
      window.addEventListener('message', function (event) {
        var data = event && event.data;
        if (!data || typeof data.type !== 'string') return;
        if (data.type === '__SVGVIDEO_SET_STATE') {
          window.__SVGVIDEO_SET_STATE(data.sceneId, data.step, data.direction);
          return;
        }
        if (data.type === '__SVGVIDEO_PLAY') {
          startPlayback();
          return;
        }
        if (data.type === '__SVGVIDEO_STOP') {
          stopPlayback();
          return;
        }
        if (data.type === '__SVGVIDEO_SET_HOST_CAPS') {
          hostCaps.canRetry = Boolean(data.canRetry);
          hostCaps.canEditPanel = Boolean(data.canEditPanel);
          hostCaps.locale = data.locale === 'en' ? 'en' : 'zh';
          hostCaps.theme = data.theme === 'light' ? 'light' : 'dark';
          applyI18nTexts();
          applyHostCaps();
          if (isEditorOpen) renderEditorList();
          return;
        }
        if (data.type === '__SVGVIDEO_SET_SCENE_STATUS') {
          var sceneId = typeof data.sceneId === 'string' ? data.sceneId : '';
          var status = typeof data.status === 'string' ? data.status : '';
          if (sceneId && status) {
            setSceneStatus(sceneId, status);
            if (isEditorOpen) renderEditorList();
          }
          return;
        }
        if (data.type === '__SVGVIDEO_SET_RECORDING') {
          if (data && data.active) {
            document.body.classList.add('sv-recording');
            // 如果指定了初始分镜，无过渡动画地跳转过去，避免录制开头残留旧分镜画面
            if (data.sceneId) {
              var noTransStyle = document.createElement('style');
              noTransStyle.id = 'sv-no-transition';
              noTransStyle.textContent = '.scene, [data-step] { transition: none !important; }';
              document.head.appendChild(noTransStyle);
              window.__SVGVIDEO_SET_STATE(data.sceneId, 0, 1);
              // 强制 reflow 后恢复过渡动画
              void document.body.offsetHeight;
              requestAnimationFrame(function () {
                var s = document.getElementById('sv-no-transition');
                if (s) s.remove();
              });
            }
          } else {
            document.body.classList.remove('sv-recording');
          }
          return;
        }
        if (data.type === '__SVGVIDEO_SET_META' && data.meta && typeof data.meta === 'object') {
          meta = data.meta;
          var freshScenes = Array.isArray(meta.scenes) ? meta.scenes : [];
          freshScenes.forEach(function (scene) {
            var idx = getSceneIndexById(scene.id);
            if (idx < 0) return;
            sceneModels[idx].prompt = typeof scene.prompt === 'string' ? scene.prompt : '';
            sceneModels[idx].scriptText = typeof scene.scriptText === 'string' ? scene.scriptText : '';
            sceneModels[idx].audioObjectKey = typeof scene.audioObjectKey === 'string' ? scene.audioObjectKey : '';
            sceneModels[idx].audioDurationMs = Number(scene.audioDurationMs) > 0 ? Number(scene.audioDurationMs) : 0;
            sceneModels[idx].voiceName = typeof scene.voiceName === 'string' ? scene.voiceName : '';
          });
          persistMetaToScript();
          if (isEditorOpen) renderEditorList();
        }
      });
      document.addEventListener('keydown', function (event) {
        if (event.key === 'Escape' && isEditorOpen) closeEditor();
      });
      window.__SVGVIDEO_PLAY = startPlayback;
      window.__SVGVIDEO_STOP = stopPlayback;
      window.__SVGVIDEO_OPEN_EDITOR = openEditor;
      window.__SVGVIDEO_CLOSE_EDITOR = closeEditor;
      applyEditorHeight(editorHeightVh);
      renderStatus();
      updatePlayButton();
      applyHostCaps();
      clearEditorDirty();
      applyI18nTexts();
      applySceneState();
      if (autoplayFromFirstScene && sceneModels.length) {
        state.sceneId = sceneModels[0].id;
        state.step = 0;
        state.direction = 1;
        applySceneState();
        startPlayback();
      }
    })();
  </script>
</body>
</html>14:T3a04,<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>3D Glass Text Animation</title>
<style>
    /* 基础重置与全屏居中 */
    body, html {
        margin: 0;
        padding: 0;
        width: 100%;
        height: 100%;
        background-color: #f0f4f8; /* 亮色主题底色 */
        display: flex;
        justify-content: center;
        align-items: center;
        overflow: hidden;
        font-family: 'Impact', 'Arial Black', sans-serif;
        touch-action: none; /* 阻止移动端默认滚动，便于拖拽 */
    }

    /* 响应式缩放容器 */
    .scale-wrapper {
        position: relative;
        width: 1200px;
        height: 700px;
        /* 默认居中，利用 JS 动态计算缩放比例保持画面完整 */
        transform-origin: center center;
    }

    /* 背景层：亮色渐变与光斑 */
    .bg-layer {
        position: absolute;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        z-index: 1;
        border-radius: 16px;
        overflow: hidden;
        background: radial-gradient(circle at 50% 50%, #ffffff 0%, #e2eaf4 100%);
        box-shadow: 0 20px 60px rgba(150, 170, 200, 0.2);
    }

    /* 3D 场景容器：开启透视 */
    .scene {
        position: absolute;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        z-index: 2;
        perspective: 1500px;
        display: flex;
        justify-content: center;
        align-items: center;
    }

    /* 旋转组：承载所有 3D 图层 */
    .text-group {
        position: relative;
        width: 100%;
        height: 100%;
        transform-style: preserve-3d;
        /* 初始角度将在 JS 中设置并更新 */
        cursor: grab;
    }
    
    .text-group:active {
        cursor: grabbing;
    }

    /* 每一层 SVG 的统一样式 */
    .slice {
        position: absolute;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        display: flex;
        justify-content: center;
        align-items: center;
        pointer-events: none; /* 避免遮挡鼠标事件 */
    }

    .slice svg {
        width: 100%;
        height: 100%;
        overflow: visible;
    }

    /* 中文副标题排版 */
    .subtitle-container {
        position: absolute;
        bottom: 80px;
        width: 100%;
        text-align: center;
        z-index: 3;
        pointer-events: none;
    }

    .subtitle {
        font-family: 'PingFang SC', 'Microsoft YaHei', sans-serif;
        font-size: 20px;
        font-weight: 300;
        color: #798da3;
        letter-spacing: 16px;
        text-transform: uppercase;
        opacity: 0;
        transform: translateY(20px);
        animation: fadeUp 1.5s cubic-bezier(0.2, 0.8, 0.2, 1) 1s forwards;
        text-shadow: 0 2px 4px rgba(255, 255, 255, 0.8);
    }

    /* 装饰线 */
    .deco-line {
        width: 60px;
        height: 1px;
        background: #aab8c8;
        margin: 15px auto 0;
        opacity: 0;
        animation: expandLine 1s cubic-bezier(0.2, 0.8, 0.2, 1) 1.5s forwards;
    }

    @keyframes fadeUp {
        to { opacity: 1; transform: translateY(0); }
    }
    @keyframes expandLine {
        to { opacity: 0.6; width: 120px; }
    }

    /* 交互提示 */
    .hint {
        position: absolute;
        top: 40px;
        width: 100%;
        text-align: center;
        font-family: 'PingFang SC', sans-serif;
        font-size: 14px;
        color: #aab8c8;
        letter-spacing: 2px;
        z-index: 3;
        opacity: 0;
        animation: fadeInOut 4s ease-in-out 2s forwards;
        pointer-events: none;
    }
    @keyframes fadeInOut {
        0% { opacity: 0; }
        20% { opacity: 0.8; }
        80% { opacity: 0.8; }
        100% { opacity: 0; }
    }
</style>
</head>
<body>

<div class="scale-wrapper" id="scaleWrapper">
    <!-- 1. 背景层 (SVG 光斑与网格) -->
    <div class="bg-layer">
        <svg width="100%" height="100%" viewBox="0 0 1200 700" preserveAspectRatio="xMidYMid slice">
            <defs>
                <filter id="superBlur" x="-50%" y="-50%" width="200%" height="200%">
                    <feGaussianBlur stdDeviation="80" />
                </filter>
                <pattern id="grid" width="40" height="40" patternUnits="userSpaceOnUse">
                    <path d="M 40 0 L 0 0 0 40" fill="none" stroke="rgba(150, 170, 200, 0.1)" stroke-width="1"/>
                </pattern>
            </defs>
            
            <!-- 极简网格 -->
            <rect width="100%" height="100%" fill="url(#grid)" />

            <!-- 缓慢漂浮的玻璃态光斑 -->
            <g filter="url(#superBlur)">
                <circle cx="300" cy="200" r="150" fill="#a1c4fd" opacity="0.5">
                    <animate attributeName="cx" values="300; 500; 300" dur="15s" ease="ease-in-out" repeatCount="indefinite"/>
                    <animate attributeName="cy" values="200; 400; 200" dur="18s" ease="ease-in-out" repeatCount="indefinite"/>
                </circle>
                <circle cx="900" cy="500" r="180" fill="#c2e9fb" opacity="0.6">
                    <animate attributeName="cx" values="900; 700; 900" dur="20s" ease="ease-in-out" repeatCount="indefinite"/>
                    <animate attributeName="cy" values="500; 300; 500" dur="16s" ease="ease-in-out" repeatCount="indefinite"/>
                </circle>
                <circle cx="600" cy="350" r="250" fill="#fdfbfb" opacity="0.8">
                    <animate attributeName="r" values="250; 300; 250" dur="12s" ease="ease-in-out" repeatCount="indefinite"/>
                </circle>
            </g>
        </svg>
    </div>

    <!-- 交互提示 -->
    <div class="hint">拖拽以自由旋转视角</div>

    <!-- 2. 核心 3D 文本场景 -->
    <div class="scene" id="sceneContainer">
        <div class="text-group" id="textGroup">
            <!-- JS 将动态生成多层 SVG 挤出 3D 玻璃厚度 -->
        </div>
    </div>

    <!-- 3. 前景字幕排版 -->
    <div class="subtitle-container">
        <div class="subtitle">全 矢 量 玻 璃 态 空 间 渲 染</div>
        <div class="deco-line"></div>
    </div>
</div>

<script>
    /**
     * 1. 响应式缩放逻辑
     * 保持 1200x700 的比例，在浏览器窗口中完美居中缩放
     */
    const wrapper = document.getElementById('scaleWrapper');
    const TARGET_WIDTH = 1200;
    const TARGET_HEIGHT = 700;

    function handleResize() {
        const winW = window.innerWidth;
        const winH = window.innerHeight;
        const scaleX = winW / TARGET_WIDTH;
        const scaleY = winH / TARGET_HEIGHT;
        // 留出一点边距，取最小缩放比
        const scale = Math.min(scaleX, scaleY) * 0.95; 
        wrapper.style.transform = `scale(${scale})`;
    }
    window.addEventListener('resize', handleResize);
    handleResize(); // 初始化调用

    /**
     * 2. 生成 3D 玻璃文本层
     * 核心思想：利用 DOM 叠层 + CSS translateZ 模拟 3D 实体的厚度 (Isometric / Extrusion)
     * 不使用 Canvas，纯 SVG 高清渲染
     */
    const textGroup = document.getElementById('textGroup');
    const textContent = "SvgAnimate";
    const LAYERS = 50; // 厚度层数，层数越多越平滑，但影响性能，50是甜点值
    const DEPTH_STEP = 1.2; // 每层 Z 轴间距 (px)
    
    // 预定义 SVG 滤镜和渐变资源，只在第一层生成一次供后续复用
    const defsHTML = `
        <defs>
            <!-- 玻璃高光流光渐变 -->
            <linearGradient id="shineGrad" x1="-100%" y1="0%" x2="0%" y2="0%">
                <stop offset="0%" stop-color="rgba(255,255,255,0)" />
                <stop offset="30%" stop-color="rgba(255,255,255,0)" />
                <stop offset="50%" stop-color="rgba(255,255,255,0.9)" />
                <stop offset="70%" stop-color="rgba(255,255,255,0)" />
                <stop offset="100%" stop-color="rgba(255,255,255,0)" />
                <animate attributeName="x1" values="-150%; 250%" dur="4s" repeatCount="indefinite" />
                <animate attributeName="x2" values="-50%; 350%" dur="4s" repeatCount="indefinite" />
            </linearGradient>

            <!-- 基础玻璃表面渐变 -->
            <linearGradient id="glassSurface" x1="0%" y1="0%" x2="0%" y2="100%">
                <stop offset="0%" stop-color="rgba(255,255,255,0.8)" />
                <stop offset="100%" stop-color="rgba(230,240,255,0.3)" />
            </linearGradient>

            <!-- 底部阴影滤镜 -->
            <filter id="dropShadow" x="-20%" y="-20%" width="140%" height="140%">
                <feGaussianBlur in="SourceAlpha" stdDeviation="15" result="blur" />
                <feOffset dx="0" dy="20" result="offsetBlur" />
                <feComponentTransfer>
                    <feFuncA type="linear" slope="0.15" />
                </feComponentTransfer>
                <feMerge> 
                    <feMergeNode />
                    <feMergeNode in="SourceGraphic" />
                </feMerge>
            </filter>
        </defs>
    `;

    let htmlBuffer = '';

    // A. 生成底部投影层 (最远离相机的层)
    htmlBuffer += `
        <div class="slice" style="transform: translateZ(${(-LAYERS/2 * DEPTH_STEP) - 40}px);">
            <svg viewBox="0 0 1200 700">
                <text x="50%" y="50%" text-anchor="middle" dominant-baseline="middle" 
                      font-size="170" font-weight="900" letter-spacing="4px" font-style="italic"
                      fill="#8a9eb5" filter="blur(25px)" opacity="0.6">
                    ${textContent}
                </text>
            </svg>
        </div>
    `;

    // B. 循环生成玻璃厚度体 (挤出侧面)
    for (let i = 0; i <= LAYERS; i++) {
        const z = (i - LAYERS / 2) * DEPTH_STEP;
        const isTop = (i === LAYERS);
        const isBottom = (i === 0);
        
        // 样式计算逻辑：营造玻璃的清透与边缘折射感
        let fill, stroke, strokeWidth, filter, additionalElements;
        
        if (isTop) {
            // 正面：清晰的表面，带有流光和基础半透明填充
            fill = "url(#glassSurface)";
            stroke = "rgba(255, 255, 255, 1)"; // 锐利的白色边缘
            strokeWidth = "2";
            filter = "none";
            // 顶层附加流光层
            additionalElements = `
                <text x="50%" y="50%" text-anchor="middle" dominant-baseline="middle" 
                      font-size="170" font-weight="900" letter-spacing="4px" font-style="italic"
                      fill="url(#shineGrad)" pointer-events="none">
                    ${textContent}
                </text>
            `;
        } else if (isBottom) {
            // 底面：稍暗，封底
            fill = "rgba(200, 215, 235, 0.4)";
            stroke = "rgba(255, 255, 255, 0.5)";
            strokeWidth = "1";
            filter = "none";
            additionalElements = "";
        } else {
            // 侧边厚度层：极低透明度交替叠加，模拟毛玻璃内部浑浊与折射
            const opacity = 0.04;
            // 细微交替颜色制造条纹质感（类似亚克力切割边缘）
            const color = i % 2 === 0 ? `rgba(255, 255, 255, ${opacity})` : `rgba(180, 210, 255, ${opacity})`;
            fill = color;
            stroke = `rgba(200, 220, 255, ${opacity * 1.5})`;
            strokeWidth = "1.5";
            filter = "none";
            additionalElements = "";
        }

        htmlBuffer += `
            <div class="slice" style="transform: translateZ(${z}px);">
                <svg viewBox="0 0 1200 700">
                    ${i === 0 ? defsHTML : ''} <!-- 只注入一次 defs -->
                    <text x="50%" y="50%" text-anchor="middle" dominant-baseline="middle" 
                          font-size="170" font-weight="900" letter-spacing="4px" font-style="italic"
                          fill="${fill}" stroke="${stroke}" stroke-width="${strokeWidth}"
                          stroke-linejoin="round">
                        ${textContent}
                    </text>
                    ${additionalElements}
                </svg>
            </div>
        `;
    }

    // 渲染到 DOM
    textGroup.innerHTML = htmlBuffer;


    /**
     * 3. 动画与交互逻辑 (核心主循环)
     * 使用唯一的 requestAnimationFrame 处理自动旋转和平滑拖拽过渡
     */
    const sceneContainer = document.getElementById('sceneContainer');
    
    // 旋转状态变量
    let currentRotateX = -15; // 初始微微俯视
    let currentRotateY = 30;  // 初始偏转角度
    let targetRotateX = -15;
    let targetRotateY = 30;
    
    // 交互状态
    let isDragging = false;
    let startX = 0;
    let startY = 0;
    let autoRotate = true;
    let time = 0;

    // 监听拖拽事件 (Pointer Events 兼容鼠标和触摸)
    sceneContainer.addEventListener('pointerdown', (e) => {
        isDragging = true;
        autoRotate = false; // 交互时暂停自动旋转
        startX = e.clientX;
        startY = e.clientY;
        sceneContainer.setPointerCapture(e.pointerId);
    });

    sceneContainer.addEventListener('pointermove', (e) => {
        if (!isDragging) return;
        
        const deltaX = e.clientX - startX;
        const deltaY = e.clientY - startY;
        
        // 调整灵敏度
        targetRotateY += deltaX * 0.4;
        targetRotateX -= deltaY * 0.4;
        
        // 限制 X 轴翻转，防止倒置，保持最佳观感
        targetRotateX = Math.max(-60, Math.min(60, targetRotateX));
        
        startX = e.clientX;
        startY = e.clientY;
    });

    sceneContainer.addEventListener('pointerup', (e) => {
        isDragging = false;
        sceneContainer.releasePointerCapture(e.pointerId);
        // 设置一个延时恢复自动旋转
        setTimeout(() => {
            if (!isDragging) autoRotate = true;
        }, 2000);
    });

    // 唯一主循环
    function renderLoop() {
        if (autoRotate) {
            // 自动缓慢旋转：利用正弦函数做优雅的摇摆，而不是死板的单向旋转
            time += 0.005;
            targetRotateY += 0.2; // 持续围绕 Y 轴旋转
            targetRotateX = -15 + Math.sin(time) * 10; // X 轴轻微呼吸摇摆
        }

        // 平滑插值 (Lerp) 实现惯性顺滑感
        currentRotateX += (targetRotateX - currentRotateX) * 0.1;
        currentRotateY += (targetRotateY - currentRotateY) * 0.1;

        // 应用 3D 变换
        textGroup.style.transform = `rotateX(${currentRotateX}deg) rotateY(${currentRotateY}deg)`;

        requestAnimationFrame(renderLoop);
    }

    // 启动动画循环
    renderLoop();

</script>
</body>
</html>15:T3bbd,<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>Tracert 命令运行逻辑动画</title>
    <style>
        :root {
            --bg-color: #0a0e14;
            --panel-color: rgba(20, 26, 35, 0.9);
            --accent-blue: #00d2ff;
            --accent-green: #00ff9d;
            --accent-red: #ff4b2b;
            --text-main: #e0e6ed;
            --text-dim: #718096;
            --font-mono: 'Fira Code', 'JetBrains Mono', 'Courier New', monospace;
        }

        body, html {
            margin: 0;
            padding: 0;
            width: 100%;
            height: 100%;
            background-color: var(--bg-color);
            color: var(--text-main);
            font-family: 'PingFang SC', 'Microsoft YaHei', sans-serif;
            overflow: hidden;
            display: flex;
            justify-content: center;
            align-items: center;
        }

        #container {
            position: relative;
            width: 1200px;
            height: 700px;
            background: var(--bg-color);
            box-shadow: 0 0 50px rgba(0,0,0,0.5);
            border: 1px solid #2d3748;
            overflow: hidden;
        }

        .svg-layer {
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
        }

        /* 终端窗口样式 */
        #terminal {
            position: absolute;
            bottom: 30px;
            left: 50%;
            transform: translateX(-50%);
            width: 1100px;
            height: 180px;
            background: var(--panel-color);
            border: 1px solid #4a5568;
            border-radius: 8px;
            padding: 15px;
            font-family: var(--font-mono);
            font-size: 14px;
            color: var(--accent-green);
            box-shadow: 0 10px 30px rgba(0,0,0,0.5);
            overflow-y: auto;
            scrollbar-width: thin;
        }

        .cursor {
            display: inline-block;
            width: 8px;
            height: 15px;
            background: var(--accent-green);
            animation: blink 1s infinite;
            vertical-align: middle;
        }

        @keyframes blink {
            50% { opacity: 0; }
        }

        /* 顶部标题栏 */
        #header {
            position: absolute;
            top: 20px;
            left: 40px;
            display: flex;
            flex-direction: column;
        }

        #header h1 {
            margin: 0;
            font-size: 24px;
            letter-spacing: 2px;
            color: var(--accent-blue);
            text-transform: uppercase;
        }

        #header p {
            margin: 5px 0 0 0;
            font-size: 14px;
            color: var(--text-dim);
        }

        .step-label {
            font-size: 16px;
            font-weight: bold;
            fill: var(--text-main);
        }

        .node-label {
            font-size: 12px;
            fill: var(--text-dim);
            text-anchor: middle;
        }

        .ttl-badge {
            font-family: var(--font-mono);
            font-weight: bold;
            font-size: 14px;
        }

        /* 科技感动画线条 */
        .path-line {
            fill: none;
            stroke: #2d3748;
            stroke-width: 2;
            stroke-dasharray: 8 4;
        }

        .path-active {
            stroke: var(--accent-blue);
            stroke-width: 3;
            stroke-dasharray: 1000;
            stroke-dashoffset: 1000;
        }

        @keyframes dash {
            to { stroke-dashoffset: 0; }
        }
    </style>
</head>
<body>

<div id="container">
    <!-- 背景装饰网格 -->
    <svg class="svg-layer" viewBox="0 0 1200 700">
        <defs>
            <pattern id="grid" width="40" height="40" patternUnits="userSpaceOnUse">
                <path d="M 40 0 L 0 0 0 40" fill="none" stroke="#1a202c" stroke-width="1"/>
            </pattern>
            <filter id="glow">
                <feGaussianBlur stdDeviation="3" result="coloredBlur"/>
                <feMerge>
                    <feMergeNode in="coloredBlur"/>
                    <feMergeNode in="SourceGraphic"/>
                </feMerge>
            </filter>
            
            <!-- 图标定义 -->
            <symbol id="icon-pc" viewBox="0 0 24 24">
                <path fill="currentColor" d="M20,18H4V4H20M20,2H4C2.89,2 2,2.89 2,4V16A2,2 0 0,0 4,18H9L7.5,20.5V22H16.5V20.5L15,18H20A2,2 0 0,0 22,16V4C22,2.89 21.1,2 20,2Z" />
            </symbol>
            <symbol id="icon-router" viewBox="0 0 24 24">
                <path fill="currentColor" d="M20,13H4V11H20M20,17H4V15H20M20,7H4V5H20M22,3H2V21H22V3Z" />
            </symbol>
            <symbol id="icon-server" viewBox="0 0 24 24">
                <path fill="currentColor" d="M19,15H5V13H19M19,19H5V17H19M19,11H5V9H19M5,7V5H19V7H5M21,3H3V21H21V3Z" />
            </symbol>
        </defs>
        <rect width="100%" height="100%" fill="url(#grid)" />

        <!-- 节点连线 -->
        <g id="connections"></g>

        <!-- 节点 -->
        <g id="nodes">
            <!-- 源主机 -->
            <g transform="translate(100, 320)" id="node-source">
                <use href="#icon-pc" x="-25" y="-25" width="50" height="50" style="color: var(--accent-blue); filter: url(#glow);"/>
                <text y="45" class="node-label">源主机 (Client)</text>
                <text y="60" class="node-label" style="font-size:10px">192.168.1.10</text>
            </g>

            <!-- 路由器 1 -->
            <g transform="translate(350, 320)" id="node-r1">
                <use href="#icon-router" x="-20" y="-20" width="40" height="40" style="color: var(--text-dim);"/>
                <text y="40" class="node-label">跃点 1</text>
                <text y="55" class="node-label" style="font-size:10px">10.0.0.1</text>
            </g>

            <!-- 路由器 2 -->
            <g transform="translate(600, 320)" id="node-r2">
                <use href="#icon-router" x="-20" y="-20" width="40" height="40" style="color: var(--text-dim);"/>
                <text y="40" class="node-label">跃点 2</text>
                <text y="55" class="node-label" style="font-size:10px">20.0.0.5</text>
            </g>

            <!-- 路由器 3 -->
            <g transform="translate(850, 320)" id="node-r3">
                <use href="#icon-router" x="-20" y="-20" width="40" height="40" style="color: var(--text-dim);"/>
                <text y="40" class="node-label">跃点 3</text>
                <text y="55" class="node-label" style="font-size:10px">30.0.0.8</text>
            </g>

            <!-- 目标服务器 -->
            <g transform="translate(1100, 320)" id="node-target">
                <use href="#icon-server" x="-25" y="-25" width="50" height="50" style="color: var(--accent-green);"/>
                <text y="45" class="node-label">目标服务器</text>
                <text y="60" class="node-label" style="font-size:10px">8.8.8.8</text>
            </g>
        </g>

        <!-- 动画元素：数据包 -->
        <g id="packet-layer"></g>

        <!-- 步骤提示文字 -->
        <text id="step-description" x="600" y="80" text-anchor="middle" class="step-label" fill="var(--accent-blue)">准备执行 Tracert 命令...</text>
    </svg>

    <div id="header">
        <h1>Tracert 运行逻辑可视化</h1>
        <p>ICMP 生存时间 (TTL) 机制演示</p>
    </div>

    <div id="terminal">
        <div>C:\Users\Admin&gt; <span id="command-text"></span><span class="cursor"></span></div>
        <div id="log-content"></div>
    </div>
</div>

<script>
    const CONFIG = {
        nodes: [
            { id: 'source', x: 100, y: 320, ip: '192.168.1.10' },
            { id: 'r1', x: 350, y: 320, ip: '10.0.0.1' },
            { id: 'r2', x: 600, y: 320, ip: '20.0.0.5' },
            { id: 'r3', x: 850, y: 320, ip: '30.0.0.8' },
            { id: 'target', x: 1100, y: 320, ip: '8.8.8.8' }
        ],
        packetColor: '#00d2ff',
        errorColor: '#ff4b2b',
        successColor: '#00ff9d'
    };

    const terminal = document.getElementById('log-content');
    const stepDesc = document.getElementById('step-description');
    const packetLayer = document.getElementById('packet-layer');
    const commandText = document.getElementById('command-text');

    async function sleep(ms) {
        return new Promise(resolve => setTimeout(resolve, ms));
    }

    function addLog(text, color = 'var(--accent-green)') {
        const div = document.createElement('div');
        div.style.color = color;
        div.style.marginTop = '2px';
        div.innerHTML = text;
        terminal.appendChild(div);
        terminal.scrollTop = terminal.scrollHeight;
    }

    async function typeCommand(text) {
        for (let i = 0; i <= text.length; i++) {
            commandText.textContent = text.slice(0, i);
            await sleep(50);
        }
        await sleep(500);
    }

    function createPacket(x, y, ttl, color) {
        const g = document.createElementNS('http://www.w3.org/2000/svg', 'g');
        g.setAttribute('transform', `translate(${x}, ${y})`);
        
        const circle = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
        circle.setAttribute('r', '8');
        circle.setAttribute('fill', color);
        circle.setAttribute('filter', 'url(#glow)');
        
        const text = document.createElementNS('http://www.w3.org/2000/svg', 'text');
        text.setAttribute('y', '-15');
        text.setAttribute('text-anchor', 'middle');
        text.setAttribute('class', 'ttl-badge');
        text.setAttribute('fill', color);
        text.textContent = `TTL=${ttl}`;
        
        g.appendChild(circle);
        g.appendChild(text);
        packetLayer.appendChild(g);
        return g;
    }

    async function movePacket(packet, startNodeIdx, endNodeIdx, duration) {
        const start = CONFIG.nodes[startNodeIdx];
        const end = CONFIG.nodes[endNodeIdx];
        const startTime = performance.now();

        return new Promise(resolve => {
            function animate(time) {
                let progress = (time - startTime) / duration;
                if (progress > 1) progress = 1;
                
                const currentX = start.x + (end.x - start.x) * progress;
                const currentY = start.y + (end.y - start.y) * progress;
                packet.setAttribute('transform', `translate(${currentX}, ${currentY})`);
                
                if (progress < 1) {
                    requestAnimationFrame(animate);
                } else {
                    resolve();
                }
            }
            requestAnimationFrame(animate);
        });
    }

    async function runTracert() {
        // 1. 初始化
        await typeCommand('tracert 8.8.8.8');
        addLog('通过最多 30 个跃点跟踪到 8.8.8.8 的路由:', 'var(--text-main)');
        await sleep(1000);

        // 循环每一跳
        for (let hop = 1; hop <= 4; hop++) {
            const isTarget = hop === 4;
            const ttl = hop;
            stepDesc.textContent = `步骤 ${hop}: 发送 TTL=${ttl} 的数据包`;
            stepDesc.setAttribute('fill', CONFIG.packetColor);

            // 发送包
            const p = createPacket(CONFIG.nodes[0].x, CONFIG.nodes[0].y, ttl, CONFIG.packetColor);
            
            // 沿途经过路由器
            for (let currentPos = 0; currentPos < hop; currentPos++) {
                await movePacket(p, currentPos, currentPos + 1, 600);
                if (currentPos + 1 < hop) {
                    // TTL 递减动画
                    const newTTL = ttl - (currentPos + 1);
                    p.querySelector('text').textContent = `TTL=${newTTL}`;
                    // 闪烁一下表示处理
                    p.querySelector('circle').setAttribute('fill', '#fff');
                    await sleep(100);
                    p.querySelector('circle').setAttribute('fill', CONFIG.packetColor);
                }
            }

            // 到达目标点（TTL变为0或到达目的地）
            await sleep(300);
            
            if (!isTarget) {
                // 路由器返回 ICMP Time Exceeded
                stepDesc.textContent = `跃点 ${hop} (${CONFIG.nodes[hop].ip}): TTL 耗尽，返回 ICMP Time Exceeded`;
                stepDesc.setAttribute('fill', CONFIG.errorColor);
                p.querySelector('circle').setAttribute('fill', CONFIG.errorColor);
                p.querySelector('text').textContent = 'Time Exceeded';
                p.querySelector('text').setAttribute('fill', CONFIG.errorColor);
                
                // 高亮当前路由器
                const routerIcon = document.querySelector(`#node-r${hop} use`);
                routerIcon.style.color = CONFIG.packetColor;
                routerIcon.style.filter = 'url(#glow)';

                await sleep(500);
                // 返回源主机
                await movePacket(p, hop, 0, 800);
                addLog(`&nbsp;&nbsp;${hop}&nbsp;&nbsp;&nbsp;&nbsp;1 ms&nbsp;&nbsp;&nbsp;&nbsp;1 ms&nbsp;&nbsp;&nbsp;&nbsp;1 ms&nbsp;&nbsp;${CONFIG.nodes[hop].ip}`);
            } else {
                // 到达目标服务器返回 ICMP Echo Reply
                stepDesc.textContent = `目标已到达: 8.8.8.8 返回 ICMP Echo Reply`;
                stepDesc.setAttribute('fill', CONFIG.successColor);
                p.querySelector('circle').setAttribute('fill', CONFIG.successColor);
                p.querySelector('text').textContent = 'Echo Reply';
                p.querySelector('text').setAttribute('fill', CONFIG.successColor);

                const targetIcon = document.querySelector(`#node-target use`);
                targetIcon.style.color = CONFIG.successColor;
                targetIcon.style.filter = 'url(#glow)';

                await sleep(500);
                await movePacket(p, 4, 0, 800);
                addLog(`&nbsp;&nbsp;${hop}&nbsp;&nbsp;&nbsp;&nbsp;2 ms&nbsp;&nbsp;&nbsp;&nbsp;1 ms&nbsp;&nbsp;&nbsp;&nbsp;2 ms&nbsp;&nbsp;${CONFIG.nodes[hop].ip}`);
            }

            packetLayer.removeChild(p);
            await sleep(800);
        }

        stepDesc.textContent = "跟踪完成。所有路径节点已识别。";
        stepDesc.setAttribute('fill', CONFIG.successColor);
        addLog('<br>跟踪完成。', 'var(--text-main)');
        
        await sleep(5000);
        location.reload(); // 循环演示
    }

    // 初始化连线
    function initLines() {
        const connGroup = document.getElementById('connections');
        for (let i = 0; i < CONFIG.nodes.length - 1; i++) {
            const line = document.createElementNS('http://www.w3.org/2000/svg', 'line');
            line.setAttribute('x1', CONFIG.nodes[i].x);
            line.setAttribute('y1', CONFIG.nodes[i].y);
            line.setAttribute('x2', CONFIG.nodes[i+1].x);
            line.setAttribute('y2', CONFIG.nodes[i+1].y);
            line.setAttribute('class', 'path-line');
            connGroup.appendChild(line);
        }
    }

    window.onload = () => {
        initLines();
        runTracert();
    };
</script>

</body>
</html>16:T4f1f,<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Minimalist Billiards</title>
    <style>
        @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap');

        :root {
            --bg-color: #042F23;
            --table-color: #064E3B;
            --text-primary: #FFFFFF;
            --text-secondary: #9CA3AF;
            --accent: #FACC15;
            --danger: #EF4444;
        }

        body, html {
            margin: 0;
            padding: 0;
            width: 100%;
            height: 100%;
            overflow: hidden;
            background-color: var(--bg-color);
            font-family: 'Inter', system-ui, -apple-system, sans-serif;
            cursor: none;
            touch-action: none;
        }

        #game-container {
            width: 100vw;
            height: 100vh;
            display: flex;
            justify-content: center;
            align-items: center;
            position: relative;
        }

        svg {
            width: 100%;
            height: 100%;
            display: block;
        }

        /* 文本无法选中 */
        text {
            user-select: none;
            pointer-events: none;
        }

        /* 动画类 */
        .fade-out {
            opacity: 0;
            transition: opacity 0.5s ease-out;
        }

        .score-popup {
            font-weight: bold;
            font-size: 24px;
            fill: var(--accent);
            animation: floatUp 1s ease-out forwards;
        }

        @keyframes floatUp {
            0% { transform: translateY(0) scale(0.5); opacity: 1; }
            50% { transform: translateY(-20px) scale(1.2); opacity: 1; }
            100% { transform: translateY(-40px) scale(1); opacity: 0; }
        }
    </style>
</head>
<body>

<div id="game-container">
    <svg id="game-svg" viewBox="0 0 1200 700" preserveAspectRatio="xMidYMid meet">
        <defs>
            <!-- 桌布阴影 -->
            <filter id="inner-shadow">
                <feOffset dx="0" dy="0"/>
                <feGaussianBlur stdDeviation="15" result="offset-blur"/>
                <feComposite operator="out" in="SourceGraphic" in2="offset-blur" result="inverse"/>
                <feFlood flood-color="black" flood-opacity="0.5" result="color"/>
                <feComposite operator="in" in="color" in2="inverse" result="shadow"/>
                <feComposite operator="over" in="shadow" in2="SourceGraphic"/>
            </filter>

            <!-- 球体立体高光滤镜与渐变 -->
            <radialGradient id="ball-highlight" cx="30%" cy="30%" r="70%">
                <stop offset="0%" stop-color="#ffffff" stop-opacity="0.6"/>
                <stop offset="50%" stop-color="#ffffff" stop-opacity="0"/>
                <stop offset="100%" stop-color="#000000" stop-opacity="0.4"/>
            </radialGradient>

            <filter id="drop-shadow" x="-20%" y="-20%" width="150%" height="150%">
                <feDropShadow dx="3" dy="5" stdDeviation="3" flood-color="#000" flood-opacity="0.4"/>
            </filter>

            <filter id="glow" x="-20%" y="-20%" width="140%" height="140%">
                <feGaussianBlur stdDeviation="5" result="blur" />
                <feComposite in="SourceGraphic" in2="blur" operator="over" />
            </filter>
        </defs>

        <!-- 台球桌外框 -->
        <rect x="40" y="40" width="1120" height="620" rx="30" fill="#111827" />
        <rect x="50" y="50" width="1100" height="600" rx="20" fill="#1F2937" />
        <!-- 台球桌布 -->
        <rect x="70" y="70" width="1060" height="560" rx="10" fill="#064E3B" filter="url(#inner-shadow)" />
        
        <!-- 发球线 -->
        <line x1="300" y1="70" x2="300" y2="630" stroke="#047857" stroke-width="2" opacity="0.5" />
        <circle cx="300" cy="350" r="40" fill="none" stroke="#047857" stroke-width="2" opacity="0.5" />

        <!-- 球袋洞口 -->
        <g id="pockets-layer">
            <circle cx="80" cy="80" r="28" fill="#030712" />
            <circle cx="600" cy="70" r="25" fill="#030712" />
            <circle cx="1120" cy="80" r="28" fill="#030712" />
            <circle cx="80" cy="620" r="28" fill="#030712" />
            <circle cx="600" cy="630" r="25" fill="#030712" />
            <circle cx="1120" cy="620" r="28" fill="#030712" />
        </g>

        <!-- 瞄准辅助层 -->
        <g id="aim-layer" style="display: none;">
            <!-- 预测虚线 -->
            <line id="aim-line" x1="0" y1="0" x2="0" y2="0" stroke="#FFFFFF" stroke-width="1.5" stroke-dasharray="6 4" opacity="0.6" />
            <!-- 力度指示圈 -->
            <circle id="power-ring" cx="0" cy="0" r="0" fill="none" stroke="#FACC15" stroke-width="2" opacity="0.8" />
            <!-- 球杆 -->
            <g id="cue-stick">
                <rect x="0" y="-3" width="400" height="6" fill="#D1D5DB" rx="2" />
                <rect x="0" y="-3" width="20" height="6" fill="#3B82F6" rx="2" /> <!-- 杆头 -->
                <rect x="250" y="-4" width="150" height="8" fill="#1F2937" rx="2" /> <!-- 握把 -->
            </g>
        </g>

        <!-- 球体渲染层 -->
        <g id="balls-layer"></g>

        <!-- UI 层 (瑞士风格排版) -->
        <g id="ui-layer">
            <!-- 左上角标题 -->
            <text x="40" y="30" fill="#9CA3AF" font-size="12" font-weight="700" letter-spacing="2">PROJECT: BILLIARDS</text>
            <text x="210" y="30" fill="#4B5563" font-size="12" font-weight="400">/ 动效与物理驱动</text>
            
            <!-- 右上角分数 -->
            <text x="1160" y="30" fill="#9CA3AF" font-size="12" font-weight="700" letter-spacing="2" text-anchor="end">SCORE</text>
            <text id="score-text" x="1160" y="65" fill="#FFFFFF" font-size="36" font-weight="700" text-anchor="end" font-variant-numeric="tabular-nums">000</text>

            <!-- 底部状态栏 -->
            <text x="40" y="685" fill="#9CA3AF" font-size="10" font-weight="700" letter-spacing="1">STATUS</text>
            <text id="status-text" x="100" y="685" fill="#FACC15" font-size="10" font-weight="700" letter-spacing="1">AWAITING BREAK</text>

            <!-- 操作提示 -->
            <text x="1160" y="685" fill="#6B7280" font-size="10" font-weight="400" text-anchor="end">拖拽白球调整角度与力度发球</text>
        </g>

        <!-- 进球特效层 -->
        <g id="fx-layer"></g>

        <!-- 交互光标 -->
        <g id="cursor-group" pointer-events="none">
            <circle cx="0" cy="0" r="6" fill="none" stroke="#FFFFFF" stroke-width="1.5" />
            <circle cx="0" cy="0" r="1" fill="#FACC15" />
        </g>
    </svg>
</div>

<script>
    /**
     * 极简 2D 物理引擎与台球逻辑
     */
    const svg = document.getElementById('game-svg');
    const ballsLayer = document.getElementById('balls-layer');
    const aimLayer = document.getElementById('aim-layer');
    const aimLine = document.getElementById('aim-line');
    const cueStick = document.getElementById('cue-stick');
    const powerRing = document.getElementById('power-ring');
    const scoreText = document.getElementById('score-text');
    const statusText = document.getElementById('status-text');
    const fxLayer = document.getElementById('fx-layer');
    const cursorGroup = document.getElementById('cursor-group');

    // 物理与几何常量
    const TABLE_BOUNDS = { left: 70, right: 1130, top: 70, bottom: 630 };
    const POCKETS = [
        { x: 80, y: 80 }, { x: 600, y: 70 }, { x: 1120, y: 80 },
        { x: 80, y: 620 }, { x: 600, y: 630 }, { x: 1120, y: 620 }
    ];
    const POCKET_RADIUS = 30;
    const BALL_RADIUS = 14;
    const FRICTION = 0.988; // 摩擦力衰减
    const RESTITUTION = 0.92; // 碰撞弹性系数
    const MAX_POWER = 35; // 最大击球速度
    const MIN_VELOCITY = 0.05; // 停止阈值

    let balls = [];
    let score = 0;
    let gameState = 'IDLE'; // IDLE, AIMING, ROLLING
    let dragStart = { x: 0, y: 0 };
    let dragCurrent = { x: 0, y: 0 };
    let cueBall = null;

    // 颜色配置
    const COLORS = [
        '#FACC15', '#3B82F6', '#EF4444', '#8B5CF6', 
        '#F97316', '#10B981', '#881337', '#111827' // 黑球最后
    ];

    /**
     * 初始化球局
     */
    function initBalls() {
        balls = [];
        ballsLayer.innerHTML = '';

        // 白球
        cueBall = createBall('cue', 300, 350, '#FFFFFF');
        
        // 摆放目标球 (三角形阵列)
        const startX = 850;
        const startY = 350;
        const rowSpacing = BALL_RADIUS * Math.sqrt(3) + 1;
        const colSpacing = BALL_RADIUS * 2 + 1;

        let colorIdx = 0;
        let ballId = 1;

        for (let row = 0; row < 5; row++) {
            for (let col = 0; col <= row; col++) {
                const x = startX + row * rowSpacing;
                const y = startY - (row * colSpacing) / 2 + col * colSpacing;
                
                // 黑球放在中间特定位置 (第3排中间)
                let color = '#EF4444'; // 默认红球
                if (row === 2 && col === 1) {
                    color = '#111827'; // 黑球
                } else {
                    color = COLORS[colorIdx % (COLORS.length - 1)];
                    colorIdx++;
                }

                createBall(`target-${ballId++}`, x, y, color);
            }
        }
    }

    function createBall(id, x, y, color) {
        const ball = {
            id, x, y, vx: 0, vy: 0, mass: 1, radius: BALL_RADIUS, color, active: true
        };
        balls.push(ball);

        const g = document.createElementNS('http://www.w3.org/2000/svg', 'g');
        g.setAttribute('id', id);
        g.setAttribute('transform', `translate(${x}, ${y})`);
        
        // 投影阴影
        const shadow = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
        shadow.setAttribute('r', BALL_RADIUS);
        shadow.setAttribute('fill', 'none');
        shadow.setAttribute('filter', 'url(#drop-shadow)');
        
        // 基础颜色
        const circle = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
        circle.setAttribute('r', BALL_RADIUS);
        circle.setAttribute('fill', color);
        
        // 立体高光
        const highlight = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
        highlight.setAttribute('r', BALL_RADIUS);
        highlight.setAttribute('fill', 'url(#ball-highlight)');

        g.appendChild(shadow);
        g.appendChild(circle);
        g.appendChild(highlight);
        ballsLayer.appendChild(g);

        ball.element = g;
        return ball;
    }

    /**
     * 物理更新核心
     */
    function updatePhysics() {
        let isMoving = false;

        // 1. 更新位置与摩擦力
        for (let ball of balls) {
            if (!ball.active) continue;

            ball.x += ball.vx;
            ball.y += ball.vy;

            ball.vx *= FRICTION;
            ball.vy *= FRICTION;

            if (Math.abs(ball.vx) < MIN_VELOCITY) ball.vx = 0;
            if (Math.abs(ball.vy) < MIN_VELOCITY) ball.vy = 0;

            if (ball.vx !== 0 || ball.vy !== 0) isMoving = true;

            // 2. 边界碰撞检测
            if (ball.x - ball.radius < TABLE_BOUNDS.left) {
                ball.x = TABLE_BOUNDS.left + ball.radius;
                ball.vx *= -RESTITUTION;
            } else if (ball.x + ball.radius > TABLE_BOUNDS.right) {
                ball.x = TABLE_BOUNDS.right - ball.radius;
                ball.vx *= -RESTITUTION;
            }

            if (ball.y - ball.radius < TABLE_BOUNDS.top) {
                ball.y = TABLE_BOUNDS.top + ball.radius;
                ball.vy *= -RESTITUTION;
            } else if (ball.y + ball.radius > TABLE_BOUNDS.bottom) {
                ball.y = TABLE_BOUNDS.bottom - ball.radius;
                ball.vy *= -RESTITUTION;
            }
        }

        // 3. 球与球之间的碰撞 (使用动量守恒)
        for (let i = 0; i < balls.length; i++) {
            for (let j = i + 1; j < balls.length; j++) {
                const b1 = balls[i];
                const b2 = balls[j];
                if (!b1.active || !b2.active) continue;

                const dx = b2.x - b1.x;
                const dy = b2.y - b1.y;
                const dist = Math.hypot(dx, dy);
                const minDist = b1.radius + b2.radius;

                if (dist < minDist) {
                    // 分离重叠
                    const overlap = (minDist - dist) / 2;
                    const nx = dx / dist;
                    const ny = dy / dist;

                    b1.x -= overlap * nx;
                    b1.y -= overlap * ny;
                    b2.x += overlap * nx;
                    b2.y += overlap * ny;

                    // 相对速度
                    const kx = b1.vx - b2.vx;
                    const ky = b1.vy - b2.vy;

                    // 沿法线的冲量
                    const p = 2 * (nx * kx + ny * ky) / (b1.mass + b2.mass);

                    // 应用冲量与弹性系数
                    b1.vx -= p * b2.mass * nx * RESTITUTION;
                    b1.vy -= p * b2.mass * ny * RESTITUTION;
                    b2.vx += p * b1.mass * nx * RESTITUTION;
                    b2.vy += p * b1.mass * ny * RESTITUTION;
                }
            }
        }

        // 4. 进袋检测
        for (let ball of balls) {
            if (!ball.active) continue;
            for (let pocket of POCKETS) {
                const dist = Math.hypot(ball.x - pocket.x, ball.y - pocket.y);
                if (dist < POCKET_RADIUS) {
                    handlePocketIn(ball, pocket);
                    break;
                }
            }
        }

        // 状态切换
        if (gameState === 'ROLLING' && !isMoving) {
            gameState = 'IDLE';
            statusText.textContent = 'READY / 等待击球';
            statusText.setAttribute('fill', '#3B82F6');
            
            // 检查白球是否进袋
            if (!cueBall.active) {
                resetCueBall();
            }
        }
    }

    function handlePocketIn(ball, pocket) {
        ball.active = false;
        ball.vx = 0;
        ball.vy = 0;
        
        // 视觉效果：球缩小消失
        ball.element.style.transition = 'all 0.3s ease-in';
        ball.element.setAttribute('transform', `translate(${pocket.x}, ${pocket.y}) scale(0)`);
        
        if (ball.id !== 'cue') {
            const points = ball.color === '#111827' ? 50 : 10;
            updateScore(points);
            showFloatingText(`+${points}`, pocket.x, pocket.y - 20);
        } else {
            showFloatingText("FOUL", pocket.x, pocket.y - 20, '#EF4444');
            updateScore(-20);
        }
    }

    function resetCueBall() {
        cueBall.x = 300;
        cueBall.y = 350;
        cueBall.vx = 0;
        cueBall.vy = 0;
        cueBall.active = true;
        cueBall.element.style.transition = 'none';
        cueBall.element.setAttribute('transform', `translate(${cueBall.x}, ${cueBall.y}) scale(1)`);
    }

    function updateScore(delta) {
        score = Math.max(0, score + delta);
        scoreText.textContent = score.toString().padStart(3, '0');
    }

    function showFloatingText(text, x, y, color = '#FACC15') {
        const textEl = document.createElementNS('http://www.w3.org/2000/svg', 'text');
        textEl.setAttribute('x', x);
        textEl.setAttribute('y', y);
        textEl.setAttribute('class', 'score-popup');
        textEl.setAttribute('text-anchor', 'middle');
        textEl.setAttribute('fill', color);
        textEl.textContent = text;
        fxLayer.appendChild(textEl);
        
        setTimeout(() => {
            if (fxLayer.contains(textEl)) fxLayer.removeChild(textEl);
        }, 1000);
    }

    /**
     * 渲染同步
     */
    function render() {
        for (let ball of balls) {
            if (ball.active) {
                ball.element.setAttribute('transform', `translate(${ball.x}, ${ball.y})`);
            }
        }
    }

    /**
     * 交互逻辑 (鼠标/触摸)
     */
    function getMousePos(e) {
        const pt = svg.createSVGPoint();
        pt.x = e.clientX || (e.touches && e.touches[0].clientX);
        pt.y = e.clientY || (e.touches && e.touches[0].clientY);
        return pt.matrixTransform(svg.getScreenCTM().inverse());
    }

    svg.addEventListener('pointermove', (e) => {
        const pos = getMousePos(e);
        
        // 更新光标
        cursorGroup.setAttribute('transform', `translate(${pos.x}, ${pos.y})`);

        if (gameState === 'AIMING') {
            dragCurrent = pos;
            updateAimingVisuals();
        }
    });

    svg.addEventListener('pointerdown', (e) => {
        if (gameState !== 'IDLE' || !cueBall.active) return;
        
        const pos = getMousePos(e);
        // 判断是否点在白球附近
        const distToCue = Math.hypot(pos.x - cueBall.x, pos.y - cueBall.y);
        
        if (distToCue < BALL_RADIUS * 4) {
            gameState = 'AIMING';
            dragStart = pos;
            dragCurrent = pos;
            aimLayer.style.display = 'block';
            statusText.textContent = 'AIMING / 蓄力中...';
            statusText.setAttribute('fill', '#FACC15');
            svg.setPointerCapture(e.pointerId);
        }
    });

    svg.addEventListener('pointerup', (e) => {
        if (gameState === 'AIMING') {
            gameState = 'ROLLING';
            aimLayer.style.display = 'none';
            svg.releasePointerCapture(e.pointerId);
            statusText.textContent = 'ROLLING / 运动中';
            statusText.setAttribute('fill', '#10B981');

            // 计算拉力向量并发球
            const dx = dragStart.x - dragCurrent.x;
            const dy = dragStart.y - dragCurrent.y;
            const distance = Math.hypot(dx, dy);
            
            if (distance > 5) { // 最小击球力度防误触
                const power = Math.min(distance * 0.15, MAX_POWER);
                const angle = Math.atan2(dy, dx);
                
                cueBall.vx = Math.cos(angle) * power;
                cueBall.vy = Math.sin(angle) * power;
            } else {
                gameState = 'IDLE';
                statusText.textContent = 'READY / 等待击球';
            }
        }
    });

    function updateAimingVisuals() {
        const dx = dragStart.x - dragCurrent.x;
        const dy = dragStart.y - dragCurrent.y;
        const distance = Math.hypot(dx, dy);
        const angle = Math.atan2(dy, dx);
        
        // 限制最大拉力视觉
        const visualDist = Math.min(distance, 200);
        
        // 预测线 (白球出发方向)
        aimLine.setAttribute('x1', cueBall.x);
        aimLine.setAttribute('y1', cueBall.y);
        aimLine.setAttribute('x2', cueBall.x + Math.cos(angle) * 500);
        aimLine.setAttribute('y2', cueBall.y + Math.sin(angle) * 500);

        // 力度圈
        powerRing.setAttribute('cx', cueBall.x);
        powerRing.setAttribute('cy', cueBall.y);
        powerRing.setAttribute('r', BALL_RADIUS + visualDist * 0.2);
        const powerRatio = visualDist / 200;
        powerRing.setAttribute('stroke', powerRatio > 0.8 ? '#EF4444' : (powerRatio > 0.4 ? '#F97316' : '#FACC15'));

        // 球杆 (在白球反方向，且根据力度后退)
        const cueAngleDeg = angle * (180 / Math.PI) + 180;
        const cueOffset = BALL_RADIUS + 5 + visualDist * 0.5;
        cueStick.setAttribute('transform', `translate(${cueBall.x}, ${cueBall.y}) rotate(${cueAngleDeg}) translate(${cueOffset}, 0)`);
    }

    /**
     * 主循环
     */
    function gameLoop() {
        updatePhysics();
        render();
        requestAnimationFrame(gameLoop);
    }

    // 启动
    initBalls();
    gameLoop();

    // 防止默认拖拽和右键菜单影响体验
    document.addEventListener('contextmenu', e => e.preventDefault());
    document.addEventListener('dragstart', e => e.preventDefault());

</script>
</body>
</html>17:T599,Create a high-quality isometric animation based on SVG code. Two minimalist modern smartphones are positioned in the center of the screen, tilted at a 30-degree angle with rounded edges and a matte texture. The left phone screen features a soft mint green UI representing the sender, while the right phone screen features a serene sky blue UI with a simplified digital wallet icon representing the receiver. The core of the animation is a bright, flat-designed gold coin with subtle edge highlights. At the start, the coin pops out from the center of the left screen with a spring easing effect and a slight vertical displacement. It follows an elegant parabolic trajectory made of dynamic flowing dashed lines in translucent deep cyan, creating an anisotropic flow sensation in the direction of movement. Upon touching the right phone screen, the digital wallet icon produces a subtle scaling 'breathing' or ripple effect to simulate the 'received' feedback, and the coin smoothly shrinks and disappears into the wallet opening. The overall visual style strictly adheres to Flat Design, using no complex shadows and relying on color brightness for layering. The background is a very light off-white (#F5F7FA), creating a clean, professional fintech atmosphere. The animation is set to a 2-3 second seamless loop, using Ease-in-out easing functions between keyframes to ensure smooth and natural movement of the coin and UI feedback.18:T3bcd,<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Isometric Crypto Transfer Animation</title>
    <style>
        :root {
            --bg-color: #F5F7FA;
            --mint-primary: #34D399;
            --mint-light: #D1FAE5;
            --sky-primary: #38BDF8;
            --sky-light: #E0F2FE;
            --text-main: #334155;
            --text-sub: #94A3B8;
            --phone-edge: #CBD5E1;
            --phone-face: #FFFFFF;
        }

        body, html {
            margin: 0;
            padding: 0;
            width: 100%;
            height: 100%;
            background-color: var(--bg-color);
            display: flex;
            align-items: center;
            justify-content: center;
            font-family: 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
            overflow: hidden;
        }

        #animation-container {
            width: 100%;
            height: 100%;
            max-width: 1200px;
            max-height: 700px;
            position: relative;
        }

        svg {
            width: 100%;
            height: 100%;
            display: block;
            user-select: none;
        }

        /* 轨迹虚线流动动画 */
        @keyframes dashFlow {
            from { stroke-dashoffset: 24; }
            to { stroke-dashoffset: 0; }
        }

        .trajectory-line {
            stroke-dasharray: 12 12;
            animation: dashFlow 0.8s linear infinite;
        }

        /* 提升渲染质量 */
        * {
            shape-rendering: geometricPrecision;
            text-rendering: optimizeLegibility;
        }
    </style>
</head>
<body>

<div id="animation-container">
    <svg viewBox="0 0 1200 700" preserveAspectRatio="xMidYMid meet">
        <defs>
            <!-- 手机主体阴影 -->
            <filter id="phone-shadow" x="-20%" y="-20%" width="150%" height="150%">
                <feDropShadow dx="0" dy="24" stdDeviation="16" flood-color="#64748B" flood-opacity="0.15" />
            </filter>

            <!-- 金币发光效果 -->
            <filter id="coin-glow" x="-50%" y="-50%" width="200%" height="200%">
                <feGaussianBlur stdDeviation="6" result="blur" />
                <feComposite in="SourceGraphic" in2="blur" operator="over" />
            </filter>

            <!-- 渐变定义 -->
            <linearGradient id="mint-grad" x1="0%" y1="0%" x2="100%" y2="100%">
                <stop offset="0%" stop-color="#6EE7B7" />
                <stop offset="100%" stop-color="#10B981" />
            </linearGradient>
            
            <linearGradient id="sky-grad" x1="0%" y1="0%" x2="100%" y2="100%">
                <stop offset="0%" stop-color="#7DD3FC" />
                <stop offset="100%" stop-color="#0284C7" />
            </linearGradient>
        </defs>

        <!-- 背景装饰网格 (极简) -->
        <g opacity="0.4" stroke="#E2E8F0" stroke-width="1">
            <line x1="0" y1="350" x2="1200" y2="350" stroke-dasharray="4 8" />
            <line x1="600" y1="0" x2="600" y2="700" stroke-dasharray="4 8" />
        </g>

        <!-- 抛物线轨迹 (在手机和金币底层) -->
        <path id="fly-path" class="trajectory-line" 
              d="M 320,480 C 320,180 720,80 920,220" 
              fill="none" stroke="rgba(14, 116, 144, 0.3)" stroke-width="4" stroke-linecap="round" />

        <!-- ================= 左侧手机 (发送端) ================= -->
        <g id="left-phone" transform="translate(320, 480)">
            <!-- 侧边厚度层 (向下偏移产生3D感) -->
            <g transform="translate(0, 18)">
                <g transform="matrix(0.866, 0.5, -0.866, 0.5, 0, 0)">
                    <rect x="-85" y="-175" width="170" height="350" rx="28" fill="var(--phone-edge)" filter="url(#phone-shadow)"/>
                </g>
            </g>
            
            <!-- 顶面层 (屏幕与面板) -->
            <g transform="matrix(0.866, 0.5, -0.866, 0.5, 0, 0)">
                <!-- 手机外壳 -->
                <rect x="-85" y="-175" width="170" height="350" rx="28" fill="var(--phone-face)" />
                <!-- 屏幕内框 -->
                <rect x="-75" y="-165" width="150" height="330" rx="20" fill="#F8FAFC" />
                
                <!-- UI 界面: 薄荷绿主题 -->
                <g transform="translate(0, -70)">
                    <!-- 顶部背景 -->
                    <path d="M -75,-95 L 75,-95 L 75,30 Q 0,60 -75,30 Z" fill="var(--mint-light)" opacity="0.6"/>
                    
                    <!-- 标题 -->
                    <text x="0" y="-60" fill="var(--mint-primary)" font-size="14" font-weight="bold" text-anchor="middle" letter-spacing="1">资产转账</text>
                    
                    <!-- 金额显示区 -->
                    <rect x="-55" y="-35" width="110" height="50" rx="12" fill="#FFFFFF" opacity="0.9" />
                    <text x="0" y="-15" fill="var(--text-sub)" font-size="10" text-anchor="middle">发送金额 (CNY)</text>
                    <text x="0" y="5" fill="var(--text-main)" font-size="16" font-weight="bold" text-anchor="middle">¥ 10,000</text>
                    
                    <!-- 骨架屏装饰线条 -->
                    <rect x="-55" y="45" width="110" height="8" rx="4" fill="#E2E8F0" />
                    <rect x="-55" y="65" width="80" height="8" rx="4" fill="#E2E8F0" />
                    <rect x="-55" y="85" width="95" height="8" rx="4" fill="#E2E8F0" />
                </g>

                <!-- 发送按钮 (带交互动画) -->
                <g id="send-btn-group" transform="translate(0, 110)">
                    <rect x="-60" y="-22" width="120" height="44" rx="22" fill="url(#mint-grad)" />
                    <text x="0" y="5" fill="#FFFFFF" font-size="14" font-weight="bold" text-anchor="middle" letter-spacing="2">确认发送</text>
                </g>
            </g>
        </g>

        <!-- ================= 右侧手机 (接收端) ================= -->
        <g id="right-phone" transform="translate(920, 220)">
            <!-- 侧边厚度层 -->
            <g transform="translate(0, 18)">
                <g transform="matrix(0.866, 0.5, -0.866, 0.5, 0, 0)">
                    <rect x="-85" y="-175" width="170" height="350" rx="28" fill="var(--phone-edge)" filter="url(#phone-shadow)"/>
                </g>
            </g>
            
            <!-- 顶面层 -->
            <g transform="matrix(0.866, 0.5, -0.866, 0.5, 0, 0)">
                <!-- 手机外壳 -->
                <rect x="-85" y="-175" width="170" height="350" rx="28" fill="var(--phone-face)" />
                <!-- 屏幕内框 -->
                <rect x="-75" y="-165" width="150" height="330" rx="20" fill="#F0F9FF" />
                
                <!-- UI 界面: 天空蓝主题 -->
                <text x="0" y="-120" fill="var(--sky-primary)" font-size="14" font-weight="bold" text-anchor="middle" letter-spacing="1">数字钱包</text>
                
                <rect x="-60" y="-90" width="120" height="6" rx="3" fill="#BAE6FD" opacity="0.5" />
                <rect x="-40" y="-75" width="80" height="6" rx="3" fill="#BAE6FD" opacity="0.5" />

                <!-- 入账涟漪特效圈 -->
                <circle id="wallet-ripple-1" cx="0" cy="10" r="0" fill="none" stroke="var(--sky-primary)" stroke-width="4" opacity="0" />
                <circle id="wallet-ripple-2" cx="0" cy="10" r="0" fill="none" stroke="var(--sky-primary)" stroke-width="2" opacity="0" />

                <!-- 钱包图标核心组件 -->
                <g id="wallet-icon-group" transform="translate(0, 10)">
                    <!-- 钱包后盖 -->
                    <rect x="-44" y="-28" width="88" height="56" rx="10" fill="#7DD3FC" />
                    <!-- 内部卡片露出 -->
                    <rect x="-30" y="-36" width="60" height="20" rx="4" fill="#FFFFFF" opacity="0.8" />
                    <rect x="-20" y="-30" width="40" height="4" rx="2" fill="#E2E8F0" />
                    <!-- 钱包前盖 -->
                    <rect x="-44" y="-8" width="88" height="36" rx="10" fill="url(#sky-grad)" />
                    <!-- 钱包扣子 -->
                    <circle cx="0" cy="-8" r="7" fill="#FFFFFF" />
                    <circle cx="0" cy="-8" r="3" fill="#0284C7" />
                    <!-- 钱包开口缝隙标识 -->
                    <path d="M -30,-8 L -15,-8 M 15,-8 L 30,-8" stroke="#0284C7" stroke-width="2" stroke-linecap="round" opacity="0.4" />
                </g>

                <!-- 底部状态文本 -->
                <rect x="-50" y="100" width="100" height="30" rx="15" fill="#FFFFFF" opacity="0.8" />
                <text id="receive-status" x="0" y="120" fill="var(--text-sub)" font-size="12" font-weight="bold" text-anchor="middle">等待接收...</text>
            </g>
        </g>

        <!-- ================= 飞行的金币 ================= -->
        <!-- 放置在最顶层，不受等距投影矩阵影响，保持正视扁平感 -->
        <g id="coin-master" transform="translate(320, 480)">
            <g id="coin-scaler" transform="scale(0)">
                <g filter="url(#coin-glow)">
                    <!-- 金币厚度/阴影 -->
                    <circle cx="0" cy="4" r="26" fill="#D97706" />
                    <!-- 金币主面 -->
                    <circle cx="0" cy="0" r="26" fill="#FBBF24" />
                    <!-- 边缘高光 -->
                    <path d="M -20,0 A 20 20 0 0 1 20,0" fill="none" stroke="#FDE68A" stroke-width="3" stroke-linecap="round" />
                    <!-- 内部图案 (极简菱形/星芒代表数字资产) -->
                    <path d="M 0,-14 L 6,0 L 0,14 L -6,0 Z" fill="#FFFBEB" opacity="0.9" />
                    <path d="M -14,0 L 0,-6 L 14,0 L 0,6 Z" fill="#FFFBEB" opacity="0.9" />
                </g>
            </g>
        </g>
    </svg>
</div>

<script>
    document.addEventListener("DOMContentLoaded", () => {
        // 获取DOM元素
        const path = document.getElementById('fly-path');
        const coinMaster = document.getElementById('coin-master');
        const coinScaler = document.getElementById('coin-scaler');
        const walletGroup = document.getElementById('wallet-icon-group');
        const ripple1 = document.getElementById('wallet-ripple-1');
        const ripple2 = document.getElementById('wallet-ripple-2');
        const sendBtn = document.getElementById('send-btn-group');
        const statusText = document.getElementById('receive-status');
        
        const pathLength = path.getTotalLength();
        const DURATION = 2800; // 动画总时长 (ms)
        let startTime = null;

        // 缓动函数定义
        // Quartic Ease In Out (平滑加速后减速)
        const easeInOutQuart = (t) => t < 0.5 ? 8 * t * t * t * t : 1 - Math.pow(-2 * t + 2, 4) / 2;
        
        // Elastic Ease Out (弹簧效果)
        const easeOutElastic = (t) => {
            const c4 = (2 * Math.PI) / 3;
            return t === 0 ? 0 : t === 1 ? 1 : Math.pow(2, -10 * t) * Math.sin((t * 10 - 0.75) * c4) + 1;
        };

        // Cubic Ease In (加速缩小)
        const easeInCubic = (t) => t * t * t;

        // 核心动画主循环 (严禁叠加，仅启动一次)
        function renderLoop(timestamp) {
            if (!startTime) startTime = timestamp;
            const elapsed = (timestamp - startTime) % DURATION;
            const t = elapsed / DURATION; // 全局进度 0.0 ~ 1.0

            // --------------------------------------------------
            // 阶段规划:
            // 0.00 - 0.10: 按钮按下，金币弹簧弹出
            // 0.10 - 0.85: 金币沿抛物线飞行
            // 0.85 - 0.95: 金币到达，缩入钱包，钱包呼吸，涟漪扩散
            // 0.95 - 1.00: 状态重置，静默等待下一轮
            // --------------------------------------------------

            // 1. 发送按钮交互 (0.0 - 0.1)
            let btnScale = 1;
            if (t < 0.1) {
                const btnT = t / 0.1;
                btnScale = 1 - Math.sin(btnT * Math.PI) * 0.08; // 轻微按压反弹
            }
            sendBtn.setAttribute('transform', `translate(0, 110) scale(${btnScale})`);

            // 2. 金币位置计算 (0.1 - 0.85)
            let moveT = 0;
            if (t > 0.1 && t < 0.85) {
                const normT = (t - 0.1) / 0.75;
                moveT = easeInOutQuart(normT);
            } else if (t >= 0.85) {
                moveT = 1;
            }
            
            // 获取路径上的坐标
            const point = path.getPointAtLength(moveT * pathLength);
            coinMaster.setAttribute('transform', `translate(${point.x}, ${point.y})`);

            // 3. 金币缩放与弹簧效果
            let coinScale = 0;
            if (t < 0.1) {
                // 弹簧弹出
                coinScale = easeOutElastic(t / 0.1);
            } else if (t >= 0.1 && t < 0.85) {
                // 飞行中保持原大小
                coinScale = 1;
            } else if (t >= 0.85 && t < 0.95) {
                // 缩入钱包消失
                const shrinkT = (t - 0.85) / 0.1;
                coinScale = 1 - easeInCubic(shrinkT);
            }
            coinScaler.setAttribute('transform', `scale(${coinScale})`);

            // 4. 钱包接收反馈 (0.85 - 0.95)
            let walletScale = 1;
            let rScale = 0;
            let rOp = 0;

            if (t >= 0.82 && t < 0.98) {
                // 稍微提前一点点触发钱包呼吸，让衔接更自然
                const wT = (t - 0.82) / 0.16;
                walletScale = 1 + Math.sin(wT * Math.PI) * 0.12; // 放大再缩小
                
                // 涟漪效果
                if (t >= 0.85) {
                    const ripT = (t - 0.85) / 0.1;
                    rScale = ripT;
                    rOp = 1 - ripT;
                }
            }
            walletGroup.setAttribute('transform', `translate(0, 10) scale(${walletScale})`);
            
            ripple1.setAttribute('r', rScale * 60);
            ripple1.setAttribute('opacity', rOp);
            
            // 第二层涟漪稍有延迟
            let r2Scale = 0, r2Op = 0;
            if (t >= 0.87 && t < 0.97) {
                const rip2T = (t - 0.87) / 0.1;
                r2Scale = rip2T;
                r2Op = 1 - rip2T;
            }
            ripple2.setAttribute('r', r2Scale * 45);
            ripple2.setAttribute('opacity', r2Op * 0.8);

            // 5. 状态文字更新
            if (t > 0.85 && t < 0.98) {
                statusText.textContent = "入账成功 +10,000";
                statusText.setAttribute('fill', 'var(--mint-primary)');
            } else {
                statusText.textContent = "等待接收...";
                statusText.setAttribute('fill', 'var(--text-sub)');
            }

            // 循环调用
            requestAnimationFrame(renderLoop);
        }

        // 启动唯一动画主循环
        requestAnimationFrame(renderLoop);
    });
</script>
</body>
</html>19:T85d5,<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>马年起飞 - 坦克大战2026 (加强版)</title>
    <!-- 引入像素字体 -->
    <link href="https://fonts.googleapis.com/css2?family=Press+Start+2P&family=ZCOOL+KuaiLe&display=swap" rel="stylesheet">
    <!-- 引入烟花库 -->
    <script src="https://cdn.jsdelivr.net/npm/canvas-confetti@1.6.0/dist/confetti.browser.min.js"></script>
    <style>
        :root {
            --bg-color: #000000;
            --wall-color: #b53120;
            --wall-stroke: #802015;
            --text-inactive: #4a4a4a;
            --text-active: #f7d010;
            --player-color: #f7d010;
            --enemy-color: #cccccc;
        }

        body, html {
            margin: 0;
            padding: 0;
            width: 100%;
            height: 100%;
            background-color: var(--bg-color);
            overflow: hidden;
            font-family: 'Press Start 2P', 'ZCOOL KuaiLe', cursive, monospace;
            user-select: none;
        }

        #game-container {
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            width: 1200px;
            height: 700px;
            background-color: #050505;
            box-shadow: 0 0 20px rgba(0, 0, 0, 0.8);
            border: 4px solid #333;
        }

        /* UI Overlay */
        .ui-layer {
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            pointer-events: none;
            display: flex;
            flex-direction: column;
            align-items: center;
            z-index: 10;
        }

        .header-hud {
            margin-top: 20px;
            display: flex;
            gap: 40px;
            align-items: center;
        }

        /* 通用HUD容器样式 (血条和文字共用) */
        .hud-panel {
            display: flex;
            align-items: center;
            gap: 15px;
            background: rgba(0, 0, 0, 0.6);
            padding: 10px 20px;
            border: 2px solid #555;
            border-radius: 4px;
            box-shadow: 0 4px 0 rgba(0,0,0,0.5);
        }

        .char-container {
            display: flex;
            gap: 20px;
        }

        .char-slot {
            font-size: 32px;
            color: var(--text-inactive);
            text-shadow: 2px 2px 0px #000;
            transition: all 0.3s ease;
            position: relative;
        }

        .char-slot.active {
            color: var(--text-active);
            text-shadow: 0 0 10px var(--text-active), 4px 4px 0px #b53120;
            transform: scale(1.2);
            animation: bounce 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275);
        }

        /* HP Bar Styles */
        .hp-label {
            color: #f7d010;
            font-size: 20px;
            text-shadow: 2px 2px 0 #000;
        }

        .hp-bar-bg {
            width: 150px;
            height: 20px;
            background: #333;
            border: 2px solid #fff;
            position: relative;
        }

        .hp-bar-fill {
            width: 100%;
            height: 100%;
            background: #2ecc71;
            transition: width 0.2s ease-out, background-color 0.2s;
        }

        @keyframes bounce {
            0% { transform: scale(1); }
            50% { transform: scale(1.5); }
            100% { transform: scale(1.2); }
        }

        /* SVG Game Layer */
        svg {
            width: 100%;
            height: 100%;
            display: block;
        }

        /* Start/End Screen */
        .overlay-screen {
            position: absolute;
            top: 0; left: 0; width: 100%; height: 100%;
            background: rgba(0,0,0,0.85);
            display: flex;
            flex-direction: column;
            justify-content: center;
            align-items: center;
            z-index: 20;
            color: white;
            text-align: center;
        }

        .hidden { display: none !important; }

        h1 { color: #b53120; text-shadow: 4px 4px 0 #fff; font-size: 60px; margin-bottom: 20px; }
        p { font-size: 18px; line-height: 1.6; color: #ccc; margin-bottom: 40px; }
        .btn {
            background: #b53120;
            color: white;
            border: 4px solid white;
            padding: 15px 40px;
            font-size: 24px;
            font-family: inherit;
            cursor: pointer;
            text-transform: uppercase;
            box-shadow: 6px 6px 0px rgba(255,255,255,0.2);
            pointer-events: auto;
        }
        .btn:hover { background: #d94230; transform: translate(-2px, -2px); box-shadow: 8px 8px 0px rgba(255,255,255,0.2); }
        .btn:active { transform: translate(2px, 2px); box-shadow: 2px 2px 0px rgba(255,255,255,0.2); }

    </style>
</head>
<body>

<div id="game-container">
    <!-- Game SVG -->
    <svg id="game-svg" viewBox="0 0 1200 700" preserveAspectRatio="xMidYMid slice">
        <defs>
            <!-- Brick Pattern -->
            <pattern id="brick-pattern" x="0" y="0" width="20" height="20" patternUnits="userSpaceOnUse">
                <rect width="20" height="20" fill="#b53120"/>
                <path d="M0 10 H20 M10 0 V10 M10 10 V20" stroke="#802015" stroke-width="2"/>
                <rect x="2" y="2" width="16" height="6" fill="#d44530"/>
                <rect x="2" y="12" width="6" height="6" fill="#d44530"/>
                <rect x="12" y="12" width="6" height="6" fill="#d44530"/>
            </pattern>
            
            <!-- Glow Filter for Red Enemies -->
            <filter id="red-glow" x="-50%" y="-50%" width="200%" height="200%">
                <feGaussianBlur in="SourceGraphic" stdDeviation="4" result="blur" />
                <feColorMatrix in="blur" type="matrix" values="
                    0 0 0 0 1
                    0 0 0 0 0
                    0 0 0 0 0
                    0 0 0 1 0" result="redBlur" />
                <feMerge>
                    <feMergeNode in="redBlur" />
                    <feMergeNode in="SourceGraphic" />
                </feMerge>
            </filter>

            <!-- Gold Shine Filter -->
            <filter id="gold-shine">
                <feSpecularLighting result="spec" specularConstant="1.2" specularExponent="20" lighting-color="#fff">
                    <fePointLight x="10" y="10" z="20"/>
                </feSpecularLighting>
                <feComposite in="spec" in2="SourceAlpha" operator="in" result="specOut"/>
                <feComposite in="SourceGraphic" in2="specOut" operator="arithmetic" k1="0" k2="1" k3="1" k4="0"/>
            </filter>
        </defs>

        <g id="layer-ground">
            <rect width="1200" height="700" fill="#111"/>
            <!-- Grid Lines -->
            <path d="M0 0" stroke="#222" stroke-width="1" id="grid-lines"/>
        </g>
        
        <g id="layer-base"></g>
        <g id="layer-walls"></g>
        <g id="layer-items"></g> <!-- Layer for PowerUps -->
        <g id="layer-bullets"></g>
        <g id="layer-enemies"></g>
        <g id="layer-player"></g>
        <g id="layer-fx"></g>
    </svg>

    <!-- UI Overlay -->
    <div class="ui-layer">
        <div class="header-hud">
            <!-- HP Bar -->
            <div class="hud-panel">
                <div class="hp-label">1P HP</div>
                <div class="hp-bar-bg">
                    <div id="player-hp-bar" class="hp-bar-fill"></div>
                </div>
            </div>
            <!-- Text Collection (Now with border) -->
            <div class="hud-panel">
                <div class="char-container">
                    <div class="char-slot" id="char-0">马</div>
                    <div class="char-slot" id="char-1">年</div>
                    <div class="char-slot" id="char-2">起</div>
                    <div class="char-slot" id="char-3">飞</div>
                </div>
            </div>
        </div>
    </div>

    <!-- Start Screen -->
    <div id="start-screen" class="overlay-screen">
        <h1>TANK 2026</h1>
        <p>方向键移动 / 自动射击<br>击毁<span style="color:#ff4444; text-shadow:0 0 5px red">红色发光坦克</span>点亮文字<br>击毁<span style="color:#ffd700">金色坦克</span>获取超级武器<br>集齐 "马年起飞" 召唤烟花</p>
        <button class="btn" onclick="startGame()">开始游戏</button>
    </div>

    <!-- Win Screen -->
    <div id="win-screen" class="overlay-screen hidden">
        <h1 style="color:#f7d010">胜利!</h1>
        <p>2026 马年大吉<br>所有字符已点亮</p>
        <button class="btn" onclick="location.reload()">再玩一次</button>
    </div>

    <!-- Fail Screen -->
    <div id="fail-screen" class="overlay-screen hidden">
        <h1 style="color:#e74c3c">任务失败</h1>
        <p id="fail-reason">堡垒已沦陷</p>
        <button class="btn" onclick="startGame()">重新挑战</button>
    </div>
</div>

<script>
    /**
     * Constants & Config
     */
    const CONFIG = {
        width: 1200,
        height: 700,
        gridSize: 40,
        playerSpeed: 3,
        enemySpeed: 1.5,
        bulletSpeed: 7,
        fireRate: 400, // ms
        enemyFireRate: 1500,
        maxEnemies: 5,
        spawnInterval: 2000,
        chars: ['马', '年', '起', '飞'],
        colors: {
            player: '#f7d010',
            enemyNormal: '#cccccc', 
            enemyRed: '#e74c3c', 
            enemyGold: '#ffd700',
        },
        maxPlayerHp: 3
    };

    /**
     * Game State
     */
    const state = {
        isRunning: false,
        lastTime: 0,
        keys: { ArrowUp: false, ArrowDown: false, ArrowLeft: false, ArrowRight: false },
        player: null,
        enemies: [],
        bullets: [],
        walls: [],
        items: [], // PowerUps
        progress: [false, false, false, false],
        enemySpawnTimer: 0,
        frameCount: 0,
        base: null
    };

    /**
     * Map Data "2026"
     */
    function generateMap() {
        const map = [];
        const offsetX = 5; 
        const offsetY = 3;

        // Helper to add pixel block
        const addBlock = (x, y) => map.push({ x: (x + offsetX) * CONFIG.gridSize, y: (y + offsetY) * CONFIG.gridSize, w: CONFIG.gridSize, h: CONFIG.gridSize, hp: 1 });

        // Digit 2
        const draw2 = (ox) => {
            [0,1,2,3].forEach(i => addBlock(ox+i, 0)); 
            addBlock(ox+3, 1); addBlock(ox+3, 2);
            [0,1,2,3].forEach(i => addBlock(ox+i, 3)); 
            addBlock(ox, 4); addBlock(ox, 5);
            [0,1,2,3].forEach(i => addBlock(ox+i, 6)); 
        };

        // Digit 0
        const draw0 = (ox) => {
            [0,1,2,3].forEach(i => addBlock(ox+i, 0)); 
            [0,1,2,3].forEach(i => addBlock(ox+i, 6)); 
            [1,2,3,4,5].forEach(i => { addBlock(ox, i); addBlock(ox+3, i); }); 
        };

        // Digit 6
        const draw6 = (ox) => {
            [0,1,2,3].forEach(i => addBlock(ox+i, 0)); 
            [1,2].forEach(i => addBlock(ox, i)); 
            [0,1,2,3].forEach(i => addBlock(ox+i, 3)); 
            [4,5].forEach(i => { addBlock(ox, i); addBlock(ox+3, i); }); 
            [0,1,2,3].forEach(i => addBlock(ox+i, 6)); 
        };

        draw2(0);
        draw0(5);
        draw2(10);
        draw6(15);

        // Base Protection Walls
        const bx = 15; 
        const by = 16;
        
        const baseWalls = [
            {x: bx-1, y: by},   // Left
            {x: bx-1, y: by-1}, // Top-Left
            {x: bx, y: by-1},   // Top
            {x: bx+1, y: by-1}, // Top-Right
            {x: bx+1, y: by}    // Right
        ];

        baseWalls.forEach(p => {
            map.push({ x: p.x * CONFIG.gridSize, y: p.y * CONFIG.gridSize, w: CONFIG.gridSize, h: CONFIG.gridSize, hp: 1 });
        });

        // Random scattered blocks
        for(let i=0; i<12; i++) {
            let rx = Math.floor(Math.random() * 28);
            let ry = Math.floor(Math.random() * 15);
            if (ry > 10 && rx > 10 && rx < 20) continue;
            let exists = map.some(b => b.x === rx * CONFIG.gridSize && b.y === ry * CONFIG.gridSize);
            if(!exists) map.push({ x: rx * CONFIG.gridSize, y: ry * CONFIG.gridSize, w: CONFIG.gridSize, h: CONFIG.gridSize, hp: 1 });
        }

        return map;
    }

    /**
     * SVG Helpers
     */
    const svgNS = "http://www.w3.org/2000/svg";
    const layers = {
        ground: document.getElementById('layer-ground'),
        base: document.getElementById('layer-base'),
        walls: document.getElementById('layer-walls'),
        items: document.getElementById('layer-items'),
        bullets: document.getElementById('layer-bullets'),
        enemies: document.getElementById('layer-enemies'),
        player: document.getElementById('layer-player'),
        fx: document.getElementById('layer-fx')
    };

    function createRect(x, y, w, h, fill, parent) {
        const rect = document.createElementNS(svgNS, 'rect');
        rect.setAttribute('x', x);
        rect.setAttribute('y', y);
        rect.setAttribute('width', w);
        rect.setAttribute('height', h);
        rect.setAttribute('fill', fill);
        if(parent) parent.appendChild(rect);
        return rect;
    }

    function createTankSVG(type) {
        const g = document.createElementNS(svgNS, 'g');
        let mainColor = CONFIG.colors.enemyNormal;
        let isPlayer = type === 'player';
        
        if (isPlayer) mainColor = CONFIG.colors.player;
        else if (type === 'red') mainColor = CONFIG.colors.enemyRed;
        else if (type === 'gold') mainColor = CONFIG.colors.enemyGold;

        // Apply filters
        if (type === 'red') g.setAttribute('filter', 'url(#red-glow)');
        if (type === 'gold') g.setAttribute('filter', 'url(#gold-shine)');

        // Tracks
        createRect(0, 0, 10, 36, '#222', g);
        createRect(30, 0, 10, 36, '#222', g);
        
        // Body
        createRect(5, 5, 30, 26, mainColor, g);
        
        // Turret
        const turret = createRect(12, 10, 16, 16, isPlayer ? '#fff' : '#444', g);
        turret.setAttribute('opacity', '0.6');

        // Gun
        createRect(18, -4, 4, 18, '#111', g); 
        
        // Detail
        if(isPlayer) createRect(16, 14, 8, 8, '#b53120', g);
        if(type === 'red') createRect(16, 14, 8, 8, '#fff', g); // Core for red tank

        return g;
    }

    function createEagleSVG(x, y) {
        const g = document.createElementNS(svgNS, 'g');
        g.setAttribute('transform', `translate(${x}, ${y})`);
        createRect(0, 0, 40, 40, '#000', g);
        const eagleColor = '#bdc3c7';
        createRect(4, 12, 32, 8, eagleColor, g);
        createRect(4, 8, 8, 4, eagleColor, g);
        createRect(28, 8, 8, 4, eagleColor, g);
        createRect(16, 4, 8, 8, eagleColor, g);
        createRect(22, 6, 4, 2, '#e74c3c', g);
        createRect(12, 20, 16, 12, eagleColor, g);
        createRect(12, 32, 4, 4, '#f39c12', g);
        createRect(24, 32, 4, 4, '#f39c12', g);
        createRect(18, 24, 4, 4, '#b53120', g);
        layers.base.appendChild(g);
        return g;
    }

    function createWeaponSVG(x, y) {
        const g = document.createElementNS(svgNS, 'g');
        g.setAttribute('transform', `translate(${x}, ${y})`);
        
        // Star/Weapon shape
        const star = document.createElementNS(svgNS, 'polygon');
        star.setAttribute('points', '20,2 25,14 38,14 28,22 32,34 20,26 8,34 12,22 2,14 15,14');
        star.setAttribute('fill', '#f7d010');
        star.setAttribute('stroke', '#fff');
        star.setAttribute('stroke-width', '2');
        
        // Animation
        const anim = document.createElementNS(svgNS, 'animateTransform');
        anim.setAttribute('attributeName', 'transform');
        anim.setAttribute('type', 'scale');
        anim.setAttribute('values', '0.8;1.2;0.8');
        anim.setAttribute('dur', '1s');
        anim.setAttribute('repeatCount', 'indefinite');
        anim.setAttribute('additive', 'sum');
        
        // Center pivot for scale
        const center = document.createElementNS(svgNS, 'animateTransform');
        center.setAttribute('attributeName', 'transform');
        center.setAttribute('type', 'translate');
        center.setAttribute('values', '20 20');
        center.setAttribute('additive', 'sum');
        
        const uncenter = document.createElementNS(svgNS, 'animateTransform');
        uncenter.setAttribute('attributeName', 'transform');
        uncenter.setAttribute('type', 'translate');
        uncenter.setAttribute('values', '-20 -20');
        uncenter.setAttribute('additive', 'sum');

        star.appendChild(center);
        star.appendChild(anim);
        star.appendChild(uncenter);
        
        g.appendChild(star);
        layers.items.appendChild(g);
        return g;
    }

    /**
     * Classes
     */
    class Base {
        constructor() {
            this.x = 600; 
            this.y = 640; 
            this.w = 40;
            this.h = 40;
            this.active = true;
            this.el = createEagleSVG(this.x, this.y);
        }

        destroy() {
            if (!this.active) return;
            this.active = false;
            if (this.el) {
                this.el.innerHTML = ''; 
                createRect(0, 0, 40, 40, '#000', this.el);
                createRect(5, 5, 10, 10, '#555', this.el);
                createRect(20, 25, 15, 10, '#555', this.el);
                createExplosion(this.x + 20, this.y + 20, 60);
            }
        }
    }

    class PowerUp {
        constructor(x, y) {
            this.x = x;
            this.y = y;
            this.w = 40;
            this.h = 40;
            this.active = true;
            this.el = createWeaponSVG(x, y);
        }
        
        collect() {
            this.active = false;
            if(this.el.parentNode) this.el.parentNode.removeChild(this.el);
        }
    }

    class Tank {
        constructor(x, y, type) {
            this.x = x;
            this.y = y;
            this.w = 40;
            this.h = 40;
            this.dir = 0; // 0:Up, 1:Right, 2:Down, 3:Left
            this.type = type; // 'player', 'normal', 'red', 'gold'
            this.isPlayer = (type === 'player');
            this.speed = this.isPlayer ? CONFIG.playerSpeed : CONFIG.enemySpeed;
            this.lastFire = 0;
            this.hp = this.isPlayer ? CONFIG.maxPlayerHp : (type === 'gold' ? 2 : 1); // Gold tanks tougher
            
            // Player stats
            this.fireRate = this.isPlayer ? CONFIG.fireRate : CONFIG.enemyFireRate;
            this.bulletSpeed = CONFIG.bulletSpeed;
            this.isPoweredUp = false;

            this.el = createTankSVG(type);
            this.el.setAttribute('transform', `translate(${x},${y})`);
            
            (this.isPlayer ? layers.player : layers.enemies).appendChild(this.el);
        }

        powerUp() {
            if (!this.isPlayer) return;
            this.isPoweredUp = true;
            // Enhance stats: 3x firepower (speed) and fire rate
            this.fireRate = CONFIG.fireRate / 3; 
            this.bulletSpeed = CONFIG.bulletSpeed * 2.5; // 3x might be too fast for collision detection
            
            // Visual feedback
            this.el.querySelector('rect[width="4"]').setAttribute('fill', '#ff0000'); // Red gun
            createExplosion(this.x+20, this.y+20, 50); // Effect
        }

        update() {
            let dx = 0, dy = 0;
            let moved = false;

            if (this.isPlayer) {
                if (state.keys.ArrowUp) { dy = -this.speed; this.dir = 0; moved = true; }
                else if (state.keys.ArrowDown) { dy = this.speed; this.dir = 2; moved = true; }
                else if (state.keys.ArrowLeft) { dx = -this.speed; this.dir = 3; moved = true; }
                else if (state.keys.ArrowRight) { dx = this.speed; this.dir = 1; moved = true; }
                
                const now = Date.now();
                if (now - this.lastFire > this.fireRate) {
                    this.fire();
                    this.lastFire = now;
                }
            } else {
                // AI
                const moveDirs = [{x:0, y:-1}, {x:1, y:0}, {x:0, y:1}, {x:-1, y:0}];
                dx = moveDirs[this.dir].x * this.speed;
                dy = moveDirs[this.dir].y * this.speed;
                moved = true;

                if (Math.random() < 0.02) this.dir = Math.floor(Math.random() * 4);
                
                const now = Date.now();
                if (now - this.lastFire > this.fireRate) {
                    this.fire();
                    this.lastFire = now + Math.random() * 500;
                }
            }

            let nx = this.x + dx;
            let ny = this.y + dy;

            if (nx < 0) nx = 0;
            if (ny < 0) ny = 0;
            if (nx > CONFIG.width - this.w) nx = CONFIG.width - this.w;
            if (ny > CONFIG.height - this.h) ny = CONFIG.height - this.h;

            const hitWall = checkWallCollision(nx, ny, this.w, this.h);
            const hitOther = checkTankCollision(this, nx, ny);
            const hitBase = state.base && state.base.active && intersect(nx, ny, this.w, this.h, state.base.x, state.base.y, state.base.w, state.base.h);

            if (!hitWall && !hitOther && !hitBase) {
                this.x = nx;
                this.y = ny;
            } else if (!this.isPlayer) {
                this.dir = Math.floor(Math.random() * 4);
            }

            const rot = this.dir * 90;
            this.el.setAttribute('transform', `translate(${this.x + 20}, ${this.y + 20}) rotate(${rot}) translate(-20, -20)`);
        }

        fire() {
            const bx = this.x + 20 - 4; 
            const by = this.y + 20 - 4;
            let vx = 0, vy = 0;
            if (this.dir === 0) { vy = -this.bulletSpeed; }
            if (this.dir === 1) { vx = this.bulletSpeed; }
            if (this.dir === 2) { vy = this.bulletSpeed; }
            if (this.dir === 3) { vx = -this.bulletSpeed; }
            
            const startX = bx + vx * 2;
            const startY = by + vy * 2;

            state.bullets.push(new Bullet(startX, startY, vx, vy, this.isPlayer));
        }

        destroy() {
            if (this.el.parentNode) this.el.parentNode.removeChild(this.el);
            createExplosion(this.x + 20, this.y + 20, 40);

            // Special Logic based on Type
            if (this.type === 'red') {
                handleTextUnlock();
            } else if (this.type === 'gold') {
                spawnWeapon();
            }
        }
    }

    class Bullet {
        constructor(x, y, vx, vy, isPlayer) {
            this.x = x;
            this.y = y;
            this.vx = vx;
            this.vy = vy;
            this.w = 8;
            this.h = 8;
            this.isPlayer = isPlayer;
            this.dead = false;
            
            this.el = document.createElementNS(svgNS, 'circle');
            this.el.setAttribute('r', 4);
            this.el.setAttribute('fill', isPlayer ? '#fff' : '#ff0000');
            layers.bullets.appendChild(this.el);
        }

        update() {
            this.x += this.vx;
            this.y += this.vy;

            if (this.x < 0 || this.x > CONFIG.width || this.y < 0 || this.y > CONFIG.height) {
                this.dead = true;
            }

            const wallIdx = getWallCollisionIndex(this.x, this.y, this.w, this.h);
            if (wallIdx !== -1) {
                this.dead = true;
                state.walls[wallIdx].hp--;
                if(state.walls[wallIdx].hp <= 0) {
                    const w = state.walls[wallIdx];
                    if(w.el.parentNode) w.el.parentNode.removeChild(w.el);
                    createExplosion(w.x + w.w/2, w.y + w.h/2, 20);
                    state.walls.splice(wallIdx, 1);
                }
            }

            if (state.base && state.base.active) {
                if (intersect(this.x, this.y, this.w, this.h, state.base.x, state.base.y, state.base.w, state.base.h)) {
                    this.dead = true;
                    state.base.destroy();
                    gameFail("堡垒已沦陷");
                    return;
                }
            }

            if (this.isPlayer) {
                for (let i = 0; i < state.enemies.length; i++) {
                    const e = state.enemies[i];
                    if (intersect(this.x, this.y, this.w, this.h, e.x, e.y, e.w, e.h)) {
                        this.dead = true;
                        e.hp--;
                        if (e.hp <= 0) {
                            e.destroy();
                            state.enemies.splice(i, 1);
                        } else {
                            // Hit effect
                            createExplosion(this.x, this.y, 10);
                        }
                        break;
                    }
                }
            } else {
                if (state.player && intersect(this.x, this.y, this.w, this.h, state.player.x, state.player.y, state.player.w, state.player.h)) {
                    this.dead = true;
                    state.player.hp--;
                    updateHpUI();
                    createExplosion(this.x, this.y, 10);
                    
                    if (state.player.hp <= 0) {
                        state.player.destroy();
                        state.player = null;
                        gameFail("坦克被摧毁");
                    }
                }
            }

            if (this.dead) {
                if (this.el.parentNode) this.el.parentNode.removeChild(this.el);
            } else {
                this.el.setAttribute('cx', this.x + 4);
                this.el.setAttribute('cy', this.y + 4);
            }
        }
    }

    /**
     * Logic Helpers
     */
    function intersect(x1, y1, w1, h1, x2, y2, w2, h2) {
        return x1 < x2 + w2 && x1 + w1 > x2 && y1 < y2 + h2 && y1 + h1 > y2;
    }

    function checkWallCollision(x, y, w, h) {
        for (let wall of state.walls) {
            if (intersect(x, y, w, h, wall.x, wall.y, wall.w, wall.h)) return true;
        }
        return false;
    }

    function getWallCollisionIndex(x, y, w, h) {
        for (let i = 0; i < state.walls.length; i++) {
            let wall = state.walls[i];
            if (intersect(x, y, w, h, wall.x, wall.y, wall.w, wall.h)) return i;
        }
        return -1;
    }

    function checkTankCollision(self, x, y) {
        for (let e of state.enemies) {
            if (e === self) continue;
            if (intersect(x, y, self.w, self.h, e.x, e.y, e.w, e.h)) return true;
        }
        if (state.player && self !== state.player) {
            if (intersect(x, y, self.w, self.h, state.player.x, state.player.y, state.player.w, state.player.h)) return true;
        }
        return false;
    }

    function createExplosion(x, y, size) {
        const exp = document.createElementNS(svgNS, 'circle');
        exp.setAttribute('cx', x);
        exp.setAttribute('cy', y);
        exp.setAttribute('r', 1);
        exp.setAttribute('fill', 'orange');
        exp.setAttribute('stroke', 'yellow');
        exp.setAttribute('stroke-width', 2);
        layers.fx.appendChild(exp);

        exp.style.transition = 'all 0.3s ease-out';
        setTimeout(() => {
            exp.setAttribute('r', size);
            exp.setAttribute('opacity', 0);
        }, 10);
        setTimeout(() => {
            if(exp.parentNode) exp.parentNode.removeChild(exp);
        }, 310);
    }

    function handleTextUnlock() {
        const remaining = state.progress.map((v, i) => v ? -1 : i).filter(i => i !== -1);
        
        if (remaining.length > 0) {
            const idx = remaining[Math.floor(Math.random() * remaining.length)];
            state.progress[idx] = true;
            const el = document.getElementById(`char-${idx}`);
            el.classList.add('active');
            
            if (state.progress.every(v => v)) {
                gameWin();
            }
        }
    }

    function spawnWeapon() {
        let attempts = 0;
        let x, y;
        let valid = false;
        
        while(attempts < 50 && !valid) {
            x = Math.floor(Math.random() * (CONFIG.width - 40));
            y = Math.floor(Math.random() * (CONFIG.height - 40));
            
            // Align to grid roughly
            x = Math.floor(x / 20) * 20;
            y = Math.floor(y / 20) * 20;

            // Check collisions
            if (!checkWallCollision(x, y, 40, 40) && 
                !intersect(x, y, 40, 40, state.base.x, state.base.y, state.base.w, state.base.h)) {
                valid = true;
            }
            attempts++;
        }

        if (valid) {
            state.items.push(new PowerUp(x, y));
        }
    }

    function updateHpUI() {
        const bar = document.getElementById('player-hp-bar');
        const pct = state.player ? (state.player.hp / CONFIG.maxPlayerHp) * 100 : 0;
        bar.style.width = `${pct}%`;
        
        if (pct > 60) bar.style.backgroundColor = '#2ecc71';
        else if (pct > 30) bar.style.backgroundColor = '#f1c40f';
        else bar.style.backgroundColor = '#e74c3c';
    }

    /**
     * Main Game Loop
     */
    function startGame() {
        document.getElementById('start-screen').classList.add('hidden');
        document.getElementById('win-screen').classList.add('hidden');
        document.getElementById('fail-screen').classList.add('hidden');
        
        // Reset Entities
        state.enemies.forEach(e => { if(e.el.parentNode) e.el.parentNode.removeChild(e.el); });
        state.bullets.forEach(b => { if(b.el.parentNode) b.el.parentNode.removeChild(b.el); });
        state.walls.forEach(w => { if(w.el.parentNode) w.el.parentNode.removeChild(w.el); });
        state.items.forEach(i => { if(i.el.parentNode) i.el.parentNode.removeChild(i.el); });
        if (state.base && state.base.el.parentNode) state.base.el.parentNode.removeChild(state.base.el);
        
        state.enemies = [];
        state.bullets = [];
        state.walls = [];
        state.items = [];
        state.progress = [false, false, false, false];
        state.isRunning = true;
        
        document.querySelectorAll('.char-slot').forEach(el => el.classList.remove('active'));

        const mapData = generateMap();
        mapData.forEach(b => {
            const el = createRect(b.x, b.y, b.w, b.h, 'url(#brick-pattern)', layers.walls);
            el.setAttribute('stroke', '#000');
            state.walls.push({ ...b, el });
        });

        state.base = new Base();

        if(state.player && state.player.el.parentNode) state.player.el.parentNode.removeChild(state.player.el);
        state.player = new Tank(480, 640, 'player');
        updateHpUI();

        state.lastTime = Date.now();
        requestAnimationFrame(loop);
    }

    function gameWin() {
        state.isRunning = false;
        document.getElementById('win-screen').classList.remove('hidden');
        
        const end = Date.now() + 5000;
        const colors = ['#bb0000', '#f7d010', '#ffffff'];

        (function frame() {
            confetti({
                particleCount: 3,
                angle: 60,
                spread: 55,
                origin: { x: 0 },
                colors: colors
            });
            confetti({
                particleCount: 3,
                angle: 120,
                spread: 55,
                origin: { x: 1 },
                colors: colors
            });

            if (Date.now() < end) {
                requestAnimationFrame(frame);
            }
        }());
    }

    function gameFail(reason) {
        state.isRunning = false;
        document.getElementById('fail-reason').innerText = reason;
        document.getElementById('fail-screen').classList.remove('hidden');
    }

    function loop() {
        if (!state.isRunning) return;

        const now = Date.now();
        state.lastTime = now;

        // Spawn Enemies
        if (state.enemies.length < CONFIG.maxEnemies && now > state.enemySpawnTimer) {
            const spawnPoints = [{x:0,y:0}, {x:CONFIG.width-40, y:0}, {x:0, y:CONFIG.height-40}];
            const pt = spawnPoints[Math.floor(Math.random()*spawnPoints.length)];
            
            if (!checkTankCollision({w:40, h:40}, pt.x, pt.y)) {
                // Determine Type
                const rand = Math.random();
                let type = 'normal';
                if (rand < 0.15) type = 'gold'; // 15% Gold
                else if (rand < 0.45) type = 'red'; // 30% Red (Text carrier)
                
                state.enemies.push(new Tank(pt.x, pt.y, type));
                state.enemySpawnTimer = now + CONFIG.spawnInterval;
            }
        }

        // Update Entities
        if (state.player) {
            state.player.update();
            
            // Check Item Collection
            for (let i = state.items.length - 1; i >= 0; i--) {
                const item = state.items[i];
                if (intersect(state.player.x, state.player.y, state.player.w, state.player.h, item.x, item.y, item.w, item.h)) {
                    item.collect();
                    state.items.splice(i, 1);
                    state.player.powerUp();
                }
            }
        }

        state.enemies.forEach(e => e.update());
        
        for (let i = state.bullets.length - 1; i >= 0; i--) {
            state.bullets[i].update();
            if (state.bullets[i].dead) {
                state.bullets.splice(i, 1);
            }
        }

        requestAnimationFrame(loop);
    }

    /**
     * Input Handling
     */
    window.addEventListener('keydown', (e) => {
        if (state.keys.hasOwnProperty(e.code)) state.keys[e.code] = true;
    });

    window.addEventListener('keyup', (e) => {
        if (state.keys.hasOwnProperty(e.code)) state.keys[e.code] = false;
    });

</script>
</body>
</html>1a:T6d2f,<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Global Expedition: Cape to Cape</title>
    <script src="https://cdn.jsdelivr.net/npm/d3@7"></script>
    <script src="https://cdn.jsdelivr.net/npm/topojson-client@3"></script>
    <script src="https://unpkg.com/lucide@latest"></script>
    <style>
        :root {
            --bg-color: #0F172A;
            --primary: #38BDF8;
            --secondary: #94A3B8;
            --accent: #FBBF24;
            --danger: #EF4444;
            --grid: rgba(56, 189, 248, 0.1);
            --font-main: 'Montserrat', 'PingFang SC', sans-serif;
            --font-mono: 'Courier New', monospace;
        }

        body, html {
            margin: 0;
            padding: 0;
            width: 100%;
            height: 100%;
            background-color: var(--bg-color);
            overflow: hidden;
            font-family: var(--font-main);
            color: var(--primary);
            display: flex;
            justify-content: center;
            align-items: center;
        }

        #container {
            position: relative;
            width: 100vw;
            height: 100vh;
            display: flex;
            justify-content: center;
            align-items: center;
            background: radial-gradient(circle at center, #1e293b 0%, #0f172a 100%);
        }

        svg {
            width: 100%;
            height: 100%;
            max-width: 1200px;
            max-height: 700px;
            filter: drop-shadow(0 0 30px rgba(0,0,0,0.5));
        }

        /* UI Elements */
        .hud-text {
            font-family: var(--font-mono);
            text-transform: uppercase;
            letter-spacing: 1px;
            pointer-events: none;
        }

        .title-group text {
            fill: #F8FAFC;
            text-shadow: 0 0 10px rgba(56, 189, 248, 0.5);
        }

        .panel-bg {
            fill: rgba(15, 23, 42, 0.85);
            stroke: var(--primary);
            stroke-width: 1;
            stroke-opacity: 0.5;
        }

        .marker-label {
            font-size: 12px;
            font-weight: bold;
            fill: var(--accent);
            text-shadow: 0 2px 4px rgba(0,0,0,0.8);
            opacity: 0;
            transition: opacity 0.3s;
        }

        .marker-point {
            fill: var(--accent);
            stroke: #fff;
            stroke-width: 2;
            cursor: pointer;
        }

        .pulse-ring {
            fill: none;
            stroke: var(--accent);
            stroke-width: 1;
            opacity: 0;
            transform-origin: center;
        }

        /* Loading Screen */
        #loader {
            position: absolute;
            top: 0; left: 0; width: 100%; height: 100%;
            background: var(--bg-color);
            display: flex;
            flex-direction: column;
            justify-content: center;
            align-items: center;
            z-index: 100;
            transition: opacity 0.8s ease-out;
        }
        .loader-bar {
            width: 200px;
            height: 2px;
            background: #1e293b;
            margin-top: 20px;
            position: relative;
            overflow: hidden;
        }
        .loader-progress {
            position: absolute;
            left: 0; top: 0; bottom: 0;
            width: 0%;
            background: var(--primary);
            box-shadow: 0 0 10px var(--primary);
            transition: width 0.2s;
        }

        /* Animations */
        @keyframes blink { 0%, 100% { opacity: 1; } 50% { opacity: 0.3; } }
        .blink { animation: blink 2s infinite; }
        
        @keyframes rotate-ui { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }
        .rotating { animation: rotate-ui 10s linear infinite; transform-origin: center; }

    </style>
</head>
<body>

    <div id="loader">
        <div style="color: var(--primary); font-family: var(--font-mono); letter-spacing: 4px;">SYSTEM INITIALIZING</div>
        <div class="loader-bar"><div class="loader-progress" id="load-bar"></div></div>
    </div>

    <div id="container">
        <svg id="scene" viewBox="0 0 1200 700" preserveAspectRatio="xMidYMid meet">
            <defs>
                <!-- Filters & Gradients -->
                <radialGradient id="oceanGradient" cx="50%" cy="50%" r="50%" fx="25%" fy="25%">
                    <stop offset="0%" stop-color="#334155" />
                    <stop offset="100%" stop-color="#020617" />
                </radialGradient>
                
                <filter id="glow-blue" x="-50%" y="-50%" width="200%" height="200%">
                    <feGaussianBlur stdDeviation="4" result="blur" />
                    <feComposite in="SourceGraphic" in2="blur" operator="over" />
                </filter>

                <filter id="glow-gold" x="-50%" y="-50%" width="200%" height="200%">
                    <feGaussianBlur stdDeviation="3" result="blur" />
                    <feMerge>
                        <feMergeNode in="blur" />
                        <feMergeNode in="SourceGraphic" />
                    </feMerge>
                </filter>

                <mask id="globeMask">
                    <circle cx="600" cy="350" r="250" fill="white" />
                </mask>

                <pattern id="gridPattern" width="40" height="40" patternUnits="userSpaceOnUse">
                    <path d="M 40 0 L 0 0 0 40" fill="none" stroke="var(--grid)" stroke-width="0.5"/>
                </pattern>
            </defs>

            <!-- Background Elements -->
            <g id="stars-bg"></g>
            <rect width="100%" height="100%" fill="url(#gridPattern)" opacity="0.3" pointer-events="none"/>

            <!-- Main Globe Group -->
            <g id="globe-group">
                <!-- Atmosphere Glow -->
                <circle cx="600" cy="350" r="255" fill="#38BDF8" opacity="0.1" filter="url(#glow-blue)" />
                
                <!-- Ocean -->
                <circle cx="600" cy="350" r="250" fill="url(#oceanGradient)" stroke="#475569" stroke-width="1" />

                <!-- Map Content (Clipped to Sphere) -->
                <g id="geo-content">
                    <path id="graticule" fill="none" stroke="#475569" stroke-width="0.5" opacity="0.3" />
                    <path id="land" fill="#1e293b" stroke="#64748b" stroke-width="0.5" />
                    
                    <!-- Route Lines -->
                    <path id="route-bg" fill="none" stroke="#38BDF8" stroke-width="1" opacity="0.3" stroke-dasharray="2 4" />
                    <path id="route-active" fill="none" stroke="#FBBF24" stroke-width="2.5" stroke-linecap="round" filter="url(#glow-gold)" />
                    
                    <!-- Markers Container -->
                    <g id="markers-layer"></g>

                    <!-- Moving Target Indicator -->
                    <g id="target-cursor" style="display: none;">
                        <circle r="6" fill="none" stroke="#FBBF24" stroke-width="2" />
                        <circle r="12" fill="none" stroke="#FBBF24" stroke-width="1" opacity="0.5" />
                        <line x1="-15" y1="0" x2="15" y2="0" stroke="#FBBF24" stroke-width="1" />
                        <line x1="0" y1="-15" x2="0" y2="15" stroke="#FBBF24" stroke-width="1" />
                    </g>
                </g>
                
                <!-- Globe Shadow (Night side) -->
                <path id="night-shadow" fill="#020617" opacity="0.4" style="mix-blend-mode: multiply;" pointer-events="none" />
            </g>

            <!-- HUD / UI Overlay -->
            <g class="hud-ui">
                <!-- Top Header -->
                <g transform="translate(600, 60)">
                    <text text-anchor="middle" font-size="24" font-weight="800" fill="#F8FAFC" letter-spacing="4">GLOBAL EXPEDITION</text>
                    <text y="25" text-anchor="middle" font-size="12" fill="#94A3B8" letter-spacing="2">TRANS-CONTINENTAL TRAJECTORY // 跨大陆征途</text>
                    <line x1="-200" y1="40" x2="200" y2="40" stroke="url(#oceanGradient)" stroke-width="1" />
                    <rect x="-150" y="39" width="300" height="2" fill="#38BDF8" opacity="0.5" />
                </g>

                <!-- Left Panel: Location Info -->
                <g transform="translate(50, 150)">
                    <path d="M0,0 L240,0 L260,20 L260,120 L20,120 L0,100 Z" class="panel-bg" />
                    <rect x="0" y="0" width="4" height="40" fill="#FBBF24" />
                    
                    <text x="20" y="30" class="hud-text" font-size="10" fill="#94A3B8">CURRENT LOCATION / 当前位置</text>
                    <text id="ui-location-name" x="20" y="55" class="hud-text" font-size="18" font-weight="bold" fill="#F8FAFC">INITIALIZING...</text>
                    
                    <text x="20" y="85" class="hud-text" font-size="10" fill="#94A3B8">COORDINATES / 坐标</text>
                    <text id="ui-coords" x="20" y="100" class="hud-text" font-size="14" fill="#38BDF8">00°00' N, 00°00' E</text>
                </g>

                <!-- Right Panel: Mission Status -->
                <g transform="translate(890, 150)">
                    <path d="M20,0 L260,0 L260,100 L240,120 L0,120 L0,20 Z" class="panel-bg" />
                    <rect x="256" y="0" width="4" height="40" fill="#38BDF8" />

                    <text x="240" y="30" text-anchor="end" class="hud-text" font-size="10" fill="#94A3B8">MISSION PROGRESS / 进程</text>
                    <text id="ui-progress" x="240" y="60" text-anchor="end" class="hud-text" font-size="32" font-weight="bold" fill="#F8FAFC">0%</text>
                    
                    <rect x="40" y="80" width="200" height="4" fill="#1e293b" />
                    <rect id="ui-progress-bar" x="40" y="80" width="0" height="4" fill="#FBBF24" filter="url(#glow-gold)" />
                </g>

                <!-- Bottom Panel: Waypoints Log -->
                <g transform="translate(300, 620)">
                    <line x1="0" y1="0" x2="600" y2="0" stroke="#334155" stroke-width="1" />
                    <circle cx="0" cy="0" r="3" fill="#38BDF8" />
                    <circle cx="600" cy="0" r="3" fill="#38BDF8" />
                    
                    <text x="300" y="25" text-anchor="middle" class="hud-text blink" font-size="12" fill="#FBBF24">
                        <tspan id="ui-status-msg">WAITING FOR SATELLITE LINK...</tspan>
                    </text>
                </g>

                <!-- Decorative Compass -->
                <g transform="translate(1100, 600)" opacity="0.6">
                    <circle r="40" fill="none" stroke="#38BDF8" stroke-width="1" stroke-dasharray="4 4" />
                    <circle r="30" fill="none" stroke="#94A3B8" stroke-width="0.5" />
                    <path d="M0,-35 L5,-5 L35,0 L5,5 L0,35 L-5,5 L-35,0 L-5,-5 Z" fill="#1e293b" stroke="#38BDF8" stroke-width="1" class="rotating" />
                    <text x="0" y="-50" text-anchor="middle" font-size="10" fill="#38BDF8" font-family="monospace">N</text>
                </g>
            </g>
        </svg>
    </div>

    <script>
        // --- Configuration ---
        const CONFIG = {
            width: 1200,
            height: 700,
            globeRadius: 250,
            center: [600, 350],
            speed: 0.0015, // Animation speed
            pauseOnWaypoint: 60, // Frames to pause at key locations
            waypoints: [
                { name: "好望角 (起点)", en: "Cape of Good Hope", coords: [18.47, -34.35], type: "start" },
                { name: "坦桑尼亚", en: "Tanzania", coords: [34.88, -6.36], type: "pass" },
                { name: "开罗", en: "Cairo, Egypt", coords: [31.23, 30.04], type: "major" },
                { name: "德黑兰", en: "Tehran, Iran", coords: [51.38, 35.68], type: "pass" },
                { name: "中亚腹地", en: "Central Asia", coords: [65.00, 42.00], type: "pass" },
                { name: "新西伯利亚", en: "Novosibirsk", coords: [82.93, 55.00], type: "major" },
                { name: "雅库茨克", en: "Yakutsk", coords: [129.73, 62.03], type: "pass" },
                { name: "白令海峡", en: "Bering Strait", coords: [-169.00, 65.50], type: "critical" }, // Crossing point
                { name: "安克雷奇", en: "Anchorage", coords: [-149.90, 61.21], type: "major" },
                { name: "温哥华", en: "Vancouver", coords: [-123.12, 49.28], type: "pass" },
                { name: "巴拿马城", en: "Panama City", coords: [-79.51, 8.98], type: "major" },
                { name: "利马", en: "Lima, Peru", coords: [-77.04, -12.04], type: "pass" },
                { name: "合恩角 (终点)", en: "Cape Horn", coords: [-67.29, -55.98], type: "end" }
            ]
        };

        // --- Globals ---
        let projection, pathGenerator;
        let routePoints = [];
        let currentProgress = 0;
        let isPaused = false;
        let pauseTimer = 0;
        let animationId;
        let landFeature, graticuleFeature;
        
        // --- Initialization ---
        async function init() {
            const loaderBar = document.getElementById('load-bar');
            
            // 1. Setup D3 Projection
            projection = d3.geoOrthographic()
                .scale(CONFIG.globeRadius)
                .translate(CONFIG.center)
                .clipAngle(90);

            pathGenerator = d3.geoPath().projection(projection);

            // 2. Generate Stars
            generateStars();
            loaderBar.style.width = "30%";

            // 3. Load Map Data
            try {
                const worldData = await d3.json('https://cdn.jsdelivr.net/npm/world-atlas@2/countries-110m.json');
                landFeature = topojson.feature(worldData, worldData.objects.land);
                graticuleFeature = d3.geoGraticule()();
                loaderBar.style.width = "70%";
            } catch (e) {
                console.error("Failed to load map data", e);
                document.getElementById('ui-status-msg').textContent = "DATA LINK FAILED";
                return;
            }

            // 4. Calculate Route
            calculateRoute();
            loaderBar.style.width = "100%";

            // 5. Render Static Elements
            renderStaticMap();
            renderMarkers();

            // 6. Start Animation
            setTimeout(() => {
                document.getElementById('loader').style.opacity = 0;
                setTimeout(() => document.getElementById('loader').remove(), 800);
                animate();
            }, 500);

            // 7. Interaction
            setupInteraction();
        }

        function generateStars() {
            const starGroup = d3.select("#stars-bg");
            for(let i=0; i<300; i++) {
                starGroup.append("circle")
                    .attr("cx", Math.random() * CONFIG.width)
                    .attr("cy", Math.random() * CONFIG.height)
                    .attr("r", Math.random() * 1.5)
                    .attr("fill", "#fff")
                    .attr("opacity", Math.random() * 0.8);
            }
        }

        function calculateRoute() {
            // Create a dense array of points along the waypoints using Great Circle interpolation
            routePoints = [];
            for (let i = 0; i < CONFIG.waypoints.length - 1; i++) {
                const start = CONFIG.waypoints[i].coords;
                const end = CONFIG.waypoints[i+1].coords;
                const interpolator = d3.geoInterpolate(start, end);
                
                // Distance determines number of steps for constant speed
                const dist = d3.geoDistance(start, end);
                const steps = Math.max(20, Math.floor(dist * 200)); 

                for (let t = 0; t < steps; t++) {
                    routePoints.push(interpolator(t / steps));
                }
                // Add waypoint index marker to the point data for UI triggers
                routePoints[routePoints.length - 1].waypointIndex = i + 1;
            }
            routePoints.push(CONFIG.waypoints[CONFIG.waypoints.length - 1].coords);
        }

        function renderStaticMap() {
            d3.select("#land").datum(landFeature).attr("d", pathGenerator);
            d3.select("#graticule").datum(graticuleFeature).attr("d", pathGenerator);
            
            // Full Route Background (faint)
            const routeLineString = { type: "LineString", coordinates: routePoints };
            d3.select("#route-bg").datum(routeLineString).attr("d", pathGenerator);
        }

        function renderMarkers() {
            const container = d3.select("#markers-layer");
            
            CONFIG.waypoints.forEach((wp, index) => {
                const g = container.append("g")
                    .attr("class", "marker-group")
                    .attr("id", `marker-${index}`);

                // Pulse Ring
                g.append("circle")
                    .attr("class", "pulse-ring")
                    .attr("r", 8);

                // Center Point
                g.append("circle")
                    .attr("class", "marker-point")
                    .attr("r", index === 0 || index === CONFIG.waypoints.length-1 ? 5 : 3)
                    .attr("fill", wp.type === 'critical' ? '#EF4444' : '#FBBF24');

                // Label
                g.append("text")
                    .attr("class", "marker-label")
                    .attr("x", 10)
                    .attr("y", 4)
                    .text(wp.name);
            });
        }

        // --- Main Animation Loop ---
        function animate() {
            if (!isPaused) {
                // Handle Pausing at Waypoints
                if (pauseTimer > 0) {
                    pauseTimer--;
                    updateGlobeRotation(); // Keep rotating or holding? Let's hold slightly or drift.
                    requestAnimationFrame(animate);
                    return;
                }

                // Advance Progress
                currentProgress += CONFIG.speed;
                if (currentProgress >= 1) {
                    currentProgress = 1;
                    isPaused = true; // End of journey
                    document.getElementById('ui-status-msg').textContent = "MISSION ACCOMPLISHED / 抵达终点";
                    document.getElementById('ui-status-msg').parentElement.classList.remove('blink');
                }

                updateScene();
            }
            requestAnimationFrame(animate);
        }

        function updateScene() {
            const totalPoints = routePoints.length;
            const currentIndex = Math.floor(currentProgress * (totalPoints - 1));
            const currentPos = routePoints[currentIndex];

            // 1. Rotate Globe to center on current position
            // Smoothly interpolate rotation
            const targetRotate = [-currentPos[0], -currentPos[1]];
            const currentRotate = projection.rotate();
            
            // Simple easing for rotation
            const newRotate = [
                currentRotate[0] + (targetRotate[0] - currentRotate[0]) * 0.1,
                currentRotate[1] + (targetRotate[1] - currentRotate[1]) * 0.1
            ];
            projection.rotate(newRotate);

            // 2. Update Map Paths
            d3.select("#land").attr("d", pathGenerator);
            d3.select("#graticule").attr("d", pathGenerator);
            d3.select("#route-bg").attr("d", pathGenerator);

            // 3. Draw Active Route (from start to current)
            const activeRoute = {
                type: "LineString",
                coordinates: routePoints.slice(0, currentIndex + 1)
            };
            d3.select("#route-active").datum(activeRoute).attr("d", pathGenerator);

            // 4. Update Markers Visibility & Position
            updateMarkers(newRotate);

            // 5. Update Target Cursor
            const cursor = d3.select("#target-cursor");
            const projectedPos = projection(currentPos);
            
            // Check if point is visible (not on back of globe)
            const centerPos = projection.invert(CONFIG.center);
            const dist = d3.geoDistance(currentPos, centerPos);
            
            if (dist < 1.57) { // < 90 degrees approx
                cursor.style("display", "block")
                      .attr("transform", `translate(${projectedPos[0]}, ${projectedPos[1]})`);
            } else {
                cursor.style("display", "none");
            }

            // 6. Update UI
            updateUI(currentPos, currentIndex);

            // 7. Check for Waypoint Arrival
            if (currentPos.waypointIndex !== undefined) {
                // We hit a waypoint node
                const wpIndex = currentPos.waypointIndex;
                // Only pause if we haven't processed this index recently (simple debounce logic implied by linear progress)
                // To avoid multiple pauses, we remove the property or check a flag. 
                // Since we regenerate points, let's just use the fact that we are AT the index.
                // But float math is tricky. 
                
                // Better approach: Check distance to actual waypoints
                const nearestWP = CONFIG.waypoints.find(wp => d3.geoDistance(wp.coords, currentPos) < 0.005);
                if (nearestWP && !nearestWP.visited) {
                    nearestWP.visited = true;
                    pauseTimer = CONFIG.pauseOnWaypoint;
                    document.getElementById('ui-status-msg').textContent = `ARRIVED AT: ${nearestWP.en}`;
                    
                    // Trigger marker pulse
                    const markerSel = d3.select(`#marker-${CONFIG.waypoints.indexOf(nearestWP)}`);
                    markerSel.select(".pulse-ring")
                        .transition().duration(1000)
                        .attr("r", 20).style("opacity", 0)
                        .on("end", function() { d3.select(this).attr("r", 8).style("opacity", 1); });
                }
            }
        }

        function updateMarkers(rotation) {
            const centerPos = projection.invert(CONFIG.center);

            CONFIG.waypoints.forEach((wp, i) => {
                const dist = d3.geoDistance(wp.coords, centerPos);
                const isVisible = dist < 1.57; // 90 degrees
                const el = d3.select(`#marker-${i}`);
                
                if (isVisible) {
                    const pos = projection(wp.coords);
                    el.style("display", "block")
                      .attr("transform", `translate(${pos[0]}, ${pos[1]})`);
                    
                    // Show label if close to center or active
                    if (dist < 0.5 || wp.visited) {
                        el.select(".marker-label").style("opacity", 1);
                    } else {
                        el.select(".marker-label").style("opacity", 0.5);
                    }
                } else {
                    el.style("display", "none");
                }
            });
        }

        function updateUI(pos, idx) {
            // Coordinates
            const lat = pos[1];
            const lon = pos[0];
            const latStr = Math.abs(lat).toFixed(2) + "° " + (lat >= 0 ? "N" : "S");
            const lonStr = Math.abs(lon).toFixed(2) + "° " + (lon >= 0 ? "E" : "W");
            document.getElementById('ui-coords').textContent = `${latStr}, ${lonStr}`;

            // Progress
            const pct = Math.floor(currentProgress * 100);
            document.getElementById('ui-progress').textContent = pct + "%";
            document.getElementById('ui-progress-bar').setAttribute("width", pct * 2);

            // Location Name (Find nearest or generic)
            // Already handled by waypoint arrival, but for "In transit":
            if (pauseTimer === 0) {
                // Find previous waypoint
                let prevName = CONFIG.waypoints[0].name;
                for(let i=0; i<CONFIG.waypoints.length; i++) {
                    if (CONFIG.waypoints[i].visited) prevName = CONFIG.waypoints[i].name;
                }
                document.getElementById('ui-location-name').textContent = `EN ROUTE: ${prevName} -> ...`;
                document.getElementById('ui-status-msg').textContent = "TRACKING SATELLITE FEED...";
            } else {
                 // Paused at location
                 const currentWP = CONFIG.waypoints.find(wp => wp.visited && d3.geoDistance(wp.coords, pos) < 0.01);
                 if(currentWP) document.getElementById('ui-location-name').textContent = currentWP.name;
            }
        }

        function updateGlobeRotation() {
            // Helper to keep rotation smooth even when paused
            // (Optional: add subtle idle rotation)
        }

        // --- Interaction ---
        function setupInteraction() {
            let drag = false;
            let lastX, lastY;
            const svg = document.getElementById('scene');

            svg.addEventListener('pointerdown', (e) => {
                drag = true;
                lastX = e.clientX;
                lastY = e.clientY;
                isPaused = true; // Pause animation on drag
                svg.setPointerCapture(e.pointerId);
                document.getElementById('ui-status-msg').textContent = "MANUAL OVERRIDE ENGAGED";
            });

            svg.addEventListener('pointermove', (e) => {
                if (!drag) return;
                const dx = e.clientX - lastX;
                const dy = e.clientY - lastY;
                
                const rotate = projection.rotate();
                const k = 0.25; // sensitivity
                projection.rotate([rotate[0] + dx * k, rotate[1] - dy * k]);
                
                lastX = e.clientX;
                lastY = e.clientY;
                
                // Manual update of static elements only (path doesn't grow)
                d3.select("#land").attr("d", pathGenerator);
                d3.select("#graticule").attr("d", pathGenerator);
                d3.select("#route-bg").attr("d", pathGenerator);
                
                // Re-calculate active route visual for new angle
                const currentIndex = Math.floor(currentProgress * (routePoints.length - 1));
                const activeRoute = { type: "LineString", coordinates: routePoints.slice(0, currentIndex + 1) };
                d3.select("#route-active").datum(activeRoute).attr("d", pathGenerator);
                
                updateMarkers();
                
                // Hide cursor during manual drag usually, or update it
                const currentPos = routePoints[currentIndex];
                const projPos = projection(currentPos);
                const centerPos = projection.invert(CONFIG.center);
                if (d3.geoDistance(currentPos, centerPos) < 1.57) {
                    d3.select("#target-cursor")
                        .style("display", "block")
                        .attr("transform", `translate(${projPos[0]}, ${projPos[1]})`);
                } else {
                    d3.select("#target-cursor").style("display", "none");
                }
            });

            svg.addEventListener('pointerup', (e) => {
                drag = false;
                isPaused = false; // Resume
                svg.releasePointerCapture(e.pointerId);
                document.getElementById('ui-status-msg').textContent = "RESUMING AUTO-NAVIGATION...";
            });
        }

        // Run
        init();

    </script>
</body>
</html>1b:T5026,<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>四川旅游数据可视化</title>
    <script src="https://d3js.org/d3.v7.min.js"></script>
    <style>
        :root {
            --bg-dark: #0f172a;
            --bg-panel: rgba(30, 41, 59, 0.7);
            --primary: #38bdf8; /* Sky blue */
            --accent: #00f2ff; /* Cyan glow */
            --gold: #ffd700;   /* Gold trace */
            --text-main: #f1f5f9;
            --text-sub: #94a3b8;
            --grid-color: rgba(56, 189, 248, 0.1);
        }

        body {
            margin: 0;
            padding: 0;
            width: 100vw;
            height: 100vh;
            background-color: var(--bg-dark);
            background-image: 
                radial-gradient(circle at 50% 50%, #1e293b 0%, #0f172a 100%);
            font-family: 'PingFang SC', 'Microsoft YaHei', 'Helvetica Neue', sans-serif;
            overflow: hidden;
            color: var(--text-main);
            user-select: none;
        }

        /* Container for Aspect Ratio */
        #container {
            position: relative;
            width: 100vw;
            height: 100vh;
            display: flex;
            justify-content: center;
            align-items: center;
        }

        /* SVG Canvas */
        svg {
            width: 100%;
            height: 100%;
            max-width: 1200px;
            max-height: 700px;
            filter: drop-shadow(0 0 20px rgba(0,0,0,0.5));
        }

        /* Background Grid */
        .bg-grid {
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background-image: 
                linear-gradient(var(--grid-color) 1px, transparent 1px),
                linear-gradient(90deg, var(--grid-color) 1px, transparent 1px);
            background-size: 40px 40px;
            pointer-events: none;
            z-index: -1;
            opacity: 0.3;
        }

        /* Map Styles */
        .province {
            fill: rgba(51, 65, 85, 0.3);
            stroke: rgba(148, 163, 184, 0.3);
            stroke-width: 0.5px;
            transition: fill 0.3s ease;
        }

        .province:hover {
            fill: rgba(56, 189, 248, 0.2);
        }

        .sichuan-base {
            fill: rgba(15, 23, 42, 0.8);
            stroke: var(--primary);
            stroke-width: 1px;
            opacity: 0; /* Hidden initially, shown after zoom */
        }

        .golden-trace {
            fill: none;
            stroke: var(--gold);
            stroke-width: 2.5px;
            stroke-linecap: round;
            stroke-linejoin: round;
            filter: url(#glow-gold);
            pointer-events: none;
        }

        /* Marker Styles */
        .marker-group {
            cursor: pointer;
        }

        .marker-ring {
            fill: none;
            stroke: var(--accent);
            stroke-width: 1.5px;
            opacity: 0;
        }

        .marker-core {
            fill: #fff;
            filter: url(#glow-cyan);
            opacity: 0;
        }

        .marker-line {
            stroke: var(--accent);
            stroke-width: 1px;
            stroke-dasharray: 2, 2;
            opacity: 0;
        }

        .marker-text {
            fill: var(--text-main);
            font-size: 14px;
            font-weight: bold;
            text-shadow: 0 2px 4px rgba(0,0,0,0.8);
            opacity: 0;
            pointer-events: none;
        }

        .marker-text-bg {
            fill: rgba(15, 23, 42, 0.8);
            opacity: 0;
        }

        /* UI Overlay */
        .ui-overlay {
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            pointer-events: none;
            max-width: 1200px;
            max-height: 700px;
            margin: auto;
        }

        .header {
            position: absolute;
            top: 30px;
            left: 30px;
            opacity: 0;
            transform: translateY(-20px);
            animation: slideIn 1s forwards 0.5s;
        }

        .header h1 {
            margin: 0;
            font-size: 32px;
            letter-spacing: 4px;
            background: linear-gradient(90deg, #fff, var(--accent));
            -webkit-background-clip: text;
            -webkit-text-fill-color: transparent;
            text-shadow: 0 0 15px rgba(0, 242, 255, 0.4);
        }

        .header .subtitle {
            font-size: 12px;
            color: var(--text-sub);
            letter-spacing: 2px;
            margin-top: 5px;
            display: block;
        }

        .info-card {
            position: absolute;
            bottom: 40px;
            right: 40px;
            width: 280px;
            background: rgba(15, 23, 42, 0.85);
            backdrop-filter: blur(10px);
            border: 1px solid rgba(56, 189, 248, 0.3);
            border-left: 4px solid var(--gold);
            padding: 20px;
            border-radius: 4px;
            transform: translateX(50px);
            opacity: 0;
            transition: all 0.5s cubic-bezier(0.23, 1, 0.32, 1);
            box-shadow: 0 10px 40px rgba(0,0,0,0.6);
        }

        .info-card.active {
            transform: translateX(0);
            opacity: 1;
        }

        .info-card h2 {
            margin: 0 0 8px 0;
            color: var(--gold);
            font-size: 20px;
            display: flex;
            align-items: center;
            justify-content: space-between;
        }

        .info-card .type-tag {
            font-size: 10px;
            background: rgba(255, 215, 0, 0.15);
            color: var(--gold);
            padding: 2px 6px;
            border-radius: 2px;
            border: 1px solid rgba(255, 215, 0, 0.3);
        }

        .info-card p {
            margin: 0;
            font-size: 13px;
            line-height: 1.6;
            color: #cbd5e1;
        }

        /* Loading Spinner */
        #loader {
            position: absolute;
            top: 0; left: 0; right: 0; bottom: 0;
            background: #020617;
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            z-index: 1000;
            transition: opacity 0.8s;
        }

        .spinner-ring {
            width: 60px;
            height: 60px;
            border: 2px solid transparent;
            border-top-color: var(--accent);
            border-radius: 50%;
            animation: spin 1s linear infinite;
        }
        
        .spinner-text {
            margin-top: 20px;
            color: var(--accent);
            font-size: 12px;
            letter-spacing: 3px;
            animation: blink 1.5s infinite;
        }

        @keyframes spin { 100% { transform: rotate(360deg); } }
        @keyframes blink { 50% { opacity: 0.5; } }
        @keyframes slideIn { to { opacity: 1; transform: translateY(0); } }

    </style>
</head>
<body>

    <div id="loader">
        <div class="spinner-ring"></div>
        <div class="spinner-text">SYSTEM INITIALIZING...</div>
    </div>

    <div id="container">
        <div class="bg-grid"></div>
        
        <svg id="main-svg" viewBox="0 0 1200 700" preserveAspectRatio="xMidYMid meet">
            <defs>
                <filter id="glow-gold" x="-50%" y="-50%" width="200%" height="200%">
                    <feGaussianBlur stdDeviation="3" result="coloredBlur"/>
                    <feMerge>
                        <feMergeNode in="coloredBlur"/>
                        <feMergeNode in="SourceGraphic"/>
                    </feMerge>
                </filter>
                <filter id="glow-cyan" x="-50%" y="-50%" width="200%" height="200%">
                    <feGaussianBlur stdDeviation="2" result="coloredBlur"/>
                    <feMerge>
                        <feMergeNode in="coloredBlur"/>
                        <feMergeNode in="SourceGraphic"/>
                    </feMerge>
                </filter>
            </defs>
            
            <g id="map-group">
                <!-- Provinces will be drawn here -->
            </g>
            <g id="sichuan-highlight-group"></g>
            <g id="trace-group"></g>
            <g id="markers-group"></g>
        </svg>

        <div class="ui-overlay">
            <div class="header">
                <h1>巴蜀胜地</h1>
                <span class="subtitle">SICHUAN TOURISM INTELLIGENCE MAP</span>
            </div>

            <div id="info-panel" class="info-card">
                <h2><span id="info-title">中国</span> <span id="info-tag" class="type-tag">REGION</span></h2>
                <p id="info-desc">正在加载地理数据，请稍候...</p>
            </div>
        </div>
    </div>

    <script>
        // Data & Configuration
        const CONFIG = {
            width: 1200,
            height: 700,
            geoUrl: 'https://gist.githubusercontent.com/zhulinpinyu/8e18d57b3b18fb74e776/raw/efbb74cfea53decb1fe7d6bf279fd351c28c4810/china_simplify.json',
            sichuanName: '四川',
            // Manual offsets to prevent overlap: [x, y] relative to dot
            spots: [
                {
                    name: "九寨沟",
                    coord: [103.918, 33.263],
                    type: "自然遗产",
                    desc: "童话世界，人间仙境。翠海、叠瀑、彩林、雪峰、藏情、蓝冰，六绝驰名中外。",
                    offset: [30, -30] // Top Right
                },
                {
                    name: "三星堆",
                    coord: [104.200, 30.950],
                    type: "古蜀文明",
                    desc: "沉睡数千年，一醒惊天下。青铜神树、纵目面具，揭示了古蜀文明的神秘面纱。",
                    offset: [40, -10] // Right
                },
                {
                    name: "峨眉山",
                    coord: [103.322, 29.568],
                    type: "佛教名山",
                    desc: "普贤菩萨道场，地势陡峭，风景秀丽，素有'峨眉天下秀'之称。",
                    offset: [-60, 20] // Bottom Left (Away from Leshan)
                },
                {
                    name: "乐山大佛",
                    coord: [103.770, 29.540],
                    type: "世界遗产",
                    desc: "山是一尊佛，佛是一座山。中国最大的一尊摩崖石刻造像。",
                    offset: [40, 30] // Bottom Right (Away from Emei)
                },
                {
                    name: "贡嘎山",
                    coord: [101.960, 29.580],
                    type: "蜀山之王",
                    desc: "海拔7556米，四川省最高峰。周围林立着145座海拔五六千米的冰峰。",
                    offset: [-50, -30] // Top Left
                },
                {
                    name: "稻城亚丁",
                    coord: [100.340, 28.530],
                    type: "最后净土",
                    desc: "水蓝色星球上的最后一片净土。仙乃日、央迈勇、夏诺多吉三座神山巍然耸立。",
                    offset: [-40, 20] // Bottom Left
                }
            ]
        };

        // State
        let svg, projection, pathGen;
        let mapGroup, traceGroup, markersGroup, highlightGroup;
        let geoData, sichuanFeature;

        // Initialize
        document.addEventListener('DOMContentLoaded', () => {
            initSystem();
        });

        function initSystem() {
            svg = d3.select("#main-svg");
            mapGroup = d3.select("#map-group");
            highlightGroup = d3.select("#sichuan-highlight-group");
            traceGroup = d3.select("#trace-group");
            markersGroup = d3.select("#markers-group");

            loadData();
        }

        function loadData() {
            fetch(CONFIG.geoUrl)
                .then(res => {
                    if (!res.ok) throw new Error("Network response was not ok");
                    return res.json();
                })
                .then(data => {
                    geoData = data;
                    // Find Sichuan
                    sichuanFeature = geoData.features.find(f => f.properties.name.indexOf(CONFIG.sichuanName) > -1);
                    
                    if (!sichuanFeature) throw new Error("Sichuan data not found");

                    renderInitialMap();
                    
                    // Hide loader
                    const loader = document.getElementById('loader');
                    loader.style.opacity = 0;
                    setTimeout(() => loader.remove(), 800);

                    // Start Animation Sequence
                    setTimeout(runAnimationSequence, 1000);
                })
                .catch(err => {
                    console.error(err);
                    document.querySelector('.spinner-text').textContent = "DATA ERROR - CHECK CONSOLE";
                    document.querySelector('.spinner-ring').style.borderColor = "red";
                });
        }

        function renderInitialMap() {
            // Initial Projection: Fit whole China to canvas
            projection = d3.geoMercator().fitSize([CONFIG.width, CONFIG.height], geoData);
            pathGen = d3.geoPath().projection(projection);

            // Draw all provinces
            mapGroup.selectAll("path")
                .data(geoData.features)
                .enter()
                .append("path")
                .attr("d", pathGen)
                .attr("class", "province")
                .attr("id", d => `prov-${d.properties.id}`);
        }

        function runAnimationSequence() {
            // Sequence:
            // 1. Zoom to Sichuan
            // 2. Highlight & Gold Trace
            // 3. Show Markers one by one

            zoomToSichuan(() => {
                highlightSichuan(() => {
                    showMarkers();
                });
            });
        }

        function zoomToSichuan(callback) {
            // Calculate bounds of Sichuan
            const bounds = pathGen.bounds(sichuanFeature);
            const dx = bounds[1][0] - bounds[0][0];
            const dy = bounds[1][1] - bounds[0][1];
            const x = (bounds[0][0] + bounds[1][0]) / 2;
            const y = (bounds[0][1] + bounds[1][1]) / 2;
            
            // Determine scale (add padding)
            const scale = 0.65 / Math.max(dx / CONFIG.width, dy / CONFIG.height);
            
            // Target ViewBox
            const targetWidth = CONFIG.width / scale;
            const targetHeight = CONFIG.height / scale;
            const targetX = x - targetWidth / 2;
            const targetY = y - targetHeight / 2;

            // Update UI
            updateInfo("定位中...", "NAVIGATION", "正在锁定目标区域：四川省 (Sichuan Province)");

            // Animate ViewBox
            svg.transition()
                .duration(2000)
                .ease(d3.easeCubicInOut)
                .attr("viewBox", `${targetX} ${targetY} ${targetWidth} ${targetHeight}`)
                .on("end", callback);

            // Fade out other provinces slightly during zoom to focus attention
            mapGroup.selectAll("path")
                .transition().duration(2000)
                .style("opacity", 0.2);
        }

        function highlightSichuan(callback) {
            // 1. Draw Sichuan Background (Darker/Distinct)
            highlightGroup.append("path")
                .datum(sichuanFeature)
                .attr("d", pathGen)
                .attr("class", "sichuan-base")
                .transition().duration(1000)
                .style("opacity", 1);

            updateInfo("四川省", "SICHUAN", "天府之国，沃野千里。准备展示核心旅游资源。");

            // 2. Gold Trace Animation
            const pathNode = traceGroup.append("path")
                .datum(sichuanFeature)
                .attr("d", pathGen)
                .attr("class", "golden-trace");
            
            const totalLength = pathNode.node().getTotalLength();

            pathNode
                .attr("stroke-dasharray", totalLength + " " + totalLength)
                .attr("stroke-dashoffset", totalLength)
                .transition()
                .duration(2500)
                .ease(d3.easeLinear)
                .attr("stroke-dashoffset", 0)
                .on("end", callback);
        }

        function showMarkers() {
            let delay = 0;
            const interval = 1200;

            CONFIG.spots.forEach((spot, i) => {
                setTimeout(() => {
                    createMarker(spot, i);
                }, delay);
                delay += interval;
            });
        }

        function createMarker(spot, index) {
            const [lon, lat] = spot.coord;
            const [x, y] = projection([lon, lat]);
            
            const g = markersGroup.append("g")
                .attr("class", "marker-group")
                .attr("transform", `translate(${x}, ${y})`)
                .on("click", () => updateInfo(spot.name, spot.type, spot.desc));

            // 1. Ring Expansion
            g.append("circle")
                .attr("class", "marker-ring")
                .attr("r", 0)
                .transition()
                .duration(800)
                .ease(d3.easeBackOut)
                .attr("r", 6)
                .style("opacity", 1);

            // Continuous pulse for ring
            startPulse(g.select(".marker-ring"));

            // 2. Core Dot
            g.append("circle")
                .attr("class", "marker-core")
                .attr("r", 0)
                .transition()
                .delay(200)
                .duration(500)
                .attr("r", 2.5)
                .style("opacity", 1);

            // 3. Leader Line
            const [offsetX, offsetY] = spot.offset;
            const line = g.append("line")
                .attr("class", "marker-line")
                .attr("x1", 0).attr("y1", 0)
                .attr("x2", 0).attr("y2", 0) // Start at center
                .transition()
                .delay(400)
                .duration(600)
                .attr("x2", offsetX)
                .attr("y2", offsetY)
                .style("opacity", 0.8);

            // 4. Text Label
            // Background rect for readability (optional, simple text shadow used instead)
            
            const textAnchor = offsetX > 0 ? "start" : "end";
            const textX = offsetX + (offsetX > 0 ? 5 : -5);
            const textY = offsetY + 5; // vertical align fix

            g.append("text")
                .attr("class", "marker-text")
                .attr("x", textX)
                .attr("y", textY)
                .attr("text-anchor", textAnchor)
                .text(spot.name)
                .transition()
                .delay(800)
                .duration(500)
                .style("opacity", 1);

            // Update Info Card automatically
            updateInfo(spot.name, spot.type, spot.desc);
        }

        function startPulse(selection) {
            selection.transition()
                .duration(1500)
                .attr("r", 12)
                .style("stroke-opacity", 0)
                .on("end", function() {
                    d3.select(this)
                        .attr("r", 6)
                        .style("stroke-opacity", 1);
                    startPulse(d3.select(this));
                });
        }

        function updateInfo(title, tag, desc) {
            const card = document.getElementById('info-panel');
            const titleEl = document.getElementById('info-title');
            const tagEl = document.getElementById('info-tag');
            const descEl = document.getElementById('info-desc');

            // Visual feedback
            card.style.borderColor = 'var(--gold)';
            setTimeout(() => card.style.borderColor = 'rgba(56, 189, 248, 0.3)', 300);

            if (!card.classList.contains('active')) card.classList.add('active');

            titleEl.innerText = title;
            tagEl.innerText = tag;
            descEl.innerText = desc;
        }

    </script>
</body>
</html>1c:T4c36,<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>地球往事：三体三部曲全景导览</title>
    <!-- Core Dependencies -->
    <script src="https://cdn.tailwindcss.com"></script>
    <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" rel="stylesheet">
    <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;600;700&family=Noto+Sans+SC:wght@300;400;700&display=swap" rel="stylesheet">
    
    <!-- Design System Override -->
    <script>
        tailwind.config = {
            theme: {
                extend: {
                    fontFamily: { 
                        sans: ['Inter', 'Noto Sans SC', 'sans-serif'],
                        mono: ['Fira Code', 'monospace']
                    },
                    colors: {
                        space: {
                            950: '#020617',
                            900: '#0f172a',
                            800: '#1e293b',
                        },
                        trisolaris: '#ff4d4d',
                        earth: '#3b82f6',
                        forest: '#10b981'
                    },
                    animation: {
                        'pulse-slow': 'pulse 4s cubic-bezier(0.4, 0, 0.6, 1) infinite',
                        'float': 'float 6s ease-in-out infinite',
                    },
                    keyframes: {
                        float: {
                            '0%, 100%': { transform: 'translateY(0)' },
                            '50%': { transform: 'translateY(-20px)' },
                        }
                    }
                }
            }
        }
    </script>
    <style>
        body { 
            background-color: #020617;
            color: #e2e8f0;
            overflow-x: hidden;
        }
        .glass-card {
            background: rgba(30, 41, 59, 0.7);
            backdrop-filter: blur(12px);
            border: 1px solid rgba(255, 255, 255, 0.1);
        }
        .star-bg {
            position: fixed;
            top: 0; left: 0; width: 100%; height: 100%;
            z-index: -1;
            background: radial-gradient(circle at center, #1e293b 0%, #020617 100%);
        }
        .glow-text {
            text-shadow: 0 0 15px rgba(59, 130, 246, 0.5);
        }
        .book-tab.active {
            border-bottom: 3px solid #3b82f6;
            color: #3b82f6;
        }
        ::-webkit-scrollbar { width: 4px; }
        ::-webkit-scrollbar-thumb { background: #334155; border-radius: 10px; }
    </style>
</head>
<body class="font-sans">
    <div class="star-bg" id="starfield"></div>

    <!-- Navigation -->
    <nav class="sticky top-0 z-50 glass-card border-b border-white/10 px-6 py-4">
        <div class="max-w-7xl mx-auto flex justify-between items-center">
            <div class="flex items-center space-x-3">
                <div class="w-10 h-10 bg-blue-600 rounded-full flex items-center justify-center animate-pulse">
                    <i class="fas fa-atom text-white"></i>
                </div>
                <span class="text-xl font-bold tracking-tighter uppercase">Remembrance of Earth's Past</span>
            </div>
            <div class="hidden md:flex space-x-8 text-sm font-medium">
                <a href="#overview" class="hover:text-blue-400 transition">概览</a>
                <a href="#trilogy" class="hover:text-blue-400 transition">三部曲</a>
                <a href="#concepts" class="hover:text-blue-400 transition">核心概念</a>
                <a href="#characters" class="hover:text-blue-400 transition">关键人物</a>
            </div>
        </div>
    </nav>

    <!-- Hero Section -->
    <header class="relative h-[80vh] flex items-center justify-center overflow-hidden">
        <div class="absolute inset-0 z-0">
            <img src="https://images.unsplash.com/photo-1462331940025-496dfbfc7564?auto=format&fit=crop&w=2000&q=80" 
                 class="w-full h-full object-cover opacity-30 scale-110" alt="Cosmos">
        </div>
        <div class="relative z-10 text-center px-4 max-w-4xl">
            <h1 class="text-5xl md:text-8xl font-bold mb-6 glow-text tracking-tight">三体</h1>
            <p class="text-xl md:text-2xl text-slate-300 font-light leading-relaxed mb-8">
                “给时光以生命，给岁月以文明。” —— 刘慈欣史诗级科幻巨作
            </p>
            <div class="flex flex-wrap justify-center gap-4">
                <button onclick="document.getElementById('trilogy').scrollIntoView({behavior:'smooth'})" class="px-8 py-3 bg-blue-600 hover:bg-blue-700 rounded-full font-semibold transition transform hover:scale-105">
                    开启探索
                </button>
                <button class="px-8 py-3 bg-white/10 hover:bg-white/20 rounded-full font-semibold backdrop-blur-md transition">
                    了解作者
                </button>
            </div>
        </div>
        <!-- Floating Elements -->
        <div class="absolute bottom-10 left-1/2 -translate-x-1/2 animate-bounce text-slate-500">
            <i class="fas fa-chevron-down text-2xl"></i>
        </div>
    </header>

    <main class="max-w-7xl mx-auto px-6 py-20 space-y-32">
        
        <!-- Trilogy Section -->
        <section id="trilogy">
            <div class="flex flex-col md:flex-row justify-between items-end mb-12 gap-4">
                <div>
                    <h2 class="text-3xl font-bold mb-2">三部曲全景</h2>
                    <p class="text-slate-400">从红岸基地到宇宙的终结</p>
                </div>
                <div class="flex space-x-2 glass-card p-1 rounded-xl">
                    <button onclick="switchBook(1)" id="btn-1" class="book-tab active px-6 py-2 rounded-lg transition">I. 三体</button>
                    <button onclick="switchBook(2)" id="btn-2" class="book-tab px-6 py-2 rounded-lg transition">II. 黑暗森林</button>
                    <button onclick="switchBook(3)" id="btn-3" class="book-tab px-6 py-2 rounded-lg transition">III. 死神永生</button>
                </div>
            </div>

            <div id="book-content" class="grid md:grid-cols-2 gap-12 items-center min-h-[400px]">
                <!-- Content injected by JS -->
            </div>
        </section>

        <!-- Core Concepts Grid -->
        <section id="concepts">
            <div class="text-center mb-16">
                <h2 class="text-3xl font-bold mb-4">核心宇宙法则</h2>
                <div class="h-1 w-20 bg-blue-600 mx-auto"></div>
            </div>
            <div class="grid md:grid-cols-3 gap-8">
                <div class="glass-card p-8 rounded-2xl hover:border-blue-500/50 transition group">
                    <div class="w-14 h-14 bg-blue-500/20 rounded-lg flex items-center justify-center mb-6 group-hover:scale-110 transition">
                        <i class="fas fa-tree text-2xl text-blue-400"></i>
                    </div>
                    <h3 class="text-xl font-bold mb-4">黑暗森林理论</h3>
                    <p class="text-slate-400 leading-relaxed">宇宙就是一座黑暗森林，每个文明都是带枪的猎人。他人即地狱，任何暴露自己存在的文明都将很快被消灭。</p>
                </div>
                <div class="glass-card p-8 rounded-2xl hover:border-trisolaris/50 transition group">
                    <div class="w-14 h-14 bg-red-500/20 rounded-lg flex items-center justify-center mb-6 group-hover:scale-110 transition">
                        <i class="fas fa-microchip text-2xl text-red-400"></i>
                    </div>
                    <h3 class="text-xl font-bold mb-4">智子 (Sophon)</h3>
                    <p class="text-slate-400 leading-relaxed">三体人将质子改造为智能计算机，锁死了人类的基础物理研究，并实现了对地球的实时全方位监控。</p>
                </div>
                <div class="glass-card p-8 rounded-2xl hover:border-forest/50 transition group">
                    <div class="w-14 h-14 bg-emerald-500/20 rounded-lg flex items-center justify-center mb-6 group-hover:scale-110 transition">
                        <i class="fas fa-layer-group text-2xl text-emerald-400"></i>
                    </div>
                    <h3 class="text-xl font-bold mb-4">维度打击</h3>
                    <p class="text-slate-400 leading-relaxed">高级文明使用“二向箔”等武器将目标空间降维，从三维坍缩至二维，是宇宙中最彻底的毁灭方式。</p>
                </div>
            </div>
        </section>

        <!-- Characters Section -->
        <section id="characters">
            <h2 class="text-3xl font-bold mb-12">关键见证者</h2>
            <div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6">
                <!-- Character Card 1 -->
                <div class="glass-card rounded-2xl overflow-hidden group">
                    <div class="h-48 overflow-hidden">
                        <img src="https://images.unsplash.com/photo-1534447677768-be436bb09401?auto=format&fit=crop&w=800&q=80" class="w-full h-full object-cover group-hover:scale-110 transition duration-500" alt="Ye Wenjie">
                    </div>
                    <div class="p-6">
                        <h4 class="text-xl font-bold mb-1">叶文洁</h4>
                        <p class="text-blue-400 text-sm mb-4">统帅 / 启蒙者</p>
                        <p class="text-slate-400 text-sm">红岸基地的天体物理学家，在绝望中向宇宙发出了第一声呼唤。</p>
                    </div>
                </div>
                <!-- Character Card 2 -->
                <div class="glass-card rounded-2xl overflow-hidden group">
                    <div class="h-48 overflow-hidden">
                        <img src="https://images.unsplash.com/photo-1506318137071-a8e063b4bcc0?auto=format&fit=crop&w=800&q=80" class="w-full h-full object-cover group-hover:scale-110 transition duration-500" alt="Luo Ji">
                    </div>
                    <div class="p-6">
                        <h4 class="text-xl font-bold mb-1">罗辑</h4>
                        <p class="text-blue-400 text-sm mb-4">面壁者 / 执剑人</p>
                        <p class="text-slate-400 text-sm">领悟了黑暗森林法则，以一人之力威慑三体文明，守护地球半个世纪。</p>
                    </div>
                </div>
                <!-- Character Card 3 -->
                <div class="glass-card rounded-2xl overflow-hidden group">
                    <div class="h-48 overflow-hidden">
                        <img src="https://images.unsplash.com/photo-1446776811953-b23d57bd21aa?auto=format&fit=crop&w=800&q=80" class="w-full h-full object-cover group-hover:scale-110 transition duration-500" alt="Cheng Xin">
                    </div>
                    <div class="p-6">
                        <h4 class="text-xl font-bold mb-1">程心</h4>
                        <p class="text-blue-400 text-sm mb-4">第二任执剑人</p>
                        <p class="text-slate-400 text-sm">代表了人类的母性与仁慈，在宇宙终极的抉择面前，她选择了爱。</p>
                    </div>
                </div>
                <!-- Character Card 4 -->
                <div class="glass-card rounded-2xl overflow-hidden group">
                    <div class="h-48 overflow-hidden">
                        <img src="https://images.unsplash.com/photo-1451187580459-43490279c0fa?auto=format&fit=crop&w=800&q=80" class="w-full h-full object-cover group-hover:scale-110 transition duration-500" alt="Zhang Beihai">
                    </div>
                    <div class="p-6">
                        <h4 class="text-xl font-bold mb-1">章北海</h4>
                        <p class="text-blue-400 text-sm mb-4">增援未来特遣军</p>
                        <p class="text-slate-400 text-sm">坚定的逃亡主义者，为人类文明在星际间保留了最后的火种。</p>
                    </div>
                </div>
            </div>
        </section>

        <!-- Interactive Quote Section -->
        <section class="relative py-20 rounded-3xl overflow-hidden">
            <div class="absolute inset-0 bg-gradient-to-r from-blue-900/40 to-purple-900/40 z-0"></div>
            <div class="relative z-10 text-center px-6">
                <i class="fas fa-quote-left text-4xl text-blue-500 mb-8"></i>
                <h2 id="dynamic-quote" class="text-2xl md:text-4xl font-light italic leading-relaxed max-w-4xl mx-auto mb-8">
                    失去人性，失去很多；失去兽性，失去一切。
                </h2>
                <button onclick="refreshQuote()" class="text-blue-400 hover:text-blue-300 transition flex items-center mx-auto space-x-2">
                    <i class="fas fa-sync-alt"></i> <span>换一句名言</span>
                </button>
            </div>
        </section>

    </main>

    <!-- Footer -->
    <footer class="border-t border-white/10 py-12 bg-space-950">
        <div class="max-w-7xl mx-auto px-6 flex flex-col md:flex-row justify-between items-center gap-8">
            <div class="text-center md:text-left">
                <div class="text-2xl font-bold mb-2">地球往事</div>
                <p class="text-slate-500 text-sm">致敬刘慈欣，致敬伟大的想象力。</p>
            </div>
            <div class="flex space-x-6 text-2xl text-slate-400">
                <a href="#" class="hover:text-white transition"><i class="fab fa-github"></i></a>
                <a href="#" class="hover:text-white transition"><i class="fab fa-twitter"></i></a>
                <a href="#" class="hover:text-white transition"><i class="fas fa-envelope"></i></a>
            </div>
            <div class="text-slate-500 text-sm">
                &copy; 2023 三体迷社区. 仅供学习交流使用.
            </div>
        </div>
    </footer>

    <!-- Application Logic -->
    <script>
        const books = {
            1: {
                title: "三体 I",
                subtitle: "地球往事",
                desc: "文化大革命期间，红岸基地的一名天体物理学家叶文洁向宇宙发出了寻找外星文明的信息。四光年外的三体文明收到了信息，并决定入侵地球。人类的命运从此改变。",
                tags: ["红岸基地", "三体游戏", "智子", "古筝行动"],
                img: "https://images.unsplash.com/photo-1614728894747-a83421e2b9c9?auto=format&fit=crop&w=800&q=80",
                color: "text-blue-400"
            },
            2: {
                title: "三体 II",
                subtitle: "黑暗森林",
                desc: "三体舰队向地球进发，智子封锁了人类科技。人类启动“面壁计划”，选出四位面壁者利用三体人思维透明的弱点进行反击。罗辑最终发现了宇宙的黑暗森林法则。",
                tags: ["面壁计划", "水滴", "黑暗森林", "摇篮系统"],
                img: "https://images.unsplash.com/photo-1446776811953-b23d57bd21aa?auto=format&fit=crop&w=800&q=80",
                color: "text-emerald-400"
            },
            3: {
                title: "三体 III",
                subtitle: "死神永生",
                desc: "威慑纪元结束，人类与三体文明的博弈进入最终阶段。程心作为执剑人面临终极抉择。宇宙的真相逐渐揭开：这是一场跨越维度的、永无止境的战争。",
                tags: ["阶梯计划", "掩体计划", "二向箔", "小宇宙"],
                img: "https://images.unsplash.com/photo-1451187580459-43490279c0fa?auto=format&fit=crop&w=800&q=80",
                color: "text-red-400"
            }
        };

        const quotes = [
            "失去人性，失去很多；失去兽性，失去一切。",
            "给时光以生命，给岁月以文明。",
            "弱小和无知不是生存的障碍，傲慢才是。",
            "你的无畏来源于无知。",
            "我消灭你，与你何干？",
            "这是人类的落日。",
            "前进！前进！！不择手段地前进！！！"
        ];

        function switchBook(id) {
            const book = books[id];
            const container = document.getElementById('book-content');
            
            // Update tabs
            document.querySelectorAll('.book-tab').forEach(btn => btn.classList.remove('active'));
            document.getElementById(`btn-${id}`).classList.add('active');

            // Animate content change
            container.style.opacity = 0;
            setTimeout(() => {
                container.innerHTML = `
                    <div class="space-y-6 animate-fade-in">
                        <h3 class="text-4xl font-bold ${book.color}">${book.title}</h3>
                        <h4 class="text-2xl text-slate-300">${book.subtitle}</h4>
                        <p class="text-slate-400 leading-relaxed text-lg">${book.desc}</p>
                        <div class="flex flex-wrap gap-2">
                            ${book.tags.map(tag => `<span class="px-3 py-1 bg-white/5 border border-white/10 rounded-full text-xs text-slate-300">${tag}</span>`).join('')}
                        </div>
                    </div>
                    <div class="relative group">
                        <div class="absolute -inset-4 bg-gradient-to-r from-blue-500 to-purple-600 rounded-2xl blur opacity-20 group-hover:opacity-40 transition"></div>
                        <img src="${book.img}" class="relative rounded-2xl shadow-2xl w-full h-80 object-cover" alt="${book.title}">
                    </div>
                `;
                container.style.opacity = 1;
            }, 200);
        }

        function refreshQuote() {
            const quoteEl = document.getElementById('dynamic-quote');
            const randomQuote = quotes[Math.floor(Math.random() * quotes.length)];
            quoteEl.style.opacity = 0;
            setTimeout(() => {
                quoteEl.innerText = randomQuote;
                quoteEl.style.opacity = 1;
            }, 300);
        }

        // Starfield Animation
        function initStars() {
            const starfield = document.getElementById('starfield');
            const count = 150;
            for (let i = 0; i < count; i++) {
                const star = document.createElement('div');
                const size = Math.random() * 2 + 'px';
                star.style.position = 'absolute';
                star.style.width = size;
                star.style.height = size;
                star.style.backgroundColor = 'white';
                star.style.top = Math.random() * 100 + '%';
                star.style.left = Math.random() * 100 + '%';
                star.style.opacity = Math.random();
                star.style.borderRadius = '50%';
                if (Math.random() > 0.8) {
                    star.classList.add('animate-pulse-slow');
                }
                starfield.appendChild(star);
            }
        }

        // Initialize
        window.onload = () => {
            switchBook(1);
            initStars();
            console.log('Three-Body View Loaded');
        };
    </script>
</body>
</html>1f:T7a0,“Brothers, a super ridiculous Linux kernel vulnerability has just been exposed! A normal user can grab the server's root privileges with just one command! Its ID is CVE-2026-31431, codenamed Copy Fail! This vulnerability affects almost all mainstream Linux distributions since 2017, including Ubuntu, CentOS, RHEL, Alibaba Cloud, AWS, and SUSE. As long as you have a regular account on the server, you can complete privilege escalation, and even escape from containers to the host. The principle is actually very clever, let me explain it clearly: Normally, file content is cached in memory, called page cache. We regular users can only read it, not change it at will. However, hackers exploited an encryption interface provided by the kernel called AF_ALG, combined with the splice system call, to secretly insert the target file—for example, /usr/bin/su—into the kernel's encryption process. Because the kernel made a performance optimization in 2017, this optimization caused the kernel to use the memory page of this file as a 'writable buffer'! As a result, during the encryption calculation process, the hacker precisely wrote 4 bytes under their control into it. Although the encryption verification failed in the end, those 4 bytes had already physically modified the su program in memory! By repeating the operation a few times, su can be transformed into a malicious program that 'gives me root directly'. Once executed, privileges are obtained! The whole process doesn't require guessing kernel addresses or race conditions; it's extremely stable.” “Currently, major vendors have urgently pushed patches, so everyone should update their kernels quickly! For temporary mitigation, you can disable the algif_aead module. This vulnerability tells us once again: no matter how small a code optimization is, if not thought through, it can plant a fatal bomb. Alright, that's all for today's share. Like and follow, see you next time!”20:[]
21:T34572,<!doctype html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Linux内核超级漏洞：Copy Fail 深度解析</title>
  <style>
    @import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;700&display=swap');
    @font-face {
      font-family: "SVGVideoAlimamaShuHei";
      src:
        url("/fonts/AlimamaShuHeiTi-Bold.woff2") format("woff2"),
        url("/fonts/AlimamaShuHeiTi-Bold.woff") format("woff"),
        url("/fonts/AlimamaShuHeiTi-Bold.ttf") format("truetype"),
        local("Alimama ShuHeiTi Bold"),
        local("Alimama ShuHeiTi"),
        local("AlimamaShuHeiTi-Bold"),
        local("AlimamaShuHeiTi");
      font-display: swap;
      font-weight: 700;
      font-style: normal;
    }
    :root {
      color-scheme: dark;
      --bg: #202020;
      --fg: #e2e8f0;
      --muted: #94a3b8;
      --card: rgba(255, 255, 255, 0.05);
      --border: rgba(255, 255, 255, 0.12);
      --sv-editor-height: 50vh;
      --sv-font-display-cn: "JetBrains Mono", "Courier New", Consolas, monospace;
      --sv-font-body-cn: "JetBrains Mono", "Courier New", Consolas, monospace;
      --sv-font-mono: "SFMono-Regular", "JetBrains Mono", "Fira Code", "Courier New", monospace;
      --sv-stage-perspective: 900px;
        --sv-trans-enter-opacity: 0;
        --sv-trans-enter-transform: rotateY(90deg);
        --sv-trans-enter-filter: blur(6px);
        --sv-trans-active-opacity: 1;
        --sv-trans-active-transform: rotateY(0deg);
        --sv-trans-active-filter: blur(0px);
        --sv-trans-exit-opacity: 0;
        --sv-trans-exit-transform: rotateY(-90deg);
        --sv-trans-exit-filter: blur(4px);
        --sv-trans-duration: 0.65s;
        --sv-trans-ease: cubic-bezier(0.36, 0, 0.66, 1);
    }
    * { box-sizing: border-box; }
    html, body {
      margin: 0; width: 100%; height: 100%; overflow: hidden;
      background: var(--bg);
      color: var(--fg);
      font-family: var(--sv-font-body-cn);
    }
    #app { width: 100%; height: 100%; display: flex; flex-direction: column; }
    #viewport { position: relative; flex: 1; min-height: 0; overflow: hidden; }
    #stage {
      position: relative; width: 100%; height: 100%; overflow: hidden;
      perspective: var(--sv-stage-perspective, none);
      perspective-origin: 50% 50%;
    }
    .scene {
      position: absolute; inset: 0; overflow: hidden;
      backface-visibility: hidden;
      -webkit-backface-visibility: hidden;
      opacity: var(--sv-trans-enter-opacity, 0); pointer-events: none;
      transform: var(--sv-trans-enter-transform, translateX(var(--scene-enter-offset, 56px)) scale(0.96));
      filter: var(--sv-trans-enter-filter, blur(10px));
      transition: opacity var(--sv-trans-duration, .55s) var(--sv-trans-ease, cubic-bezier(.22,1,.36,1)), transform var(--sv-trans-duration, .55s) var(--sv-trans-ease, cubic-bezier(.22,1,.36,1)), filter var(--sv-trans-duration, .55s) ease;
      will-change: opacity, transform, filter;
      z-index: 1;
    }
    .scene.scene-active {
      opacity: var(--sv-trans-active-opacity, 1); pointer-events: auto;
      transform: var(--sv-trans-active-transform, translateX(0) scale(1));
      filter: var(--sv-trans-active-filter, blur(0px));
      z-index: 3;
    }
    .scene.scene-exit {
      opacity: var(--sv-trans-exit-opacity, 0); pointer-events: none;
      transform: var(--sv-trans-exit-transform, translateX(var(--scene-exit-offset, -56px)) scale(0.96));
      filter: var(--sv-trans-exit-filter, blur(10px));
      z-index: 2;
    }
    .scene-placeholder {
      height: 100%; border: 1px dashed var(--border); border-radius: 20px; background: var(--card);
      display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 12px; color: var(--muted);
    }
    .sv-scene-root { width: 100%; height: 100%; position: relative; overflow: hidden; }
    .scene-placeholder-title { font-size: 28px; color: var(--fg); font-weight: 700; }
    [data-step] { transition: opacity .35s ease, transform .35s ease, filter .35s ease; }
    #sv-controls {
      background: rgba(19, 19, 19, 0.95);
      border-top: 1px solid rgba(255,255,255,0.08);
      padding: 8px 12px 10px;
      display: flex;
      flex-direction: column;
      gap: 8px;
      backdrop-filter: blur(8px);
      flex-shrink: 0;
    }
    .sv-progress-wrap {
      position: relative;
      height: 22px;
      display: flex;
      align-items: center;
      cursor: pointer;
    }
    .sv-progress-track {
      width: 100%;
      height: 4px;
      border-radius: 999px;
      background: rgba(120, 120, 120, 0.55);
      position: relative;
      overflow: hidden;
    }
    .sv-progress-fill {
      position: absolute;
      left: 0;
      top: 0;
      bottom: 0;
      width: 0%;
      border-radius: 999px;
      background: linear-gradient(90deg, #f97316, #fb923c);
      transition: width 220ms ease;
    }
    .sv-progress-thumb {
      position: absolute;
      top: 50%;
      width: 12px;
      height: 12px;
      border-radius: 50%;
      background: #fff;
      transform: translate(-50%, -50%);
      box-shadow: 0 0 0 1px rgba(0,0,0,0.28);
      pointer-events: none;
    }
    .sv-toolbar {
      display: grid;
      grid-template-columns: 1fr auto 1fr;
      align-items: center;
      gap: 8px;
      min-height: 30px;
    }
    .sv-toolbar-left {
      display: inline-flex;
      align-items: center;
      gap: 6px;
      min-width: 0;
    }
    .sv-toolbar-title {
      font-size: 11px;
      color: #a8b0bb;
      white-space: nowrap;
      overflow: hidden;
      text-overflow: ellipsis;
    }
    .sv-expand-btn {
      width: 20px;
      height: 20px;
      border: none;
      border-radius: 6px;
      background: rgba(255,255,255,0.08);
      color: #cbd5e1;
      display: inline-flex;
      align-items: center;
      justify-content: center;
      cursor: pointer;
      flex: 0 0 auto;
      transition: background 140ms ease, color 140ms ease, transform 120ms ease;
    }
    .sv-expand-btn:hover {
      background: rgba(255,255,255,0.15);
      color: #f8fafc;
    }
    .sv-expand-btn:active {
      transform: scale(0.94);
    }
    body:not(.sv-host-can-edit-panel) .sv-expand-btn {
      display: none;
    }
    .sv-play-btn {
      width: 28px;
      height: 28px;
      border: none;
      border-radius: 999px;
      color: #fff;
      background: #f97316;
      cursor: pointer;
      display: inline-flex;
      align-items: center;
      justify-content: center;
      transition: transform 120ms ease, background 160ms ease;
    }
    .sv-play-btn:hover { background: #fb923c; }
    .sv-play-btn:active { transform: scale(0.94); }
    .sv-play-btn:disabled { opacity: 0.45; cursor: not-allowed; }
    .sv-status {
      text-align: right;
      font-size: 11px;
      color: #8f98a5;
      white-space: nowrap;
      overflow: hidden;
      text-overflow: ellipsis;
    }
    .sv-scene-grid {
      display: flex;
      align-items: stretch;
      gap: 6px;
      flex-wrap: nowrap;
      overflow: hidden;
    }
    .scene-chip {
      position: relative;
      width: 100%;
      border: none;
      border-bottom: 2px solid transparent;
      border-radius: 8px;
      background: rgba(255,255,255,0.06);
      color: #a3a3a3;
      padding: 7px 6px;
      text-align: center;
      font-size: 11px;
      line-height: 1.1;
      white-space: nowrap;
      overflow: hidden;
      text-overflow: ellipsis;
      cursor: pointer;
      transition: transform 140ms ease, color 160ms ease, background 160ms ease, border-color 160ms ease;
    }
    .scene-chip-label {
      display: block;
      width: 100%;
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
    }
    .scene-chip-wrap {
      position: relative;
      flex: 1 1 0;
      min-width: 0;
    }
    .scene-chip-retry {
      position: absolute;
      top: 50%;
      right: 4px;
      transform: translateY(-50%);
      width: 24px;
      height: 24px;
      border: none;
      border-radius: 999px;
      background: #f97316;
      color: #fff;
      display: inline-flex;
      align-items: center;
      justify-content: center;
      box-shadow: 0 4px 10px rgba(0, 0, 0, 0.3);
      opacity: 0;
      pointer-events: none;
      transition: opacity 160ms ease;
      cursor: pointer;
    }
    body.sv-host-can-retry .scene-chip-wrap:hover .scene-chip-retry {
      opacity: 1;
      pointer-events: auto;
    }
    .scene-chip-retry:disabled {
      opacity: 1;
      pointer-events: none;
      background: #f97316;
    }
    .scene-chip-retry-icon {
      width: 18px;
      height: 18px;
      display: block;
    }
    .scene-chip-retry-spinner {
      width: 14px;
      height: 14px;
      aspect-ratio: 1 / 1;
      border: 2px solid #ffffff;
      border-top-color: transparent;
      border-radius: 999px;
      box-sizing: border-box;
      animation: sceneChipSpin 1s linear infinite;
      display: block;
      flex: 0 0 auto;
    }
    @keyframes sceneChipSpin {
      from { transform: rotate(0deg); }
      to { transform: rotate(360deg); }
    }
    .scene-chip:hover { transform: scale(1.02); }
    .scene-chip.is-active {
      color: #fb923c;
      border-bottom-color: #fb923c;
      background: linear-gradient(135deg, rgba(249,115,22,0.2), rgba(168,85,247,0.2));
    }
    .scene-chip.is-failed {
      color: #fca5a5;
      border-bottom-color: rgba(239,68,68,0.95);
      background: rgba(239,68,68,0.12);
    }
    .scene-chip.is-generating {
      color: #fcd34d;
      background: rgba(245,158,11,0.11);
    }
    .sv-editor-overlay {
      position: relative;
      width: 100%;
      height: 0;
      z-index: 40;
      pointer-events: none;
      overflow: hidden;
      transition: height 260ms cubic-bezier(.22,1,.36,1);
    }
    .sv-editor-backdrop {
      position: absolute;
      inset: auto 0 0 0;
      height: 100%;
      background: rgba(0, 0, 0, 0.45);
      opacity: 0;
      transition: opacity 220ms ease;
    }
    .sv-editor-sheet {
      position: absolute;
      left: 0;
      right: 0;
      bottom: 0;
      height: 100%;
      background: rgba(16, 16, 16, 0.96);
      border-top: 1px solid rgba(255,255,255,0.12);
      box-shadow: 0 -14px 30px rgba(0,0,0,0.45);
      transform: translateY(102%);
      transition: transform 260ms cubic-bezier(.22,1,.36,1);
      display: flex;
      flex-direction: column;
    }
    .sv-editor-overlay.is-open {
      pointer-events: auto;
      height: var(--sv-editor-height, 50vh);
    }
    .sv-editor-overlay.is-open .sv-editor-backdrop {
      opacity: 1;
    }
    .sv-editor-overlay.is-open .sv-editor-sheet {
      transform: translateY(0);
    }
    .sv-editor-resize-handle {
      width: 100%;
      height: 14px;
      display: flex;
      align-items: center;
      justify-content: center;
      cursor: ns-resize;
      flex: 0 0 auto;
      touch-action: none;
    }
    .sv-editor-resize-handle::before {
      content: "";
      width: 52px;
      height: 4px;
      border-radius: 999px;
      background: rgba(148, 163, 184, 0.7);
    }
    .sv-editor-header {
      padding: 10px 12px;
      border-bottom: 1px solid rgba(255,255,255,0.1);
      display: grid;
      grid-template-columns: 1fr auto;
      align-items: center;
      gap: 8px;
    }
    .sv-editor-header-title {
      color: #f1f5f9;
      font-size: 13px;
      font-weight: 600;
      white-space: nowrap;
      overflow: hidden;
      text-overflow: ellipsis;
    }
    .sv-editor-actions {
      display: inline-flex;
      align-items: center;
      gap: 6px;
    }
    .sv-editor-btn {
      border: none;
      border-radius: 8px;
      cursor: pointer;
      font-size: 12px;
      line-height: 1;
      padding: 7px 10px;
      color: #e2e8f0;
      background: rgba(255,255,255,0.1);
      transition: background 120ms ease;
    }
    .sv-editor-btn:hover {
      background: rgba(255,255,255,0.18);
    }
    .sv-editor-btn.is-primary {
      color: #fff;
      background: #f97316;
    }
    .sv-editor-btn.is-primary:hover {
      background: #fb923c;
    }
    .sv-editor-list {
      flex: 1;
      min-height: 0;
      overflow: auto;
      padding: 10px 12px 14px;
      display: grid;
      gap: 10px;
      grid-template-columns: repeat(2, minmax(0, 1fr));
      align-items: start;
      align-content: start;
    }
    .sv-editor-item {
      border: 1px solid rgba(255,255,255,0.12);
      border-radius: 10px;
      background: rgba(255,255,255,0.03);
      padding: 10px;
      display: grid;
      grid-template-rows: auto auto auto;
      gap: 10px;
      align-content: start;
      cursor: pointer;
    }
    .sv-editor-item.is-active {
      border-color: rgba(249,115,22,0.65);
      box-shadow: inset 0 0 0 1px rgba(249,115,22,0.28);
    }
    .sv-editor-item-head {
      display: flex;
      align-items: center;
      justify-content: space-between;
      gap: 8px;
      min-height: 30px;
    }
    .sv-editor-item-title {
      border: none;
      border-radius: 8px;
      color: #fb923c;
      background: rgba(255,255,255,0.08);
      font-size: 12px;
      font-weight: 600;
      padding: 6px 8px;
      cursor: pointer;
      white-space: nowrap;
      overflow: hidden;
      text-overflow: ellipsis;
      text-align: left;
    }
    .sv-editor-item-title:hover {
      background: rgba(255,255,255,0.14);
    }
    .sv-editor-status {
      color: #94a3b8;
      font-size: 11px;
      white-space: nowrap;
    }
    .sv-editor-status-wrap {
      display: inline-flex;
      align-items: center;
      gap: 6px;
    }
    .scene-chip-retry.sv-editor-retry {
      position: static;
      transform: none;
      opacity: 1;
      pointer-events: auto;
      width: 24px;
      height: 24px;
      flex: 0 0 auto;
    }
    .sv-editor-field {
      display: grid;
      gap: 6px;
    }
    .sv-editor-label {
      color: #a3a3a3;
      font-size: 11px;
      font-weight: 700;
    }
    .sv-editor-textarea {
      width: 100%;
      border-radius: 8px;
      border: 1px solid rgba(255,255,255,0.14);
      background: rgba(0,0,0,0.24);
      color: #f1f5f9;
      font-size: 12px;
      line-height: 1.45;
      padding: 8px 9px;
      resize: none;
      min-height: 110px;
      height: 110px;
    }
    .sv-editor-textarea[data-field="scriptText"] {
      min-height: 84px;
    }
    .sv-editor-textarea:focus {
      outline: none;
      border-color: rgba(249,115,22,0.7);
      box-shadow: 0 0 0 2px rgba(249,115,22,0.18);
    }
    body.sv-theme-light .sv-editor-textarea {
      border-color: rgba(15,23,42,0.18);
      background: rgba(248,250,252,0.92);
      color: #0f172a;
    }
    body.sv-theme-light .sv-editor-textarea:focus {
      border-color: rgba(249,115,22,0.72);
      box-shadow: 0 0 0 2px rgba(249,115,22,0.2);
    }
    .sv-editor-script-view {
      width: 100%;
      color: #e2e8f0;
      font-size: 12px;
      line-height: 1.5;
      white-space: pre-wrap;
      word-break: break-word;
      min-height: 84px;
      max-height: 84px;
      overflow: auto;
    }
    .sv-editor-save-tip {
      color: #fbbf24;
      font-size: 11px;
      opacity: 0;
      transition: opacity 140ms ease;
      justify-self: end;
    }
    .sv-editor-save-tip.is-visible {
      opacity: 1;
    }
    body.sv-theme-light .sv-editor-backdrop {
      background: rgba(15, 23, 42, 0.2);
    }
    body.sv-theme-light .sv-editor-sheet {
      background: rgba(255, 255, 255, 0.98);
      border-top-color: rgba(15, 23, 42, 0.12);
      box-shadow: 0 -14px 30px rgba(15, 23, 42, 0.16);
    }
    body.sv-theme-light .sv-editor-header {
      border-bottom-color: rgba(15, 23, 42, 0.1);
    }
    body.sv-theme-light .sv-editor-header-title {
      color: #0f172a;
    }
    body.sv-theme-light .sv-editor-btn {
      color: #334155;
      background: rgba(15, 23, 42, 0.08);
    }
    body.sv-theme-light .sv-editor-btn:hover {
      background: rgba(15, 23, 42, 0.14);
    }
    body.sv-theme-light .sv-editor-btn.is-primary {
      color: #ffffff;
      background: #f97316;
    }
    body.sv-theme-light .sv-editor-btn.is-primary:hover {
      background: #fb923c;
    }
    body.sv-theme-light .sv-editor-item {
      border-color: rgba(15, 23, 42, 0.12);
      background: rgba(248, 250, 252, 0.95);
    }
    body.sv-theme-light .sv-editor-item.is-active {
      border-color: rgba(249,115,22,0.72);
      box-shadow: inset 0 0 0 1px rgba(249,115,22,0.3);
    }
    body.sv-theme-light .sv-editor-item-title {
      color: #ea580c;
      background: rgba(249, 115, 22, 0.12);
    }
    body.sv-theme-light .sv-editor-item-title:hover {
      background: rgba(249, 115, 22, 0.18);
    }
    body.sv-theme-light .sv-editor-status {
      color: #64748b;
    }
    body.sv-theme-light .sv-editor-label {
      color: #475569;
    }
    body.sv-theme-light .sv-editor-script-view {
      color: #334155;
    }
    body.sv-theme-light .sv-editor-save-tip {
      color: #c2410c;
    }
    body.sv-editor-open #viewport {
      flex: 1 1 calc(100vh - var(--sv-editor-height, 50vh));
      min-height: 0;
    }
    body.sv-editor-open #sv-controls {
      display: none;
    }
    body.sv-recording .sv-toolbar,
    body.sv-recording .sv-scene-grid {
      display: none;
    }
    body.sv-recording #sv-controls {
      display: none;
    }
    body.sv-theme-light .sv-editor-list,
    body.sv-theme-light .sv-editor-script-view,
    body.sv-theme-light .sv-editor-textarea {
      scrollbar-color: rgba(148, 163, 184, 0.85) rgba(226, 232, 240, 0.85);
    }
    body.sv-theme-light .sv-editor-list::-webkit-scrollbar,
    body.sv-theme-light .sv-editor-script-view::-webkit-scrollbar,
    body.sv-theme-light .sv-editor-textarea::-webkit-scrollbar {
      width: 10px;
      height: 10px;
    }
    body.sv-theme-light .sv-editor-list::-webkit-scrollbar-track,
    body.sv-theme-light .sv-editor-script-view::-webkit-scrollbar-track,
    body.sv-theme-light .sv-editor-textarea::-webkit-scrollbar-track {
      background: rgba(226, 232, 240, 0.85);
      border-radius: 999px;
    }
    body.sv-theme-light .sv-editor-list::-webkit-scrollbar-thumb,
    body.sv-theme-light .sv-editor-script-view::-webkit-scrollbar-thumb,
    body.sv-theme-light .sv-editor-textarea::-webkit-scrollbar-thumb {
      background: rgba(148, 163, 184, 0.85);
      border-radius: 999px;
      border: 2px solid rgba(226, 232, 240, 0.85);
    }
    body.sv-theme-light .sv-editor-list::-webkit-scrollbar-thumb:hover,
    body.sv-theme-light .sv-editor-script-view::-webkit-scrollbar-thumb:hover,
    body.sv-theme-light .sv-editor-textarea::-webkit-scrollbar-thumb:hover {
      background: rgba(100, 116, 139, 0.95);
    }
/* scene-01 */
.sv-scene-root-scene-01 .s-scene-01-container{
  position: relative;
  width: 100%;
  height: 100%;
  background: #000000;
  color: #00FF41;
  font-family: var(--sv-font-mono);
  overflow: hidden;
  display: flex;
  justify-content: center;
  align-items: center;
}.sv-scene-root-scene-01 .scanline{
  position: absolute;
  inset: 0;
  background: repeating-linear-gradient(rgba(0,0,0,0) 0px, rgba(0,0,0,0) 1px, rgba(0,255,65,0.08) 2px, rgba(0,255,65,0.08) 3px);
  pointer-events: none;
  z-index: 10;
}.sv-scene-root-scene-01 .bg-grid{
  position: absolute;
  inset: 0;
  background-image: linear-gradient(rgba(0, 255, 65, 0.05) 1px, transparent 1px), linear-gradient(90deg, rgba(0, 255, 65, 0.05) 1px, transparent 1px);
  background-size: 40px 40px;
  animation: grid-move 20s linear infinite;
}@keyframes grid-move{
  from { background-position: 0 0; }
  to { background-position: 40px 40px; }
}.sv-scene-root-scene-01 .alert-overlay{
  position: absolute;
  z-index: 5;
  text-align: center;
  animation: alert-flicker 0.2s infinite alternate;
}.sv-scene-root-scene-01 .ascii-alert{
  font-size: 42px;
  font-weight: bold;
  border: 4px solid #00FF41;
  padding: 20px 40px;
  background: rgba(0, 255, 65, 0.1);
}@keyframes alert-flicker{
  0% { opacity: 1; transform: scale(1); }
  100% { opacity: 0.7; transform: scale(1.02); }
}.sv-scene-root-scene-01 .terminal-wrapper{
  display: flex;
  gap: 120px;
  z-index: 4;
}.sv-scene-root-scene-01 .terminal-card{
  width: 320px;
  height: 200px;
  background: rgba(0, 0, 0, 0.9);
  border: 1px solid rgba(0, 255, 65, 0.4);
  box-shadow: 0 0 20px rgba(0, 255, 65, 0.1);
  display: flex;
  flex-direction: column;
  transition: transform 0.5s cubic-bezier(0.22, 1, 0.36, 1), opacity 0.5s;
}.sv-scene-root-scene-01 .terminal-header{
  background: rgba(0, 255, 65, 0.2);
  padding: 6px 12px;
  font-size: 14px;
  border-bottom: 1px solid rgba(0, 255, 65, 0.4);
}.sv-scene-root-scene-01 .terminal-body{
  padding: 15px;
  font-size: 18px;
  line-height: 1.6;
}.sv-scene-root-scene-01 .prompt{ color: #39FF14; margin-right: 8px; }.sv-scene-root-scene-01 .cursor{
  animation: blink 0.8s infinite;
}@keyframes blink{
  50% { opacity: 0; }
}.sv-scene-root-scene-01 .lightning-container{
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  pointer-events: none;
}.sv-scene-root-scene-01 .lightning-path{
  fill: none;
  stroke: #39FF14;
  stroke-width: 4;
  stroke-dasharray: 600;
  stroke-dashoffset: 600;
  filter: url(#glow);
  animation: strike 0.5s forwards;
}@keyframes strike{
  to { stroke-dashoffset: 0; }
}.sv-scene-root-scene-01 .status-text{
  margin-top: 250px;
  font-size: 24px;
  background: #00FF41;
  color: #000;
  padding: 5px 15px;
  animation: slide-up 0.4s ease-out;
}@keyframes slide-up{
  from { opacity: 0; transform: translateY(20px); }
  to { opacity: 1; transform: translateY(0); }
}.sv-scene-root-scene-01 /* Step Transitions */
[data-step="1"]{ opacity: 0; transform: translateY(30px); }.sv-scene-root-scene-01 .active [data-step="1"]{ opacity: 1; transform: translateY(0); }.sv-scene-root-scene-01 [data-step="2"]{ opacity: 0; }.sv-scene-root-scene-01 .active [data-step="2"]{ opacity: 1; }.sv-scene-root-scene-01 .terminal-card.right.active-root{
  border-color: #39FF14;
  box-shadow: 0 0 30px rgba(0, 255, 65, 0.4);
}

/* scene-02 */
.sv-scene-root-scene-02 .s-scene-02-container{
  position: relative;
  width: 100%;
  height: 100%;
  background: #000000;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  font-family: var(--sv-font-mono);
}.sv-scene-root-scene-02 .crt-overlay{
  position: absolute;
  inset: 0;
  background: repeating-linear-gradient(rgba(0,0,0,0) 0px, rgba(0,0,0,0) 1px, rgba(0,255,65,0.06) 2px, rgba(0,255,65,0.06) 3px);
  pointer-events: none;
  z-index: 10;
}.sv-scene-root-scene-02 .ambient-phosphor{
  position: absolute;
  width: 120%;
  height: 120%;
  background: radial-gradient(circle, rgba(0,255,65,0.05) 0%, transparent 70%);
  animation: phosphor-pulse 8s ease-in-out infinite;
  pointer-events: none;
}.sv-scene-root-scene-02 .grid-bg{
  position: absolute;
  inset: 0;
  background-image: linear-gradient(rgba(0, 255, 65, 0.03) 1px, transparent 1px), linear-gradient(90deg, rgba(0, 255, 65, 0.03) 1px, transparent 1px);
  background-size: 50px 50px;
  opacity: 0.5;
}.sv-scene-root-scene-02 .vulnerability-wrapper{
  position: relative;
  z-index: 5;
  opacity: 0;
  transform: scale(0);
  transition: all 0.6s cubic-bezier(0.175, 0.885, 0.32, 1.275);
}.sv-scene-root-scene-02 .active [data-step="0"]{
  opacity: 1;
  transform: scale(1);
}.sv-scene-root-scene-02 .cve-text{
  font-size: 108px;
  font-weight: 900;
  color: #00FF41;
  letter-spacing: -2px;
  text-shadow: 0 0 20px rgba(0, 255, 65, 0.6);
  margin: 0;
  animation: blue-breath 4s ease-in-out infinite;
}@keyframes blue-breath{
  0%, 100% { text-shadow: 0 0 20px rgba(0, 255, 65, 0.4); }
  50% { text-shadow: 0 0 40px rgba(60, 150, 255, 0.7), 0 0 10px rgba(0, 255, 65, 0.5); }
}.sv-scene-root-scene-02 .fail-container{
  position: absolute;
  top: 60%;
  display: flex;
  flex-direction: column;
  align-items: center;
  opacity: 0;
  transform: translateY(40px);
  transition: all 0.5s ease-out;
}.sv-scene-root-scene-02 .active [data-step="1"]{
  opacity: 1;
  transform: translateY(0);
}.sv-scene-root-scene-02 .fail-text{
  font-size: 140px;
  font-weight: 900;
  color: #00FF41; 
  /* Semantically red but visually terminal phosphor */
  background: #000;
  border: 4px solid #00FF41;
  padding: 0 40px;
  mix-blend-mode: screen;
  animation: critical-glitch 0.2s steps(2) infinite;
}@keyframes critical-glitch{
  0% { transform: skewX(0); opacity: 1; border-color: #00FF41; }
  50% { transform: skewX(5deg); opacity: 0.8; border-color: rgba(255, 50, 50, 0.8); }
}.sv-scene-root-scene-02 .tear-particles{
  position: absolute;
  inset: -100px -200px;
  pointer-events: none;
  z-index: -1;
}.sv-scene-root-scene-02 .tear-line{
  fill: none;
  stroke: #00FF41;
  stroke-width: 2;
  stroke-dasharray: 1000;
  stroke-dashoffset: 1000;
  opacity: 0.4;
}.sv-scene-root-scene-02 .active [data-step="1"] .tear-line{
  animation: tear-draw 0.8s forwards;
}.sv-scene-root-scene-02 .l2{ animation-delay: 0.1s; }.sv-scene-root-scene-02 .l3{ animation-delay: 0.2s; }@keyframes tear-draw{
  to { stroke-dashoffset: 0; opacity: 0.1; }
}@keyframes phosphor-pulse{
  0%, 100% { opacity: 0.4; transform: scale(1); }
  50% { opacity: 0.6; transform: scale(1.05); }
}

/* scene-03 */
.sv-scene-root-scene-03 .s-scene-03-container{
  position: relative;
  width: 100%;
  height: 100%;
  background: #000000;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  overflow: hidden;
  font-family: var(--sv-font-mono);
  color: #00FF41;
}.sv-scene-root-scene-03 .crt-lines{
  position: absolute;
  inset: 0;
  background: repeating-linear-gradient(rgba(0,0,0,0) 0px, rgba(0,0,0,0) 2px, rgba(0,255,65,0.08) 3px, rgba(0,255,65,0.08) 4px);
  pointer-events: none;
  z-index: 10;
}.sv-scene-root-scene-03 .ambient-glow{
  position: absolute;
  width: 100%;
  height: 100%;
  background: radial-gradient(circle at center, rgba(0,255,65,0.04) 0%, transparent 70%);
  pointer-events: none;
}.sv-scene-root-scene-03 /* Step 0: Timeline */
.timeline-section{
  width: 80%;
  margin-bottom: 60px;
  opacity: 0;
  transform: translateX(-50px);
  transition: all 0.6s cubic-bezier(0.23, 1, 0.32, 1);
}.sv-scene-root-scene-03 .active [data-step="0"]{ opacity: 1; transform: translateX(0); }.sv-scene-root-scene-03 .timeline-wrapper{
  display: flex;
  align-items: center;
  gap: 20px;
  font-size: 28px;
}.sv-scene-root-scene-03 .timeline-bar{
  flex-grow: 1;
  height: 4px;
  background: rgba(0, 255, 65, 0.2);
  position: relative;
  display: flex;
  align-items: center;
}.sv-scene-root-scene-03 .timeline-fill{
  position: absolute;
  left: 0;
  height: 100%;
  width: 0%;
  background: #00FF41;
  box-shadow: 0 0 10px #00FF41;
  transition: width 1.5s ease-in-out;
}.sv-scene-root-scene-03 .active [data-step="0"] .timeline-fill{ width: 100%; }.sv-scene-root-scene-03 .timeline-arrow{
  position: absolute;
  right: -10px;
  font-weight: bold;
  text-shadow: 0 0 5px #00FF41;
}.sv-scene-root-scene-03 .pulse-text{
  animation: phosphor-flicker 2s infinite;
}.sv-scene-root-scene-03 /* Step 1: Grid */
.distro-grid-container{
  width: 85%;
  position: relative;
  opacity: 0;
  transition: opacity 0.5s ease;
}.sv-scene-root-scene-03 .active [data-step="1"]{ opacity: 1; }.sv-scene-root-scene-03 .grid-header{
  font-size: 14px;
  margin-bottom: 10px;
  color: rgba(0, 255, 65, 0.6);
  letter-spacing: 2px;
}.sv-scene-root-scene-03 .distro-grid{
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 30px;
  padding: 40px;
  border: 1px solid rgba(0, 255, 65, 0.3);
  background: rgba(0, 255, 65, 0.02);
}.sv-scene-root-scene-03 .distro-card{
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 15px;
  transition: all 0.4s ease;
}.sv-scene-root-scene-03 .distro-card iconify-icon{
  font-size: 64px;
  color: #00FF41;
  filter: drop-shadow(0 0 5px rgba(0, 255, 65, 0.4));
}.sv-scene-root-scene-03 .distro-name{
  font-size: 18px;
  text-transform: uppercase;
}.sv-scene-root-scene-03 /* Step 2: Vulnerable State */
.active [data-step="2"] .distro-card iconify-icon{
  color: #FF3131;
  filter: drop-shadow(0 0 8px rgba(255, 49, 49, 0.6));
  animation: icon-glitch 0.3s infinite alternate;
}.sv-scene-root-scene-03 .shimmer-overlay{
  position: absolute;
  inset: 0;
  background: linear-gradient(135deg, transparent 0%, rgba(0,255,65,0.1) 45%, rgba(0,255,65,0.2) 50%, rgba(0,255,65,0.1) 55%, transparent 100%);
  background-size: 200% 200%;
  pointer-events: none;
  opacity: 0;
}.sv-scene-root-scene-03 .active [data-step="2"] .shimmer-overlay{
  opacity: 1;
  animation: shimmer-scan 3s infinite linear;
}.sv-scene-root-scene-03 .vulnerable-overlay{
  position: absolute;
  inset: 0;
  display: flex;
  justify-content: center;
  align-items: center;
  z-index: 20;
  pointer-events: none;
  opacity: 0;
  transform: scale(2);
  transition: all 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
}.sv-scene-root-scene-03 .active [data-step="2"].vulnerable-overlay{
  opacity: 1;
  transform: scale(1) rotate(-10deg);
}.sv-scene-root-scene-03 .stamp-box{
  border: 8px double #FF3131;
  padding: 20px 40px;
  background: rgba(0, 0, 0, 0.9);
  box-shadow: 0 0 30px rgba(255, 49, 49, 0.4);
  text-align: center;
}.sv-scene-root-scene-03 .stamp-text{
  font-size: 84px;
  font-weight: 900;
  color: #FF3131;
  line-height: 1;
}.sv-scene-root-scene-03 .stamp-subtext{
  font-size: 24px;
  color: #FF3131;
  letter-spacing: 10px;
  margin-top: 10px;
}@keyframes phosphor-flicker{
  0%, 100% { opacity: 1; }
  50% { opacity: 0.6; }
  55% { opacity: 0.9; }
}@keyframes shimmer-scan{
  0% { background-position: -200% -200%; }
  100% { background-position: 200% 200%; }
}@keyframes icon-glitch{
  0% { transform: translate(0); }
  50% { transform: translate(-2px, 1px); }
  100% { transform: translate(2px, -1px); }
}

/* scene-04 */
.sv-scene-root-scene-04 .s-scene-04-wrapper{
  position: relative;
  width: 100%;
  height: 100%;
  background: #000000;
  overflow: hidden;
  font-family: var(--sv-font-mono);
  display: flex;
  justify-content: center;
  align-items: center;
}.sv-scene-root-scene-04 .crt-lines{
  position: absolute;
  inset: 0;
  background: repeating-linear-gradient(rgba(0,0,0,0) 0px, rgba(0,0,0,0) 2px, rgba(0,255,65,0.05) 3px, rgba(0,255,65,0.05) 4px);
  pointer-events: none;
  z-index: 10;
}.sv-scene-root-scene-04 .ambient-glow{
  position: absolute;
  width: 100%;
  height: 100%;
  background: radial-gradient(circle at center, rgba(0,255,65,0.03) 0%, transparent 70%);
  pointer-events: none;
}.sv-scene-root-scene-04 .system-layout{
  width: 100%;
  max-width: 1200px;
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 0 10%;
  z-index: 5;
}.sv-scene-root-scene-04 .system-node{
  width: 280px;
  height: 280px;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  border: 2px solid rgba(0, 255, 65, 0.4);
  background: rgba(0, 255, 65, 0.02);
  transition: all 0.5s cubic-bezier(0.165, 0.84, 0.44, 1);
  position: relative;
}.sv-scene-root-scene-04 .box-node{
  border-radius: 4px;
}.sv-scene-root-scene-04 .circle-node{
  border-radius: 50%;
  border-color: rgba(255, 255, 255, 0.3);
}.sv-scene-root-scene-04 .node-label{
  position: absolute;
  top: -40px;
  color: rgba(0, 255, 65, 0.6);
  font-size: 14px;
  letter-spacing: 2px;
}.sv-scene-root-scene-04 .internal-process{
  display: flex;
  align-items: center;
  gap: 10px;
}.sv-scene-root-scene-04 .mono-text, .sv-scene-root-scene-04 .status-text{
  color: #00FF41;
  font-size: 18px;
}.sv-scene-root-scene-04 .cursor-blink{
  width: 10px;
  height: 20px;
  background: #00FF41;
  animation: cursor-blink 1s infinite steps(1);
}.sv-scene-root-scene-04 .vector-layer{
  position: absolute;
  inset: 0;
  pointer-events: none;
}.sv-scene-root-scene-04 .vector-layer svg{
  width: 100%;
  height: 100%;
}.sv-scene-root-scene-04 /* Step 0 Animation */
.active[data-scene-id="scene-04"] #attack-path{
  transition: stroke-dashoffset 0.8s ease-in-out 0.3s;
  stroke-dashoffset: 0;
}.sv-scene-root-scene-04 .active[data-scene-id="scene-04"] #arrow-head{
  transition: opacity 0.2s ease 1.1s;
  opacity: 1;
}.sv-scene-root-scene-04 /* Step 1 Styles */
[data-step="1"] .circle-node{
  border-color: #FF3131;
  background: rgba(255, 49, 49, 0.1);
  box-shadow: 0 0 30px rgba(255, 49, 49, 0.3);
  transform: scale(1.05);
}.sv-scene-root-scene-04 [data-step="1"] .circle-node .status-text{
  color: #FF3131;
  animation: text-warning 0.5s infinite alternate;
}.sv-scene-root-scene-04 .success-banner{
  position: absolute;
  bottom: 10%;
  opacity: 0;
  transform: translateY(20px);
  transition: all 0.5s ease;
}.sv-scene-root-scene-04 .active .success-banner[data-step="1"]{
  opacity: 1;
  transform: translateY(0);
}.sv-scene-root-scene-04 .glitch-box{
  border: 1px solid #FF3131;
  padding: 15px 40px;
  background: rgba(0, 0, 0, 0.9);
  box-shadow: 4px 4px 0px #FF3131;
}.sv-scene-root-scene-04 .success-text{
  color: #FF3131;
  font-size: 28px;
  font-weight: bold;
  letter-spacing: 2px;
}@keyframes cursor-blink{
  0%, 100% { opacity: 1; }
  50% { opacity: 0; }
}@keyframes text-warning{
  from { opacity: 1; text-shadow: 0 0 5px #FF3131; }
  to { opacity: 0.7; text-shadow: 0 0 15px #FF3131; }
}

/* scene-05 */
.sv-scene-root-scene-05 .s-scene-05-wrapper{
  position: relative;
  width: 100%;
  height: 100%;
  background: #000000;
  overflow: hidden;
  display: flex;
  justify-content: center;
  align-items: center;
  font-family: var(--sv-font-mono);
  color: #00FF41;
}.sv-scene-root-scene-05 .crt-scanlines{
  position: absolute;
  inset: 0;
  background: repeating-linear-gradient(rgba(0,0,0,0) 0px, rgba(0,0,0,0) 2px, rgba(0,255,65,0.05) 3px, rgba(0,255,65,0.05) 4px);
  pointer-events: none;
  z-index: 10;
}.sv-scene-root-scene-05 .ambient-phosphor{
  position: absolute;
  width: 100%;
  height: 100%;
  background: radial-gradient(circle at 50% 50%, rgba(0, 255, 65, 0.04) 0%, transparent 80%);
  pointer-events: none;
}.sv-scene-root-scene-05 .memory-container{
  position: relative;
  width: 80%;
  height: 70%;
  border: 1px solid rgba(0, 255, 65, 0.2);
  background: rgba(0, 255, 65, 0.02);
  padding: 40px;
  box-sizing: border-box;
  display: flex;
  justify-content: center;
  align-items: center;
}.sv-scene-root-scene-05 .memory-label{
  position: absolute;
  top: 15px;
  left: 20px;
  font-size: 14px;
  opacity: 0.6;
  letter-spacing: 2px;
}.sv-scene-root-scene-05 .memory-grid{
  position: absolute;
  inset: 0;
  opacity: 0.1;
  background-image: radial-gradient(#00FF41 1px, transparent 1px);
  background-size: 30px 30px;
}.sv-scene-root-scene-05 .page-cache-card{
  width: 480px;
  height: 300px;
  background: rgba(0, 20, 0, 0.9);
  border: 2px solid #00FF41;
  box-shadow: 0 0 30px rgba(0, 255, 65, 0.2);
  padding: 20px;
  position: relative;
  z-index: 5;
  opacity: 0;
  transform: scale(0.9) translateY(20px);
  transition: all 0.6s cubic-bezier(0.16, 1, 0.3, 1);
}.sv-scene-root-scene-05 .page-cache-card[data-step="1"]{
  opacity: 1;
  transform: scale(1) translateY(0);
}.sv-scene-root-scene-05 .card-header{
  border-bottom: 1px solid rgba(0, 255, 65, 0.3);
  padding-bottom: 10px;
  margin-bottom: 20px;
  display: flex;
  justify-content: space-between;
  align-items: center;
}.sv-scene-root-scene-05 .card-title{
  font-size: 20px;
  font-weight: bold;
}.sv-scene-root-scene-05 .memory-addr{
  font-size: 14px;
  opacity: 0.7;
}.sv-scene-root-scene-05 .binary-content{
  font-size: 24px;
  line-height: 1.5;
  letter-spacing: 4px;
  color: rgba(0, 255, 65, 0.8);
}.sv-scene-root-scene-05 .security-status{
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.7);
  backdrop-filter: blur(2px);
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  opacity: 0;
  transition: opacity 0.5s ease;
}.sv-scene-root-scene-05 .security-status[data-step="2"]{
  opacity: 1;
}.sv-scene-root-scene-05 .lock-overlay{
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  margin-bottom: 30px;
  color: #39FF14;
}.sv-scene-root-scene-05 .lock-overlay i{
  width: 64px;
  height: 64px;
}.sv-scene-root-scene-05 .read-only-text{
  font-size: 32px;
  font-weight: 800;
  letter-spacing: 4px;
  border: 2px solid #39FF14;
  padding: 5px 20px;
}.sv-scene-root-scene-05 .forbidden-action{
  display: flex;
  gap: 40px;
}.sv-scene-root-scene-05 .action-item{
  position: relative;
  padding: 5px 15px;
  opacity: 0.6;
}.sv-scene-root-scene-05 .forbidden-sign{
  position: absolute;
  top: 50%;
  left: 50%;
  width: 120%;
  height: 2px;
  background: #FF3131;
  transform: translate(-50%, -50%) rotate(-25deg);
  box-shadow: 0 0 10px #FF3131;
}.sv-scene-root-scene-05 .ghost-text{
  position: absolute;
  bottom: 5%;
  right: 5%;
  font-size: 80px;
  opacity: 0.03;
  font-weight: 900;
  pointer-events: none;
}@keyframes pulse-border{
  0% { border-color: rgba(0, 255, 65, 0.4); }
  50% { border-color: rgba(0, 255, 65, 1); }
  100% { border-color: rgba(0, 255, 65, 0.4); }
}

/* scene-06 */
.sv-scene-root-scene-06 .s-scene-06-wrapper{
  position: relative;
  width: 100%;
  height: 100%;
  background: #000000;
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: space-around;
  font-family: var(--sv-font-mono);
  color: #00FF41;
}.sv-scene-root-scene-06 .crt-overlay{
  position: absolute;
  inset: 0;
  background: repeating-linear-gradient(rgba(0,0,0,0) 0px, rgba(0,0,0,0) 2px, rgba(0,255,65,0.08) 3px, rgba(0,255,65,0.08) 4px);
  pointer-events: none;
  z-index: 100;
}.sv-scene-root-scene-06 .ambient-glow{
  position: absolute;
  width: 100%;
  height: 100%;
  background: radial-gradient(circle at 50% 50%, rgba(0, 255, 65, 0.03) 0%, transparent 70%);
  pointer-events: none;
}.sv-scene-root-scene-06 .terminal-container{
  width: 320px;
  height: 200px;
  border: 1px solid rgba(0, 255, 65, 0.4);
  background: rgba(0, 20, 0, 0.8);
  box-shadow: 0 0 15px rgba(0, 255, 65, 0.1);
  z-index: 10;
  transform: translateX(-100px);
  opacity: 0;
  transition: all 0.6s cubic-bezier(0.16, 1, 0.3, 1);
}.sv-scene-root-scene-06 .term-header{
  background: rgba(0, 255, 65, 0.15);
  padding: 6px 10px;
  display: flex;
  justify-content: space-between;
  align-items: center;
  border-bottom: 1px solid rgba(0, 255, 65, 0.3);
}.sv-scene-root-scene-06 .term-controls{
  display: flex;
  gap: 6px;
}.sv-scene-root-scene-06 .term-controls span{
  width: 8px;
  height: 8px;
  border: 1px solid #00FF41;
  border-radius: 50%;
}.sv-scene-root-scene-06 .term-title{
  font-size: 10px;
  opacity: 0.7;
}.sv-scene-root-scene-06 .term-body{
  padding: 15px;
  font-size: 14px;
}.sv-scene-root-scene-06 .typing-line{
  margin-bottom: 5px;
  overflow: hidden;
  white-space: nowrap;
}.sv-scene-root-scene-06 .highlight{ color: #39FF14; text-shadow: 0 0 5px #00FF41; }.sv-scene-root-scene-06 .conduit-system{
  position: absolute;
  width: 100%;
  height: 100%;
  pointer-events: none;
}.sv-scene-root-scene-06 .conduit-svg{
  position: absolute;
  top: 0; left: 0; width: 100%; height: 100%;
}.sv-scene-root-scene-06 .file-node{
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%) scale(0.9);
  opacity: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  z-index: 20;
  transition: all 0.5s ease;
}.sv-scene-root-scene-06 .file-icon-box{
  font-size: 64px;
  border: 2px solid #00FF41;
  padding: 15px;
  background: rgba(0, 255, 65, 0.05);
  box-shadow: 0 0 20px rgba(0, 255, 65, 0.2);
}.sv-scene-root-scene-06 .file-name{
  font-size: 20px;
  font-weight: bold;
}.sv-scene-root-scene-06 .file-perm{
  font-size: 12px;
  opacity: 0.6;
}.sv-scene-root-scene-06 .pipeline-stage{
  width: 300px;
  height: 250px;
  border: 2px dashed rgba(0, 255, 65, 0.3);
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  opacity: 0;
  transform: translateX(50px);
  transition: all 0.6s ease;
}.sv-scene-root-scene-06 .pipeline-box{
  width: 90%;
  height: 90%;
  background: rgba(0, 255, 65, 0.02);
  border: 1px solid #00FF41;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 20px;
}.sv-scene-root-scene-06 .pipeline-label{
  position: absolute;
  top: -12px;
  background: #000;
  padding: 0 10px;
  font-size: 14px;
}.sv-scene-root-scene-06 .chain-container{
  display: flex;
  gap: 5px;
  opacity: 0;
}.sv-scene-root-scene-06 .chain-link{
  width: 15px;
  height: 30px;
  border: 2px solid #00FF41;
  border-radius: 10px;
}.sv-scene-root-scene-06 .flow-indicator{
  font-size: 12px;
  animation: blink 0.8s infinite;
}.sv-scene-root-scene-06 .side-status{
  position: absolute;
  bottom: 40px;
  right: 40px;
  text-align: right;
}.sv-scene-root-scene-06 .status-item{
  font-size: 14px;
  opacity: 0;
  margin-top: 5px;
}@keyframes shake{
  0%, 100% { transform: translate(-50%, -50%); }
  20% { transform: translate(-52%, -48%); }
  40% { transform: translate(-48%, -52%); }
  60% { transform: translate(-51%, -49%); }
  80% { transform: translate(-49%, -51%); }
}@keyframes blink{
  0%, 100% { opacity: 1; }
  50% { opacity: 0.3; }
}.sv-scene-root-scene-06 .shaking{ animation: shake 0.2s infinite; }.sv-scene-root-scene-06 [data-step="1"] .status-item[data-step="1"]{ opacity: 1; }.sv-scene-root-scene-06 [data-step="2"] .status-item[data-step="2"]{ opacity: 1; }

/* scene-07 */
.sv-scene-root-scene-07 .s-scene-07-wrapper{ width: 100%; height: 100%; background: #000; display: flex; align-items: center; justify-content: center; font-family: var(--sv-font-mono); color: #00FF41; overflow: hidden; position: relative; }.sv-scene-root-scene-07 .crt-overlay{ position: absolute; inset: 0; background: repeating-linear-gradient(rgba(0,0,0,0) 0px, rgba(0,0,0,0) 2px, rgba(0,255,65,0.05) 3px, rgba(0,255,65,0.05) 4px); pointer-events: none; z-index: 10; }.sv-scene-root-scene-07 .ambient-glow{ position: absolute; width: 100%; height: 100%; background: radial-gradient(circle at 50% 50%, rgba(0, 255, 65, 0.05) 0%, transparent 70%); }.sv-scene-root-scene-07 .layout-container{ display: flex; align-items: center; justify-content: space-between; width: 90%; gap: 20px; z-index: 5; }.sv-scene-root-scene-07 .section-label{ font-size: 14px; color: rgba(0,255,65,0.6); margin-bottom: 10px; text-transform: uppercase; letter-spacing: 2px; }.sv-scene-root-scene-07 .terminal-window{ width: 420px; border: 1px solid rgba(0,255,65,0.3); background: rgba(0,20,0,0.9); box-shadow: 0 0 20px rgba(0,255,65,0.1); }.sv-scene-root-scene-07 .term-header{ background: rgba(0,255,65,0.1); padding: 8px 12px; display: flex; align-items: center; border-bottom: 1px solid rgba(0,255,65,0.2); }.sv-scene-root-scene-07 .term-dots{ display: flex; gap: 5px; margin-right: 15px; }.sv-scene-root-scene-07 .term-dots span{ width: 6px; height: 6px; border: 1px solid #00FF41; border-radius: 50%; }.sv-scene-root-scene-07 .term-title{ font-size: 12px; opacity: 0.8; }.sv-scene-root-scene-07 .code-content{ padding: 20px; font-size: 16px; line-height: 1.5; }.sv-scene-root-scene-07 .highlight-target{ background: rgba(0,255,65,0.2); border-left: 3px solid #00FF41; padding-left: 5px; }.sv-scene-root-scene-07 .code-keyword{ color: #39FF14; font-weight: bold; }.sv-scene-root-scene-07 .code-comment{ color: rgba(0,255,65,0.4); }.sv-scene-root-scene-07 .conduit-container{ flex: 1; height: 100px; display: flex; align-items: center; justify-content: center; }.sv-scene-root-scene-07 .arrow-path{ stroke-dasharray: 200; stroke-dashoffset: 200; transition: stroke-dashoffset 0.8s ease; }.sv-scene-root-scene-07 .memory-section{ display: flex; flex-direction: column; align-items: center; }.sv-scene-root-scene-07 .page-frame{ width: 280px; height: 180px; border: 2px solid #00FF41; position: relative; background: rgba(0,255,65,0.02); display: flex; flex-direction: column; justify-content: center; align-items: center; }.sv-scene-root-scene-07 .page-grid{ display: grid; grid-template-columns: repeat(4, 1fr); gap: 10px; opacity: 0.2; position: absolute; inset: 20px; }.sv-scene-root-scene-07 .page-grid span{ border: 1px solid #00FF41; height: 20px; }.sv-scene-root-scene-07 .permission-status{ position: relative; z-index: 2; text-align: center; }.sv-scene-root-scene-07 .lock-container{ transition: all 0.4s ease; }.sv-scene-root-scene-07 .lock-icon{ font-size: 48px; margin-bottom: 10px; }.sv-scene-root-scene-07 .lock-text{ font-size: 18px; }.sv-scene-root-scene-07 .rw-status{ position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%) scale(0); opacity: 0; color: #FF3131; font-weight: 900; font-size: 42px; text-shadow: 0 0 15px rgba(255,49,49,0.8); transition: all 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275); }.sv-scene-root-scene-07 .shattered .lock-container{ transform: scale(1.5) rotate(15deg); opacity: 0; filter: blur(5px); }.sv-scene-root-scene-07 .shattered .rw-status{ transform: translate(-50%, -50%) scale(1); opacity: 1; }.sv-scene-root-scene-07 .alert-pulse-overlay{ position: absolute; inset: 0; background: radial-gradient(circle, rgba(255,0,0,0.15) 0%, transparent 70%); opacity: 0; pointer-events: none; animation: pulse-red 1.5s infinite; }@keyframes pulse-red{ 0%, 100% { opacity: 0; transform: scale(1); } 50% { opacity: 1; transform: scale(1.1); } }.sv-scene-root-scene-07 .status-bar{ position: absolute; bottom: 30px; left: 50%; transform: translateX(-50%); display: flex; gap: 40px; }.sv-scene-root-scene-07 .status-msg{ font-size: 12px; border: 1px solid rgba(0,255,65,0.3); padding: 4px 12px; background: rgba(0,20,0,0.8); }

/* scene-08 */
.sv-scene-root-scene-08 .s-scene-08-wrapper{ width: 100%; height: 100%; background: #000; display: flex; align-items: center; justify-content: center; font-family: var(--sv-font-mono); color: #00FF41; overflow: hidden; position: relative; }.sv-scene-root-scene-08 .crt-scanlines{ position: absolute; inset: 0; background: linear-gradient(rgba(18, 16, 16, 0) 50%, rgba(0, 0, 0, 0.15) 50%), linear-gradient(90deg, rgba(255, 0, 0, 0.03), rgba(0, 255, 0, 0.01), rgba(0, 0, 255, 0.03)); background-size: 100% 3px, 3px 100%; pointer-events: none; z-index: 20; }.sv-scene-root-scene-08 .ambient-glow{ position: absolute; width: 100%; height: 100%; background: radial-gradient(circle, rgba(0, 255, 65, 0.05) 0%, transparent 80%); pointer-events: none; }.sv-scene-root-scene-08 .hex-viewport{ width: 800px; height: 400px; border: 1px solid rgba(0, 255, 65, 0.3); background: rgba(0, 10, 0, 0.9); position: relative; box-shadow: 0 0 40px rgba(0, 0, 0, 0.8); transition: all 0.8s ease; transform: scale(1.1); }.sv-scene-root-scene-08 .viewport-header{ background: rgba(0, 255, 65, 0.1); padding: 8px 15px; display: flex; justify-content: space-between; border-bottom: 1px solid rgba(0, 255, 65, 0.2); font-size: 12px; }.sv-scene-root-scene-08 .hex-grid-container{ padding: 30px; display: flex; justify-content: center; }.sv-scene-root-scene-08 .hex-grid{ display: grid; grid-template-columns: repeat(8, 70px); gap: 15px; }.sv-scene-root-scene-08 .hex-cell{ height: 40px; display: flex; align-items: center; justify-content: center; border: 1px solid rgba(0, 255, 65, 0.1); font-size: 22px; color: #0088ff; transition: all 0.5s ease; position: relative; }.sv-scene-root-scene-08 .hex-cell.infected{ color: #d400ff !important; border-color: #d400ff; text-shadow: 0 0 10px rgba(212, 0, 255, 0.5); }.sv-scene-root-scene-08 .hex-cell.target-active{ background: rgba(0, 255, 65, 0.1); border-color: #00FF41; }.sv-scene-root-scene-08 .missile-container{ position: absolute; inset: 0; pointer-events: none; }.sv-scene-root-scene-08 .byte-missile{ position: absolute; font-size: 24px; font-weight: bold; background: #000; border: 2px solid #00FF41; padding: 5px 10px; box-shadow: 0 0 15px #00FF41; z-index: 10; transition: all 0.6s cubic-bezier(0.6, -0.28, 0.735, 0.045); }.sv-scene-root-scene-08 .byte-missile[data-step="0"]{ opacity: 1; transform: translate(var(--start-x), var(--start-y)); }.sv-scene-root-scene-08 .byte-missile.launched{ transform: translate(var(--end-x), var(--end-y)) scale(0.8); opacity: 0.5; }.sv-scene-root-scene-08 .alert-overlay{ position: absolute; inset: 0; z-index: 30; display: flex; align-items: center; justify-content: center; background: rgba(255, 0, 0, 0.1); backdrop-filter: blur(2px); }.sv-scene-root-scene-08 .alert-box{ background: #000; border: 4px solid #FF3131; padding: 40px; text-align: center; box-shadow: 0 0 50px rgba(255, 49, 49, 0.4); animation: alert-shake 0.2s infinite; }.sv-scene-root-scene-08 .alert-header{ color: #FF3131; font-weight: 900; font-size: 32px; letter-spacing: 4px; margin-bottom: 10px; }.sv-scene-root-scene-08 .alert-content{ color: #FFF; font-size: 20px; border-top: 1px solid #FF3131; padding-top: 10px; }.sv-scene-root-scene-08 .alert-sub{ font-size: 14px; opacity: 0.7; margin-top: 5px; }.sv-scene-root-scene-08 .terminal-footer{ position: absolute; bottom: 40px; left: 100px; right: 100px; }.sv-scene-root-scene-08 .prompt{ color: #00FF41; margin-right: 10px; }.sv-scene-root-scene-08 .typing-text{ font-size: 18px; }.sv-scene-root-scene-08 .cursor{ display: inline-block; width: 10px; height: 20px; background: #00FF41; animation: blink 1s infinite; vertical-align: middle; }.sv-scene-root-scene-08 .progress-section{ margin-top: 15px; display: flex; align-items: center; gap: 15px; }.sv-scene-root-scene-08 .bar-bg{ flex: 1; height: 10px; background: rgba(0, 255, 65, 0.1); border: 1px solid rgba(0, 255, 65, 0.3); }.sv-scene-root-scene-08 .bar-fill{ display: block; width: 0%; height: 100%; background: #00FF41; transition: width 0.6s ease; }@keyframes alert-shake{ 0% { transform: translate(1px, 1px); } 50% { transform: translate(-1px, -1px); } 100% { transform: translate(1px, -1px); } }@keyframes blink{ 0%, 100% { opacity: 1; } 50% { opacity: 0; } }.sv-scene-root-scene-08 .modified-state .hex-viewport{ border-color: #d400ff; box-shadow: 0 0 30px rgba(212, 0, 255, 0.2); }.sv-scene-root-scene-08 .modified-state #mem-status{ color: #d400ff; content: "[ MEMORY_MODIFIED_PERMANENT ]"; }

/* scene-09 */
.sv-scene-root-scene-09 .s-scene-09-wrapper{ width: 100%; height: 100%; background: #000; font-family: var(--sv-font-mono); color: #00FF41; display: flex; align-items: center; justify-content: center; position: relative; overflow: hidden; }.sv-scene-root-scene-09 .crt-scanlines{ position: absolute; inset: 0; background: linear-gradient(rgba(0, 255, 65, 0.05) 50%, transparent 50%); background-size: 100% 4px; pointer-events: none; z-index: 10; }.sv-scene-root-scene-09 .ambient-glow{ position: absolute; inset: 0; background: radial-gradient(circle at center, rgba(0, 255, 65, 0.03) 0%, transparent 70%); pointer-events: none; }.sv-scene-root-scene-09 .terminal-interface{ width: 80%; max-width: 900px; height: 60%; background: rgba(0, 10, 0, 0.95); border: 1px solid rgba(0, 255, 65, 0.3); box-shadow: 0 20px 50px rgba(0, 0, 0, 0.8); display: flex; flex-direction: column; position: relative; z-index: 5; }.sv-scene-root-scene-09 .terminal-header{ height: 30px; background: rgba(0, 255, 65, 0.1); border-bottom: 1px solid rgba(0, 255, 65, 0.2); display: flex; align-items: center; padding: 0 15px; }.sv-scene-root-scene-09 .terminal-buttons{ display: flex; gap: 6px; margin-right: 20px; }.sv-scene-root-scene-09 .terminal-buttons span{ width: 8px; height: 8px; border: 1px solid rgba(0, 255, 65, 0.4); border-radius: 50%; }.sv-scene-root-scene-09 .terminal-title{ font-size: 12px; opacity: 0.7; letter-spacing: 1px; }.sv-scene-root-scene-09 .terminal-content{ flex: 1; padding: 30px; overflow: hidden; position: relative; }.sv-scene-root-scene-09 .log-line{ font-size: 14px; opacity: 0.4; margin-bottom: 8px; font-weight: 300; }.sv-scene-root-scene-09 .prompt-area{ font-size: 32px; display: flex; align-items: center; margin-top: 20px; }.sv-scene-root-scene-09 .prompt-char{ margin-right: 15px; color: #39FF14; font-weight: bold; }.sv-scene-root-scene-09 .command-text{ letter-spacing: 2px; }.sv-scene-root-scene-09 .cursor{ display: inline-block; width: 15px; height: 32px; background: #00FF41; margin-left: 10px; animation: blink 1s infinite steps(2); }.sv-scene-root-scene-09 .result-output{ margin-top: 40px; display: flex; justify-content: center; }.sv-scene-root-scene-09 .root-banner{ font-size: 120px; font-weight: 900; letter-spacing: 15px; text-transform: uppercase; text-shadow: 0 0 30px rgba(0, 255, 65, 0.8); }.sv-scene-root-scene-09 .terminal-footer-bar{ height: 25px; background: rgba(0, 255, 65, 0.1); display: flex; align-items: center; padding: 0 15px; gap: 30px; font-size: 10px; border-top: 1px solid rgba(0, 255, 65, 0.2); }.sv-scene-root-scene-09 .blink-text{ animation: blink 0.5s infinite; }.sv-scene-root-scene-09 .lightning-flash{ position: absolute; inset: 0; background: #00FF41; opacity: 0; pointer-events: none; z-index: 100; }.sv-scene-root-scene-09 .success-frame{ position: absolute; inset: 10px; border: 4px solid #00FF41; box-shadow: inset 0 0 50px rgba(0, 255, 65, 0.3), 0 0 30px rgba(0, 255, 65, 0.2); pointer-events: none; z-index: 101; opacity: 0; transform: scale(1.02); transition: all 0.3s ease; }.sv-scene-root-scene-09 .success-frame[data-step="2"]{ opacity: 1; transform: scale(1); }@keyframes blink{ 0% { opacity: 1; } 100% { opacity: 0; } }@keyframes glitch-flash{ 0% { opacity: 0; } 10% { opacity: 0.8; } 20% { opacity: 0.1; } 30% { opacity: 0.9; } 100% { opacity: 0; } }

/* scene-10 */
.sv-scene-root-scene-10 .s-scene-10-wrapper{ width: 100%; height: 100%; background: #000; font-family: var(--sv-font-mono); color: #00FF41; position: relative; overflow: hidden; }.sv-scene-root-scene-10 .crt-scanlines{ position: absolute; inset: 0; background: linear-gradient(rgba(0, 255, 65, 0.08) 50%, transparent 50%); background-size: 100% 4px; pointer-events: none; z-index: 10; }.sv-scene-root-scene-10 .ambient-glow{ position: absolute; inset: 0; background: radial-gradient(circle at center, rgba(0, 255, 65, 0.03) 0%, transparent 70%); pointer-events: none; }.sv-scene-root-scene-10 .thirds-container{ display: flex; width: 100%; height: 100%; align-items: center; justify-content: space-between; padding: 0 5%; box-sizing: border-box; }.sv-scene-root-scene-10 .col-left, .sv-scene-root-scene-10 .col-center, .sv-scene-root-scene-10 .col-right{ flex: 1; display: flex; flex-direction: column; align-items: center; transition: all 0.5s cubic-bezier(0.22, 1, 0.36, 1); }.sv-scene-root-scene-10 /* Left: Update Box */
.status-box{ border: 2px solid rgba(0, 255, 65, 0.4); padding: 30px; background: rgba(0, 255, 65, 0.05); text-align: center; width: 300px; }.sv-scene-root-scene-10 .status-header{ font-size: 14px; letter-spacing: 2px; border-bottom: 1px solid rgba(0, 255, 65, 0.4); padding-bottom: 10px; margin-bottom: 20px; opacity: 0.8; }.sv-scene-root-scene-10 .main-patch-icon{ font-size: 80px; filter: drop-shadow(0 0 10px rgba(0, 255, 65, 0.5)); }.sv-scene-root-scene-10 .update-text-glitch{ font-size: 28px; font-weight: 900; margin-top: 15px; text-shadow: 2px 0 #39FF14; animation: blink 0.8s infinite; }.sv-scene-root-scene-10 .status-details{ margin-top: 20px; text-align: left; font-size: 12px; line-height: 1.8; opacity: 0.7; }.sv-scene-root-scene-10 /* Right: Terminal */
.command-window{ width: 400px; height: 200px; background: #000; border: 1px solid rgba(0, 255, 65, 0.3); box-shadow: 0 10px 40px rgba(0, 0, 0, 0.8); }.sv-scene-root-scene-10 .window-bar{ height: 25px; background: rgba(0, 255, 65, 0.1); display: flex; align-items: center; padding: 0 10px; gap: 5px; }.sv-scene-root-scene-10 .dot{ width: 6px; height: 6px; border: 1px solid #00FF41; border-radius: 50%; }.sv-scene-root-scene-10 .window-title{ font-size: 10px; margin-left: 10px; opacity: 0.6; }.sv-scene-root-scene-10 .terminal-body{ padding: 15px; font-size: 16px; }.sv-scene-root-scene-10 .user{ color: #39FF14; }.sv-scene-root-scene-10 .shell-output{ margin-top: 15px; font-size: 12px; color: #39FF14; opacity: 0; transform: translateY(10px); transition: 0.4s; }.sv-scene-root-scene-10 [data-step="1"] .shell-output{ opacity: 1; transform: translateY(0); }.sv-scene-root-scene-10 .terminal-cursor{ display: inline-block; width: 8px; height: 18px; background: #00FF41; animation: blink 1s infinite steps(2); vertical-align: middle; margin-left: 4px; }.sv-scene-root-scene-10 /* Center: Bomb */
.bomb-stage{ position: relative; text-align: center; }.sv-scene-root-scene-10 .bomb-svg{ width: 220px; height: 220px; }.sv-scene-root-scene-10 .fuse-path{ stroke-dasharray: 100; stroke-dashoffset: 0; }.sv-scene-root-scene-10 .cut-mark{ opacity: 0; transform: scale(0.5); transform-origin: center; transition: all 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275); }.sv-scene-root-scene-10 [data-step="2"] .cut-mark{ opacity: 1; transform: scale(1); }.sv-scene-root-scene-10 [data-step="2"] .fuse-path{ stroke-dasharray: 10 5; opacity: 0.5; }.sv-scene-root-scene-10 .threat-status{ margin-top: 20px; font-size: 24px; font-weight: 900; letter-spacing: 4px; color: #39FF14; text-shadow: 0 0 15px rgba(0, 255, 65, 0.6); }.sv-scene-root-scene-10 .footer-deco{ position: absolute; bottom: 30px; left: 50%; transform: translateX(-50%); font-size: 12px; opacity: 0.3; letter-spacing: 5px; }@keyframes blink{ 0% { opacity: 1; } 100% { opacity: 0; } }@keyframes draw{ to { stroke-dashoffset: 0; } }

/* scene-11 */
.sv-scene-root-scene-11 .s-scene-11-wrapper{ width: 100%; height: 100%; background: #000; font-family: var(--sv-font-mono); color: #00FF41; position: relative; overflow: hidden; display: flex; flex-direction: column; align-items: center; justify-content: center; }.sv-scene-root-scene-11 .crt-scanlines{ position: absolute; inset: 0; background: linear-gradient(rgba(0, 255, 65, 0.1) 50%, transparent 50%); background-size: 100% 3px; pointer-events: none; z-index: 100; opacity: 0.12; }.sv-scene-root-scene-11 .ambient-glow{ position: absolute; inset: 0; background: radial-gradient(circle at center, rgba(0, 255, 65, 0.04) 0%, transparent 80%); animation: pulse 10s infinite; }.sv-scene-root-scene-11 .logo-overlay{ text-align: center; z-index: 20; transition: transform 0.6s cubic-bezier(0.22, 1, 0.36, 1); }.sv-scene-root-scene-11 .avatar-frame{ width: 120px; height: 120px; border: 2px solid #00FF41; position: relative; padding: 10px; box-shadow: 0 0 20px rgba(0, 255, 65, 0.15); display: inline-block; background: #000; }.sv-scene-root-scene-11 .terminal-logo{ width: 100%; height: 100%; }.sv-scene-root-scene-11 .channel-id{ margin-top: 15px; font-size: 14px; letter-spacing: 2px; opacity: 0.8; }.sv-scene-root-scene-11 .action-container{ position: absolute; inset: 0; pointer-events: none; display: flex; align-items: center; justify-content: center; }.sv-scene-root-scene-11 .action-item{ position: absolute; transition: all 0.6s cubic-bezier(0.175, 0.885, 0.32, 1.275); opacity: 0; transform: scale(0.5); }.sv-scene-root-scene-11 .like-item{ left: 15%; }.sv-scene-root-scene-11 .follow-item{ right: 15%; }.sv-scene-root-scene-11 [data-step="1"] .action-item{ opacity: 1; transform: scale(1); }.sv-scene-root-scene-11 .action-box{ border: 1px solid #00FF41; background: rgba(0, 255, 65, 0.05); padding: 20px; display: flex; flex-direction: column; align-items: center; gap: 10px; }.sv-scene-root-scene-11 .action-box i{ width: 40px; height: 40px; }.sv-scene-root-scene-11 .action-label{ font-size: 12px; font-weight: bold; }.sv-scene-root-scene-11 .closing-text{ position: absolute; bottom: 15%; text-align: center; opacity: 0; transform: translateY(20px); transition: all 0.8s ease; display: flex; align-items: center; gap: 10px; }.sv-scene-root-scene-11 [data-step="1"] .closing-text{ opacity: 1; transform: translateY(0); }.sv-scene-root-scene-11 .rainbow-terminal-text{ font-size: 64px; font-family: var(--sv-font-display-cn); font-weight: 900; background: linear-gradient(90deg, #00FF41, #39FF14, #00FF41); -webkit-background-clip: text; color: transparent; animation: shift 4s linear infinite; text-shadow: 0 0 10px rgba(0, 255, 65, 0.3); }.sv-scene-root-scene-11 .cursor-blink{ width: 25px; height: 60px; background: #00FF41; animation: blink 1s infinite steps(2); }.sv-scene-root-scene-11 .system-footer{ position: absolute; bottom: 5%; font-size: 10px; opacity: 0.4; letter-spacing: 1px; }@keyframes pulse{ 0%, 100% { opacity: 0.04; } 50% { opacity: 0.08; } }@keyframes blink{ 0% { opacity: 1; } 100% { opacity: 0; } }@keyframes shift{ 0% { background-position: 0% 50%; } 100% { background-position: 200% 50%; } }
  </style>
</head>
<body data-orientation="landscape">
  <div id="app">
    <div id="viewport">
      <div id="stage">

<section class="scene" data-scene-id="scene-01">
  <div class="sv-scene-root sv-scene-root-scene-01" data-scene-root-id="scene-01">
<div class="s-scene-01-container">
  <div class="scanline"></div>
  <div class="vignette"></div>

  <!-- Step 0: Alert System -->
  <div class="alert-overlay" data-step="0">
    <div class="alert-content">
      <div class="ascii-alert">
        !!! CRITICAL_SYSTEM_VULNERABILITY !!!
      </div>
      <div class="alert-tag">[ KERNEL_EXPLOIT_DETECTED ]</div>
    </div>
  </div>

  <!-- Step 1: Terminal Cards -->
  <div class="terminal-wrapper">
    <div class="terminal-card left" data-step="1">
      <div class="terminal-header">guest@linux: ~</div>
      <div class="terminal-body">
        <div class="line"><span class="prompt">$</span> whoami</div>
        <div class="line glow-text">guest</div>
        <div class="line cursor">_</div>
      </div>
    </div>

    <div class="terminal-card right" data-step="1">
      <div class="terminal-header">root@local: #</div>
      <div class="terminal-body">
        <div class="line"><span class="prompt">#</span> whoami</div>
        <div class="line ghost-text">??????</div>
        <div class="line cursor">_</div>
      </div>
    </div>
  </div>

  <!-- Step 2: Connection / Seizure -->
  <div class="lightning-container" data-step="2">
    <svg class="lightning-svg" viewBox="0 0 800 400">
      <path class="lightning-path" d="M 280 200 L 350 180 L 400 220 L 450 180 L 520 200" />
      <filter id="glow">
        <feGaussianBlur stdDeviation="4" result="coloredBlur"/>
        <feMerge>
          <feMergeNode in="coloredBlur"/>
          <feMergeNode in="SourceGraphic"/>
        </feMerge>
      </filter>
    </svg>
    <div class="status-text">PRIVILEGE ESCALATION SUCCESSFUL</div>
  </div>

  <!-- Background Decor -->
  <div class="bg-grid"></div>
</div>
  </div>
</section>

<section class="scene" data-scene-id="scene-02">
  <div class="sv-scene-root sv-scene-root-scene-02" data-scene-root-id="scene-02">
<div class="s-scene-02-container">
  <div class="crt-overlay"></div>
  <div class="ambient-phosphor"></div>
  <div class="grid-bg"></div>

  <!-- Step 0: Vulnerability ID -->
  <div class="vulnerability-wrapper" data-step="0">
    <h1 class="cve-text">CVE-2026-31431</h1>
    <div class="cve-glow"></div>
  </div>

  <!-- Step 1: Copy Fail & Tearing -->
  <div class="fail-container" data-step="1">
    <h2 class="fail-text">COPY FAIL</h2>
    <div class="tear-particles">
      <svg viewBox="0 0 1000 400" preserveAspectRatio="none">
        <path class="tear-line l1" d="M0,100 L200,120 L400,90 L600,110 L800,85 L1000,105" />
        <path class="tear-line l2" d="M0,250 L250,230 L500,260 L750,240 L1000,270" />
        <path class="tear-line l3" d="M0,320 L300,340 L700,310 L1000,330" />
      </svg>
    </div>
    <div class="glitch-block"></div>
  </div>
</div>
  </div>
</section>

<section class="scene" data-scene-id="scene-03">
  <div class="sv-scene-root sv-scene-root-scene-03" data-scene-root-id="scene-03">
<div class="s-scene-03-container">
  <div class="crt-lines"></div>
  <div class="ambient-glow"></div>

  <!-- Step 0: Timeline -->
  <div class="timeline-section" data-step="0">
    <div class="timeline-wrapper">
      <span class="time-label">[ 2017 ]</span>
      <div class="timeline-bar">
        <div class="timeline-fill"></div>
        <div class="timeline-arrow">>></div>
      </div>
      <span class="time-label pulse-text">至今</span>
    </div>
  </div>

  <!-- Step 1: Linux Distribution Grid -->
  <div class="distro-grid-container" data-step="1">
    <div class="grid-header">SYSTEM_SCAN_TARGETS: IN_PROGRESS...</div>
    <div class="distro-grid">
      <div class="distro-card" data-distro="ubuntu">
        <iconify-icon icon="simple-icons:ubuntu"></iconify-icon>
        <span class="distro-name">Ubuntu</span>
      </div>
      <div class="distro-card" data-distro="centos">
        <iconify-icon icon="simple-icons:centos"></iconify-icon>
        <span class="distro-name">CentOS</span>
      </div>
      <div class="distro-card" data-distro="rhel">
        <iconify-icon icon="simple-icons:redhat"></iconify-icon>
        <span class="distro-name">RHEL</span>
      </div>
      <div class="distro-card" data-distro="aws">
        <iconify-icon icon="simple-icons:amazonwebservices"></iconify-icon>
        <span class="distro-name">AWS Linux</span>
      </div>
      <div class="distro-card" data-distro="aliyun">
        <iconify-icon icon="simple-icons:alibabacloud"></iconify-icon>
        <span class="distro-name">AliyunOS</span>
      </div>
      <div class="distro-card" data-distro="suse">
        <iconify-icon icon="simple-icons:suse"></iconify-icon>
        <span class="distro-name">SUSE</span>
      </div>
    </div>
    <div class="shimmer-overlay"></div>
  </div>

  <!-- Step 2: Vulnerable Stamp -->
  <div class="vulnerable-overlay" data-step="2">
    <div class="stamp-box">
      <div class="stamp-text">VULNERABLE</div>
      <div class="stamp-subtext">受威胁状态</div>
    </div>
  </div>
</div>
  </div>
</section>

<section class="scene" data-scene-id="scene-04">
  <div class="sv-scene-root sv-scene-root-scene-04" data-scene-root-id="scene-04">
<div class="s-scene-04-wrapper">
  <div class="crt-lines"></div>
  <div class="ambient-glow"></div>

  <div class="system-layout">
    <!-- Left: Container -->
    <div class="node-container">
      <div class="system-node box-node">
        <div class="node-label">[ CONTAINER ]</div>
        <div class="internal-process">
          <div class="cursor-blink"></div>
          <span class="mono-text">UID: 1000</span>
        </div>
      </div>
    </div>

    <!-- SVG Layer for the Arrow -->
    <div class="vector-layer">
      <svg viewBox="0 0 800 400" fill="none" xmlns="http://www.w3.org/2000/svg">
        <path id="attack-path" d="M 240 200 L 560 200" stroke="#FF3131" stroke-width="4" stroke-dasharray="320" stroke-dashoffset="320" />
        <path id="arrow-head" d="M 545 185 L 565 200 L 545 215" stroke="#FF3131" stroke-width="4" opacity="0" />
      </svg>
    </div>

    <!-- Right: Host -->
    <div class="node-container">
      <div class="system-node circle-node" id="host-node">
        <div class="node-label">[ HOST_SYSTEM ]</div>
        <div class="internal-process">
          <span class="status-text">KERNEL_IDLE</span>
        </div>
      </div>
    </div>
  </div>

  <!-- Success Banner -->
  <div class="success-banner" data-step="1">
    <div class="glitch-box">
      <span class="success-text">> 提权成功: ESCAPED_TO_ROOT</span>
    </div>
  </div>
</div>
  </div>
</section>

<section class="scene" data-scene-id="scene-05">
  <div class="sv-scene-root sv-scene-root-scene-05" data-scene-root-id="scene-05">
<div class="s-scene-05-wrapper">
  <div class="crt-scanlines"></div>
  <div class="ambient-phosphor"></div>

  <!-- Step 0: Memory Container -->
  <div class="memory-container">
    <div class="memory-label">[ PHYSICAL_RAM_MAPPING ]</div>
    <div class="memory-grid"></div>

    <!-- Step 1: Page Cache Card -->
    <div class="page-cache-card" data-step="1">
      <div class="card-header">
        <span class="card-title">PAGE_CACHE_OBJECT</span>
        <span class="memory-addr">0x7FFF0042</span>
      </div>
      <div class="binary-content">
        <div class="binary-line">10110010 11100001 00011101</div>
        <div class="binary-line">11001010 01111100 11010001</div>
        <div class="binary-line">00110101 10100011 00011110</div>
        <div class="binary-line">11110000 00001111 10101010</div>
      </div>
      
      <!-- Step 2: Security Overlay -->
      <div class="security-status" data-step="2">
        <div class="lock-overlay">
          <i data-lucide="lock"></i>
          <span class="read-only-text">READ_ONLY</span>
        </div>
        <div class="forbidden-action">
          <div class="action-item">
            <span class="action-label">WRITE_REQ</span>
            <div class="forbidden-sign"></div>
          </div>
          <div class="action-item">
            <span class="action-label">MODIFY_REQ</span>
            <div class="forbidden-sign"></div>
          </div>
        </div>
      </div>
    </div>
  </div>

  <!-- Background Text -->
  <div class="ghost-text">KERNEL_SPACE</div>
</div>
  </div>
</section>

<section class="scene" data-scene-id="scene-06">
  <div class="sv-scene-root sv-scene-root-scene-06" data-scene-root-id="scene-06">
<div class="s-scene-06-wrapper">
  <div class="crt-overlay"></div>
  <div class="ambient-glow"></div>

  <!-- Step 0: Terminal Window -->
  <div class="terminal-container" id="term-06">
    <div class="term-header">
      <div class="term-controls"><span></span><span></span><span></span></div>
      <div class="term-title">K-SHELL v4.1</div>
    </div>
    <div class="term-content">
      <div class="typing-line" data-delay="1">$ modprobe af_alg</div>
      <div class="typing-line" data-delay="2">$ call <span class="highlight">splice()</span></div>
      <div class="typing-line blinking-cursor">_</div>
    </div>
  </div>

  <!-- Step 1: Conduit & Target File -->
  <div class="conduit-system">
    <svg class="conduit-svg" viewBox="0 0 1000 600">
      <path id="conduit-path" d="M280,300 L450,300" fill="none" stroke="#00FF41" stroke-width="4" stroke-dasharray="1000" stroke-dashoffset="1000" />
      <circle cx="280" cy="300" r="5" fill="#00FF41" class="node-point" />
    </svg>
    <div class="file-node" id="su-file-node">
      <div class="file-icon-box">
        <iconify-icon icon="mdi:file-lock-outline"></iconify-icon>
      </div>
      <div class="file-info">
        <span class="file-name">/usr/bin/su</span>
        <span class="file-perm">ROOT_SUID: 4755</span>
      </div>
    </div>
  </div>

  <!-- Step 2: Encryption Flow & Chains -->
  <div class="pipeline-stage" id="encrypt-stage">
    <div class="pipeline-box">
      <div class="pipeline-label">[ KERNEL_CRYPTO_API ]</div>
      <div class="chain-container">
         <div class="chain-link"></div>
         <div class="chain-link"></div>
         <div class="chain-link"></div>
      </div>
      <div class="flow-indicator">ENCRYPTION_IN_PROGRESS</div>
    </div>
  </div>

  <div class="side-status">
    <div class="status-item" data-step="1">> CHANNEL_ESTABLISHED</div>
    <div class="status-item" data-step="2">> FILE_INGESTION: FORCE</div>
  </div>
</div>
  </div>
</section>

<section class="scene" data-scene-id="scene-07">
  <div class="sv-scene-root sv-scene-root-scene-07" data-scene-root-id="scene-07">
<div class="s-scene-07-wrapper">
  <div class="crt-overlay"></div>
  <div class="ambient-glow"></div>
  
  <!-- Alert Overlay (Step 2) -->
  <div class="alert-pulse-overlay" data-step="2"></div>

  <div class="layout-container">
    <!-- Left: Code Section -->
    <div class="code-section">
      <div class="section-label">2017 内核优化代码</div>
      <div class="terminal-window">
        <div class="term-header">
           <div class="term-dots"><span></span><span></span><span></span></div>
           <div class="term-title">mm/gup.c</div>
        </div>
        <div class="code-content">
          <pre><code><span class="line">/* Optimization: No COW needed? */</span>
<span class="line">if (flags & FOLL_FORCE) {</span>
<span class="line highlight-target">  vma-&gt;vm_flags |= <span class="code-keyword">VM_MAYWRITE</span>;</span>
<span class="line">  <span class="code-comment">// Treat as writable buffer</span></span>
<span class="line">}</span></code></pre>
        </div>
      </div>
    </div>

    <!-- Middle: Conduit -->
    <div class="conduit-container">
      <svg class="conduit-svg" viewBox="0 0 200 100">
        <path class="arrow-path" d="M0,50 L180,50" fill="none" stroke="#00FF41" stroke-width="2" />
        <polygon points="180,45 195,50 180,55" fill="#00FF41" class="arrow-head" />
      </svg>
    </div>

    <!-- Right: Memory Page Section -->
    <div class="memory-section">
      <div class="section-label">物理内存页面</div>
      <div class="page-frame">
        <div class="page-grid">
          <span></span><span></span><span></span><span></span>
          <span></span><span></span><span></span><span></span>
        </div>
        <div class="permission-status">
          <div class="lock-container" id="perm-lock">
             <div class="lock-icon"><iconify-icon icon="mdi:lock-outline"></iconify-icon></div>
             <div class="lock-text">只读 (RO)</div>
          </div>
          <div class="rw-status" id="perm-rw">
             <div class="rw-glitch">可写 (RW)</div>
          </div>
        </div>
      </div>
    </div>
  </div>

  <div class="status-bar">
    <div class="status-msg">SYSTEM_OPTIMIZATION: ACTIVE</div>
    <div class="status-msg" data-step="1">CRITICAL: PERMISSION_BYPASS_DETECTED</div>
  </div>
</div>
  </div>
</section>

<section class="scene" data-scene-id="scene-08">
  <div class="sv-scene-root sv-scene-root-scene-08" data-scene-root-id="scene-08">
<div class="s-scene-08-wrapper">
  <div class="crt-scanlines"></div>
  <div class="ambient-glow"></div>

  <!-- Zoomed Hex Grid Viewport -->
  <div class="hex-viewport">
    <div class="viewport-header">
      <span class="addr-label">OFFSET: 0x00007FFF</span>
      <span class="status-tag" id="mem-status">[ MEMORY_READ_ONLY ]</span>
    </div>
    <div class="hex-grid-container">
      <div class="hex-grid" id="main-hex-grid">
        <!-- Generated cells will be here -->
      </div>
    </div>
  </div>

  <!-- Flying Bytes (Step 0: Visible, Step 1: Animate) -->
  <div class="missile-container">
    <div class="byte-missile" data-target="target-1" data-step="0">0x53</div>
    <div class="byte-missile" data-target="target-2" data-step="0">0x55</div>
    <div class="byte-missile" data-target="target-3" data-step="0">0x49</div>
    <div class="byte-missile" data-target="target-4" data-step="0">0x44</div>
  </div>

  <!-- Alert Overlay (Step 2) -->
  <div class="alert-overlay" data-step="2">
    <div class="alert-box">
      <div class="alert-header">CRITICAL_ERROR</div>
      <div class="alert-content">VALIDATION_FAILED: HASH_MISMATCH</div>
      <div class="alert-sub">ENCRYPTED_BLOCK_CORRUPTED</div>
    </div>
  </div>

  <!-- Bottom Status -->
  <div class="terminal-footer">
    <div class="cursor-line"><span class="prompt">$</span> <span class="typing-text">patching su_binary...</span><span class="cursor"></span></div>
    <div class="progress-section" data-step="1">
      <span class="label">INJECTION:</span>
      <span class="bar-bg"><span class="bar-fill" id="injection-bar"></span></span>
    </div>
  </div>
</div>
  </div>
</section>

<section class="scene" data-scene-id="scene-09">
  <div class="sv-scene-root sv-scene-root-scene-09" data-scene-root-id="scene-09">
<div class="s-scene-09-wrapper">
  <div class="crt-scanlines"></div>
  <div class="ambient-glow"></div>

  <!-- Terminal Interface Container -->
  <div class="terminal-interface">
    <div class="terminal-header">
      <div class="terminal-buttons">
        <span></span><span></span><span></span>
      </div>
      <div class="terminal-title">TERMINAL - su_exploit_session</div>
    </div>

    <div class="terminal-content">
      <!-- Input History -->
      <div class="log-line">[ OK ] Payload injected into memory segment 0x0804...</div>
      <div class="log-line">[ OK ] Hooking execution vector...</div>
      
      <!-- Main Prompt Line -->
      <div class="prompt-area">
        <span class="prompt-char" id="current-prompt">$</span>
        <span class="command-text">whoami</span>
        <span class="cursor"></span>
      </div>

      <!-- Result Output (Step 0-1 Hidden) -->
      <div class="result-output" data-step="2">
        <div class="root-banner" id="root-text-container">
          <!-- root text will be typed here -->
        </div>
      </div>
    </div>

    <!-- Bottom Status Bar -->
    <div class="terminal-footer-bar">
      <div class="status-item">STATUS: <span class="blink-text">RUNNING</span></div>
      <div class="status-item">PRIVILEGE: <span id="priv-level">USER</span></div>
      <div class="status-item" data-step="2">UID: 0 (root)</div>
    </div>
  </div>

  <!-- Flash Overlay for Step 1 -->
  <div class="lightning-flash" id="flash-overlay"></div>

  <!-- Success Glow Frame (Step 2) -->
  <div class="success-frame" data-step="2"></div>
</div>
  </div>
</section>

<section class="scene" data-scene-id="scene-10">
  <div class="sv-scene-root sv-scene-root-scene-10" data-scene-root-id="scene-10">
<div class="s-scene-10-wrapper">
  <div class="crt-scanlines"></div>
  <div class="ambient-glow"></div>

  <div class="thirds-container">
    <!-- Step 0: Left Column - Patch Info -->
    <div class="col-left" data-step="0">
      <div class="status-box">
        <div class="status-header">SYSTEM_ALERT</div>
        <div class="patch-icon-wrapper">
          <iconify-icon icon="mdi:shield-sync-outline" class="main-patch-icon"></iconify-icon>
          <div class="update-text-glitch">UPDATE NOW</div>
        </div>
        <div class="status-details">
          <div class="status-line">> KERNEL: VULNERABLE</div>
          <div class="status-line blinking">> PATCH: AVAILABLE</div>
        </div>
      </div>
    </div>

    <!-- Step 2: Center Column - Defused Bomb -->
    <div class="col-center" data-step="2">
      <div class="bomb-stage">
        <svg viewBox="0 0 200 200" class="bomb-svg">
          <!-- Fuse -->
          <path d="M120 40 Q 150 20, 160 60" fill="none" stroke="#00FF41" stroke-width="4" class="fuse-path" />
          <!-- Bomb Body -->
          <circle cx="100" cy="110" r="60" fill="none" stroke="#00FF41" stroke-width="6" />
          <rect x="85" y="45" width="30" height="15" fill="#000" stroke="#00FF41" stroke-width="4" />
          <!-- Cut Mark -->
          <path d="M135 25 L 155 45 M 155 25 L 135 45" stroke="#39FF14" stroke-width="8" class="cut-mark" />
        </svg>
        <div class="threat-status">THREAT_NEUTRALIZED</div>
      </div>
    </div>

    <!-- Step 1: Right Column - Terminal Command -->
    <div class="col-right" data-step="1">
      <div class="command-window">
        <div class="window-bar">
          <span class="dot"></span><span class="dot"></span><span class="dot"></span>
          <span class="window-title">bash - root</span>
        </div>
        <div class="terminal-body">
          <div class="shell-prompt">
            <span class="user">root@system</span>:<span class="path">~</span>#
            <span class="typing-cmd" id="cmd-text"></span><span class="terminal-cursor"></span>
          </div>
          <div class="shell-output" data-step="1">[ SUCCESS ] Module 'algif_aead' disabled.</div>
        </div>
      </div>
    </div>
  </div>

  <div class="footer-deco">SECURITY_ENFORCED // 0xPATCH_SUCCESS</div>
</div>
  </div>
</section>

<section class="scene" data-scene-id="scene-11">
  <div class="sv-scene-root sv-scene-root-scene-11" data-scene-root-id="scene-11">
<div class="s-scene-11-wrapper">
  <div class="crt-scanlines"></div>
  <div class="ambient-glow"></div>

  <!-- Step 0: Center Logo/Avatar -->
  <div class="logo-overlay" data-step="0">
    <div class="avatar-frame">
      <div class="avatar-glitch"></div>
      <svg viewBox="0 0 100 100" class="terminal-logo">
        <rect x="20" y="20" width="60" height="60" stroke="#00FF41" stroke-width="2" fill="none" />
        <path d="M35 40 L50 55 L65 40" stroke="#00FF41" stroke-width="4" fill="none" />
        <path d="M30 70 L70 70" stroke="#00FF41" stroke-width="2" stroke-dasharray="4 2" />
      </svg>
    </div>
    <div class="channel-id">[ CHANNEL_ID: ROOT_NODE ]</div>
  </div>

  <!-- Step 1: Interactive Actions & Outro -->
  <div class="action-container" data-step="1">
    <!-- Like Action -->
    <div class="action-item like-item">
      <div class="action-box">
        <i data-lucide="thumbs-up"></i>
        <span class="action-label">LIKE_SUCCESS</span>
      </div>
    </div>

    <!-- Follow Action -->
    <div class="action-item follow-item">
      <div class="action-box">
        <i data-lucide="user-plus"></i>
        <span class="action-label">SUBSCRIBE_OK</span>
      </div>
    </div>

    <!-- Closing Text -->
    <div class="closing-text">
      <div class="rainbow-terminal-text">下期再见</div>
      <div class="cursor-blink"></div>
    </div>
  </div>

  <div class="system-footer">
    SESSION_TERMINATED: 0x00000000
  </div>
</div>
  </div>
</section>
      </div>
    </div>
  </div>
  <script id="__SVGVIDEO_META__" type="application/json">{"version":2,"format":"json","title":"Linux内核超级漏洞：Copy Fail 深度解析","fullScript":"兄弟们，一个超级离谱的Linux内核漏洞刚刚曝光！普通用户一句话，就能把服务器的root权限直接抢到手！它的编号叫CVE-2026-31431，代号Copy Fail！这个漏洞影响了2017年之后几乎所有主流Linux发行版，Ubuntu、CentOS、RHEL、阿里云、AWS、SUSE通通中招。只要你在服务器上有一个普通账号，就能完成提权，连容器都能逃逸到主机上。原理其实特别巧妙，听我给你捋清楚：正常情况下，文件的内容会缓存在内存里，叫做page cache。我们普通用户只能读它，不能随便改它。但是黑客利用了内核提供的一个加密接口AF_ALG，再配合splice这个系统调用，把目标文件——比如/usr/bin/su——偷偷塞进了内核的加密流程里。因为2017年内核做了一个性能优化，这个优化让内核把这个文件的内存页面，当成了“可写的缓冲区”来使用！结果，黑客在加密计算的过程中，就往里面精准地写入了4个自己控制的字节。虽然最后加密验证失败了，但那4个字节已经实实在在地改进了内存里的su程序！重复操作几次，就能把su改造成一个“直接给我root”的恶意程序。一执行，权限就到手了！整个过程不需要猜内核地址，也不用竞态条件，稳得一批。目前各大厂商已经紧急推送了补丁，大家赶紧更新内核！临时缓解的话，可以把algif_aead模块禁用掉。这个漏洞再次告诉我们：再小的代码优化，如果没想清楚，也可能埋下致命的炸弹。好了，今天的分享就到这里，点赞关注，我们下期再见！","visualStyleId":"terminal-green","orientation":"landscape","scenes":[{"id":"scene-01","title":"震撼开场","steps":3,"status":"success","prompt":"横屏构图。背景为深暗色科技感网格。Step 0: 屏幕中心出现巨大的红色警示图标，伴随闪烁效果。Step 1: 左右两侧弹出两个黑色毛玻璃卡片，左边写着‘普通用户’，右边写着‘ROOT权限’。Step 2: 一个金色的闪电特效连接两个卡片，表示权限瞬间被夺取。视觉风格：赛博朋克、高对比度。","scriptText":"兄弟们，一个超级离谱的Linux内核漏洞刚刚曝光！普通用户一句话，就能把服务器的root权限直接抢到手！","audioObjectKey":"sv-video/v1/u_1aa5e8b3-d1b1-4121-b796-bb5ac9fcd45a/p_62574c6d-8ac1-43ff-8ab8-d4a1a16f8fc4/s_scene-01/v_xiaoxiao-hd/a_d40ec182_1777539050814_nzb69g.mp3","audioDurationMs":8424,"voiceName":"xiaoxiao-hd","relatedSceneIds":["scene-02"],"transition":"door"},{"id":"scene-02","title":"漏洞代号曝光","steps":2,"status":"success","prompt":"横屏构图。中心位置展示加粗的渐变文字‘CVE-2026-31431’，带有蓝色的呼吸光效。Step 0: 文字从屏幕中央通过弹簧缩放效果出现。Step 1: 下方浮现更大的红色文字‘Copy Fail’，背景出现类似文件被撕裂的SVG粒子效果。整体色调偏冷。","scriptText":"它的编号叫CVE 2026 31431，代号Copy Fail！","audioObjectKey":"sv-video/v1/u_1aa5e8b3-d1b1-4121-b796-bb5ac9fcd45a/p_62574c6d-8ac1-43ff-8ab8-d4a1a16f8fc4/s_scene-02/v_xiaoxiao-hd-cheerful/a_e74d6042_1777541496139_ldxv7g.mp3","audioDurationMs":5112,"voiceName":"xiaoxiao-hd","voiceStyle":"cheerful","relatedSceneIds":["scene-03"],"transition":"door"},{"id":"scene-03","title":"受影响范围","steps":3,"status":"success","prompt":"横屏，左右平铺布局。Step 0: 屏幕左侧出现一个‘2017’的时间标签，向右拉出一条时间轴到‘至今’。Step 1: 屏幕中间网格化排列主流Linux Logo（Ubuntu, CentOS, RHEL, AWS, 阿里云）。Step 2: 所有图标由蓝转红，并出现‘Vulnerable’（受威胁）的印章动画覆盖。使用微光（shimmer）特效扫描整个网格。","scriptText":"这个漏洞影响了2017年之后几乎所有主流Linux发行版","audioObjectKey":"sv-video/v1/u_1aa5e8b3-d1b1-4121-b796-bb5ac9fcd45a/p_62574c6d-8ac1-43ff-8ab8-d4a1a16f8fc4/s_scene-03/v_xiaoxiao-hd-cheerful/a_7c58a451_1777541525511_w8aeas.mp3","audioDurationMs":4392,"voiceName":"xiaoxiao-hd","voiceStyle":"cheerful","relatedSceneIds":["scene-04"],"transition":"door"},{"id":"scene-04","title":"攻击场景描述","steps":2,"status":"success","prompt":"横屏。左侧是一个代表‘容器’的方块，右侧是一个代表‘宿主机’的大圆圈。Step 0: 一个红色小箭从容器内部穿透边界射向宿主机。Step 1: 宿主机图标由灰白变为鲜红色，屏幕底部显示‘提权成功’的文字框。布局采用左右对称的极简矢量风格。","scriptText":"只要你在服务器上有一个普通账号，就能完成提权，连容器都能逃逸到主机上。","audioObjectKey":"sv-video/v1/u_1aa5e8b3-d1b1-4121-b796-bb5ac9fcd45a/p_62574c6d-8ac1-43ff-8ab8-d4a1a16f8fc4/s_scene-04/v_xiaoxiao-hd/a_3bb62f42_1777539099072_3xsdd0.mp3","audioDurationMs":5508,"voiceName":"xiaoxiao-hd","relatedSceneIds":["scene-05"],"transition":"door"},{"id":"scene-05","title":"原理科普：Page Cache","steps":3,"status":"success","prompt":"横屏。Step 0: 背景出现一个矩形区域代表‘内存’。Step 1: 内存中心浮现一个名为‘Page Cache’的半透明卡片，里面显示二进制代码流。Step 2: 卡片右上角出现一个‘只读（Read Only）’的锁头标志，一个禁止符号出现在‘修改’操作上。","scriptText":"原理其实特别巧妙，听我给你捋清楚：正常情况下，文件的内容会缓存在内存里，叫做page cache。我们普通用户只能读它，不能随便改它。","audioObjectKey":"sv-video/v1/u_1aa5e8b3-d1b1-4121-b796-bb5ac9fcd45a/p_62574c6d-8ac1-43ff-8ab8-d4a1a16f8fc4/s_scene-05/v_xiaoxiao-hd-cheerful/a_17a6b492_1777541700692_6vl3vo.mp3","audioDurationMs":10548,"voiceName":"xiaoxiao-hd","voiceStyle":"cheerful","relatedSceneIds":["scene-06"],"transition":"door"},{"id":"scene-06","title":"利用机制：AF_ALG","steps":2,"status":"success","prompt":"横屏，展示代码调用逻辑。Step 0: 屏幕左侧淡入一个终端窗口，显示代码‘AF_ALG’和‘splice()’。Step 1: 窗口中伸出一根导管，指向中间的‘/usr/bin/su’文件图标。Step 2: 整个su文件图标开始微微抖动，并被强制拖入一个带有锁链图案的‘加密流程’框内。","scriptText":"但是黑客利用了内核提供的一个加密接口AF_ALG，再配合splice这个系统调用，把目标文件——比如/usr/bin/su——偷偷塞进了内核的加密流程里。","audioObjectKey":"sv-video/v1/u_1aa5e8b3-d1b1-4121-b796-bb5ac9fcd45a/p_62574c6d-8ac1-43ff-8ab8-d4a1a16f8fc4/s_scene-06/v_xiaoxiao-hd-cheerful/a_8075a4cf_1777541754264_fwsej6.mp3","audioDurationMs":12348,"voiceName":"xiaoxiao-hd","voiceStyle":"cheerful","relatedSceneIds":["scene-07"],"transition":"door"},{"id":"scene-07","title":"漏洞核心：错误的写权限","steps":2,"status":"success","prompt":"横屏。左侧显示‘2017 内核优化代码’。Step 0: 高亮一段代码逻辑，箭头指向右侧的内存页面。Step 1: 原本的‘只读锁’瞬间碎裂，变成红色的‘可写（RW）’标识。背景出现警示性质的红色脉冲扩散动画。","scriptText":"因为2017年内核做了一个性能优化，这个优化让内核把这个文件的内存页面，当成了“可写的缓冲区”来使用！","audioObjectKey":"sv-video/v1/u_1aa5e8b3-d1b1-4121-b796-bb5ac9fcd45a/p_62574c6d-8ac1-43ff-8ab8-d4a1a16f8fc4/s_scene-07/v_xiaoxiao-hd/a_7db73a0d_1777539106616_dmys7t.mp3","audioDurationMs":8424,"voiceName":"xiaoxiao-hd","relatedSceneIds":["scene-08"],"transition":"door"},{"id":"scene-08","title":"精准写入过程","steps":4,"status":"success","prompt":"横屏。中心放大展示代码块的一角。Step 0: 四个高亮的16进制字节（如 0x01 0x02 0x03 0x04）浮现。Step 1: 字节像导弹一样精准嵌入内存代码块的空隙。Step 2: 出现一个‘验证失败’的红色提示。Step 3: 但字节已停留在内存中，代码块由蓝变紫。细节采用微缩模型动画风格。","scriptText":"结果，黑客在加密计算的过程中，就往里面精准地写入了4个自己控制的字节。虽然最后加密验证失败了，但那4个字节已经实实在在地改进了内存里的su程序！","audioObjectKey":"sv-video/v1/u_1aa5e8b3-d1b1-4121-b796-bb5ac9fcd45a/p_62574c6d-8ac1-43ff-8ab8-d4a1a16f8fc4/s_scene-08/v_xiaoxiao-hd/a_18d0460b_1777539109881_nhl5sl.mp3","audioDurationMs":12348,"voiceName":"xiaoxiao-hd","relatedSceneIds":["scene-09"],"transition":"door"},{"id":"scene-09","title":"获取Root权限","steps":3,"status":"success","prompt":"横屏，模拟终端界面。Step 0: 屏幕显示提示符‘$ whoami’。Step 1: 闪电式刷新，提示符变为‘# whoami’。Step 2: 屏幕爆发出巨大的‘root’字样，伴随成功的绿色发光边框。文字采用打字机效果逐字弹出。","scriptText":"重复操作几次，就能把su改造成一个“直接给我root”的恶意程序。一执行，权限就到手了！整个过程不需要猜内核地址，也不用竞态条件，稳得一批。","audioObjectKey":"sv-video/v1/u_1aa5e8b3-d1b1-4121-b796-bb5ac9fcd45a/p_62574c6d-8ac1-43ff-8ab8-d4a1a16f8fc4/s_scene-09/v_xiaoxiao-hd-cheerful/a_d4dbfe01_1777541610215_shlcai.mp3","audioDurationMs":11124,"voiceName":"xiaoxiao-hd","voiceStyle":"cheerful","relatedSceneIds":["scene-10"],"transition":"door"},{"id":"scene-10","title":"防御与总结","steps":3,"status":"success","prompt":"横屏，三分构图。Step 0: 左侧显示补丁图标‘UPDATE NOW’。Step 1: 右侧显示命令代码‘modprobe -r algif_aead’。Step 2: 屏幕中央出现一个引信被切断的炸弹图标。视觉风格：清晰、专业、带有安全提示感。","scriptText":"目前各大厂商已经紧急推送了补丁，大家赶紧更新内核！临时缓解的话，可以把algif_aead模块禁用掉。这个漏洞再次告诉我们：再小的代码优化，如果没想清楚，也可能埋下致命的炸弹。","audioObjectKey":"sv-video/v1/u_1aa5e8b3-d1b1-4121-b796-bb5ac9fcd45a/p_62574c6d-8ac1-43ff-8ab8-d4a1a16f8fc4/s_scene-10/v_xiaoxiao-hd-cheerful/a_bfafacde_1777541890973_en47qg.mp3","audioDurationMs":13860,"voiceName":"xiaoxiao-hd","voiceStyle":"cheerful","relatedSceneIds":["scene-11"],"transition":"door"},{"id":"scene-11","title":"结尾致谢","steps":2,"status":"success","prompt":"横屏。Step 0: 背景变暗，中心出现博主的头像或频道Logo。Step 1: 左右两侧飞入‘点赞’和‘关注’的SVG动态图标，带有Q弹的弹簧反馈效果。文字‘下期再见’以彩虹渐变色淡入。整体风格欢快、整洁。","scriptText":"好了，今天的分享就到这里，点赞关注，我们下期再见！","audioObjectKey":"sv-video/v1/u_1aa5e8b3-d1b1-4121-b796-bb5ac9fcd45a/p_62574c6d-8ac1-43ff-8ab8-d4a1a16f8fc4/s_scene-11/v_xiaoxiao-hd-cheerful/a_1f8ff479_1777541651756_6js77f.mp3","audioDurationMs":4608,"voiceName":"xiaoxiao-hd","voiceStyle":"cheerful","transition":"door"}]}</script>
  <script id="__SVGVIDEO_SCENE_DATA__" type="application/json">[{"id":"scene-01","title":"震撼开场","steps":3,"html":"<div class=\"s-scene-01-container\">\n  <div class=\"scanline\"></div>\n  <div class=\"vignette\"></div>\n\n  <!-- Step 0: Alert System -->\n  <div class=\"alert-overlay\" data-step=\"0\">\n    <div class=\"alert-content\">\n      <div class=\"ascii-alert\">\n        !!! CRITICAL_SYSTEM_VULNERABILITY !!!\n      </div>\n      <div class=\"alert-tag\">[ KERNEL_EXPLOIT_DETECTED ]</div>\n    </div>\n  </div>\n\n  <!-- Step 1: Terminal Cards -->\n  <div class=\"terminal-wrapper\">\n    <div class=\"terminal-card left\" data-step=\"1\">\n      <div class=\"terminal-header\">guest@linux: ~</div>\n      <div class=\"terminal-body\">\n        <div class=\"line\"><span class=\"prompt\">$</span> whoami</div>\n        <div class=\"line glow-text\">guest</div>\n        <div class=\"line cursor\">_</div>\n      </div>\n    </div>\n\n    <div class=\"terminal-card right\" data-step=\"1\">\n      <div class=\"terminal-header\">root@local: #</div>\n      <div class=\"terminal-body\">\n        <div class=\"line\"><span class=\"prompt\">#</span> whoami</div>\n        <div class=\"line ghost-text\">??????</div>\n        <div class=\"line cursor\">_</div>\n      </div>\n    </div>\n  </div>\n\n  <!-- Step 2: Connection / Seizure -->\n  <div class=\"lightning-container\" data-step=\"2\">\n    <svg class=\"lightning-svg\" viewBox=\"0 0 800 400\">\n      <path class=\"lightning-path\" d=\"M 280 200 L 350 180 L 400 220 L 450 180 L 520 200\" />\n      <filter id=\"glow\">\n        <feGaussianBlur stdDeviation=\"4\" result=\"coloredBlur\"/>\n        <feMerge>\n          <feMergeNode in=\"coloredBlur\"/>\n          <feMergeNode in=\"SourceGraphic\"/>\n        </feMerge>\n      </filter>\n    </svg>\n    <div class=\"status-text\">PRIVILEGE ESCALATION SUCCESSFUL</div>\n  </div>\n\n  <!-- Background Decor -->\n  <div class=\"bg-grid\"></div>\n</div>","css":".s-scene-01-container {\n  position: relative;\n  width: 100%;\n  height: 100%;\n  background: #000000;\n  color: #00FF41;\n  font-family: var(--sv-font-mono);\n  overflow: hidden;\n  display: flex;\n  justify-content: center;\n  align-items: center;\n}\n\n.scanline {\n  position: absolute;\n  inset: 0;\n  background: repeating-linear-gradient(rgba(0,0,0,0) 0px, rgba(0,0,0,0) 1px, rgba(0,255,65,0.08) 2px, rgba(0,255,65,0.08) 3px);\n  pointer-events: none;\n  z-index: 10;\n}\n\n.bg-grid {\n  position: absolute;\n  inset: 0;\n  background-image: linear-gradient(rgba(0, 255, 65, 0.05) 1px, transparent 1px), linear-gradient(90deg, rgba(0, 255, 65, 0.05) 1px, transparent 1px);\n  background-size: 40px 40px;\n  animation: grid-move 20s linear infinite;\n}\n\n@keyframes grid-move {\n  from { background-position: 0 0; }\n  to { background-position: 40px 40px; }\n}\n\n.alert-overlay {\n  position: absolute;\n  z-index: 5;\n  text-align: center;\n  animation: alert-flicker 0.2s infinite alternate;\n}\n\n.ascii-alert {\n  font-size: 42px;\n  font-weight: bold;\n  border: 4px solid #00FF41;\n  padding: 20px 40px;\n  background: rgba(0, 255, 65, 0.1);\n}\n\n@keyframes alert-flicker {\n  0% { opacity: 1; transform: scale(1); }\n  100% { opacity: 0.7; transform: scale(1.02); }\n}\n\n.terminal-wrapper {\n  display: flex;\n  gap: 120px;\n  z-index: 4;\n}\n\n.terminal-card {\n  width: 320px;\n  height: 200px;\n  background: rgba(0, 0, 0, 0.9);\n  border: 1px solid rgba(0, 255, 65, 0.4);\n  box-shadow: 0 0 20px rgba(0, 255, 65, 0.1);\n  display: flex;\n  flex-direction: column;\n  transition: transform 0.5s cubic-bezier(0.22, 1, 0.36, 1), opacity 0.5s;\n}\n\n.terminal-header {\n  background: rgba(0, 255, 65, 0.2);\n  padding: 6px 12px;\n  font-size: 14px;\n  border-bottom: 1px solid rgba(0, 255, 65, 0.4);\n}\n\n.terminal-body {\n  padding: 15px;\n  font-size: 18px;\n  line-height: 1.6;\n}\n\n.prompt { color: #39FF14; margin-right: 8px; }\n\n.cursor {\n  animation: blink 0.8s infinite;\n}\n\n@keyframes blink {\n  50% { opacity: 0; }\n}\n\n.lightning-container {\n  position: absolute;\n  inset: 0;\n  display: flex;\n  flex-direction: column;\n  justify-content: center;\n  align-items: center;\n  pointer-events: none;\n}\n\n.lightning-path {\n  fill: none;\n  stroke: #39FF14;\n  stroke-width: 4;\n  stroke-dasharray: 600;\n  stroke-dashoffset: 600;\n  filter: url(#glow);\n  animation: strike 0.5s forwards;\n}\n\n@keyframes strike {\n  to { stroke-dashoffset: 0; }\n}\n\n.status-text {\n  margin-top: 250px;\n  font-size: 24px;\n  background: #00FF41;\n  color: #000;\n  padding: 5px 15px;\n  animation: slide-up 0.4s ease-out;\n}\n\n@keyframes slide-up {\n  from { opacity: 0; transform: translateY(20px); }\n  to { opacity: 1; transform: translateY(0); }\n}\n\n/* Step Transitions */\n[data-step=\"1\"] { opacity: 0; transform: translateY(30px); }\n.active [data-step=\"1\"] { opacity: 1; transform: translateY(0); }\n\n[data-step=\"2\"] { opacity: 0; }\n.active [data-step=\"2\"] { opacity: 1; }\n\n.terminal-card.right.active-root {\n  border-color: #39FF14;\n  box-shadow: 0 0 30px rgba(0, 255, 65, 0.4);\n}","js":"window.SceneLifecycle.register('scene-01', {\n  enter() {\n    const sceneRoot = document.querySelector('[data-scene-id=\"scene-01\"]');\n    sceneRoot.classList.add('active');\n  },\n  step(step) {\n    const sceneRoot = document.querySelector('[data-scene-id=\"scene-01\"]');\n    const alert = sceneRoot.querySelector('.alert-overlay');\n    const rightTerminalBody = sceneRoot.querySelector('.terminal-card.right .terminal-body');\n\n    if (step >= 1) {\n      alert.style.opacity = '0.2';\n      alert.style.transform = 'scale(0.8) translateY(-150px)';\n      alert.style.transition = 'all 0.6s cubic-bezier(0.22, 1, 0.36, 1)';\n    }\n\n    if (step >= 2) {\n      const ghostText = rightTerminalBody.querySelector('.ghost-text');\n      if (ghostText) {\n        ghostText.textContent = 'root';\n        ghostText.classList.remove('ghost-text');\n        ghostText.style.color = '#39FF14';\n        ghostText.style.fontWeight = 'bold';\n      }\n      sceneRoot.querySelector('.terminal-card.right').classList.add('active-root');\n      \n      // Create rapid flicker effect on success\n      const bg = sceneRoot.querySelector('.bg-grid');\n      bg.style.animationDuration = '2s';\n    }\n  },\n  leave() {\n    const sceneRoot = document.querySelector('[data-scene-id=\"scene-01\"]');\n    sceneRoot.classList.remove('active');\n  }\n});"},{"id":"scene-02","title":"漏洞代号曝光","steps":2,"html":"<div class=\"s-scene-02-container\">\n  <div class=\"crt-overlay\"></div>\n  <div class=\"ambient-phosphor\"></div>\n  <div class=\"grid-bg\"></div>\n\n  <!-- Step 0: Vulnerability ID -->\n  <div class=\"vulnerability-wrapper\" data-step=\"0\">\n    <h1 class=\"cve-text\">CVE-2026-31431</h1>\n    <div class=\"cve-glow\"></div>\n  </div>\n\n  <!-- Step 1: Copy Fail & Tearing -->\n  <div class=\"fail-container\" data-step=\"1\">\n    <h2 class=\"fail-text\">COPY FAIL</h2>\n    <div class=\"tear-particles\">\n      <svg viewBox=\"0 0 1000 400\" preserveAspectRatio=\"none\">\n        <path class=\"tear-line l1\" d=\"M0,100 L200,120 L400,90 L600,110 L800,85 L1000,105\" />\n        <path class=\"tear-line l2\" d=\"M0,250 L250,230 L500,260 L750,240 L1000,270\" />\n        <path class=\"tear-line l3\" d=\"M0,320 L300,340 L700,310 L1000,330\" />\n      </svg>\n    </div>\n    <div class=\"glitch-block\"></div>\n  </div>\n</div>","css":".s-scene-02-container {\n  position: relative;\n  width: 100%;\n  height: 100%;\n  background: #000000;\n  overflow: hidden;\n  display: flex;\n  flex-direction: column;\n  justify-content: center;\n  align-items: center;\n  font-family: var(--sv-font-mono);\n}\n\n.crt-overlay {\n  position: absolute;\n  inset: 0;\n  background: repeating-linear-gradient(rgba(0,0,0,0) 0px, rgba(0,0,0,0) 1px, rgba(0,255,65,0.06) 2px, rgba(0,255,65,0.06) 3px);\n  pointer-events: none;\n  z-index: 10;\n}\n\n.ambient-phosphor {\n  position: absolute;\n  width: 120%;\n  height: 120%;\n  background: radial-gradient(circle, rgba(0,255,65,0.05) 0%, transparent 70%);\n  animation: phosphor-pulse 8s ease-in-out infinite;\n  pointer-events: none;\n}\n\n.grid-bg {\n  position: absolute;\n  inset: 0;\n  background-image: linear-gradient(rgba(0, 255, 65, 0.03) 1px, transparent 1px), linear-gradient(90deg, rgba(0, 255, 65, 0.03) 1px, transparent 1px);\n  background-size: 50px 50px;\n  opacity: 0.5;\n}\n\n.vulnerability-wrapper {\n  position: relative;\n  z-index: 5;\n  opacity: 0;\n  transform: scale(0);\n  transition: all 0.6s cubic-bezier(0.175, 0.885, 0.32, 1.275);\n}\n\n.active [data-step=\"0\"] {\n  opacity: 1;\n  transform: scale(1);\n}\n\n.cve-text {\n  font-size: 108px;\n  font-weight: 900;\n  color: #00FF41;\n  letter-spacing: -2px;\n  text-shadow: 0 0 20px rgba(0, 255, 65, 0.6);\n  margin: 0;\n  animation: blue-breath 4s ease-in-out infinite;\n}\n\n@keyframes blue-breath {\n  0%, 100% { text-shadow: 0 0 20px rgba(0, 255, 65, 0.4); }\n  50% { text-shadow: 0 0 40px rgba(60, 150, 255, 0.7), 0 0 10px rgba(0, 255, 65, 0.5); }\n}\n\n.fail-container {\n  position: absolute;\n  top: 60%;\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  opacity: 0;\n  transform: translateY(40px);\n  transition: all 0.5s ease-out;\n}\n\n.active [data-step=\"1\"] {\n  opacity: 1;\n  transform: translateY(0);\n}\n\n.fail-text {\n  font-size: 140px;\n  font-weight: 900;\n  color: #00FF41; \n  /* Semantically red but visually terminal phosphor */\n  background: #000;\n  border: 4px solid #00FF41;\n  padding: 0 40px;\n  mix-blend-mode: screen;\n  animation: critical-glitch 0.2s steps(2) infinite;\n}\n\n@keyframes critical-glitch {\n  0% { transform: skewX(0); opacity: 1; border-color: #00FF41; }\n  50% { transform: skewX(5deg); opacity: 0.8; border-color: rgba(255, 50, 50, 0.8); }\n}\n\n.tear-particles {\n  position: absolute;\n  inset: -100px -200px;\n  pointer-events: none;\n  z-index: -1;\n}\n\n.tear-line {\n  fill: none;\n  stroke: #00FF41;\n  stroke-width: 2;\n  stroke-dasharray: 1000;\n  stroke-dashoffset: 1000;\n  opacity: 0.4;\n}\n\n.active [data-step=\"1\"] .tear-line {\n  animation: tear-draw 0.8s forwards;\n}\n\n.l2 { animation-delay: 0.1s; }\n.l3 { animation-delay: 0.2s; }\n\n@keyframes tear-draw {\n  to { stroke-dashoffset: 0; opacity: 0.1; }\n}\n\n@keyframes phosphor-pulse {\n  0%, 100% { opacity: 0.4; transform: scale(1); }\n  50% { opacity: 0.6; transform: scale(1.05); }\n}","js":"window.SceneLifecycle.register('scene-02', {\n  enter() {\n    const sceneRoot = document.querySelector('[data-scene-id=\"scene-02\"]');\n    sceneRoot.classList.add('active');\n  },\n  step(step) {\n    const sceneRoot = document.querySelector('[data-scene-id=\"scene-02\"]');\n    if (!sceneRoot) return;\n\n    const cveWrapper = sceneRoot.querySelector('.vulnerability-wrapper');\n    const failContainer = sceneRoot.querySelector('.fail-container');\n\n    if (step >= 1) {\n      // Impact effect when Copy Fail appears\n      cveWrapper.style.transform = 'scale(0.9) translateY(-100px)';\n      cveWrapper.style.filter = 'blur(1px)';\n      \n      // Create temporary tearing jitter\n      sceneRoot.style.animation = 'screen-shake 0.3s cubic-bezier(.36,.07,.19,.97) both';\n      setTimeout(() => {\n        sceneRoot.style.animation = '';\n      }, 300);\n    }\n  },\n  leave() {\n    const sceneRoot = document.querySelector('[data-scene-id=\"scene-02\"]');\n    if (sceneRoot) sceneRoot.classList.remove('active');\n  }\n});\n\n// Added shake keyframes via style injection since it's a one-off impact\nconst style = document.createElement('style');\nstyle.innerHTML = `\n  @keyframes screen-shake {\n    10%, 90% { transform: translate3d(-1px, 0, 0); }\n    20%, 80% { transform: translate3d(2px, 0, 0); }\n    30%, 50%, 70% { transform: translate3d(-4px, 0, 0); }\n    40%, 60% { transform: translate3d(4px, 0, 0); }\n  }\n`;\ndocument.head.appendChild(style);"},{"id":"scene-03","title":"受影响范围","steps":3,"html":"<div class=\"s-scene-03-container\">\n  <div class=\"crt-lines\"></div>\n  <div class=\"ambient-glow\"></div>\n\n  <!-- Step 0: Timeline -->\n  <div class=\"timeline-section\" data-step=\"0\">\n    <div class=\"timeline-wrapper\">\n      <span class=\"time-label\">[ 2017 ]</span>\n      <div class=\"timeline-bar\">\n        <div class=\"timeline-fill\"></div>\n        <div class=\"timeline-arrow\">>></div>\n      </div>\n      <span class=\"time-label pulse-text\">至今</span>\n    </div>\n  </div>\n\n  <!-- Step 1: Linux Distribution Grid -->\n  <div class=\"distro-grid-container\" data-step=\"1\">\n    <div class=\"grid-header\">SYSTEM_SCAN_TARGETS: IN_PROGRESS...</div>\n    <div class=\"distro-grid\">\n      <div class=\"distro-card\" data-distro=\"ubuntu\">\n        <iconify-icon icon=\"simple-icons:ubuntu\"></iconify-icon>\n        <span class=\"distro-name\">Ubuntu</span>\n      </div>\n      <div class=\"distro-card\" data-distro=\"centos\">\n        <iconify-icon icon=\"simple-icons:centos\"></iconify-icon>\n        <span class=\"distro-name\">CentOS</span>\n      </div>\n      <div class=\"distro-card\" data-distro=\"rhel\">\n        <iconify-icon icon=\"simple-icons:redhat\"></iconify-icon>\n        <span class=\"distro-name\">RHEL</span>\n      </div>\n      <div class=\"distro-card\" data-distro=\"aws\">\n        <iconify-icon icon=\"simple-icons:amazonwebservices\"></iconify-icon>\n        <span class=\"distro-name\">AWS Linux</span>\n      </div>\n      <div class=\"distro-card\" data-distro=\"aliyun\">\n        <iconify-icon icon=\"simple-icons:alibabacloud\"></iconify-icon>\n        <span class=\"distro-name\">AliyunOS</span>\n      </div>\n      <div class=\"distro-card\" data-distro=\"suse\">\n        <iconify-icon icon=\"simple-icons:suse\"></iconify-icon>\n        <span class=\"distro-name\">SUSE</span>\n      </div>\n    </div>\n    <div class=\"shimmer-overlay\"></div>\n  </div>\n\n  <!-- Step 2: Vulnerable Stamp -->\n  <div class=\"vulnerable-overlay\" data-step=\"2\">\n    <div class=\"stamp-box\">\n      <div class=\"stamp-text\">VULNERABLE</div>\n      <div class=\"stamp-subtext\">受威胁状态</div>\n    </div>\n  </div>\n</div>","css":".s-scene-03-container {\n  position: relative;\n  width: 100%;\n  height: 100%;\n  background: #000000;\n  display: flex;\n  flex-direction: column;\n  justify-content: center;\n  align-items: center;\n  overflow: hidden;\n  font-family: var(--sv-font-mono);\n  color: #00FF41;\n}\n\n.crt-lines {\n  position: absolute;\n  inset: 0;\n  background: repeating-linear-gradient(rgba(0,0,0,0) 0px, rgba(0,0,0,0) 2px, rgba(0,255,65,0.08) 3px, rgba(0,255,65,0.08) 4px);\n  pointer-events: none;\n  z-index: 10;\n}\n\n.ambient-glow {\n  position: absolute;\n  width: 100%;\n  height: 100%;\n  background: radial-gradient(circle at center, rgba(0,255,65,0.04) 0%, transparent 70%);\n  pointer-events: none;\n}\n\n/* Step 0: Timeline */\n.timeline-section {\n  width: 80%;\n  margin-bottom: 60px;\n  opacity: 0;\n  transform: translateX(-50px);\n  transition: all 0.6s cubic-bezier(0.23, 1, 0.32, 1);\n}\n.active [data-step=\"0\"] { opacity: 1; transform: translateX(0); }\n\n.timeline-wrapper {\n  display: flex;\n  align-items: center;\n  gap: 20px;\n  font-size: 28px;\n}\n\n.timeline-bar {\n  flex-grow: 1;\n  height: 4px;\n  background: rgba(0, 255, 65, 0.2);\n  position: relative;\n  display: flex;\n  align-items: center;\n}\n\n.timeline-fill {\n  position: absolute;\n  left: 0;\n  height: 100%;\n  width: 0%;\n  background: #00FF41;\n  box-shadow: 0 0 10px #00FF41;\n  transition: width 1.5s ease-in-out;\n}\n\n.active [data-step=\"0\"] .timeline-fill { width: 100%; }\n\n.timeline-arrow {\n  position: absolute;\n  right: -10px;\n  font-weight: bold;\n  text-shadow: 0 0 5px #00FF41;\n}\n\n.pulse-text {\n  animation: phosphor-flicker 2s infinite;\n}\n\n/* Step 1: Grid */\n.distro-grid-container {\n  width: 85%;\n  position: relative;\n  opacity: 0;\n  transition: opacity 0.5s ease;\n}\n.active [data-step=\"1\"] { opacity: 1; }\n\n.grid-header {\n  font-size: 14px;\n  margin-bottom: 10px;\n  color: rgba(0, 255, 65, 0.6);\n  letter-spacing: 2px;\n}\n\n.distro-grid {\n  display: grid;\n  grid-template-columns: repeat(3, 1fr);\n  gap: 30px;\n  padding: 40px;\n  border: 1px solid rgba(0, 255, 65, 0.3);\n  background: rgba(0, 255, 65, 0.02);\n}\n\n.distro-card {\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  gap: 15px;\n  transition: all 0.4s ease;\n}\n\n.distro-card iconify-icon {\n  font-size: 64px;\n  color: #00FF41;\n  filter: drop-shadow(0 0 5px rgba(0, 255, 65, 0.4));\n}\n\n.distro-name {\n  font-size: 18px;\n  text-transform: uppercase;\n}\n\n/* Step 2: Vulnerable State */\n.active [data-step=\"2\"] .distro-card iconify-icon {\n  color: #FF3131;\n  filter: drop-shadow(0 0 8px rgba(255, 49, 49, 0.6));\n  animation: icon-glitch 0.3s infinite alternate;\n}\n\n.shimmer-overlay {\n  position: absolute;\n  inset: 0;\n  background: linear-gradient(135deg, transparent 0%, rgba(0,255,65,0.1) 45%, rgba(0,255,65,0.2) 50%, rgba(0,255,65,0.1) 55%, transparent 100%);\n  background-size: 200% 200%;\n  pointer-events: none;\n  opacity: 0;\n}\n.active [data-step=\"2\"] .shimmer-overlay {\n  opacity: 1;\n  animation: shimmer-scan 3s infinite linear;\n}\n\n.vulnerable-overlay {\n  position: absolute;\n  inset: 0;\n  display: flex;\n  justify-content: center;\n  align-items: center;\n  z-index: 20;\n  pointer-events: none;\n  opacity: 0;\n  transform: scale(2);\n  transition: all 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);\n}\n\n.active [data-step=\"2\"].vulnerable-overlay {\n  opacity: 1;\n  transform: scale(1) rotate(-10deg);\n}\n\n.stamp-box {\n  border: 8px double #FF3131;\n  padding: 20px 40px;\n  background: rgba(0, 0, 0, 0.9);\n  box-shadow: 0 0 30px rgba(255, 49, 49, 0.4);\n  text-align: center;\n}\n\n.stamp-text {\n  font-size: 84px;\n  font-weight: 900;\n  color: #FF3131;\n  line-height: 1;\n}\n\n.stamp-subtext {\n  font-size: 24px;\n  color: #FF3131;\n  letter-spacing: 10px;\n  margin-top: 10px;\n}\n\n@keyframes phosphor-flicker {\n  0%, 100% { opacity: 1; }\n  50% { opacity: 0.6; }\n  55% { opacity: 0.9; }\n}\n\n@keyframes shimmer-scan {\n  0% { background-position: -200% -200%; }\n  100% { background-position: 200% 200%; }\n}\n\n@keyframes icon-glitch {\n  0% { transform: translate(0); }\n  50% { transform: translate(-2px, 1px); }\n  100% { transform: translate(2px, -1px); }\n}","js":"window.SceneLifecycle.register('scene-03', {\n  enter() {\n    const sceneRoot = document.querySelector('[data-scene-id=\"scene-03\"]');\n    if (sceneRoot) {\n      sceneRoot.classList.add('active');\n      window.__SVGVIDEO_REFRESH_ICONS(sceneRoot);\n    }\n  },\n  step(step) {\n    const sceneRoot = document.querySelector('[data-scene-id=\"scene-03\"]');\n    if (!sceneRoot) return;\n\n    const header = sceneRoot.querySelector('.grid-header');\n    if (step === 2) {\n      if (header) header.textContent = 'SYSTEM_CRITICAL: VULNERABILITIES_DETECTED';\n      // Add a slight screen shake on vulnerable stamp\n      sceneRoot.style.animation = 'terminal-shake 0.4s ease-out';\n      setTimeout(() => sceneRoot.style.animation = '', 400);\n    } else if (step === 1) {\n      if (header) header.textContent = 'SYSTEM_SCAN_TARGETS: IDENTIFIED';\n    }\n  },\n  leave() {\n    const sceneRoot = document.querySelector('[data-scene-id=\"scene-03\"]');\n    if (sceneRoot) sceneRoot.classList.remove('active');\n  }\n});\n\nconst shakeStyles = document.createElement('style');\nshakeStyles.innerHTML = `\n  @keyframes terminal-shake {\n    0% { transform: translate(0,0); }\n    10% { transform: translate(-5px,-5px); }\n    20% { transform: translate(5px,5px); }\n    30% { transform: translate(-5px,5px); }\n    40% { transform: translate(5px,-5px); }\n    100% { transform: translate(0,0); }\n  }\n`;\ndocument.head.appendChild(shakeStyles);"},{"id":"scene-04","title":"攻击场景描述","steps":2,"html":"<div class=\"s-scene-04-wrapper\">\n  <div class=\"crt-lines\"></div>\n  <div class=\"ambient-glow\"></div>\n\n  <div class=\"system-layout\">\n    <!-- Left: Container -->\n    <div class=\"node-container\">\n      <div class=\"system-node box-node\">\n        <div class=\"node-label\">[ CONTAINER ]</div>\n        <div class=\"internal-process\">\n          <div class=\"cursor-blink\"></div>\n          <span class=\"mono-text\">UID: 1000</span>\n        </div>\n      </div>\n    </div>\n\n    <!-- SVG Layer for the Arrow -->\n    <div class=\"vector-layer\">\n      <svg viewBox=\"0 0 800 400\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n        <path id=\"attack-path\" d=\"M 240 200 L 560 200\" stroke=\"#FF3131\" stroke-width=\"4\" stroke-dasharray=\"320\" stroke-dashoffset=\"320\" />\n        <path id=\"arrow-head\" d=\"M 545 185 L 565 200 L 545 215\" stroke=\"#FF3131\" stroke-width=\"4\" opacity=\"0\" />\n      </svg>\n    </div>\n\n    <!-- Right: Host -->\n    <div class=\"node-container\">\n      <div class=\"system-node circle-node\" id=\"host-node\">\n        <div class=\"node-label\">[ HOST_SYSTEM ]</div>\n        <div class=\"internal-process\">\n          <span class=\"status-text\">KERNEL_IDLE</span>\n        </div>\n      </div>\n    </div>\n  </div>\n\n  <!-- Success Banner -->\n  <div class=\"success-banner\" data-step=\"1\">\n    <div class=\"glitch-box\">\n      <span class=\"success-text\">> 提权成功: ESCAPED_TO_ROOT</span>\n    </div>\n  </div>\n</div>","css":".s-scene-04-wrapper {\n  position: relative;\n  width: 100%;\n  height: 100%;\n  background: #000000;\n  overflow: hidden;\n  font-family: var(--sv-font-mono);\n  display: flex;\n  justify-content: center;\n  align-items: center;\n}\n\n.crt-lines {\n  position: absolute;\n  inset: 0;\n  background: repeating-linear-gradient(rgba(0,0,0,0) 0px, rgba(0,0,0,0) 2px, rgba(0,255,65,0.05) 3px, rgba(0,255,65,0.05) 4px);\n  pointer-events: none;\n  z-index: 10;\n}\n\n.ambient-glow {\n  position: absolute;\n  width: 100%;\n  height: 100%;\n  background: radial-gradient(circle at center, rgba(0,255,65,0.03) 0%, transparent 70%);\n  pointer-events: none;\n}\n\n.system-layout {\n  width: 100%;\n  max-width: 1200px;\n  display: flex;\n  justify-content: space-between;\n  align-items: center;\n  padding: 0 10%;\n  z-index: 5;\n}\n\n.system-node {\n  width: 280px;\n  height: 280px;\n  display: flex;\n  flex-direction: column;\n  justify-content: center;\n  align-items: center;\n  border: 2px solid rgba(0, 255, 65, 0.4);\n  background: rgba(0, 255, 65, 0.02);\n  transition: all 0.5s cubic-bezier(0.165, 0.84, 0.44, 1);\n  position: relative;\n}\n\n.box-node {\n  border-radius: 4px;\n}\n\n.circle-node {\n  border-radius: 50%;\n  border-color: rgba(255, 255, 255, 0.3);\n}\n\n.node-label {\n  position: absolute;\n  top: -40px;\n  color: rgba(0, 255, 65, 0.6);\n  font-size: 14px;\n  letter-spacing: 2px;\n}\n\n.internal-process {\n  display: flex;\n  align-items: center;\n  gap: 10px;\n}\n\n.mono-text, .status-text {\n  color: #00FF41;\n  font-size: 18px;\n}\n\n.cursor-blink {\n  width: 10px;\n  height: 20px;\n  background: #00FF41;\n  animation: cursor-blink 1s infinite steps(1);\n}\n\n.vector-layer {\n  position: absolute;\n  inset: 0;\n  pointer-events: none;\n}\n\n.vector-layer svg {\n  width: 100%;\n  height: 100%;\n}\n\n/* Step 0 Animation */\n.active[data-scene-id=\"scene-04\"] #attack-path {\n  transition: stroke-dashoffset 0.8s ease-in-out 0.3s;\n  stroke-dashoffset: 0;\n}\n\n.active[data-scene-id=\"scene-04\"] #arrow-head {\n  transition: opacity 0.2s ease 1.1s;\n  opacity: 1;\n}\n\n/* Step 1 Styles */\n[data-step=\"1\"] .circle-node {\n  border-color: #FF3131;\n  background: rgba(255, 49, 49, 0.1);\n  box-shadow: 0 0 30px rgba(255, 49, 49, 0.3);\n  transform: scale(1.05);\n}\n\n[data-step=\"1\"] .circle-node .status-text {\n  color: #FF3131;\n  animation: text-warning 0.5s infinite alternate;\n}\n\n.success-banner {\n  position: absolute;\n  bottom: 10%;\n  opacity: 0;\n  transform: translateY(20px);\n  transition: all 0.5s ease;\n}\n\n.active .success-banner[data-step=\"1\"] {\n  opacity: 1;\n  transform: translateY(0);\n}\n\n.glitch-box {\n  border: 1px solid #FF3131;\n  padding: 15px 40px;\n  background: rgba(0, 0, 0, 0.9);\n  box-shadow: 4px 4px 0px #FF3131;\n}\n\n.success-text {\n  color: #FF3131;\n  font-size: 28px;\n  font-weight: bold;\n  letter-spacing: 2px;\n}\n\n@keyframes cursor-blink {\n  0%, 100% { opacity: 1; }\n  50% { opacity: 0; }\n}\n\n@keyframes text-warning {\n  from { opacity: 1; text-shadow: 0 0 5px #FF3131; }\n  to { opacity: 0.7; text-shadow: 0 0 15px #FF3131; }\n}","js":"window.SceneLifecycle.register('scene-04', {\n  enter() {\n    const sceneRoot = document.querySelector('[data-scene-id=\"scene-04\"]');\n    if (sceneRoot) {\n      sceneRoot.classList.add('active');\n      // Reset paths for re-entry\n      const path = sceneRoot.querySelector('#attack-path');\n      const head = sceneRoot.querySelector('#arrow-head');\n      if(path) path.style.strokeDashoffset = '320';\n      if(head) head.style.opacity = '0';\n    }\n  },\n  step(step) {\n    const sceneRoot = document.querySelector('[data-scene-id=\"scene-04\"]');\n    if (!sceneRoot) return;\n    \n    const hostNode = sceneRoot.querySelector('#host-node');\n    const statusText = sceneRoot.querySelector('.status-text');\n    \n    if (step === 1) {\n      if (statusText) statusText.textContent = 'SYSTEM_COMPROMISED';\n      // Add a small shake effect on success\n      sceneRoot.style.animation = 'privilege-shake 0.3s ease-in-out';\n      setTimeout(() => { sceneRoot.style.animation = ''; }, 300);\n    } else {\n      if (statusText) statusText.textContent = 'KERNEL_IDLE';\n    }\n  },\n  leave() {\n    const sceneRoot = document.querySelector('[data-scene-id=\"scene-04\"]');\n    if (sceneRoot) sceneRoot.classList.remove('active');\n  }\n});\n\nconst style = document.createElement('style');\nstyle.innerHTML = `\n@keyframes privilege-shake {\n  0% { transform: translate(0,0); }\n  25% { transform: translate(-4px, 4px); }\n  50% { transform: translate(4px, -4px); }\n  75% { transform: translate(-4px, -4px); }\n  100% { transform: translate(0,0); }\n}`;\ndocument.head.appendChild(style);"},{"id":"scene-05","title":"原理科普：Page Cache","steps":3,"html":"<div class=\"s-scene-05-wrapper\">\n  <div class=\"crt-scanlines\"></div>\n  <div class=\"ambient-phosphor\"></div>\n\n  <!-- Step 0: Memory Container -->\n  <div class=\"memory-container\">\n    <div class=\"memory-label\">[ PHYSICAL_RAM_MAPPING ]</div>\n    <div class=\"memory-grid\"></div>\n\n    <!-- Step 1: Page Cache Card -->\n    <div class=\"page-cache-card\" data-step=\"1\">\n      <div class=\"card-header\">\n        <span class=\"card-title\">PAGE_CACHE_OBJECT</span>\n        <span class=\"memory-addr\">0x7FFF0042</span>\n      </div>\n      <div class=\"binary-content\">\n        <div class=\"binary-line\">10110010 11100001 00011101</div>\n        <div class=\"binary-line\">11001010 01111100 11010001</div>\n        <div class=\"binary-line\">00110101 10100011 00011110</div>\n        <div class=\"binary-line\">11110000 00001111 10101010</div>\n      </div>\n      \n      <!-- Step 2: Security Overlay -->\n      <div class=\"security-status\" data-step=\"2\">\n        <div class=\"lock-overlay\">\n          <i data-lucide=\"lock\"></i>\n          <span class=\"read-only-text\">READ_ONLY</span>\n        </div>\n        <div class=\"forbidden-action\">\n          <div class=\"action-item\">\n            <span class=\"action-label\">WRITE_REQ</span>\n            <div class=\"forbidden-sign\"></div>\n          </div>\n          <div class=\"action-item\">\n            <span class=\"action-label\">MODIFY_REQ</span>\n            <div class=\"forbidden-sign\"></div>\n          </div>\n        </div>\n      </div>\n    </div>\n  </div>\n\n  <!-- Background Text -->\n  <div class=\"ghost-text\">KERNEL_SPACE</div>\n</div>","css":".s-scene-05-wrapper {\n  position: relative;\n  width: 100%;\n  height: 100%;\n  background: #000000;\n  overflow: hidden;\n  display: flex;\n  justify-content: center;\n  align-items: center;\n  font-family: var(--sv-font-mono);\n  color: #00FF41;\n}\n\n.crt-scanlines {\n  position: absolute;\n  inset: 0;\n  background: repeating-linear-gradient(rgba(0,0,0,0) 0px, rgba(0,0,0,0) 2px, rgba(0,255,65,0.05) 3px, rgba(0,255,65,0.05) 4px);\n  pointer-events: none;\n  z-index: 10;\n}\n\n.ambient-phosphor {\n  position: absolute;\n  width: 100%;\n  height: 100%;\n  background: radial-gradient(circle at 50% 50%, rgba(0, 255, 65, 0.04) 0%, transparent 80%);\n  pointer-events: none;\n}\n\n.memory-container {\n  position: relative;\n  width: 80%;\n  height: 70%;\n  border: 1px solid rgba(0, 255, 65, 0.2);\n  background: rgba(0, 255, 65, 0.02);\n  padding: 40px;\n  box-sizing: border-box;\n  display: flex;\n  justify-content: center;\n  align-items: center;\n}\n\n.memory-label {\n  position: absolute;\n  top: 15px;\n  left: 20px;\n  font-size: 14px;\n  opacity: 0.6;\n  letter-spacing: 2px;\n}\n\n.memory-grid {\n  position: absolute;\n  inset: 0;\n  opacity: 0.1;\n  background-image: radial-gradient(#00FF41 1px, transparent 1px);\n  background-size: 30px 30px;\n}\n\n.page-cache-card {\n  width: 480px;\n  height: 300px;\n  background: rgba(0, 20, 0, 0.9);\n  border: 2px solid #00FF41;\n  box-shadow: 0 0 30px rgba(0, 255, 65, 0.2);\n  padding: 20px;\n  position: relative;\n  z-index: 5;\n  opacity: 0;\n  transform: scale(0.9) translateY(20px);\n  transition: all 0.6s cubic-bezier(0.16, 1, 0.3, 1);\n}\n\n.page-cache-card[data-step=\"1\"] {\n  opacity: 1;\n  transform: scale(1) translateY(0);\n}\n\n.card-header {\n  border-bottom: 1px solid rgba(0, 255, 65, 0.3);\n  padding-bottom: 10px;\n  margin-bottom: 20px;\n  display: flex;\n  justify-content: space-between;\n  align-items: center;\n}\n\n.card-title {\n  font-size: 20px;\n  font-weight: bold;\n}\n\n.memory-addr {\n  font-size: 14px;\n  opacity: 0.7;\n}\n\n.binary-content {\n  font-size: 24px;\n  line-height: 1.5;\n  letter-spacing: 4px;\n  color: rgba(0, 255, 65, 0.8);\n}\n\n.security-status {\n  position: absolute;\n  inset: 0;\n  background: rgba(0, 0, 0, 0.7);\n  backdrop-filter: blur(2px);\n  display: flex;\n  flex-direction: column;\n  justify-content: center;\n  align-items: center;\n  opacity: 0;\n  transition: opacity 0.5s ease;\n}\n\n.security-status[data-step=\"2\"] {\n  opacity: 1;\n}\n\n.lock-overlay {\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  gap: 10px;\n  margin-bottom: 30px;\n  color: #39FF14;\n}\n\n.lock-overlay i {\n  width: 64px;\n  height: 64px;\n}\n\n.read-only-text {\n  font-size: 32px;\n  font-weight: 800;\n  letter-spacing: 4px;\n  border: 2px solid #39FF14;\n  padding: 5px 20px;\n}\n\n.forbidden-action {\n  display: flex;\n  gap: 40px;\n}\n\n.action-item {\n  position: relative;\n  padding: 5px 15px;\n  opacity: 0.6;\n}\n\n.forbidden-sign {\n  position: absolute;\n  top: 50%;\n  left: 50%;\n  width: 120%;\n  height: 2px;\n  background: #FF3131;\n  transform: translate(-50%, -50%) rotate(-25deg);\n  box-shadow: 0 0 10px #FF3131;\n}\n\n.ghost-text {\n  position: absolute;\n  bottom: 5%;\n  right: 5%;\n  font-size: 80px;\n  opacity: 0.03;\n  font-weight: 900;\n  pointer-events: none;\n}\n\n@keyframes pulse-border {\n  0% { border-color: rgba(0, 255, 65, 0.4); }\n  50% { border-color: rgba(0, 255, 65, 1); }\n  100% { border-color: rgba(0, 255, 65, 0.4); }\n}","js":"window.SceneLifecycle.register('scene-05', {\n  enter() {\n    const root = document.querySelector('[data-scene-id=\"scene-05\"]');\n    const lines = root.querySelectorAll('.binary-line');\n    \n    // Randomize binary streams slightly\n    lines.forEach(line => {\n      setInterval(() => {\n        if (Math.random() > 0.7) {\n          const chars = line.innerText.split('');\n          const idx = Math.floor(Math.random() * chars.length);\n          if (chars[idx] === '0') chars[idx] = '1';\n          else if (chars[idx] === '1') chars[idx] = '0';\n          line.innerText = chars.join('');\n        }\n      }, 200);\n    });\n\n    window.__SVGVIDEO_REFRESH_ICONS(root);\n  },\n  step(n) {\n    const root = document.querySelector('[data-scene-id=\"scene-05\"]');\n    const card = root.querySelector('.page-cache-card');\n    const security = root.querySelector('.security-status');\n\n    if (n >= 1) {\n      card.setAttribute('data-step', '1');\n    } else {\n      card.removeAttribute('data-step');\n    }\n\n    if (n >= 2) {\n      security.setAttribute('data-step', '2');\n      // Add a slight glitch effect when locked\n      card.style.animation = 'privilege-shake 0.2s 1';\n      setTimeout(() => card.style.animation = '', 200);\n    } else {\n      security.removeAttribute('data-step');\n    }\n  },\n  leave() {}\n});"},{"id":"scene-06","title":"利用机制：AF_ALG","steps":2,"html":"<div class=\"s-scene-06-wrapper\">\n  <div class=\"crt-overlay\"></div>\n  <div class=\"ambient-glow\"></div>\n\n  <!-- Step 0: Terminal Window -->\n  <div class=\"terminal-container\" id=\"term-06\">\n    <div class=\"term-header\">\n      <div class=\"term-controls\"><span></span><span></span><span></span></div>\n      <div class=\"term-title\">K-SHELL v4.1</div>\n    </div>\n    <div class=\"term-content\">\n      <div class=\"typing-line\" data-delay=\"1\">$ modprobe af_alg</div>\n      <div class=\"typing-line\" data-delay=\"2\">$ call <span class=\"highlight\">splice()</span></div>\n      <div class=\"typing-line blinking-cursor\">_</div>\n    </div>\n  </div>\n\n  <!-- Step 1: Conduit & Target File -->\n  <div class=\"conduit-system\">\n    <svg class=\"conduit-svg\" viewBox=\"0 0 1000 600\">\n      <path id=\"conduit-path\" d=\"M280,300 L450,300\" fill=\"none\" stroke=\"#00FF41\" stroke-width=\"4\" stroke-dasharray=\"1000\" stroke-dashoffset=\"1000\" />\n      <circle cx=\"280\" cy=\"300\" r=\"5\" fill=\"#00FF41\" class=\"node-point\" />\n    </svg>\n    <div class=\"file-node\" id=\"su-file-node\">\n      <div class=\"file-icon-box\">\n        <iconify-icon icon=\"mdi:file-lock-outline\"></iconify-icon>\n      </div>\n      <div class=\"file-info\">\n        <span class=\"file-name\">/usr/bin/su</span>\n        <span class=\"file-perm\">ROOT_SUID: 4755</span>\n      </div>\n    </div>\n  </div>\n\n  <!-- Step 2: Encryption Flow & Chains -->\n  <div class=\"pipeline-stage\" id=\"encrypt-stage\">\n    <div class=\"pipeline-box\">\n      <div class=\"pipeline-label\">[ KERNEL_CRYPTO_API ]</div>\n      <div class=\"chain-container\">\n         <div class=\"chain-link\"></div>\n         <div class=\"chain-link\"></div>\n         <div class=\"chain-link\"></div>\n      </div>\n      <div class=\"flow-indicator\">ENCRYPTION_IN_PROGRESS</div>\n    </div>\n  </div>\n\n  <div class=\"side-status\">\n    <div class=\"status-item\" data-step=\"1\">> CHANNEL_ESTABLISHED</div>\n    <div class=\"status-item\" data-step=\"2\">> FILE_INGESTION: FORCE</div>\n  </div>\n</div>","css":".s-scene-06-wrapper {\n  position: relative;\n  width: 100%;\n  height: 100%;\n  background: #000000;\n  overflow: hidden;\n  display: flex;\n  align-items: center;\n  justify-content: space-around;\n  font-family: var(--sv-font-mono);\n  color: #00FF41;\n}\n\n.crt-overlay {\n  position: absolute;\n  inset: 0;\n  background: repeating-linear-gradient(rgba(0,0,0,0) 0px, rgba(0,0,0,0) 2px, rgba(0,255,65,0.08) 3px, rgba(0,255,65,0.08) 4px);\n  pointer-events: none;\n  z-index: 100;\n}\n\n.ambient-glow {\n  position: absolute;\n  width: 100%;\n  height: 100%;\n  background: radial-gradient(circle at 50% 50%, rgba(0, 255, 65, 0.03) 0%, transparent 70%);\n  pointer-events: none;\n}\n\n.terminal-container {\n  width: 320px;\n  height: 200px;\n  border: 1px solid rgba(0, 255, 65, 0.4);\n  background: rgba(0, 20, 0, 0.8);\n  box-shadow: 0 0 15px rgba(0, 255, 65, 0.1);\n  z-index: 10;\n  transform: translateX(-100px);\n  opacity: 0;\n  transition: all 0.6s cubic-bezier(0.16, 1, 0.3, 1);\n}\n\n.term-header {\n  background: rgba(0, 255, 65, 0.15);\n  padding: 6px 10px;\n  display: flex;\n  justify-content: space-between;\n  align-items: center;\n  border-bottom: 1px solid rgba(0, 255, 65, 0.3);\n}\n\n.term-controls {\n  display: flex;\n  gap: 6px;\n}\n\n.term-controls span {\n  width: 8px;\n  height: 8px;\n  border: 1px solid #00FF41;\n  border-radius: 50%;\n}\n\n.term-title {\n  font-size: 10px;\n  opacity: 0.7;\n}\n\n.term-body {\n  padding: 15px;\n  font-size: 14px;\n}\n\n.typing-line {\n  margin-bottom: 5px;\n  overflow: hidden;\n  white-space: nowrap;\n}\n\n.highlight { color: #39FF14; text-shadow: 0 0 5px #00FF41; }\n\n.conduit-system {\n  position: absolute;\n  width: 100%;\n  height: 100%;\n  pointer-events: none;\n}\n\n.conduit-svg {\n  position: absolute;\n  top: 0; left: 0; width: 100%; height: 100%;\n}\n\n.file-node {\n  position: absolute;\n  left: 50%;\n  top: 50%;\n  transform: translate(-50%, -50%) scale(0.9);\n  opacity: 0;\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  gap: 10px;\n  z-index: 20;\n  transition: all 0.5s ease;\n}\n\n.file-icon-box {\n  font-size: 64px;\n  border: 2px solid #00FF41;\n  padding: 15px;\n  background: rgba(0, 255, 65, 0.05);\n  box-shadow: 0 0 20px rgba(0, 255, 65, 0.2);\n}\n\n.file-name {\n  font-size: 20px;\n  font-weight: bold;\n}\n\n.file-perm {\n  font-size: 12px;\n  opacity: 0.6;\n}\n\n.pipeline-stage {\n  width: 300px;\n  height: 250px;\n  border: 2px dashed rgba(0, 255, 65, 0.3);\n  position: relative;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  opacity: 0;\n  transform: translateX(50px);\n  transition: all 0.6s ease;\n}\n\n.pipeline-box {\n  width: 90%;\n  height: 90%;\n  background: rgba(0, 255, 65, 0.02);\n  border: 1px solid #00FF41;\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  justify-content: center;\n  gap: 20px;\n}\n\n.pipeline-label {\n  position: absolute;\n  top: -12px;\n  background: #000;\n  padding: 0 10px;\n  font-size: 14px;\n}\n\n.chain-container {\n  display: flex;\n  gap: 5px;\n  opacity: 0;\n}\n\n.chain-link {\n  width: 15px;\n  height: 30px;\n  border: 2px solid #00FF41;\n  border-radius: 10px;\n}\n\n.flow-indicator {\n  font-size: 12px;\n  animation: blink 0.8s infinite;\n}\n\n.side-status {\n  position: absolute;\n  bottom: 40px;\n  right: 40px;\n  text-align: right;\n}\n\n.status-item {\n  font-size: 14px;\n  opacity: 0;\n  margin-top: 5px;\n}\n\n@keyframes shake {\n  0%, 100% { transform: translate(-50%, -50%); }\n  20% { transform: translate(-52%, -48%); }\n  40% { transform: translate(-48%, -52%); }\n  60% { transform: translate(-51%, -49%); }\n  80% { transform: translate(-49%, -51%); }\n}\n\n@keyframes blink {\n  0%, 100% { opacity: 1; }\n  50% { opacity: 0.3; }\n}\n\n.shaking { animation: shake 0.2s infinite; }\n\n[data-step=\"1\"] .status-item[data-step=\"1\"] { opacity: 1; }\n[data-step=\"2\"] .status-item[data-step=\"2\"] { opacity: 1; }","js":"window.SceneLifecycle.register('scene-06', {\n  enter() {\n    const root = document.querySelector('[data-scene-id=\"scene-06\"]');\n    const term = root.querySelector('#term-06');\n    const file = root.querySelector('#su-file-node');\n    \n    setTimeout(() => {\n      term.style.opacity = '1';\n      term.style.transform = 'translateX(0)';\n      file.style.opacity = '1';\n      file.style.transform = 'translate(-50%, -50%) scale(1)';\n    }, 300);\n\n    window.__SVGVIDEO_REFRESH_ICONS(root);\n  },\n  step(n) {\n    const root = document.querySelector('[data-scene-id=\"scene-06\"]');\n    const path = root.querySelector('#conduit-path');\n    const file = root.querySelector('#su-file-node');\n    const encrypt = root.querySelector('#encrypt-stage');\n    const chains = root.querySelector('.chain-container');\n\n    if (n === 1) {\n      path.style.transition = 'stroke-dashoffset 0.8s ease-out';\n      path.style.strokeDashoffset = '0';\n    }\n\n    if (n === 2) {\n      // Shake effect\n      file.classList.add('shaking');\n      \n      setTimeout(() => {\n        // Drag into encryption flow\n        encrypt.style.opacity = '1';\n        encrypt.style.transform = 'translateX(0)';\n        \n        file.style.transition = 'all 0.8s cubic-bezier(0.68, -0.55, 0.27, 1.55)';\n        file.style.left = '75%';\n        file.style.top = '50%';\n        file.style.transform = 'translate(-50%, -50%) scale(0.7)';\n        file.style.zIndex = '30';\n        \n        setTimeout(() => {\n          file.classList.remove('shaking');\n          chains.style.opacity = '1';\n          encrypt.style.boxShadow = '0 0 30px rgba(0, 255, 65, 0.4)';\n        }, 800);\n      }, 400);\n    }\n  },\n  leave() {}\n});"},{"id":"scene-07","title":"漏洞核心：错误的写权限","steps":2,"html":"<div class=\"s-scene-07-wrapper\">\n  <div class=\"crt-overlay\"></div>\n  <div class=\"ambient-glow\"></div>\n  \n  <!-- Alert Overlay (Step 2) -->\n  <div class=\"alert-pulse-overlay\" data-step=\"2\"></div>\n\n  <div class=\"layout-container\">\n    <!-- Left: Code Section -->\n    <div class=\"code-section\">\n      <div class=\"section-label\">2017 内核优化代码</div>\n      <div class=\"terminal-window\">\n        <div class=\"term-header\">\n           <div class=\"term-dots\"><span></span><span></span><span></span></div>\n           <div class=\"term-title\">mm/gup.c</div>\n        </div>\n        <div class=\"code-content\">\n          <pre><code><span class=\"line\">/* Optimization: No COW needed? */</span>\n<span class=\"line\">if (flags & FOLL_FORCE) {</span>\n<span class=\"line highlight-target\">  vma-&gt;vm_flags |= <span class=\"code-keyword\">VM_MAYWRITE</span>;</span>\n<span class=\"line\">  <span class=\"code-comment\">// Treat as writable buffer</span></span>\n<span class=\"line\">}</span></code></pre>\n        </div>\n      </div>\n    </div>\n\n    <!-- Middle: Conduit -->\n    <div class=\"conduit-container\">\n      <svg class=\"conduit-svg\" viewBox=\"0 0 200 100\">\n        <path class=\"arrow-path\" d=\"M0,50 L180,50\" fill=\"none\" stroke=\"#00FF41\" stroke-width=\"2\" />\n        <polygon points=\"180,45 195,50 180,55\" fill=\"#00FF41\" class=\"arrow-head\" />\n      </svg>\n    </div>\n\n    <!-- Right: Memory Page Section -->\n    <div class=\"memory-section\">\n      <div class=\"section-label\">物理内存页面</div>\n      <div class=\"page-frame\">\n        <div class=\"page-grid\">\n          <span></span><span></span><span></span><span></span>\n          <span></span><span></span><span></span><span></span>\n        </div>\n        <div class=\"permission-status\">\n          <div class=\"lock-container\" id=\"perm-lock\">\n             <div class=\"lock-icon\"><iconify-icon icon=\"mdi:lock-outline\"></iconify-icon></div>\n             <div class=\"lock-text\">只读 (RO)</div>\n          </div>\n          <div class=\"rw-status\" id=\"perm-rw\">\n             <div class=\"rw-glitch\">可写 (RW)</div>\n          </div>\n        </div>\n      </div>\n    </div>\n  </div>\n\n  <div class=\"status-bar\">\n    <div class=\"status-msg\">SYSTEM_OPTIMIZATION: ACTIVE</div>\n    <div class=\"status-msg\" data-step=\"1\">CRITICAL: PERMISSION_BYPASS_DETECTED</div>\n  </div>\n</div>","css":".s-scene-07-wrapper { width: 100%; height: 100%; background: #000; display: flex; align-items: center; justify-content: center; font-family: var(--sv-font-mono); color: #00FF41; overflow: hidden; position: relative; }\n.crt-overlay { position: absolute; inset: 0; background: repeating-linear-gradient(rgba(0,0,0,0) 0px, rgba(0,0,0,0) 2px, rgba(0,255,65,0.05) 3px, rgba(0,255,65,0.05) 4px); pointer-events: none; z-index: 10; }\n.ambient-glow { position: absolute; width: 100%; height: 100%; background: radial-gradient(circle at 50% 50%, rgba(0, 255, 65, 0.05) 0%, transparent 70%); }\n.layout-container { display: flex; align-items: center; justify-content: space-between; width: 90%; gap: 20px; z-index: 5; }\n.section-label { font-size: 14px; color: rgba(0,255,65,0.6); margin-bottom: 10px; text-transform: uppercase; letter-spacing: 2px; }\n.terminal-window { width: 420px; border: 1px solid rgba(0,255,65,0.3); background: rgba(0,20,0,0.9); box-shadow: 0 0 20px rgba(0,255,65,0.1); }\n.term-header { background: rgba(0,255,65,0.1); padding: 8px 12px; display: flex; align-items: center; border-bottom: 1px solid rgba(0,255,65,0.2); }\n.term-dots { display: flex; gap: 5px; margin-right: 15px; }\n.term-dots span { width: 6px; height: 6px; border: 1px solid #00FF41; border-radius: 50%; }\n.term-title { font-size: 12px; opacity: 0.8; }\n.code-content { padding: 20px; font-size: 16px; line-height: 1.5; }\n.highlight-target { background: rgba(0,255,65,0.2); border-left: 3px solid #00FF41; padding-left: 5px; }\n.code-keyword { color: #39FF14; font-weight: bold; }\n.code-comment { color: rgba(0,255,65,0.4); }\n.conduit-container { flex: 1; height: 100px; display: flex; align-items: center; justify-content: center; }\n.arrow-path { stroke-dasharray: 200; stroke-dashoffset: 200; transition: stroke-dashoffset 0.8s ease; }\n.memory-section { display: flex; flex-direction: column; align-items: center; }\n.page-frame { width: 280px; height: 180px; border: 2px solid #00FF41; position: relative; background: rgba(0,255,65,0.02); display: flex; flex-direction: column; justify-content: center; align-items: center; }\n.page-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 10px; opacity: 0.2; position: absolute; inset: 20px; }\n.page-grid span { border: 1px solid #00FF41; height: 20px; }\n.permission-status { position: relative; z-index: 2; text-align: center; }\n.lock-container { transition: all 0.4s ease; }\n.lock-icon { font-size: 48px; margin-bottom: 10px; }\n.lock-text { font-size: 18px; }\n.rw-status { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%) scale(0); opacity: 0; color: #FF3131; font-weight: 900; font-size: 42px; text-shadow: 0 0 15px rgba(255,49,49,0.8); transition: all 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275); }\n.shattered .lock-container { transform: scale(1.5) rotate(15deg); opacity: 0; filter: blur(5px); }\n.shattered .rw-status { transform: translate(-50%, -50%) scale(1); opacity: 1; }\n.alert-pulse-overlay { position: absolute; inset: 0; background: radial-gradient(circle, rgba(255,0,0,0.15) 0%, transparent 70%); opacity: 0; pointer-events: none; animation: pulse-red 1.5s infinite; }\n@keyframes pulse-red { 0%, 100% { opacity: 0; transform: scale(1); } 50% { opacity: 1; transform: scale(1.1); } }\n.status-bar { position: absolute; bottom: 30px; left: 50%; transform: translateX(-50%); display: flex; gap: 40px; }\n.status-msg { font-size: 12px; border: 1px solid rgba(0,255,65,0.3); padding: 4px 12px; background: rgba(0,20,0,0.8); }","js":"window.SceneLifecycle.register('scene-07', {\n  enter() {\n    const root = document.querySelector('[data-scene-id=\"scene-07\"]');\n    const arrow = root.querySelector('.arrow-path');\n    \n    // Initial entrance\n    setTimeout(() => {\n      arrow.style.strokeDashoffset = '0';\n    }, 500);\n  },\n  step(n) {\n    const root = document.querySelector('[data-scene-id=\"scene-07\"]');\n    const memFrame = root.querySelector('.page-frame');\n    const alertOverlay = root.querySelector('.alert-pulse-overlay');\n\n    if (n === 1) {\n      // Step 1: Shatter the Lock\n      memFrame.classList.add('shattered');\n      memFrame.style.borderColor = '#FF3131';\n      memFrame.style.boxShadow = '0 0 30px rgba(255,49,49,0.3)';\n    }\n\n    if (n === 2) {\n      // Step 2: Full Alert Pulse\n      alertOverlay.style.display = 'block';\n      document.body.style.animation = 'glitch-bg 0.1s 3';\n    }\n  },\n  leave() {\n    document.body.style.animation = 'none';\n  }\n});"},{"id":"scene-08","title":"精准写入过程","steps":4,"html":"<div class=\"s-scene-08-wrapper\">\n  <div class=\"crt-scanlines\"></div>\n  <div class=\"ambient-glow\"></div>\n\n  <!-- Zoomed Hex Grid Viewport -->\n  <div class=\"hex-viewport\">\n    <div class=\"viewport-header\">\n      <span class=\"addr-label\">OFFSET: 0x00007FFF</span>\n      <span class=\"status-tag\" id=\"mem-status\">[ MEMORY_READ_ONLY ]</span>\n    </div>\n    <div class=\"hex-grid-container\">\n      <div class=\"hex-grid\" id=\"main-hex-grid\">\n        <!-- Generated cells will be here -->\n      </div>\n    </div>\n  </div>\n\n  <!-- Flying Bytes (Step 0: Visible, Step 1: Animate) -->\n  <div class=\"missile-container\">\n    <div class=\"byte-missile\" data-target=\"target-1\" data-step=\"0\">0x53</div>\n    <div class=\"byte-missile\" data-target=\"target-2\" data-step=\"0\">0x55</div>\n    <div class=\"byte-missile\" data-target=\"target-3\" data-step=\"0\">0x49</div>\n    <div class=\"byte-missile\" data-target=\"target-4\" data-step=\"0\">0x44</div>\n  </div>\n\n  <!-- Alert Overlay (Step 2) -->\n  <div class=\"alert-overlay\" data-step=\"2\">\n    <div class=\"alert-box\">\n      <div class=\"alert-header\">CRITICAL_ERROR</div>\n      <div class=\"alert-content\">VALIDATION_FAILED: HASH_MISMATCH</div>\n      <div class=\"alert-sub\">ENCRYPTED_BLOCK_CORRUPTED</div>\n    </div>\n  </div>\n\n  <!-- Bottom Status -->\n  <div class=\"terminal-footer\">\n    <div class=\"cursor-line\"><span class=\"prompt\">$</span> <span class=\"typing-text\">patching su_binary...</span><span class=\"cursor\"></span></div>\n    <div class=\"progress-section\" data-step=\"1\">\n      <span class=\"label\">INJECTION:</span>\n      <span class=\"bar-bg\"><span class=\"bar-fill\" id=\"injection-bar\"></span></span>\n    </div>\n  </div>\n</div>","css":".s-scene-08-wrapper { width: 100%; height: 100%; background: #000; display: flex; align-items: center; justify-content: center; font-family: var(--sv-font-mono); color: #00FF41; overflow: hidden; position: relative; }\n.crt-scanlines { position: absolute; inset: 0; background: linear-gradient(rgba(18, 16, 16, 0) 50%, rgba(0, 0, 0, 0.15) 50%), linear-gradient(90deg, rgba(255, 0, 0, 0.03), rgba(0, 255, 0, 0.01), rgba(0, 0, 255, 0.03)); background-size: 100% 3px, 3px 100%; pointer-events: none; z-index: 20; }\n.ambient-glow { position: absolute; width: 100%; height: 100%; background: radial-gradient(circle, rgba(0, 255, 65, 0.05) 0%, transparent 80%); pointer-events: none; }\n\n.hex-viewport { width: 800px; height: 400px; border: 1px solid rgba(0, 255, 65, 0.3); background: rgba(0, 10, 0, 0.9); position: relative; box-shadow: 0 0 40px rgba(0, 0, 0, 0.8); transition: all 0.8s ease; transform: scale(1.1); }\n.viewport-header { background: rgba(0, 255, 65, 0.1); padding: 8px 15px; display: flex; justify-content: space-between; border-bottom: 1px solid rgba(0, 255, 65, 0.2); font-size: 12px; }\n.hex-grid-container { padding: 30px; display: flex; justify-content: center; }\n.hex-grid { display: grid; grid-template-columns: repeat(8, 70px); gap: 15px; }\n.hex-cell { height: 40px; display: flex; align-items: center; justify-content: center; border: 1px solid rgba(0, 255, 65, 0.1); font-size: 22px; color: #0088ff; transition: all 0.5s ease; position: relative; }\n.hex-cell.infected { color: #d400ff !important; border-color: #d400ff; text-shadow: 0 0 10px rgba(212, 0, 255, 0.5); }\n.hex-cell.target-active { background: rgba(0, 255, 65, 0.1); border-color: #00FF41; }\n\n.missile-container { position: absolute; inset: 0; pointer-events: none; }\n.byte-missile { position: absolute; font-size: 24px; font-weight: bold; background: #000; border: 2px solid #00FF41; padding: 5px 10px; box-shadow: 0 0 15px #00FF41; z-index: 10; transition: all 0.6s cubic-bezier(0.6, -0.28, 0.735, 0.045); }\n.byte-missile[data-step=\"0\"] { opacity: 1; transform: translate(var(--start-x), var(--start-y)); }\n.byte-missile.launched { transform: translate(var(--end-x), var(--end-y)) scale(0.8); opacity: 0.5; }\n\n.alert-overlay { position: absolute; inset: 0; z-index: 30; display: flex; align-items: center; justify-content: center; background: rgba(255, 0, 0, 0.1); backdrop-filter: blur(2px); }\n.alert-box { background: #000; border: 4px solid #FF3131; padding: 40px; text-align: center; box-shadow: 0 0 50px rgba(255, 49, 49, 0.4); animation: alert-shake 0.2s infinite; }\n.alert-header { color: #FF3131; font-weight: 900; font-size: 32px; letter-spacing: 4px; margin-bottom: 10px; }\n.alert-content { color: #FFF; font-size: 20px; border-top: 1px solid #FF3131; padding-top: 10px; }\n.alert-sub { font-size: 14px; opacity: 0.7; margin-top: 5px; }\n\n.terminal-footer { position: absolute; bottom: 40px; left: 100px; right: 100px; }\n.prompt { color: #00FF41; margin-right: 10px; }\n.typing-text { font-size: 18px; }\n.cursor { display: inline-block; width: 10px; height: 20px; background: #00FF41; animation: blink 1s infinite; vertical-align: middle; }\n.progress-section { margin-top: 15px; display: flex; align-items: center; gap: 15px; }\n.bar-bg { flex: 1; height: 10px; background: rgba(0, 255, 65, 0.1); border: 1px solid rgba(0, 255, 65, 0.3); }\n.bar-fill { display: block; width: 0%; height: 100%; background: #00FF41; transition: width 0.6s ease; }\n\n@keyframes alert-shake { 0% { transform: translate(1px, 1px); } 50% { transform: translate(-1px, -1px); } 100% { transform: translate(1px, -1px); } }\n@keyframes blink { 0%, 100% { opacity: 1; } 50% { opacity: 0; } }\n.modified-state .hex-viewport { border-color: #d400ff; box-shadow: 0 0 30px rgba(212, 0, 255, 0.2); }\n.modified-state #mem-status { color: #d400ff; content: \"[ MEMORY_MODIFIED_PERMANENT ]\"; }","js":"window.SceneLifecycle.register('scene-08', {\n  enter() {\n    const root = document.querySelector('[data-scene-id=\"scene-08\"]');\n    const grid = root.querySelector('#main-hex-grid');\n    const missiles = root.querySelectorAll('.byte-missile');\n\n    // Generate Background Hex Cells\n    grid.innerHTML = '';\n    for (let i = 0; i < 24; i++) {\n      const cell = document.createElement('div');\n      cell.className = 'hex-cell';\n      // Pre-defined target slots\n      if (i === 10) cell.id = 'target-1';\n      if (i === 11) cell.id = 'target-2';\n      if (i === 18) cell.id = 'target-3';\n      if (i === 19) cell.id = 'target-4';\n      \n      cell.textContent = Math.floor(Math.random() * 256).toString(16).toUpperCase().padStart(2, '0');\n      grid.appendChild(cell);\n    }\n\n    // Set Initial Missile Positions\n    const rects = [\n      { x: '10%', y: '20%' },\n      { x: '85%', y: '15%' },\n      { x: '5%', y: '75%' },\n      { x: '90%', y: '80%' }\n    ];\n    missiles.forEach((m, idx) => {\n      m.style.setProperty('--start-x', rects[idx].x);\n      m.style.setProperty('--start-y', rects[idx].y);\n    });\n  },\n  \n  step(n) {\n    const root = document.querySelector('[data-scene-id=\"scene-08\"]');\n    const missiles = root.querySelectorAll('.byte-missile');\n    const barFill = root.querySelector('#injection-bar');\n    const memStatus = root.querySelector('#mem-status');\n\n    if (n === 1) {\n      // Step 1: Launch missiles to targets\n      missiles.forEach((m) => {\n        const targetId = m.getAttribute('data-target');\n        const targetEl = root.querySelector(`#${targetId}`);\n        const targetRect = targetEl.getBoundingClientRect();\n        const rootRect = root.getBoundingClientRect();\n        \n        const endX = (targetRect.left - rootRect.left) + 'px';\n        const endY = (targetRect.top - rootRect.top) + 'px';\n        \n        m.style.setProperty('--end-x', endX);\n        m.style.setProperty('--end-y', endY);\n        m.classList.add('launched');\n        \n        setTimeout(() => {\n          targetEl.classList.add('target-active');\n          targetEl.textContent = m.textContent.replace('0x', '');\n        }, 600);\n      });\n      barFill.style.width = '100%';\n    }\n\n    if (n === 2) {\n       // Step 2 shows the Alert Overlay via CSS data-step\n       root.style.filter = 'hue-rotate(-10deg)';\n    }\n\n    if (n === 3) {\n      // Step 3: Persistence - Blue to Purple\n      root.classList.add('modified-state');\n      const targets = root.querySelectorAll('.hex-cell');\n      targets.forEach((cell, idx) => {\n        setTimeout(() => {\n          cell.classList.add('infected');\n          if (idx % 3 === 0) cell.textContent = 'FF';\n        }, idx * 20);\n      });\n      memStatus.textContent = '[ MEMORY_MODIFIED_PERMANENT ]';\n    }\n  }\n});"},{"id":"scene-09","title":"获取Root权限","steps":3,"html":"<div class=\"s-scene-09-wrapper\">\n  <div class=\"crt-scanlines\"></div>\n  <div class=\"ambient-glow\"></div>\n\n  <!-- Terminal Interface Container -->\n  <div class=\"terminal-interface\">\n    <div class=\"terminal-header\">\n      <div class=\"terminal-buttons\">\n        <span></span><span></span><span></span>\n      </div>\n      <div class=\"terminal-title\">TERMINAL - su_exploit_session</div>\n    </div>\n\n    <div class=\"terminal-content\">\n      <!-- Input History -->\n      <div class=\"log-line\">[ OK ] Payload injected into memory segment 0x0804...</div>\n      <div class=\"log-line\">[ OK ] Hooking execution vector...</div>\n      \n      <!-- Main Prompt Line -->\n      <div class=\"prompt-area\">\n        <span class=\"prompt-char\" id=\"current-prompt\">$</span>\n        <span class=\"command-text\">whoami</span>\n        <span class=\"cursor\"></span>\n      </div>\n\n      <!-- Result Output (Step 0-1 Hidden) -->\n      <div class=\"result-output\" data-step=\"2\">\n        <div class=\"root-banner\" id=\"root-text-container\">\n          <!-- root text will be typed here -->\n        </div>\n      </div>\n    </div>\n\n    <!-- Bottom Status Bar -->\n    <div class=\"terminal-footer-bar\">\n      <div class=\"status-item\">STATUS: <span class=\"blink-text\">RUNNING</span></div>\n      <div class=\"status-item\">PRIVILEGE: <span id=\"priv-level\">USER</span></div>\n      <div class=\"status-item\" data-step=\"2\">UID: 0 (root)</div>\n    </div>\n  </div>\n\n  <!-- Flash Overlay for Step 1 -->\n  <div class=\"lightning-flash\" id=\"flash-overlay\"></div>\n\n  <!-- Success Glow Frame (Step 2) -->\n  <div class=\"success-frame\" data-step=\"2\"></div>\n</div>","css":".s-scene-09-wrapper { width: 100%; height: 100%; background: #000; font-family: var(--sv-font-mono); color: #00FF41; display: flex; align-items: center; justify-content: center; position: relative; overflow: hidden; }\n.crt-scanlines { position: absolute; inset: 0; background: linear-gradient(rgba(0, 255, 65, 0.05) 50%, transparent 50%); background-size: 100% 4px; pointer-events: none; z-index: 10; }\n.ambient-glow { position: absolute; inset: 0; background: radial-gradient(circle at center, rgba(0, 255, 65, 0.03) 0%, transparent 70%); pointer-events: none; }\n\n.terminal-interface { width: 80%; max-width: 900px; height: 60%; background: rgba(0, 10, 0, 0.95); border: 1px solid rgba(0, 255, 65, 0.3); box-shadow: 0 20px 50px rgba(0, 0, 0, 0.8); display: flex; flex-direction: column; position: relative; z-index: 5; }\n.terminal-header { height: 30px; background: rgba(0, 255, 65, 0.1); border-bottom: 1px solid rgba(0, 255, 65, 0.2); display: flex; align-items: center; padding: 0 15px; }\n.terminal-buttons { display: flex; gap: 6px; margin-right: 20px; }\n.terminal-buttons span { width: 8px; height: 8px; border: 1px solid rgba(0, 255, 65, 0.4); border-radius: 50%; }\n.terminal-title { font-size: 12px; opacity: 0.7; letter-spacing: 1px; }\n\n.terminal-content { flex: 1; padding: 30px; overflow: hidden; position: relative; }\n.log-line { font-size: 14px; opacity: 0.4; margin-bottom: 8px; font-weight: 300; }\n.prompt-area { font-size: 32px; display: flex; align-items: center; margin-top: 20px; }\n.prompt-char { margin-right: 15px; color: #39FF14; font-weight: bold; }\n.command-text { letter-spacing: 2px; }\n.cursor { display: inline-block; width: 15px; height: 32px; background: #00FF41; margin-left: 10px; animation: blink 1s infinite steps(2); }\n\n.result-output { margin-top: 40px; display: flex; justify-content: center; }\n.root-banner { font-size: 120px; font-weight: 900; letter-spacing: 15px; text-transform: uppercase; text-shadow: 0 0 30px rgba(0, 255, 65, 0.8); }\n\n.terminal-footer-bar { height: 25px; background: rgba(0, 255, 65, 0.1); display: flex; align-items: center; padding: 0 15px; gap: 30px; font-size: 10px; border-top: 1px solid rgba(0, 255, 65, 0.2); }\n.blink-text { animation: blink 0.5s infinite; }\n\n.lightning-flash { position: absolute; inset: 0; background: #00FF41; opacity: 0; pointer-events: none; z-index: 100; }\n.success-frame { position: absolute; inset: 10px; border: 4px solid #00FF41; box-shadow: inset 0 0 50px rgba(0, 255, 65, 0.3), 0 0 30px rgba(0, 255, 65, 0.2); pointer-events: none; z-index: 101; opacity: 0; transform: scale(1.02); transition: all 0.3s ease; }\n.success-frame[data-step=\"2\"] { opacity: 1; transform: scale(1); }\n\n@keyframes blink { 0% { opacity: 1; } 100% { opacity: 0; } }\n@keyframes glitch-flash { 0% { opacity: 0; } 10% { opacity: 0.8; } 20% { opacity: 0.1; } 30% { opacity: 0.9; } 100% { opacity: 0; } }","js":"window.SceneLifecycle.register('scene-09', {\n  enter() {\n    const sceneRoot = document.querySelector('[data-scene-id=\"scene-09\"]');\n    const rootContainer = sceneRoot.querySelector('#root-text-container');\n    rootContainer.innerHTML = '';\n  },\n\n  step(n) {\n    const sceneRoot = document.querySelector('[data-scene-id=\"scene-09\"]');\n    const promptChar = sceneRoot.querySelector('#current-prompt');\n    const flash = sceneRoot.querySelector('#flash-overlay');\n    const privLevel = sceneRoot.querySelector('#priv-level');\n    const rootContainer = sceneRoot.querySelector('#root-text-container');\n\n    if (n === 1) {\n      // Step 1: Lightning Flash and Prompt Change\n      flash.style.animation = 'glitch-flash 0.4s ease-out forwards';\n      setTimeout(() => {\n        promptChar.textContent = '#';\n        privLevel.textContent = 'ROOT_ACCESS';\n        privLevel.style.color = '#FFF';\n        privLevel.style.textShadow = '0 0 10px #00FF41';\n      }, 150);\n    }\n\n    if (n === 2) {\n      // Step 2: Big 'root' typewriter effect\n      const text = 'root';\n      rootContainer.innerHTML = '';\n      let i = 0;\n      \n      const typeChar = () => {\n        if (i < text.length) {\n          const span = document.createElement('span');\n          span.textContent = text[i];\n          span.style.opacity = '0';\n          span.style.transform = 'scale(1.5)';\n          span.style.display = 'inline-block';\n          rootContainer.appendChild(span);\n          \n          // Trigger individual character pop\n          requestAnimationFrame(() => {\n            span.style.transition = 'all 0.15s cubic-bezier(0.175, 0.885, 0.32, 1.275)';\n            span.style.opacity = '1';\n            span.style.transform = 'scale(1)';\n          });\n          \n          i++;\n          setTimeout(typeChar, 100);\n        }\n      };\n      \n      typeChar();\n    }\n  }\n});"},{"id":"scene-10","title":"防御与总结","steps":3,"html":"<div class=\"s-scene-10-wrapper\">\n  <div class=\"crt-scanlines\"></div>\n  <div class=\"ambient-glow\"></div>\n\n  <div class=\"thirds-container\">\n    <!-- Step 0: Left Column - Patch Info -->\n    <div class=\"col-left\" data-step=\"0\">\n      <div class=\"status-box\">\n        <div class=\"status-header\">SYSTEM_ALERT</div>\n        <div class=\"patch-icon-wrapper\">\n          <iconify-icon icon=\"mdi:shield-sync-outline\" class=\"main-patch-icon\"></iconify-icon>\n          <div class=\"update-text-glitch\">UPDATE NOW</div>\n        </div>\n        <div class=\"status-details\">\n          <div class=\"status-line\">> KERNEL: VULNERABLE</div>\n          <div class=\"status-line blinking\">> PATCH: AVAILABLE</div>\n        </div>\n      </div>\n    </div>\n\n    <!-- Step 2: Center Column - Defused Bomb -->\n    <div class=\"col-center\" data-step=\"2\">\n      <div class=\"bomb-stage\">\n        <svg viewBox=\"0 0 200 200\" class=\"bomb-svg\">\n          <!-- Fuse -->\n          <path d=\"M120 40 Q 150 20, 160 60\" fill=\"none\" stroke=\"#00FF41\" stroke-width=\"4\" class=\"fuse-path\" />\n          <!-- Bomb Body -->\n          <circle cx=\"100\" cy=\"110\" r=\"60\" fill=\"none\" stroke=\"#00FF41\" stroke-width=\"6\" />\n          <rect x=\"85\" y=\"45\" width=\"30\" height=\"15\" fill=\"#000\" stroke=\"#00FF41\" stroke-width=\"4\" />\n          <!-- Cut Mark -->\n          <path d=\"M135 25 L 155 45 M 155 25 L 135 45\" stroke=\"#39FF14\" stroke-width=\"8\" class=\"cut-mark\" />\n        </svg>\n        <div class=\"threat-status\">THREAT_NEUTRALIZED</div>\n      </div>\n    </div>\n\n    <!-- Step 1: Right Column - Terminal Command -->\n    <div class=\"col-right\" data-step=\"1\">\n      <div class=\"command-window\">\n        <div class=\"window-bar\">\n          <span class=\"dot\"></span><span class=\"dot\"></span><span class=\"dot\"></span>\n          <span class=\"window-title\">bash - root</span>\n        </div>\n        <div class=\"terminal-body\">\n          <div class=\"shell-prompt\">\n            <span class=\"user\">root@system</span>:<span class=\"path\">~</span>#\n            <span class=\"typing-cmd\" id=\"cmd-text\"></span><span class=\"terminal-cursor\"></span>\n          </div>\n          <div class=\"shell-output\" data-step=\"1\">[ SUCCESS ] Module 'algif_aead' disabled.</div>\n        </div>\n      </div>\n    </div>\n  </div>\n\n  <div class=\"footer-deco\">SECURITY_ENFORCED // 0xPATCH_SUCCESS</div>\n</div>","css":".s-scene-10-wrapper { width: 100%; height: 100%; background: #000; font-family: var(--sv-font-mono); color: #00FF41; position: relative; overflow: hidden; }\n.crt-scanlines { position: absolute; inset: 0; background: linear-gradient(rgba(0, 255, 65, 0.08) 50%, transparent 50%); background-size: 100% 4px; pointer-events: none; z-index: 10; }\n.ambient-glow { position: absolute; inset: 0; background: radial-gradient(circle at center, rgba(0, 255, 65, 0.03) 0%, transparent 70%); pointer-events: none; }\n\n.thirds-container { display: flex; width: 100%; height: 100%; align-items: center; justify-content: space-between; padding: 0 5%; box-sizing: border-box; }\n.col-left, .col-center, .col-right { flex: 1; display: flex; flex-direction: column; align-items: center; transition: all 0.5s cubic-bezier(0.22, 1, 0.36, 1); }\n\n/* Left: Update Box */\n.status-box { border: 2px solid rgba(0, 255, 65, 0.4); padding: 30px; background: rgba(0, 255, 65, 0.05); text-align: center; width: 300px; }\n.status-header { font-size: 14px; letter-spacing: 2px; border-bottom: 1px solid rgba(0, 255, 65, 0.4); padding-bottom: 10px; margin-bottom: 20px; opacity: 0.8; }\n.main-patch-icon { font-size: 80px; filter: drop-shadow(0 0 10px rgba(0, 255, 65, 0.5)); }\n.update-text-glitch { font-size: 28px; font-weight: 900; margin-top: 15px; text-shadow: 2px 0 #39FF14; animation: blink 0.8s infinite; }\n.status-details { margin-top: 20px; text-align: left; font-size: 12px; line-height: 1.8; opacity: 0.7; }\n\n/* Right: Terminal */\n.command-window { width: 400px; height: 200px; background: #000; border: 1px solid rgba(0, 255, 65, 0.3); box-shadow: 0 10px 40px rgba(0, 0, 0, 0.8); }\n.window-bar { height: 25px; background: rgba(0, 255, 65, 0.1); display: flex; align-items: center; padding: 0 10px; gap: 5px; }\n.dot { width: 6px; height: 6px; border: 1px solid #00FF41; border-radius: 50%; }\n.window-title { font-size: 10px; margin-left: 10px; opacity: 0.6; }\n.terminal-body { padding: 15px; font-size: 16px; }\n.user { color: #39FF14; }\n.shell-output { margin-top: 15px; font-size: 12px; color: #39FF14; opacity: 0; transform: translateY(10px); transition: 0.4s; }\n[data-step=\"1\"] .shell-output { opacity: 1; transform: translateY(0); }\n.terminal-cursor { display: inline-block; width: 8px; height: 18px; background: #00FF41; animation: blink 1s infinite steps(2); vertical-align: middle; margin-left: 4px; }\n\n/* Center: Bomb */\n.bomb-stage { position: relative; text-align: center; }\n.bomb-svg { width: 220px; height: 220px; }\n.fuse-path { stroke-dasharray: 100; stroke-dashoffset: 0; }\n.cut-mark { opacity: 0; transform: scale(0.5); transform-origin: center; transition: all 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275); }\n[data-step=\"2\"] .cut-mark { opacity: 1; transform: scale(1); }\n[data-step=\"2\"] .fuse-path { stroke-dasharray: 10 5; opacity: 0.5; }\n.threat-status { margin-top: 20px; font-size: 24px; font-weight: 900; letter-spacing: 4px; color: #39FF14; text-shadow: 0 0 15px rgba(0, 255, 65, 0.6); }\n\n.footer-deco { position: absolute; bottom: 30px; left: 50%; transform: translateX(-50%); font-size: 12px; opacity: 0.3; letter-spacing: 5px; }\n\n@keyframes blink { 0% { opacity: 1; } 100% { opacity: 0; } }\n@keyframes draw { to { stroke-dashoffset: 0; } }","js":"window.SceneLifecycle.register('scene-10', {\n  enter() {\n    const sceneRoot = document.querySelector('[data-scene-id=\"scene-10\"]');\n    if (!sceneRoot) return;\n    window.__SVGVIDEO_REFRESH_ICONS(sceneRoot);\n    \n    // Initial state\n    const cmdText = sceneRoot.querySelector('#cmd-text');\n    if (cmdText) cmdText.textContent = '';\n  },\n\n  step(n) {\n    const sceneRoot = document.querySelector('[data-scene-id=\"scene-10\"]');\n    if (!sceneRoot) return;\n\n    if (n === 1) {\n      const cmdText = sceneRoot.querySelector('#cmd-text');\n      const command = 'modprobe -r algif_aead';\n      let i = 0;\n      cmdText.textContent = '';\n      \n      const type = () => {\n        if (i < command.length) {\n          cmdText.textContent += command[i];\n          i++;\n          setTimeout(type, 50);\n        }\n      };\n      type();\n    }\n\n    if (n === 2) {\n      // Extra impact on defusal\n      const bombStage = sceneRoot.querySelector('.bomb-stage');\n      bombStage.style.transform = 'scale(1.1)';\n      setTimeout(() => {\n        bombStage.style.transform = 'scale(1)';\n      }, 200);\n    }\n  }\n});"},{"id":"scene-11","title":"结尾致谢","steps":2,"html":"<div class=\"s-scene-11-wrapper\">\n  <div class=\"crt-scanlines\"></div>\n  <div class=\"ambient-glow\"></div>\n\n  <!-- Step 0: Center Logo/Avatar -->\n  <div class=\"logo-overlay\" data-step=\"0\">\n    <div class=\"avatar-frame\">\n      <div class=\"avatar-glitch\"></div>\n      <svg viewBox=\"0 0 100 100\" class=\"terminal-logo\">\n        <rect x=\"20\" y=\"20\" width=\"60\" height=\"60\" stroke=\"#00FF41\" stroke-width=\"2\" fill=\"none\" />\n        <path d=\"M35 40 L50 55 L65 40\" stroke=\"#00FF41\" stroke-width=\"4\" fill=\"none\" />\n        <path d=\"M30 70 L70 70\" stroke=\"#00FF41\" stroke-width=\"2\" stroke-dasharray=\"4 2\" />\n      </svg>\n    </div>\n    <div class=\"channel-id\">[ CHANNEL_ID: ROOT_NODE ]</div>\n  </div>\n\n  <!-- Step 1: Interactive Actions & Outro -->\n  <div class=\"action-container\" data-step=\"1\">\n    <!-- Like Action -->\n    <div class=\"action-item like-item\">\n      <div class=\"action-box\">\n        <i data-lucide=\"thumbs-up\"></i>\n        <span class=\"action-label\">LIKE_SUCCESS</span>\n      </div>\n    </div>\n\n    <!-- Follow Action -->\n    <div class=\"action-item follow-item\">\n      <div class=\"action-box\">\n        <i data-lucide=\"user-plus\"></i>\n        <span class=\"action-label\">SUBSCRIBE_OK</span>\n      </div>\n    </div>\n\n    <!-- Closing Text -->\n    <div class=\"closing-text\">\n      <div class=\"rainbow-terminal-text\">下期再见</div>\n      <div class=\"cursor-blink\"></div>\n    </div>\n  </div>\n\n  <div class=\"system-footer\">\n    SESSION_TERMINATED: 0x00000000\n  </div>\n</div>","css":".s-scene-11-wrapper { width: 100%; height: 100%; background: #000; font-family: var(--sv-font-mono); color: #00FF41; position: relative; overflow: hidden; display: flex; flex-direction: column; align-items: center; justify-content: center; }\n.crt-scanlines { position: absolute; inset: 0; background: linear-gradient(rgba(0, 255, 65, 0.1) 50%, transparent 50%); background-size: 100% 3px; pointer-events: none; z-index: 100; opacity: 0.12; }\n.ambient-glow { position: absolute; inset: 0; background: radial-gradient(circle at center, rgba(0, 255, 65, 0.04) 0%, transparent 80%); animation: pulse 10s infinite; }\n\n.logo-overlay { text-align: center; z-index: 20; transition: transform 0.6s cubic-bezier(0.22, 1, 0.36, 1); }\n.avatar-frame { width: 120px; height: 120px; border: 2px solid #00FF41; position: relative; padding: 10px; box-shadow: 0 0 20px rgba(0, 255, 65, 0.15); display: inline-block; background: #000; }\n.terminal-logo { width: 100%; height: 100%; }\n.channel-id { margin-top: 15px; font-size: 14px; letter-spacing: 2px; opacity: 0.8; }\n\n.action-container { position: absolute; inset: 0; pointer-events: none; display: flex; align-items: center; justify-content: center; }\n.action-item { position: absolute; transition: all 0.6s cubic-bezier(0.175, 0.885, 0.32, 1.275); opacity: 0; transform: scale(0.5); }\n.like-item { left: 15%; }\n.follow-item { right: 15%; }\n[data-step=\"1\"] .action-item { opacity: 1; transform: scale(1); }\n.action-box { border: 1px solid #00FF41; background: rgba(0, 255, 65, 0.05); padding: 20px; display: flex; flex-direction: column; align-items: center; gap: 10px; }\n.action-box i { width: 40px; height: 40px; }\n.action-label { font-size: 12px; font-weight: bold; }\n\n.closing-text { position: absolute; bottom: 15%; text-align: center; opacity: 0; transform: translateY(20px); transition: all 0.8s ease; display: flex; align-items: center; gap: 10px; }\n[data-step=\"1\"] .closing-text { opacity: 1; transform: translateY(0); }\n.rainbow-terminal-text { font-size: 64px; font-family: var(--sv-font-display-cn); font-weight: 900; background: linear-gradient(90deg, #00FF41, #39FF14, #00FF41); -webkit-background-clip: text; color: transparent; animation: shift 4s linear infinite; text-shadow: 0 0 10px rgba(0, 255, 65, 0.3); }\n.cursor-blink { width: 25px; height: 60px; background: #00FF41; animation: blink 1s infinite steps(2); }\n\n.system-footer { position: absolute; bottom: 5%; font-size: 10px; opacity: 0.4; letter-spacing: 1px; }\n\n@keyframes pulse { 0%, 100% { opacity: 0.04; } 50% { opacity: 0.08; } }\n@keyframes blink { 0% { opacity: 1; } 100% { opacity: 0; } }\n@keyframes shift { 0% { background-position: 0% 50%; } 100% { background-position: 200% 50%; } }","js":"window.SceneLifecycle.register('scene-11', {\n  enter() {\n    const sceneRoot = document.querySelector('[data-scene-id=\"scene-11\"]');\n    if (!sceneRoot) return;\n    window.__SVGVIDEO_REFRESH_ICONS(sceneRoot);\n    \n    // Initial state setup\n    const logo = sceneRoot.querySelector('.logo-overlay');\n    logo.style.transform = 'scale(1.2)';\n    setTimeout(() => {\n      logo.style.transform = 'scale(1)';\n    }, 100);\n  },\n  \n  step(n) {\n    const sceneRoot = document.querySelector('[data-scene-id=\"scene-11\"]');\n    if (!sceneRoot) return;\n\n    if (n === 1) {\n      const logo = sceneRoot.querySelector('.logo-overlay');\n      // Shift logo up to make room for closing text\n      logo.style.transform = 'translateY(-40px)';\n      \n      // Elastic feedback effect on icons is handled by CSS cubic-bezier\n      // But we can trigger a slight rotation for extra \"Q-弹\" feel\n      const items = sceneRoot.querySelectorAll('.action-item');\n      items.forEach((item, idx) => {\n        item.style.transitionDelay = `${idx * 0.1}s`;\n      });\n    }\n  }\n});"}]</script>
  <script defer src="https://unpkg.com/lucide@latest"></script>
  <script defer src="https://code.iconify.design/iconify-icon/2.3.0/iconify-icon.min.js"></script>
  <script>
    (function () {
      var state = { sceneId: 'scene-10', step: 0, direction: 1 };
      var TRANSITION_PRESETS={'door':{'--sv-stage-perspective':'900px','--sv-trans-enter-opacity':'0','--sv-trans-enter-transform':'rotateY(90deg)','--sv-trans-enter-filter':'blur(6px)','--sv-trans-active-opacity':'1','--sv-trans-active-transform':'rotateY(0deg)','--sv-trans-active-filter':'blur(0px)','--sv-trans-exit-opacity':'0','--sv-trans-exit-transform':'rotateY(-90deg)','--sv-trans-exit-filter':'blur(4px)','--sv-trans-duration':'0.65s','--sv-trans-ease':'cubic-bezier(0.36, 0, 0.66, 1)'},'burst':{'--sv-stage-perspective':'none','--sv-trans-enter-opacity':'0','--sv-trans-enter-transform':'scale(0.1)','--sv-trans-enter-filter':'blur(40px)','--sv-trans-active-opacity':'1','--sv-trans-active-transform':'scale(1)','--sv-trans-active-filter':'blur(0px)','--sv-trans-exit-opacity':'0','--sv-trans-exit-transform':'scale(1.6)','--sv-trans-exit-filter':'blur(24px)','--sv-trans-duration':'0.55s','--sv-trans-ease':'cubic-bezier(0.34, 1.3, 0.64, 1)'},'slam':{'--sv-stage-perspective':'none','--sv-trans-enter-opacity':'0','--sv-trans-enter-transform':'translateX(var(--scene-enter-offset, 180px)) rotate(8deg)','--sv-trans-enter-filter':'blur(24px)','--sv-trans-active-opacity':'1','--sv-trans-active-transform':'translateX(0) rotate(0deg)','--sv-trans-active-filter':'blur(0px)','--sv-trans-exit-opacity':'0','--sv-trans-exit-transform':'translateX(var(--scene-exit-offset, -120px)) rotate(-5deg)','--sv-trans-exit-filter':'blur(18px)','--sv-trans-duration':'0.50s','--sv-trans-ease':'cubic-bezier(0.2, 0.8, 0.2, 1)'},'push-up':{'--sv-stage-perspective':'none','--sv-trans-enter-opacity':'1','--sv-trans-enter-transform':'translateY(105%)','--sv-trans-enter-filter':'blur(0px)','--sv-trans-active-opacity':'1','--sv-trans-active-transform':'translateY(0)','--sv-trans-active-filter':'blur(0px)','--sv-trans-exit-opacity':'0.3','--sv-trans-exit-transform':'translateY(-60px)','--sv-trans-exit-filter':'blur(8px)','--sv-trans-duration':'0.55s','--sv-trans-ease':'cubic-bezier(0.22, 1, 0.36, 1)'},'spin':{'--sv-stage-perspective':'none','--sv-trans-enter-opacity':'0','--sv-trans-enter-transform':'rotate(180deg) scale(0.2)','--sv-trans-enter-filter':'blur(24px)','--sv-trans-active-opacity':'1','--sv-trans-active-transform':'rotate(0deg) scale(1)','--sv-trans-active-filter':'blur(0px)','--sv-trans-exit-opacity':'0','--sv-trans-exit-transform':'rotate(-90deg) scale(0.4)','--sv-trans-exit-filter':'blur(16px)','--sv-trans-duration':'0.55s','--sv-trans-ease':'cubic-bezier(0.34, 1.3, 0.64, 1)'},'tunnel':{'--sv-stage-perspective':'none','--sv-trans-enter-opacity':'0','--sv-trans-enter-transform':'scale(3.0)','--sv-trans-enter-filter':'blur(50px)','--sv-trans-active-opacity':'1','--sv-trans-active-transform':'scale(1)','--sv-trans-active-filter':'blur(0px)','--sv-trans-exit-opacity':'0','--sv-trans-exit-transform':'scale(0.15)','--sv-trans-exit-filter':'blur(36px)','--sv-trans-duration':'0.60s','--sv-trans-ease':'cubic-bezier(0.22, 1, 0.36, 1)'},'glitch':{'--sv-stage-perspective':'none','--sv-trans-enter-opacity':'0','--sv-trans-enter-transform':'scale(0.95)','--sv-trans-enter-filter':'blur(2px)','--sv-trans-active-opacity':'1','--sv-trans-active-transform':'scale(1)','--sv-trans-active-filter':'blur(0px)','--sv-trans-exit-opacity':'0','--sv-trans-exit-transform':'scale(0.95)','--sv-trans-exit-filter':'none','--sv-trans-duration':'0.25s','--sv-trans-ease':'ease-out'},};
      // Per-scene transition mapping — built from meta at init time
      var sceneTransitionMap = {};
      var previousSceneId = null;
      var exitCleanupTimer = null;
      var isPlaying = false;
      var playTimer = null;
      var progressRaf = null;
      var STEP_INTERVAL_MS = 1400;
      var SCENE_FINISH_PAUSE_MS = 2000;
      var autoplayFromFirstScene = false;
      var progressWrap = document.getElementById('sv-progress-wrap');
      var progressFill = document.getElementById('sv-progress-fill');
      var progressThumb = document.getElementById('sv-progress-thumb');
      var playBtn = document.getElementById('sv-play-btn');
      var expandBtn = document.getElementById('sv-expand-btn');
      var statusText = document.getElementById('sv-status-text');
      var sceneButtonList = Array.prototype.slice.call(document.querySelectorAll('[data-panel-scene-id]'));
      var sceneRetryButtonList = Array.prototype.slice.call(document.querySelectorAll('[data-retry-scene-id]'));
      var editorOverlay = document.getElementById('sv-editor-overlay');
      var editorBackdrop = document.getElementById('sv-editor-backdrop');
      var editorSheet = document.getElementById('sv-editor-sheet');
      var editorResizeHandle = document.getElementById('sv-editor-resize-handle');
      var editorCloseBtn = document.getElementById('sv-editor-close-btn');
      var editorSaveBtn = document.getElementById('sv-editor-save-btn');
      var editorList = document.getElementById('sv-editor-list');
      var editorSaveTip = document.getElementById('sv-editor-save-tip');
      var editorTipTimer = null;
      var isEditorOpen = false;
      var isEditorDirty = false;
      var editorHeightVh = 50;
      var hostCaps = { canRetry: false, canEditPanel: false, locale: 'zh', theme: 'dark' };
      var metaJson = document.getElementById('__SVGVIDEO_META__');
      var meta = { scenes: [] };
      try {
        meta = JSON.parse((metaJson && metaJson.textContent) || '{}');
      } catch (e) {
        meta = { scenes: [] };
      }
      var metaSceneMap = {};
      var metaScenes = Array.isArray(meta.scenes) ? meta.scenes : [];
      metaScenes.forEach(function (scene) {
        if (scene && typeof scene.id === 'string' && scene.id) {
          metaSceneMap[scene.id] = scene;
          sceneTransitionMap[scene.id] = (typeof scene.transition === 'string' && TRANSITION_PRESETS[scene.transition]) ? scene.transition : 'door';
        }
      });
      function refreshFrameworkIcons(root) {
        var refreshRoot = root || document;
        try {
          if (window.lucide && typeof window.lucide.createIcons === 'function') {
            window.lucide.createIcons({ root: refreshRoot });
          }
        } catch (iconErr) {
          console.warn('[svg-video] lucide refresh failed', iconErr);
        }
      }
      window.__SVGVIDEO_REFRESH_ICONS = refreshFrameworkIcons;
      var sceneModels = Array.prototype.slice.call(document.querySelectorAll('#stage > [data-scene-id]')).map(function (node, index) {
        var sceneId = node.getAttribute('data-scene-id') || ('scene-' + (index + 1));
        var metaScene = metaSceneMap[sceneId] || {};
        var sceneTitle = typeof metaScene.title === 'string' && metaScene.title ? metaScene.title : ('分镜' + (index + 1));
        var sceneSteps = Math.max(1, Number(metaScene.steps) || 1);
        var sceneStatus = typeof metaScene.status === 'string' ? metaScene.status : 'pending';
        var scenePrompt = typeof metaScene.prompt === 'string' ? metaScene.prompt : '';
        var sceneScript = typeof metaScene.scriptText === 'string' ? metaScene.scriptText : '';
        var sceneAudioObjectKey = typeof metaScene.audioObjectKey === 'string' ? metaScene.audioObjectKey : '';
        var sceneAudioDurationMs = Number(metaScene.audioDurationMs) > 0 ? Number(metaScene.audioDurationMs) : 0;
        var sceneVoiceName = typeof metaScene.voiceName === 'string' ? metaScene.voiceName : '';
        var sceneVoiceStyle = typeof metaScene.voiceStyle === 'string' ? metaScene.voiceStyle : '';
        var relatedSceneIds = Array.isArray(metaScene.relatedSceneIds) ? metaScene.relatedSceneIds : [];
        return {
          id: sceneId,
          title: sceneTitle,
          steps: sceneSteps,
          status: sceneStatus,
          prompt: scenePrompt,
          scriptText: sceneScript,
          audioObjectKey: sceneAudioObjectKey,
          audioDurationMs: sceneAudioDurationMs,
          voiceName: sceneVoiceName,
          voiceStyle: sceneVoiceStyle,
          relatedSceneIds: relatedSceneIds,
          transition: sceneTransitionMap[sceneId] || 'door'
        };
      });
      if (!sceneModels.length && metaScenes.length) {
        sceneModels = metaScenes.map(function (scene, index) {
          return {
            id: scene.id || ('scene-' + (index + 1)),
            title: scene.title || ('分镜' + (index + 1)),
            steps: Math.max(1, Number(scene.steps) || 1),
            status: typeof scene.status === 'string' ? scene.status : 'pending',
            prompt: typeof scene.prompt === 'string' ? scene.prompt : '',
            scriptText: typeof scene.scriptText === 'string' ? scene.scriptText : '',
            audioObjectKey: typeof scene.audioObjectKey === 'string' ? scene.audioObjectKey : '',
            audioDurationMs: Number(scene.audioDurationMs) > 0 ? Number(scene.audioDurationMs) : 0,
            voiceName: typeof scene.voiceName === 'string' ? scene.voiceName : '',
            voiceStyle: typeof scene.voiceStyle === 'string' ? scene.voiceStyle : '',
            relatedSceneIds: Array.isArray(scene.relatedSceneIds) ? scene.relatedSceneIds : [],
            transition: sceneTransitionMap[scene.id] || 'door'
          };
        });
      }
      function i18nText(zh, en) {
        return hostCaps.locale === 'en' ? en : zh;
      }
      function applyI18nTexts() {
        var toolbarTitleEl = document.getElementById('sv-toolbar-title');
        var titleEl = document.getElementById('sv-editor-header-title');
        if (toolbarTitleEl) toolbarTitleEl.textContent = i18nText('分镜面板', 'Storyboard');
        if (titleEl) titleEl.textContent = i18nText('分镜详情编辑', 'Storyboard details');
        if (expandBtn) {
          var expandText = i18nText('展开分镜详情', 'Open storyboard details');
          expandBtn.setAttribute('aria-label', expandText);
          expandBtn.setAttribute('title', expandText);
        }
        if (editorSaveBtn) editorSaveBtn.textContent = isEditorDirty ? i18nText('保存*', 'Save*') : i18nText('保存', 'Save');
        if (editorCloseBtn) editorCloseBtn.textContent = i18nText('关闭', 'Close');
        if (editorSaveTip) editorSaveTip.textContent = i18nText('已保存', 'Saved');
      }
      function clampEditorHeightVh(value) {
        return Math.max(32, Math.min(80, Number(value) || 50));
      }
      function applyEditorHeight(vh) {
        editorHeightVh = clampEditorHeightVh(vh);
        document.documentElement.style.setProperty('--sv-editor-height', editorHeightVh + 'vh');
      }
      function persistMetaToScript() {
        meta.title = typeof meta.title === 'string' ? meta.title : '';
        meta.version = meta.version || 1;
        meta.scenes = sceneModels.map(function (scene) {
          return {
            id: scene.id,
            title: scene.title || '',
            steps: Math.max(1, Number(scene.steps) || 1),
            status: scene.status || 'pending',
            prompt: scene.prompt || '',
            scriptText: scene.scriptText || '',
            audioObjectKey: scene.audioObjectKey || '',
            audioDurationMs: Number(scene.audioDurationMs) > 0 ? Number(scene.audioDurationMs) : 0,
            voiceName: scene.voiceName || '',
            voiceStyle: scene.voiceStyle || '',
            relatedSceneIds: Array.isArray(scene.relatedSceneIds) ? scene.relatedSceneIds : [],
            transition: scene.transition || 'door'
          };
        });
        if (metaJson) metaJson.textContent = JSON.stringify(meta);
      }
      function showSavedTip() {
        if (!editorSaveTip) return;
        if (editorTipTimer) {
          clearTimeout(editorTipTimer);
          editorTipTimer = null;
        }
        editorSaveTip.classList.add('is-visible');
        editorTipTimer = setTimeout(function () {
          editorSaveTip.classList.remove('is-visible');
        }, 1200);
      }
      function markEditorDirty() {
        isEditorDirty = true;
        applyI18nTexts();
      }
      function clearEditorDirty() {
        isEditorDirty = false;
        applyI18nTexts();
      }
      function getStatusLabel(status) {
        if (status === 'success') return i18nText('已生成', 'Done');
        if (status === 'generating') return i18nText('生成中', 'Generating');
        if (status === 'failed') return i18nText('失败', 'Failed');
        return i18nText('未生成', 'Pending');
      }
      function getStatusClass(status) {
        if (status === 'generating') return 'is-generating';
        if (status === 'success') return 'is-success';
        return 'is-failed';
      }
      function getRetryButtonInnerHtml(isGenerating) {
        if (isGenerating) return '<span class="scene-chip-retry-spinner"></span>';
        return '<svg class="scene-chip-retry-icon" viewBox="0 0 1024 1024" width="16" height="16" fill="currentColor"><path d="M698.569 531.322a39.825 39.825 0 0 0-23.898 7.949c-17.702 13.216-21.34 38.28-8.124 55.982l95.716 128.212c7.854 10.521 19.893 16.073 32.084 16.073a39.825 39.825 0 0 0 23.898-7.949c17.702-13.216 21.34-38.28 8.124-55.982l-95.716-128.212c-7.853-10.521-19.892-16.074-32.084-16.073z" /><path d="M922.498 563.818a39.825 39.825 0 0 0-23.898 7.949l-128.212 95.716c-17.703 13.216-21.34 38.28-8.124 55.982 7.854 10.521 19.893 16.073 32.084 16.073a39.825 39.825 0 0 0 23.898-7.949l128.212-95.716c17.702-13.216 21.34-38.28 8.124-55.982-7.854-10.521-19.893-16.074-32.084-16.073zM229.652 282.148a39.825 39.825 0 0 0-23.898 7.949c-17.702 13.216-21.34 38.28-8.124 55.982l95.716 128.212c7.854 10.521 19.893 16.073 32.084 16.073a39.825 39.825 0 0 0 23.898-7.949c17.702-13.216 21.34-38.28 8.124-55.982l-95.716-128.212c-7.854-10.521-19.892-16.074-32.084-16.073z" /><path d="M229.652 282.148a39.825 39.825 0 0 0-23.898 7.949L77.542 385.813c-17.702 13.216-21.34 38.28-8.124 55.982 7.854 10.521 19.893 16.073 32.084 16.073a39.825 39.825 0 0 0 23.898-7.949l128.213-95.716c17.702-13.216 21.34-38.28 8.124-55.982-7.855-10.521-19.893-16.073-32.085-16.073z" /><path d="M614.968 763.65c-44.13 18.171-94.005 25.121-146.168 16.968C353.747 762.636 261.364 670.253 243.381 555.2c-9.681-61.936 1.932-120.646 28.337-170.344l-58.657-58.657c-40.139 64.442-60.11 142.746-50.806 226.152 17.993 161.306 148.088 291.401 309.394 309.394 62.041 6.92 121.259-2.357 174.048-24.083 26.535-10.921 33.625-45.203 13.335-65.493l-0.098-0.098c-11.555-11.555-28.854-14.643-43.966-8.421zM409.033 260.35c44.131-18.171 94.007-25.122 146.169-16.968 115.051 17.985 207.431 110.365 225.416 225.416 9.682 61.935-1.931 120.646-28.337 170.346l58.658 58.658c40.139-64.443 60.109-142.747 50.805-226.155-17.993-161.305-148.087-291.399-309.391-309.391-62.042-6.92-121.26 2.356-174.05 24.082-26.535 10.921-33.625 45.203-13.335 65.493l0.099 0.099c11.555 11.554 28.854 14.642 43.966 8.42z" /></svg>';
      }
      function setSceneStatus(sceneId, status) {
        var index = getSceneIndexById(sceneId);
        if (index < 0) return;
        sceneModels[index].status = status;
        persistMetaToScript();
        var statusClass = getStatusClass(status);
        var chipWrap = document.querySelector('[data-chip-scene-id="' + sceneId + '"]');
        if (chipWrap) {
          chipWrap.classList.remove('is-success', 'is-failed', 'is-generating');
          chipWrap.classList.add(statusClass);
          var chipBtn = chipWrap.querySelector('.scene-chip');
          if (chipBtn) {
            chipBtn.classList.remove('is-success', 'is-failed', 'is-generating');
            chipBtn.classList.add(statusClass);
          }
          var retryBtn = chipWrap.querySelector('.scene-chip-retry');
          if (retryBtn) {
            retryBtn.innerHTML = getRetryButtonInnerHtml(status === 'generating');
            if (status === 'generating') retryBtn.setAttribute('disabled', 'disabled');
            else retryBtn.removeAttribute('disabled');
          }
        }
        renderStatus();
      }
      function requestSceneRetry(sceneId) {
        if (!hostCaps.canRetry) return;
        var idx = getSceneIndexById(sceneId);
        if (idx < 0) return;
        var scene = sceneModels[idx];
        setSceneStatus(sceneId, 'generating');
        focusScene(sceneId);
        if (window.parent && window.parent !== window) {
          window.parent.postMessage(
            { type: '__SVGVIDEO_RETRY_SCENE', sceneId: sceneId, prompt: scene.prompt || '' },
            '*'
          );
        }
      }
      function renderEditorList() {
        if (!editorList) return;
        editorList.innerHTML = '';
        var fragment = document.createDocumentFragment();
        sceneModels.forEach(function (scene, index) {
          var item = document.createElement('section');
          item.className = 'sv-editor-item';
          item.setAttribute('data-editor-scene-id', scene.id);

          var head = document.createElement('div');
          head.className = 'sv-editor-item-head';

          var jumpBtn = document.createElement('button');
          jumpBtn.type = 'button';
          jumpBtn.className = 'sv-editor-item-title';
          jumpBtn.textContent = i18nText('分镜', 'Scene') + (index + 1) + ' · ' + (scene.title || '');
          jumpBtn.addEventListener('click', function () {
            focusScene(scene.id);
          });
          head.appendChild(jumpBtn);

          var statusWrap = document.createElement('div');
          statusWrap.className = 'sv-editor-status-wrap';
          var status = document.createElement('span');
          status.className = 'sv-editor-status';
          status.textContent = getStatusLabel(scene.status);
          statusWrap.appendChild(status);
          var retryBtn = document.createElement('button');
          retryBtn.type = 'button';
          retryBtn.className = 'scene-chip-retry sv-editor-retry';
          retryBtn.setAttribute('aria-label', i18nText('重新生成当前分镜', 'Retry this scene'));
          retryBtn.setAttribute('title', i18nText('重新生成当前分镜', 'Retry this scene'));
          retryBtn.innerHTML = getRetryButtonInnerHtml(scene.status === 'generating');
          if (scene.status === 'generating' || !hostCaps.canRetry) retryBtn.setAttribute('disabled', 'disabled');
          retryBtn.addEventListener('click', function (event) {
            event.preventDefault();
            event.stopPropagation();
            requestSceneRetry(scene.id);
            if (isEditorOpen) renderEditorList();
          });
          statusWrap.appendChild(retryBtn);
          head.appendChild(statusWrap);

          item.appendChild(head);

          var scriptField = document.createElement('label');
          scriptField.className = 'sv-editor-field';
          var scriptLabel = document.createElement('span');
          scriptLabel.className = 'sv-editor-label';
          scriptLabel.textContent = i18nText('脚本', 'Script');
          var scriptInput = document.createElement('div');
          scriptInput.className = 'sv-editor-script-view';
          scriptInput.setAttribute('data-field-scene-id', scene.id);
          scriptInput.textContent = scene.scriptText || '';
          scriptField.appendChild(scriptLabel);
          scriptField.appendChild(scriptInput);
          item.appendChild(scriptField);

          var promptField = document.createElement('label');
          promptField.className = 'sv-editor-field';
          var promptLabel = document.createElement('span');
          promptLabel.className = 'sv-editor-label';
          promptLabel.textContent = i18nText('提示词', 'Prompt');
          var promptInput = document.createElement('textarea');
          promptInput.className = 'sv-editor-textarea';
          promptInput.setAttribute('data-field-scene-id', scene.id);
          promptInput.setAttribute('data-field', 'prompt');
          promptInput.value = scene.prompt || '';
          promptInput.addEventListener('click', function (event) {
            event.stopPropagation();
          });
          promptInput.addEventListener('input', function () {
            scene.prompt = promptInput.value;
            markEditorDirty();
          });
          promptField.appendChild(promptLabel);
          promptField.appendChild(promptInput);
          item.appendChild(promptField);
          item.addEventListener('click', function () {
            focusScene(scene.id);
          });

          fragment.appendChild(item);
        });
        editorList.appendChild(fragment);
        highlightActiveEditorItem();
      }
      function highlightActiveEditorItem() {
        if (!editorList) return;
        var itemList = Array.prototype.slice.call(editorList.querySelectorAll('[data-editor-scene-id]'));
        itemList.forEach(function (item) {
          if (item.getAttribute('data-editor-scene-id') === state.sceneId) item.classList.add('is-active');
          else item.classList.remove('is-active');
        });
      }
      function openEditor() {
        if (!hostCaps.canEditPanel) return;
        if (!editorOverlay) return;
        renderEditorList();
        applyEditorHeight(editorHeightVh);
        editorOverlay.classList.add('is-open');
        editorOverlay.setAttribute('aria-hidden', 'false');
        document.body.classList.add('sv-editor-open');
        isEditorOpen = true;
      }
      function closeEditor() {
        if (!editorOverlay) return;
        editorOverlay.classList.remove('is-open');
        editorOverlay.setAttribute('aria-hidden', 'true');
        document.body.classList.remove('sv-editor-open');
        isEditorOpen = false;
      }
      function saveEditor() {
        persistMetaToScript();
        clearEditorDirty();
        showSavedTip();
        if (window.parent && window.parent !== window) {
          window.parent.postMessage({ type: '__SVGVIDEO_META_UPDATED', meta: meta }, '*');
        }
      }
      function getTotalSteps() {
        return sceneModels.reduce(function (sum, scene) { return sum + Math.max(1, Number(scene.steps) || 1); }, 0);
      }
      function getSceneIndexById(sceneId) {
        for (var i = 0; i < sceneModels.length; i++) {
          if (sceneModels[i].id === sceneId) return i;
        }
        return -1;
      }
      function getSceneInitialStep(sceneId) {
        var index = getSceneIndexById(sceneId);
        if (index < 0) return 0;
        var maxSteps = Math.max(1, Number(sceneModels[index].steps) || 1);
        return Math.max(0, maxSteps - 1);
      }
      function focusScene(sceneId) {
        var index = getSceneIndexById(sceneId);
        if (index < 0) return;
        var maxSteps = Math.max(1, Number(sceneModels[index].steps) || 1);
        stopPlayback();
        window.__SVGVIDEO_SET_STATE(sceneId, 0, 1);
        if (maxSteps > 1) {
          setTimeout(function () {
            window.__SVGVIDEO_SET_STATE(sceneId, Math.max(0, maxSteps - 1), 1);
          }, 90);
        }
      }
      function getProgressPercent(sceneId, step) {
        var total = getTotalSteps();
        if (!total) return 0;
        var index = getSceneIndexById(sceneId);
        if (index < 0) return 0;
        var completed = 0;
        for (var i = 0; i < index; i++) completed += Math.max(1, Number(sceneModels[i].steps) || 1);
        var maxSteps = Math.max(1, Number(sceneModels[index].steps) || 1);
        var localStep = Math.max(0, Math.min(maxSteps - 1, Number(step) || 0));
        return Math.max(0, Math.min(100, ((completed + localStep) / total) * 100));
      }
      function updateProgressUi(percent) {
        var pct = Math.max(0, Math.min(100, percent || 0));
        if (progressFill) progressFill.style.width = pct + '%';
        if (progressThumb) progressThumb.style.left = pct + '%';
      }
      function stopProgressAnimation() {
        if (progressRaf) {
          cancelAnimationFrame(progressRaf);
          progressRaf = null;
        }
      }
      function startProgressSegment(fromPercent, toPercent, durationMs) {
        stopProgressAnimation();
        var from = Math.max(0, Math.min(100, Number(fromPercent) || 0));
        var to = Math.max(0, Math.min(100, Number(toPercent) || 0));
        var duration = Math.max(1, Number(durationMs) || 1);
        if (duration <= 1 || to <= from) {
          updateProgressUi(to);
          return;
        }
        var startAt = performance.now();
        var tick = function (now) {
          if (!isPlaying) {
            stopProgressAnimation();
            return;
          }
          var elapsed = now - startAt;
          var ratio = Math.max(0, Math.min(1, elapsed / duration));
          var current = from + (to - from) * ratio;
          updateProgressUi(current);
          if (ratio < 1) {
            progressRaf = requestAnimationFrame(tick);
          } else {
            stopProgressAnimation();
          }
        };
        progressRaf = requestAnimationFrame(tick);
      }
      function getPlaybackSegment(sceneIndex, step) {
        var scene = sceneModels[sceneIndex];
        if (!scene) return null;
        var currentPercent = getProgressPercent(scene.id, step);
        var maxSteps = Math.max(1, Number(scene.steps) || 1);
        if (step < maxSteps - 1) {
          return {
            from: currentPercent,
            to: getProgressPercent(scene.id, step + 1),
            duration: STEP_INTERVAL_MS
          };
        }
        if (sceneIndex < sceneModels.length - 1) {
          var nextScene = sceneModels[sceneIndex + 1];
          var nextPercent = nextScene ? getProgressPercent(nextScene.id, 0) : currentPercent;
          return {
            from: currentPercent,
            to: nextPercent,
            duration: SCENE_FINISH_PAUSE_MS
          };
        }
        return {
          from: currentPercent,
          to: 100,
          duration: STEP_INTERVAL_MS
        };
      }
      function renderStatus() {
        if (!statusText) return;
        var done = sceneModels.filter(function (scene) { return scene.status === 'success'; }).length;
        var pending = sceneModels.filter(function (scene) { return scene.status === 'pending' || scene.status === 'generating'; }).length;
        var failed = sceneModels.filter(function (scene) { return scene.status === 'failed'; }).length;
        statusText.textContent = '已生成 ' + done + '/' + sceneModels.length + ' · 待生成 ' + pending + ' · 失败 ' + failed;
      }
      function updatePlayButton() {
        if (!playBtn) return;
        playBtn.innerHTML = isPlaying
          ? '<svg viewBox="0 0 24 24" width="14" height="14" fill="currentColor"><rect x="6" y="5" width="4" height="14" rx="1"></rect><rect x="14" y="5" width="4" height="14" rx="1"></rect></svg>'
          : '<svg viewBox="0 0 24 24" width="14" height="14" fill="currentColor"><path d="M7 5v14l11-7z"></path></svg>';
        playBtn.setAttribute('aria-label', isPlaying ? '暂停播放' : '播放分镜');
        playBtn.setAttribute('title', isPlaying ? '暂停播放' : '播放分镜');
      }
      function applyHostCaps() {
        if (hostCaps.canRetry) document.body.classList.add('sv-host-can-retry');
        else document.body.classList.remove('sv-host-can-retry');
        if (hostCaps.canEditPanel) document.body.classList.add('sv-host-can-edit-panel');
        else document.body.classList.remove('sv-host-can-edit-panel');
        if (hostCaps.theme === 'light') document.body.classList.add('sv-theme-light');
        else document.body.classList.remove('sv-theme-light');
      }
      function highlightActiveSceneButton() {
        sceneButtonList.forEach(function (button) {
          var targetId = button.getAttribute('data-panel-scene-id');
          if (targetId === state.sceneId) button.classList.add('is-active');
          else button.classList.remove('is-active');
        });
      }
      function stopPlayback() {
        if (playTimer) {
          clearTimeout(playTimer);
          playTimer = null;
        }
        stopProgressAnimation();
        isPlaying = false;
        updatePlayButton();
      }
      function startPlayback() {
        if (!sceneModels.length) return;
        stopPlayback();
        var sceneIndex = Math.max(0, getSceneIndexById(state.sceneId));
        var step = 0;
        var applyPlaybackState = function (direction) {
          var scene = sceneModels[sceneIndex];
          if (!scene) return false;
          window.__SVGVIDEO_SET_STATE(scene.id, step, direction);
          updateProgressUi(getProgressPercent(scene.id, step));
          var segment = getPlaybackSegment(sceneIndex, step);
          if (segment) startProgressSegment(segment.from, segment.to, segment.duration);
          return true;
        };
        var scheduleAdvance = function (delay) {
          playTimer = setTimeout(function () {
            var currentScene = sceneModels[sceneIndex];
            if (!currentScene) {
              stopPlayback();
              return;
            }
            var maxSteps = Math.max(1, Number(currentScene.steps) || 1);
            if (step < maxSteps - 1) {
              step += 1;
              applyPlaybackState(1);
              scheduleAdvance(STEP_INTERVAL_MS);
              return;
            }
            if (sceneIndex < sceneModels.length - 1) {
              playTimer = setTimeout(function () {
                sceneIndex += 1;
                step = 0;
                if (!applyPlaybackState(1)) {
                  stopPlayback();
                  return;
                }
                scheduleAdvance(STEP_INTERVAL_MS);
              }, SCENE_FINISH_PAUSE_MS);
              return;
            }
            stopPlayback();
          }, delay);
        };
        isPlaying = true;
        updatePlayButton();
        if (!applyPlaybackState(1)) {
          stopPlayback();
          return;
        }
        scheduleAdvance(STEP_INTERVAL_MS);
      }
      window.SceneLifecycle = {
        handlers: {},
        register: function (sceneId, handler) { this.handlers[sceneId] = handler || {}; }
      };

      (function () {
        var sceneDataJson = document.getElementById('__SVGVIDEO_SCENE_DATA__');
        var runtimeScenes = [];
        try {
          runtimeScenes = JSON.parse((sceneDataJson && sceneDataJson.textContent) || '[]');
        } catch (_e) {
          runtimeScenes = [];
        }
        runtimeScenes.forEach(function (scene) {
          if (!scene || typeof scene.id !== 'string') return;
          var js = typeof scene.js === 'string' ? scene.js.trim() : '';
          if (!js) return;
          try {
            var factory = new Function(js);
            factory.call(window);
          } catch (err) {
            console.error('[svg-video] scene runtime failed', scene.id, err);
          }
        });
        refreshFrameworkIcons(document);
      })();

      function applyStepVisibility(sceneEl) {
        var stepNodes = sceneEl.querySelectorAll('[data-step]');
        stepNodes.forEach(function (node) {
          var minStep = Number(node.getAttribute('data-step') || '0');
          var visible = state.step >= minStep;
          node.style.opacity = visible ? '1' : '0';
          node.style.visibility = visible ? 'visible' : 'hidden';
          node.style.pointerEvents = visible ? 'auto' : 'none';
          node.style.transform = visible ? 'translateY(0)' : 'translateY(8px)';
          node.style.filter = visible ? 'blur(0px)' : 'blur(4px)';
        });
      }
      function applySceneState() {
        var scenes = document.querySelectorAll('#stage > [data-scene-id]');
        var currentSceneEl = null;
        var previousSceneEl = null;
        var glitchTimer = null;
        scenes.forEach(function (sceneEl) {
          var sceneId = sceneEl.getAttribute('data-scene-id');
          if (sceneId === state.sceneId) currentSceneEl = sceneEl;
          if (sceneId === previousSceneId) previousSceneEl = sceneEl;
        });
        var direction = state.direction >= 0 ? 1 : -1;
        document.documentElement.style.setProperty('--scene-enter-offset', direction > 0 ? '200px' : '-200px');
        document.documentElement.style.setProperty('--scene-exit-offset', direction > 0 ? '-140px' : '140px');
        var isSceneChange = previousSceneId !== state.sceneId;
        var targetTransition = sceneTransitionMap[state.sceneId] || 'door';
        var needsGlitch = isSceneChange && targetTransition === 'glitch' && currentSceneEl;

        // Apply per-scene transition CSS vars before switching
        if (isSceneChange) {
          var preset = TRANSITION_PRESETS[targetTransition] || TRANSITION_PRESETS['door'];
          var rootStyle = document.documentElement.style;
          for (var varKey in preset) {
            rootStyle.setProperty(varKey, preset[varKey]);
          }
        }

        function doSwitch() {
          if (exitCleanupTimer) {
            clearTimeout(exitCleanupTimer);
            exitCleanupTimer = null;
          }
          scenes.forEach(function (sceneEl) {
            if (sceneEl !== currentSceneEl && sceneEl !== previousSceneEl) {
              sceneEl.classList.remove('scene-active');
              sceneEl.classList.remove('scene-exit');
            }
          });
          if (previousSceneEl && previousSceneEl !== currentSceneEl) {
            previousSceneEl.classList.remove('scene-active');
            previousSceneEl.classList.add('scene-exit');
          }
          if (currentSceneEl) {
            currentSceneEl.classList.remove('scene-exit');
            currentSceneEl.classList.add('scene-active');
            applyStepVisibility(currentSceneEl);
          }
          if (previousSceneEl && previousSceneEl !== currentSceneEl) {
            exitCleanupTimer = setTimeout(function () { previousSceneEl.classList.remove('scene-exit'); }, 650);
          }
          if (previousSceneId !== state.sceneId) {
            if (previousSceneId && window.SceneLifecycle.handlers[previousSceneId] && typeof window.SceneLifecycle.handlers[previousSceneId].leave === 'function') {
              try { window.SceneLifecycle.handlers[previousSceneId].leave(); } catch (e) {}
            }
          }
          if (currentSceneEl && window.SceneLifecycle.handlers[state.sceneId] && typeof window.SceneLifecycle.handlers[state.sceneId].enter === 'function') {
            try { window.SceneLifecycle.handlers[state.sceneId].enter(); } catch (e) {}
          }
          if (window.SceneLifecycle.handlers[state.sceneId] && typeof window.SceneLifecycle.handlers[state.sceneId].step === 'function') {
            try { window.SceneLifecycle.handlers[state.sceneId].step(state.step); } catch (e) {}
          }
          if (currentSceneEl) {
            refreshFrameworkIcons(currentSceneEl);
          }
          previousSceneId = state.sceneId;
          highlightActiveSceneButton();
          highlightActiveEditorItem();
          updateProgressUi(getProgressPercent(state.sceneId, state.step));
        }

        function runGlitchSequence() {
          if (!currentSceneEl) return;
          // Step 1: blur bomb + shift (instant, disable transition first)
          var noTrans = document.getElementById('sv-no-transition');
          if (!noTrans) {
            noTrans = document.createElement('style');
            noTrans.id = 'sv-no-transition';
            noTrans.textContent = '.scene { transition: none !important; }';
            document.head.appendChild(noTrans);
          }
          currentSceneEl.style.filter = 'blur(24px) brightness(1.5)';
          currentSceneEl.style.transform = 'translateX(30px) scale(0.96)';
          // Step 2: snap to other side (80ms)
          glitchTimer = setTimeout(function () {
            if (!currentSceneEl) return;
            currentSceneEl.style.filter = 'blur(3px) brightness(1.1)';
            currentSceneEl.style.transform = 'translateX(-15px) scale(1.02)';
            // Step 3: settle with slight bounce (160ms)
            glitchTimer = setTimeout(function () {
              if (!currentSceneEl) return;
              currentSceneEl.style.filter = 'blur(0px) brightness(1)';
              currentSceneEl.style.transform = 'translateX(0) scale(1)';
              if (noTrans && noTrans.parentNode) noTrans.parentNode.removeChild(noTrans);
              if (currentSceneEl) {
                refreshFrameworkIcons(currentSceneEl);
              }
            }, 70);
          }, 80);
        }

        if (needsGlitch) {
          // Kill old scene instantly
          if (previousSceneEl) {
            previousSceneEl.classList.remove('scene-active');
            previousSceneEl.classList.add('scene-exit');
            exitCleanupTimer = setTimeout(function () { previousSceneEl.classList.remove('scene-exit'); }, 650);
          }
          // Show new scene for glitch base
          if (currentSceneEl) {
            currentSceneEl.classList.remove('scene-exit');
            currentSceneEl.classList.add('scene-active');
            applyStepVisibility(currentSceneEl);
          }
          // Fire lifecycle immediately so scene JS can initialize
          if (previousSceneId && window.SceneLifecycle.handlers[previousSceneId] && typeof window.SceneLifecycle.handlers[previousSceneId].leave === 'function') {
            try { window.SceneLifecycle.handlers[previousSceneId].leave(); } catch (e) {}
          }
          if (window.SceneLifecycle.handlers[state.sceneId] && typeof window.SceneLifecycle.handlers[state.sceneId].enter === 'function') {
            try { window.SceneLifecycle.handlers[state.sceneId].enter(); } catch (e) {}
          }
          if (window.SceneLifecycle.handlers[state.sceneId] && typeof window.SceneLifecycle.handlers[state.sceneId].step === 'function') {
            try { window.SceneLifecycle.handlers[state.sceneId].step(state.step); } catch (e) {}
          }
          previousSceneId = state.sceneId;
          highlightActiveSceneButton();
          highlightActiveEditorItem();
          updateProgressUi(getProgressPercent(state.sceneId, state.step));
          runGlitchSequence();
        } else {
          doSwitch();
        }
      }
      window.__SVGVIDEO_SET_STATE = function (sceneId, step, direction) {
        if (typeof sceneId === 'string' && sceneId) {
          if (typeof direction === 'number' && Number.isFinite(direction)) {
            state.direction = direction >= 0 ? 1 : -1;
          } else {
            var nextIndex = getSceneIndexById(sceneId);
            var prevIndex = getSceneIndexById(state.sceneId);
            if (nextIndex !== -1 && prevIndex !== -1) state.direction = nextIndex >= prevIndex ? 1 : -1;
          }
          state.sceneId = sceneId;
        }
        if (typeof step === 'number' && Number.isFinite(step)) state.step = Math.max(0, Math.floor(step));
        applySceneState();
      };
      if (state.sceneId && getSceneIndexById(state.sceneId) === -1 && sceneModels.length) {
        state.sceneId = sceneModels[0].id;
      }
      if (playBtn) {
        playBtn.addEventListener('click', function () {
          if (isPlaying) stopPlayback();
          else startPlayback();
        });
      }
      if (expandBtn) {
        expandBtn.addEventListener('click', function () {
          if (isEditorOpen) closeEditor();
          else openEditor();
        });
      }
      if (editorCloseBtn) {
        editorCloseBtn.addEventListener('click', closeEditor);
      }
      if (editorBackdrop) {
        editorBackdrop.addEventListener('click', closeEditor);
      }
      if (editorSaveBtn) {
        editorSaveBtn.addEventListener('click', saveEditor);
      }
      if (editorResizeHandle && editorSheet) {
        editorResizeHandle.addEventListener('mousedown', function (event) {
          event.preventDefault();
          var startY = event.clientY;
          var startHeightPx = (window.innerHeight * editorHeightVh) / 100;
          var onMove = function (moveEvent) {
            var delta = startY - moveEvent.clientY;
            var nextHeightPx = startHeightPx + delta;
            var nextHeightVh = (nextHeightPx / Math.max(1, window.innerHeight)) * 100;
            applyEditorHeight(nextHeightVh);
          };
          var onUp = function () {
            window.removeEventListener('mousemove', onMove);
            window.removeEventListener('mouseup', onUp);
          };
          window.addEventListener('mousemove', onMove);
          window.addEventListener('mouseup', onUp);
        });
      }
      sceneButtonList.forEach(function (button) {
        button.addEventListener('click', function () {
          var sceneId = button.getAttribute('data-panel-scene-id');
          if (!sceneId) return;
          focusScene(sceneId);
        });
      });
      sceneRetryButtonList.forEach(function (button) {
        button.addEventListener('click', function (event) {
          event.preventDefault();
          event.stopPropagation();
          var sceneId = button.getAttribute('data-retry-scene-id');
          if (!sceneId) return;
          requestSceneRetry(sceneId);
        });
      });
      if (progressWrap) {
        progressWrap.addEventListener('click', function (event) {
          var rect = progressWrap.getBoundingClientRect();
          var pct = Math.max(0, Math.min(100, ((event.clientX - rect.left) / rect.width) * 100));
          var totalSteps = getTotalSteps();
          var targetStep = Math.round((pct / 100) * totalSteps);
          var accumulated = 0;
          for (var i = 0; i < sceneModels.length; i++) {
            var sceneSteps = Math.max(1, Number(sceneModels[i].steps) || 1);
            if (accumulated + sceneSteps > targetStep) {
              var localStep = Math.max(0, Math.min(sceneSteps - 1, targetStep - accumulated));
              stopPlayback();
              window.__SVGVIDEO_SET_STATE(sceneModels[i].id, localStep, 1);
              updateProgressUi(pct);
              return;
            }
            accumulated += sceneSteps;
          }
        });
      }
      window.addEventListener('message', function (event) {
        var data = event && event.data;
        if (!data || typeof data.type !== 'string') return;
        if (data.type === '__SVGVIDEO_SET_STATE') {
          window.__SVGVIDEO_SET_STATE(data.sceneId, data.step, data.direction);
          return;
        }
        if (data.type === '__SVGVIDEO_PLAY') {
          startPlayback();
          return;
        }
        if (data.type === '__SVGVIDEO_STOP') {
          stopPlayback();
          return;
        }
        if (data.type === '__SVGVIDEO_SET_HOST_CAPS') {
          hostCaps.canRetry = Boolean(data.canRetry);
          hostCaps.canEditPanel = Boolean(data.canEditPanel);
          hostCaps.locale = data.locale === 'en' ? 'en' : 'zh';
          hostCaps.theme = data.theme === 'light' ? 'light' : 'dark';
          applyI18nTexts();
          applyHostCaps();
          if (isEditorOpen) renderEditorList();
          return;
        }
        if (data.type === '__SVGVIDEO_SET_SCENE_STATUS') {
          var sceneId = typeof data.sceneId === 'string' ? data.sceneId : '';
          var status = typeof data.status === 'string' ? data.status : '';
          if (sceneId && status) {
            setSceneStatus(sceneId, status);
            if (isEditorOpen) renderEditorList();
          }
          return;
        }
        if (data.type === '__SVGVIDEO_SET_RECORDING') {
          if (data && data.active) {
            document.body.classList.add('sv-recording');
            // 如果指定了初始分镜，无过渡动画地跳转过去，避免录制开头残留旧分镜画面
            if (data.sceneId) {
              var noTransStyle = document.createElement('style');
              noTransStyle.id = 'sv-no-transition';
              noTransStyle.textContent = '.scene, [data-step] { transition: none !important; }';
              document.head.appendChild(noTransStyle);
              window.__SVGVIDEO_SET_STATE(data.sceneId, 0, 1);
              // 强制 reflow 后恢复过渡动画
              void document.body.offsetHeight;
              requestAnimationFrame(function () {
                var s = document.getElementById('sv-no-transition');
                if (s) s.remove();
              });
            }
          } else {
            document.body.classList.remove('sv-recording');
          }
          return;
        }
        if (data.type === '__SVGVIDEO_SET_META' && data.meta && typeof data.meta === 'object') {
          meta = data.meta;
          var freshScenes = Array.isArray(meta.scenes) ? meta.scenes : [];
          freshScenes.forEach(function (scene) {
            var idx = getSceneIndexById(scene.id);
            if (idx < 0) return;
            sceneModels[idx].prompt = typeof scene.prompt === 'string' ? scene.prompt : '';
            sceneModels[idx].scriptText = typeof scene.scriptText === 'string' ? scene.scriptText : '';
            sceneModels[idx].audioObjectKey = typeof scene.audioObjectKey === 'string' ? scene.audioObjectKey : '';
            sceneModels[idx].audioDurationMs = Number(scene.audioDurationMs) > 0 ? Number(scene.audioDurationMs) : 0;
            sceneModels[idx].voiceName = typeof scene.voiceName === 'string' ? scene.voiceName : '';
            sceneModels[idx].voiceStyle = typeof scene.voiceStyle === 'string' ? scene.voiceStyle : '';
          });
          persistMetaToScript();
          if (isEditorOpen) renderEditorList();
        }
      });
      document.addEventListener('keydown', function (event) {
        if (event.key === 'Escape' && isEditorOpen) closeEditor();
      });
      window.__SVGVIDEO_PLAY = startPlayback;
      window.__SVGVIDEO_STOP = stopPlayback;
      window.__SVGVIDEO_OPEN_EDITOR = openEditor;
      window.__SVGVIDEO_CLOSE_EDITOR = closeEditor;
      applyEditorHeight(editorHeightVh);
      renderStatus();
      updatePlayButton();
      applyHostCaps();
      clearEditorDirty();
      applyI18nTexts();
      applySceneState();
      if (autoplayFromFirstScene && sceneModels.length) {
        state.sceneId = sceneModels[0].id;
        state.step = 0;
        state.direction = 1;
        applySceneState();
        startPlayback();
      }
    })();
  </script>
</body>
</html>1e:{"id":"62574c6d-8ac1-43ff-8ab8-d4a1a16f8fc4","exportName":"$undefined","title":"Linux Kernel Super Vulnerability: Copy Fail In-depth Analysis","description":"$1f","category":"html-video","tags":"$20","likes":0,"favorites":0,"views":213,"sourceType":"HTMLVideo","componentSource":"$21","isLiked":false,"isFavorited":false}
23:[]
24:T3489,<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Python 异常处理原理可视化</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/gsap.min.js"></script>
    <script src="https://unpkg.com/lucide@latest"></script>
    <style>
        @import url('https://fonts.googleapis.com/css2?family=Fira+Code:wght@400;700&family=Noto+Sans+SC:wght@400;700&display=swap');

        :root {
            --bg: #0F172A;
            --code-bg: #1E293B;
            --primary: #38BDF8;
            --accent-red: #EF4444;
            --accent-green: #22C55E;
            --accent-orange: #FB923C;
            --text-main: #F8FAFC;
            --text-dim: #94A3B8;
            --keyword: #F472B6;
            --comment: #64748B;
        }

        body, html {
            margin: 0; padding: 0; width: 100%; height: 100%;
            background-color: var(--bg);
            color: var(--text-main);
            font-family: 'Fira Code', 'Noto Sans SC', monospace;
            overflow: hidden;
            display: flex;
            align-items: center;
            justify-content: center;
        }

        #app-container {
            position: relative;
            width: 1200px;
            height: 700px;
            overflow: hidden;
        }

        /* 背景装饰 */
        .grid-bg {
            position: absolute;
            width: 100%;
            height: 100%;
            background-image: linear-gradient(rgba(56, 189, 248, 0.05) 1px, transparent 1px),
                              linear-gradient(90deg, rgba(56, 189, 248, 0.05) 1px, transparent 1px);
            background-size: 40px 40px;
            z-index: 0;
        }

        .svg-canvas {
            width: 100%;
            height: 100%;
            position: absolute;
            top: 0;
            left: 0;
            z-index: 10;
        }

        /* 样式定义 */
        .code-block {
            fill: var(--code-bg);
            stroke: #334155;
            stroke-width: 1;
            rx: 8;
        }

        .line-highlight {
            fill: rgba(56, 189, 248, 0.15);
            opacity: 0;
        }

        .keyword { fill: var(--keyword); font-weight: bold; }
        .code-text { fill: var(--text-main); font-size: 18px; }
        .comment { fill: var(--comment); font-size: 14px; font-style: italic; }
        .exception-name { fill: var(--accent-red); font-weight: bold; }
        
        .status-panel {
            fill: #111827;
            stroke: #334155;
            stroke-width: 2;
            rx: 12;
        }

        .data-package {
            filter: drop-shadow(0 0 8px var(--primary));
        }

        .subtitles-box {
            position: absolute;
            bottom: 40px;
            left: 50%;
            transform: translateX(-50%);
            width: 800px;
            text-align: center;
            background: rgba(15, 23, 42, 0.8);
            backdrop-filter: blur(8px);
            padding: 20px;
            border-radius: 12px;
            border-bottom: 3px solid var(--primary);
            z-index: 20;
        }

        .subtitles-text {
            font-size: 22px;
            color: var(--text-main);
            margin: 0;
        }

        #flow-indicator {
            stroke-dasharray: 8 4;
            animation: dash 1s linear infinite;
        }

        @keyframes dash {
            to { stroke-dashoffset: -12; }
        }
    </style>
</head>
<body>

<div id="app-container">
    <div class="grid-bg"></div>
    
    <svg class="svg-canvas" viewBox="0 0 1200 700">
        <defs>
            <filter id="glow" x="-20%" y="-20%" width="140%" height="140%">
                <feGaussianBlur stdDeviation="4" result="blur" />
                <feComposite in="SourceGraphic" in2="blur" operator="over" />
            </filter>
            
            <marker id="arrowhead" markerWidth="10" markerHeight="7" refX="0" refY="3.5" orient="auto">
                <polygon points="0 0, 10 3.5, 0 7" fill="var(--primary)" />
            </marker>
        </defs>

        <!-- 标题 -->
        <g transform="translate(50, 50)">
            <text x="0" y="0" fill="var(--primary)" font-size="28" font-weight="700">Python Exception Handling</text>
            <text x="0" y="30" fill="var(--text-dim)" font-size="16">异常处理机制与流程可视化</text>
            <rect x="0" y="45" width="400" height="2" fill="var(--primary)" opacity="0.3" />
        </g>

        <!-- 代码编辑器区域 -->
        <g id="editor-group" transform="translate(50, 120)">
            <rect class="code-block" width="500" height="420" />
            <rect id="line-highlighter" class="line-highlight" width="480" height="30" x="10" y="25" />
            
            <g transform="translate(30, 50)">
                <!-- 代码内容 -->
                <text class="code-text" y="0"><tspan class="keyword">try</tspan>:</text>
                <text class="code-text" y="35" x="30">result = 10 / n</text>
                <text class="comment" y="55" x="30"># 可能抛出除零异常的操作</text>

                <text class="code-text" y="100"><tspan class="keyword">except</tspan> <tspan class="exception-name">ZeroDivisionError</tspan>:</text>
                <text class="code-text" y="135" x="30">print(<tspan fill="#EAB308">"不能除以零！"</tspan>)</text>

                <text class="code-text" y="180"><tspan class="keyword">except</tspan> <tspan class="exception-name">Exception</tspan> <tspan class="keyword">as</tspan> e:</text>
                <text class="code-text" y="215" x="30">print(<tspan fill="#EAB308">"发生了其他错误"</tspan>)</text>

                <text class="code-text" y="260"><tspan class="keyword">else</tspan>:</text>
                <text class="code-text" y="295" x="30">print(<tspan fill="#EAB308">"计算成功"</tspan>)</text>

                <text class="code-text" y="340"><tspan class="keyword">finally</tspan>:</text>
                <text class="code-text" y="375" x="30">print(<tspan fill="#EAB308">"清理并退出"</tspan>)</text>
            </g>
        </g>

        <!-- 逻辑流程演示区 -->
        <g id="logic-group" transform="translate(620, 120)">
            <rect class="status-panel" width="530" height="420" />
            
            <!-- 流程节点 -->
            <g id="nodes">
                <circle id="node-try" cx="100" cy="60" r="25" fill="var(--code-bg)" stroke="var(--primary)" stroke-width="2" />
                <text x="100" y="65" text-anchor="middle" fill="var(--text-main)" font-size="12">TRY</text>

                <circle id="node-error" cx="265" cy="160" r="30" fill="var(--code-bg)" stroke="var(--accent-red)" stroke-width="2" opacity="0.3" />
                <path id="error-icon" d="M265 145 L265 165 M265 175 L265 175" stroke="var(--accent-red)" stroke-width="4" stroke-linecap="round" opacity="0" />
                <text x="265" y="210" text-anchor="middle" fill="var(--accent-red)" font-size="12" font-weight="bold">EXCEPTION!</text>

                <rect id="node-except" x="380" y="135" width="100" height="50" rx="5" fill="var(--code-bg)" stroke="var(--accent-orange)" stroke-width="2" />
                <text x="430" y="165" text-anchor="middle" fill="var(--text-main)" font-size="12">EXCEPT</text>

                <rect id="node-else" x="50" y="250" width="100" height="50" rx="5" fill="var(--code-bg)" stroke="var(--accent-green)" stroke-width="2" />
                <text x="100" y="280" text-anchor="middle" fill="var(--text-main)" font-size="12">ELSE</text>

                <rect id="node-finally" x="215" y="340" width="100" height="50" rx="5" fill="var(--code-bg)" stroke="var(--primary)" stroke-width="2" />
                <text x="265" y="370" text-anchor="middle" fill="var(--text-main)" font-size="12">FINALLY</text>
            </g>

            <!-- 路径连接线 -->
            <g id="paths" opacity="0.4">
                <path id="path-success" d="M100 85 L100 250" stroke="var(--accent-green)" stroke-width="2" fill="none" marker-end="url(#arrowhead)" />
                <path id="path-to-error" d="M125 60 L235 145" stroke="var(--accent-red)" stroke-width="2" fill="none" marker-end="url(#arrowhead)" />
                <path id="path-to-except" d="M295 160 L380 160" stroke="var(--accent-orange)" stroke-width="2" fill="none" marker-end="url(#arrowhead)" />
                <path id="path-except-to-finally" d="M430 185 L430 365 L315 365" stroke="var(--accent-orange)" stroke-width="2" fill="none" marker-end="url(#arrowhead)" />
                <path id="path-else-to-finally" d="M100 300 L100 365 L215 365" stroke="var(--accent-green)" stroke-width="2" fill="none" marker-end="url(#arrowhead)" />
            </g>

            <!-- 运行中的数据包 (动画主体) -->
            <g id="data-package" opacity="0">
                <circle r="12" fill="var(--primary)" filter="url(#glow)" />
                <path d="M-5 -2 L0 3 L5 -2" stroke="white" stroke-width="2" fill="none" />
            </g>
        </g>
    </svg>

    <div class="subtitles-box">
        <p id="subtitles" class="subtitles-text">准备演示 Python 异常处理机制...</p>
    </div>
</div>

<script>
    const sub = document.getElementById('subtitles');
    const highlighter = document.getElementById('line-highlighter');
    const dataPkg = document.getElementById('data-package');
    const tl = gsap.timeline({ repeat: -1 });

    function moveHighlighter(y) {
        return { y: y, opacity: 1, duration: 0.4, ease: "power2.inOut" };
    }

    function setSub(text) {
        sub.innerText = text;
    }

    // 动画序列定义
    tl.addLabel("start")
      // 1. 初始化
      .to(dataPkg, { opacity: 0, duration: 0 })
      .call(() => setSub("场景一：正常执行流 (No Exception)"))
      
      // 2. 进入 Try
      .to(highlighter, moveHighlighter(25))
      .to(dataPkg, { opacity: 1, x: 100, y: 60, duration: 0.5 })
      .call(() => setSub("程序进入 try 代码块，尝试执行敏感操作"))
      
      .to(highlighter, moveHighlighter(60))
      .to("#node-try", { scale: 1.2, strokeWidth: 4, duration: 0.3 })
      .to("#node-try", { scale: 1, strokeWidth: 2, duration: 0.3 })
      
      // 3. 正常路径至 Else
      .call(() => setSub("操作成功，没有异常发生，直接跳过所有 except"))
      .to(dataPkg, { 
          motionPath: {
              path: "#path-success",
              autoRotate: true
          }, 
          duration: 1.5,
          ease: "power1.inOut"
      })
      .to(highlighter, moveHighlighter(285))
      .to("#node-else", { backgroundColor: "var(--accent-green)", duration: 0.3 })
      .call(() => setSub("执行 else 块：仅在没有发生异常时运行"))
      
      // 4. 进入 Finally
      .to(dataPkg, { 
          motionPath: {
              path: "#path-else-to-finally",
              autoRotate: true
          }, 
          duration: 1.2 
      })
      .to(highlighter, moveHighlighter(365))
      .to("#node-finally", { strokeWidth: 4, duration: 0.3 })
      .call(() => setSub("最后执行 finally 块：无论是否出错都会执行"))
      .to(dataPkg, { opacity: 0, scale: 2, duration: 0.5 })
      .to("#node-finally", { strokeWidth: 2, duration: 0.1 })
      .to("#node-else", { backgroundColor: "transparent", duration: 0.1 })

      .to({}, { duration: 1.5 }) // 停顿

      // 5. 异常流
      .addLabel("error-scenario")
      .call(() => setSub("场景二：触发特定异常 (ZeroDivisionError)"))
      .to(highlighter, moveHighlighter(25))
      .to(dataPkg, { opacity: 1, x: 100, y: 60, scale: 1, duration: 0.5 })
      
      .to(highlighter, moveHighlighter(60))
      .call(() => setSub("糟糕！执行 10 / 0 触发了除零异常"))
      
      // 6. 抛出异常
      .to(dataPkg, { 
          motionPath: { path: "#path-to-error" }, 
          duration: 0.8,
          fill: "var(--accent-red)"
      })
      .to("#node-error", { opacity: 1, scale: 1.2, duration: 0.3 })
      .to("#error-icon", { opacity: 1, duration: 0.2 })
      
      // 7. 匹配 Except
      .call(() => setSub("Python 停止 try 中的后续代码，开始在 except 中寻找匹配项"))
      .to(dataPkg, { 
          motionPath: { path: "#path-to-except" }, 
          duration: 0.6 
      })
      .to(highlighter, moveHighlighter(125))
      .to("#node-except", { strokeWidth: 4, duration: 0.3 })
      .call(() => setSub("匹配到 ZeroDivisionError，执行对应的异常处理逻辑"))
      
      // 8. 异常处理后进入 Finally
      .to(dataPkg, { 
          motionPath: { path: "#path-except-to-finally" }, 
          duration: 1.5 
      })
      .to(highlighter, moveHighlighter(365))
      .to("#node-finally", { strokeWidth: 4, duration: 0.3 })
      .call(() => setSub("注意：发生异常时 else 会被跳过，但 finally 依然执行"))
      
      .to(dataPkg, { opacity: 0, scale: 2, duration: 0.5 })
      .to("#node-error", { opacity: 0.3, scale: 1, duration: 0.3 })
      .to("#error-icon", { opacity: 0, duration: 0.1 })
      .to("#node-except", { strokeWidth: 2, duration: 0.1 })
      .to("#node-finally", { strokeWidth: 2, duration: 0.1 })

      .to({}, { duration: 2 }); // 循环等待

    // 初始化 Lucide 图标 (如果需要)
    lucide.createIcons();

</script>
</body>
</html>22:{"id":"27b578da-9595-4751-9334-8710c8e8c7b7","exportName":"$undefined","title":"Demonstrating the basic syntax and principles of Python exception handling","description":"Demonstrating the basic syntax and principles of Python exception handling","category":"custom","tags":"$23","likes":1,"favorites":0,"views":427,"sourceType":"SvgAnimation","componentSource":"$24","isLiked":false,"isFavorited":false}
26:[]
27:T22cf9,<!doctype html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>腾讯公司发展历程：连接的演变</title>
  <style>
    @font-face {
      font-family: "SVGVideoAlimamaShuHei";
      src:
        url("/fonts/AlimamaShuHeiTi-Bold.woff2") format("woff2"),
        url("/fonts/AlimamaShuHeiTi-Bold.woff") format("woff"),
        url("/fonts/AlimamaShuHeiTi-Bold.ttf") format("truetype"),
        local("Alimama ShuHeiTi Bold"),
        local("Alimama ShuHeiTi"),
        local("AlimamaShuHeiTi-Bold"),
        local("AlimamaShuHeiTi");
      font-display: swap;
      font-weight: 700;
      font-style: normal;
    }
    :root {
      color-scheme: dark;
      --bg: #202020;
      --fg: #e2e8f0;
      --muted: #94a3b8;
      --card: rgba(255, 255, 255, 0.05);
      --border: rgba(255, 255, 255, 0.12);
      --sv-editor-height: 50vh;
      --sv-font-display-cn: "SVGVideoAlimamaShuHei", "Alimama ShuHeiTi", "AlimamaShuHeiTi-Bold", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif;
      --sv-font-body-cn: "SVGVideoAlimamaShuHei", "Alimama ShuHeiTi", "AlimamaShuHeiTi-Bold", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif;
      --sv-font-mono: "SFMono-Regular", "JetBrains Mono", "Fira Code", "Courier New", monospace;
    }
    * { box-sizing: border-box; }
    html, body {
      margin: 0; width: 100%; height: 100%; overflow: hidden;
      background: var(--bg);
      color: var(--fg);
      font-family: var(--sv-font-body-cn);
    }
    #app { width: 100%; height: 100%; display: flex; flex-direction: column; }
    #viewport { position: relative; flex: 1; min-height: 0; overflow: hidden; }
    #stage { position: relative; width: 100%; height: 100%; overflow: hidden; }
    .scene {
      position: absolute; inset: 0; overflow: hidden;
      opacity: 0; pointer-events: none;
      transform: translateX(var(--scene-enter-offset, 56px)) scale(0.96);
      filter: blur(10px);
      transition: opacity .55s cubic-bezier(.22,1,.36,1), transform .55s cubic-bezier(.22,1,.36,1), filter .45s ease;
      will-change: opacity, transform, filter;
      z-index: 1;
    }
    .scene.scene-active {
      opacity: 1; pointer-events: auto; transform: translateX(0) scale(1); filter: blur(0px); z-index: 3;
    }
    .scene.scene-exit {
      opacity: 0; pointer-events: none;
      transform: translateX(var(--scene-exit-offset, -56px)) scale(0.96);
      filter: blur(10px); z-index: 2;
    }
    .scene-placeholder {
      height: 100%; border: 1px dashed var(--border); border-radius: 20px; background: var(--card);
      display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 12px; color: var(--muted);
    }
    .sv-scene-root { width: 100%; height: 100%; position: relative; overflow: hidden; }
    .scene-placeholder-title { font-size: 28px; color: var(--fg); font-weight: 700; }
    [data-step] { transition: opacity .35s ease, transform .35s ease, filter .35s ease; }
    #sv-controls {
      background: rgba(19, 19, 19, 0.95);
      border-top: 1px solid rgba(255,255,255,0.08);
      padding: 8px 12px 10px;
      display: flex;
      flex-direction: column;
      gap: 8px;
      backdrop-filter: blur(8px);
      flex-shrink: 0;
    }
    .sv-progress-wrap {
      position: relative;
      height: 22px;
      display: flex;
      align-items: center;
      cursor: pointer;
    }
    .sv-progress-track {
      width: 100%;
      height: 4px;
      border-radius: 999px;
      background: rgba(120, 120, 120, 0.55);
      position: relative;
      overflow: hidden;
    }
    .sv-progress-fill {
      position: absolute;
      left: 0;
      top: 0;
      bottom: 0;
      width: 0%;
      border-radius: 999px;
      background: linear-gradient(90deg, #f97316, #fb923c);
      transition: width 220ms ease;
    }
    .sv-progress-thumb {
      position: absolute;
      top: 50%;
      width: 12px;
      height: 12px;
      border-radius: 50%;
      background: #fff;
      transform: translate(-50%, -50%);
      box-shadow: 0 0 0 1px rgba(0,0,0,0.28);
      pointer-events: none;
    }
    .sv-toolbar {
      display: grid;
      grid-template-columns: 1fr auto 1fr;
      align-items: center;
      gap: 8px;
      min-height: 30px;
    }
    .sv-toolbar-left {
      display: inline-flex;
      align-items: center;
      gap: 6px;
      min-width: 0;
    }
    .sv-toolbar-title {
      font-size: 11px;
      color: #a8b0bb;
      white-space: nowrap;
      overflow: hidden;
      text-overflow: ellipsis;
    }
    .sv-expand-btn {
      width: 20px;
      height: 20px;
      border: none;
      border-radius: 6px;
      background: rgba(255,255,255,0.08);
      color: #cbd5e1;
      display: inline-flex;
      align-items: center;
      justify-content: center;
      cursor: pointer;
      flex: 0 0 auto;
      transition: background 140ms ease, color 140ms ease, transform 120ms ease;
    }
    .sv-expand-btn:hover {
      background: rgba(255,255,255,0.15);
      color: #f8fafc;
    }
    .sv-expand-btn:active {
      transform: scale(0.94);
    }
    body:not(.sv-host-can-edit-panel) .sv-expand-btn {
      display: none;
    }
    .sv-play-btn {
      width: 28px;
      height: 28px;
      border: none;
      border-radius: 999px;
      color: #fff;
      background: #f97316;
      cursor: pointer;
      display: inline-flex;
      align-items: center;
      justify-content: center;
      transition: transform 120ms ease, background 160ms ease;
    }
    .sv-play-btn:hover { background: #fb923c; }
    .sv-play-btn:active { transform: scale(0.94); }
    .sv-play-btn:disabled { opacity: 0.45; cursor: not-allowed; }
    .sv-status {
      text-align: right;
      font-size: 11px;
      color: #8f98a5;
      white-space: nowrap;
      overflow: hidden;
      text-overflow: ellipsis;
    }
    .sv-scene-grid {
      display: flex;
      align-items: stretch;
      gap: 6px;
      flex-wrap: nowrap;
      overflow: hidden;
    }
    .scene-chip {
      position: relative;
      width: 100%;
      border: none;
      border-bottom: 2px solid transparent;
      border-radius: 8px;
      background: rgba(255,255,255,0.06);
      color: #a3a3a3;
      padding: 7px 6px;
      text-align: center;
      font-size: 11px;
      line-height: 1.1;
      white-space: nowrap;
      overflow: hidden;
      text-overflow: ellipsis;
      cursor: pointer;
      transition: transform 140ms ease, color 160ms ease, background 160ms ease, border-color 160ms ease;
    }
    .scene-chip-label {
      display: block;
      width: 100%;
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
    }
    .scene-chip-wrap {
      position: relative;
      flex: 1 1 0;
      min-width: 0;
    }
    .scene-chip-retry {
      position: absolute;
      top: 50%;
      right: 4px;
      transform: translateY(-50%);
      width: 24px;
      height: 24px;
      border: none;
      border-radius: 999px;
      background: #f97316;
      color: #fff;
      display: inline-flex;
      align-items: center;
      justify-content: center;
      box-shadow: 0 4px 10px rgba(0, 0, 0, 0.3);
      opacity: 0;
      pointer-events: none;
      transition: opacity 160ms ease;
      cursor: pointer;
    }
    body.sv-host-can-retry .scene-chip-wrap:hover .scene-chip-retry {
      opacity: 1;
      pointer-events: auto;
    }
    .scene-chip-retry:disabled {
      opacity: 1;
      pointer-events: none;
      background: #f97316;
    }
    .scene-chip-retry-icon {
      width: 18px;
      height: 18px;
      display: block;
    }
    .scene-chip-retry-spinner {
      width: 14px;
      height: 14px;
      aspect-ratio: 1 / 1;
      border: 2px solid #ffffff;
      border-top-color: transparent;
      border-radius: 999px;
      box-sizing: border-box;
      animation: sceneChipSpin 1s linear infinite;
      display: block;
      flex: 0 0 auto;
    }
    @keyframes sceneChipSpin {
      from { transform: rotate(0deg); }
      to { transform: rotate(360deg); }
    }
    .scene-chip:hover { transform: scale(1.02); }
    .scene-chip.is-active {
      color: #fb923c;
      border-bottom-color: #fb923c;
      background: linear-gradient(135deg, rgba(249,115,22,0.2), rgba(168,85,247,0.2));
    }
    .scene-chip.is-failed {
      color: #fca5a5;
      border-bottom-color: rgba(239,68,68,0.95);
      background: rgba(239,68,68,0.12);
    }
    .scene-chip.is-generating {
      color: #fcd34d;
      background: rgba(245,158,11,0.11);
    }
    .sv-editor-overlay {
      position: relative;
      width: 100%;
      height: 0;
      z-index: 40;
      pointer-events: none;
      overflow: hidden;
      transition: height 260ms cubic-bezier(.22,1,.36,1);
    }
    .sv-editor-backdrop {
      position: absolute;
      inset: auto 0 0 0;
      height: 100%;
      background: rgba(0, 0, 0, 0.45);
      opacity: 0;
      transition: opacity 220ms ease;
    }
    .sv-editor-sheet {
      position: absolute;
      left: 0;
      right: 0;
      bottom: 0;
      height: 100%;
      background: rgba(16, 16, 16, 0.96);
      border-top: 1px solid rgba(255,255,255,0.12);
      box-shadow: 0 -14px 30px rgba(0,0,0,0.45);
      transform: translateY(102%);
      transition: transform 260ms cubic-bezier(.22,1,.36,1);
      display: flex;
      flex-direction: column;
    }
    .sv-editor-overlay.is-open {
      pointer-events: auto;
      height: var(--sv-editor-height, 50vh);
    }
    .sv-editor-overlay.is-open .sv-editor-backdrop {
      opacity: 1;
    }
    .sv-editor-overlay.is-open .sv-editor-sheet {
      transform: translateY(0);
    }
    .sv-editor-resize-handle {
      width: 100%;
      height: 14px;
      display: flex;
      align-items: center;
      justify-content: center;
      cursor: ns-resize;
      flex: 0 0 auto;
      touch-action: none;
    }
    .sv-editor-resize-handle::before {
      content: "";
      width: 52px;
      height: 4px;
      border-radius: 999px;
      background: rgba(148, 163, 184, 0.7);
    }
    .sv-editor-header {
      padding: 10px 12px;
      border-bottom: 1px solid rgba(255,255,255,0.1);
      display: grid;
      grid-template-columns: 1fr auto;
      align-items: center;
      gap: 8px;
    }
    .sv-editor-header-title {
      color: #f1f5f9;
      font-size: 13px;
      font-weight: 600;
      white-space: nowrap;
      overflow: hidden;
      text-overflow: ellipsis;
    }
    .sv-editor-actions {
      display: inline-flex;
      align-items: center;
      gap: 6px;
    }
    .sv-editor-btn {
      border: none;
      border-radius: 8px;
      cursor: pointer;
      font-size: 12px;
      line-height: 1;
      padding: 7px 10px;
      color: #e2e8f0;
      background: rgba(255,255,255,0.1);
      transition: background 120ms ease;
    }
    .sv-editor-btn:hover {
      background: rgba(255,255,255,0.18);
    }
    .sv-editor-btn.is-primary {
      color: #fff;
      background: #f97316;
    }
    .sv-editor-btn.is-primary:hover {
      background: #fb923c;
    }
    .sv-editor-list {
      flex: 1;
      min-height: 0;
      overflow: auto;
      padding: 10px 12px 14px;
      display: grid;
      gap: 10px;
      grid-template-columns: repeat(2, minmax(0, 1fr));
      align-items: start;
      align-content: start;
    }
    .sv-editor-item {
      border: 1px solid rgba(255,255,255,0.12);
      border-radius: 10px;
      background: rgba(255,255,255,0.03);
      padding: 10px;
      display: grid;
      grid-template-rows: auto auto auto;
      gap: 10px;
      align-content: start;
      cursor: pointer;
    }
    .sv-editor-item.is-active {
      border-color: rgba(249,115,22,0.65);
      box-shadow: inset 0 0 0 1px rgba(249,115,22,0.28);
    }
    .sv-editor-item-head {
      display: flex;
      align-items: center;
      justify-content: space-between;
      gap: 8px;
      min-height: 30px;
    }
    .sv-editor-item-title {
      border: none;
      border-radius: 8px;
      color: #fb923c;
      background: rgba(255,255,255,0.08);
      font-size: 12px;
      font-weight: 600;
      padding: 6px 8px;
      cursor: pointer;
      white-space: nowrap;
      overflow: hidden;
      text-overflow: ellipsis;
      text-align: left;
    }
    .sv-editor-item-title:hover {
      background: rgba(255,255,255,0.14);
    }
    .sv-editor-status {
      color: #94a3b8;
      font-size: 11px;
      white-space: nowrap;
    }
    .sv-editor-status-wrap {
      display: inline-flex;
      align-items: center;
      gap: 6px;
    }
    .scene-chip-retry.sv-editor-retry {
      position: static;
      transform: none;
      opacity: 1;
      pointer-events: auto;
      width: 24px;
      height: 24px;
      flex: 0 0 auto;
    }
    .sv-editor-field {
      display: grid;
      gap: 6px;
    }
    .sv-editor-label {
      color: #a3a3a3;
      font-size: 11px;
      font-weight: 700;
    }
    .sv-editor-textarea {
      width: 100%;
      border-radius: 8px;
      border: 1px solid rgba(255,255,255,0.14);
      background: rgba(0,0,0,0.24);
      color: #f1f5f9;
      font-size: 12px;
      line-height: 1.45;
      padding: 8px 9px;
      resize: none;
      min-height: 110px;
      height: 110px;
    }
    .sv-editor-textarea[data-field="scriptText"] {
      min-height: 84px;
    }
    .sv-editor-textarea:focus {
      outline: none;
      border-color: rgba(249,115,22,0.7);
      box-shadow: 0 0 0 2px rgba(249,115,22,0.18);
    }
    body.sv-theme-light .sv-editor-textarea {
      border-color: rgba(15,23,42,0.18);
      background: rgba(248,250,252,0.92);
      color: #0f172a;
    }
    body.sv-theme-light .sv-editor-textarea:focus {
      border-color: rgba(249,115,22,0.72);
      box-shadow: 0 0 0 2px rgba(249,115,22,0.2);
    }
    .sv-editor-script-view {
      width: 100%;
      color: #e2e8f0;
      font-size: 12px;
      line-height: 1.5;
      white-space: pre-wrap;
      word-break: break-word;
      min-height: 84px;
      max-height: 84px;
      overflow: auto;
    }
    .sv-editor-save-tip {
      color: #fbbf24;
      font-size: 11px;
      opacity: 0;
      transition: opacity 140ms ease;
      justify-self: end;
    }
    .sv-editor-save-tip.is-visible {
      opacity: 1;
    }
    body.sv-theme-light .sv-editor-backdrop {
      background: rgba(15, 23, 42, 0.2);
    }
    body.sv-theme-light .sv-editor-sheet {
      background: rgba(255, 255, 255, 0.98);
      border-top-color: rgba(15, 23, 42, 0.12);
      box-shadow: 0 -14px 30px rgba(15, 23, 42, 0.16);
    }
    body.sv-theme-light .sv-editor-header {
      border-bottom-color: rgba(15, 23, 42, 0.1);
    }
    body.sv-theme-light .sv-editor-header-title {
      color: #0f172a;
    }
    body.sv-theme-light .sv-editor-btn {
      color: #334155;
      background: rgba(15, 23, 42, 0.08);
    }
    body.sv-theme-light .sv-editor-btn:hover {
      background: rgba(15, 23, 42, 0.14);
    }
    body.sv-theme-light .sv-editor-btn.is-primary {
      color: #ffffff;
      background: #f97316;
    }
    body.sv-theme-light .sv-editor-btn.is-primary:hover {
      background: #fb923c;
    }
    body.sv-theme-light .sv-editor-item {
      border-color: rgba(15, 23, 42, 0.12);
      background: rgba(248, 250, 252, 0.95);
    }
    body.sv-theme-light .sv-editor-item.is-active {
      border-color: rgba(249,115,22,0.72);
      box-shadow: inset 0 0 0 1px rgba(249,115,22,0.3);
    }
    body.sv-theme-light .sv-editor-item-title {
      color: #ea580c;
      background: rgba(249, 115, 22, 0.12);
    }
    body.sv-theme-light .sv-editor-item-title:hover {
      background: rgba(249, 115, 22, 0.18);
    }
    body.sv-theme-light .sv-editor-status {
      color: #64748b;
    }
    body.sv-theme-light .sv-editor-label {
      color: #475569;
    }
    body.sv-theme-light .sv-editor-script-view {
      color: #334155;
    }
    body.sv-theme-light .sv-editor-save-tip {
      color: #c2410c;
    }
    body.sv-editor-open #viewport {
      flex: 1 1 calc(100vh - var(--sv-editor-height, 50vh));
      min-height: 0;
    }
    body.sv-editor-open #sv-controls {
      display: none;
    }
    body.sv-recording .sv-toolbar,
    body.sv-recording .sv-scene-grid {
      display: none;
    }
    body.sv-recording #sv-controls {
      display: none;
    }
    body.sv-theme-light .sv-editor-list,
    body.sv-theme-light .sv-editor-script-view,
    body.sv-theme-light .sv-editor-textarea {
      scrollbar-color: rgba(148, 163, 184, 0.85) rgba(226, 232, 240, 0.85);
    }
    body.sv-theme-light .sv-editor-list::-webkit-scrollbar,
    body.sv-theme-light .sv-editor-script-view::-webkit-scrollbar,
    body.sv-theme-light .sv-editor-textarea::-webkit-scrollbar {
      width: 10px;
      height: 10px;
    }
    body.sv-theme-light .sv-editor-list::-webkit-scrollbar-track,
    body.sv-theme-light .sv-editor-script-view::-webkit-scrollbar-track,
    body.sv-theme-light .sv-editor-textarea::-webkit-scrollbar-track {
      background: rgba(226, 232, 240, 0.85);
      border-radius: 999px;
    }
    body.sv-theme-light .sv-editor-list::-webkit-scrollbar-thumb,
    body.sv-theme-light .sv-editor-script-view::-webkit-scrollbar-thumb,
    body.sv-theme-light .sv-editor-textarea::-webkit-scrollbar-thumb {
      background: rgba(148, 163, 184, 0.85);
      border-radius: 999px;
      border: 2px solid rgba(226, 232, 240, 0.85);
    }
    body.sv-theme-light .sv-editor-list::-webkit-scrollbar-thumb:hover,
    body.sv-theme-light .sv-editor-script-view::-webkit-scrollbar-thumb:hover,
    body.sv-theme-light .sv-editor-textarea::-webkit-scrollbar-thumb:hover {
      background: rgba(100, 116, 139, 0.95);
    }
/* scene-01 */
.sv-scene-root-scene-01 .s-scene-01-wrapper{
  position: relative;
  width: 100%;
  height: 100%;
  background-color: #1B1B1B;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  font-family: var(--sv-font-body-cn);
  color: #FFFFFF;
}.sv-scene-root-scene-01 .tech-grid{
  position: absolute;
  inset: 0;
  background-image: 
    linear-gradient(to right, rgba(255, 255, 255, 0.03) 1px, transparent 1px),
    linear-gradient(to bottom, rgba(255, 255, 255, 0.03) 1px, transparent 1px);
  background-size: 40px 40px;
  z-index: 1;
}.sv-scene-root-scene-01 .fluid-bg{
  position: absolute;
  inset: 0;
  background: radial-gradient(circle at 50% 50%, rgba(245, 53, 1, 0.05) 0%, transparent 70%);
  filter: blur(40px);
  animation: fluid-pulse 8s ease-in-out infinite alternate;
  z-index: 1;
}@keyframes fluid-pulse{
  from { transform: scale(1) translate(0, 0); }
  to { transform: scale(1.2) translate(5%, 5%); }
}.sv-scene-root-scene-01 .focus-frame{
  position: absolute;
  width: 280px;
  height: 280px;
  z-index: 2;
  transition: all 0.6s cubic-bezier(0.34, 1.56, 0.64, 1);
  opacity: 0;
  transform: scale(1.2);
}.sv-scene-root-scene-01 .focus-frame[data-step-active="true"]{
  opacity: 1;
  transform: scale(1);
}.sv-scene-root-scene-01 .corner{
  position: absolute;
  width: 20px;
  height: 20px;
  border: 2px solid #F53501;
}.sv-scene-root-scene-01 .top-left{ top: 0; left: 0; border-right: 0; border-bottom: 0; }.sv-scene-root-scene-01 .top-right{ top: 0; right: 0; border-left: 0; border-bottom: 0; }.sv-scene-root-scene-01 .bottom-left{ bottom: 0; left: 0; border-right: 0; border-top: 0; }.sv-scene-root-scene-01 .bottom-right{ bottom: 0; right: 0; border-left: 0; border-top: 0; }.sv-scene-root-scene-01 .main-content{
  position: relative;
  z-index: 10;
  text-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 40px;
}.sv-scene-root-scene-01 .logo-container{
  position: relative;
  width: 120px;
  height: 120px;
  display: flex;
  align-items: center;
  justify-content: center;
  opacity: 0;
  transition: all 0.8s cubic-bezier(0.175, 0.885, 0.32, 1.275);
}.sv-scene-root-scene-01 .logo-container[data-step-active="true"]{
  opacity: 1;
  transform: scale(1.1);
}.sv-scene-root-scene-01 .tencent-logo{
  font-size: 80px;
  color: #FFFFFF;
  filter: drop-shadow(0 0 20px rgba(245, 53, 1, 0.3));
}.sv-scene-root-scene-01 .logo-glow{
  position: absolute;
  width: 100%;
  height: 100%;
  background: #F53501;
  filter: blur(40px);
  opacity: 0.15;
  z-index: -1;
}.sv-scene-root-scene-01 .text-glass-panel{
  padding: 16px 24px;
  background: rgba(255, 255, 255, 0.05);
  backdrop-filter: blur(12px);
  border: 1px solid rgba(255, 255, 255, 0.1);
  border-radius: 4px;
  opacity: 0;
  transform: translateY(20px);
  transition: all 0.6s ease-out;
}.sv-scene-root-scene-01 .text-glass-panel[data-step-active="true"]{
  opacity: 1;
  transform: translateY(0);
}.sv-scene-root-scene-01 .shimmer-text{
  font-family: var(--sv-font-display-cn);
  font-size: 24px;
  font-weight: 900;
  margin: 0;
  background: linear-gradient(90deg, #FFFFFF 0%, #F53501 50%, #FFFFFF 100%);
  background-size: 200% auto;
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  animation: shimmer 3s linear infinite;
}@keyframes shimmer{
  to { background-position: 200% center; }
}.sv-scene-root-scene-01 .accent-bar{
  width: 40px;
  height: 3px;
  background: #F53501;
  margin: 8px auto 0;
}.sv-scene-root-scene-01 .tech-metadata{
  position: absolute;
  bottom: 60px;
  display: flex;
  align-items: center;
  gap: 20px;
  opacity: 0;
  transition: opacity 1s ease;
}.sv-scene-root-scene-01 .tech-metadata[data-step-active="true"]{
  opacity: 0.6;
}.sv-scene-root-scene-01 .meta-item{
  display: flex;
  flex-direction: column;
  align-items: center;
}.sv-scene-root-scene-01 .meta-item .label{
  font-family: var(--sv-font-mono);
  font-size: 10px;
  color: #F53501;
  letter-spacing: 2px;
}.sv-scene-root-scene-01 .meta-item .value{
  font-family: var(--sv-font-mono);
  font-size: 14px;
  font-weight: bold;
}.sv-scene-root-scene-01 .meta-line{
  width: 1px;
  height: 30px;
  background: rgba(255, 255, 255, 0.2);
}

/* scene-02 */
.sv-scene-root-scene-02 .s-scene-02-wrapper{
  position: relative;
  width: 100%;
  height: 100%;
  background-color: #1B1B1B;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  font-family: var(--sv-font-body-cn);
}.sv-scene-root-scene-02 .tech-grid{
  position: absolute;
  inset: 0;
  background-image: linear-gradient(rgba(255, 255, 255, 0.03) 1px, transparent 1px), linear-gradient(90deg, rgba(255, 255, 255, 0.03) 1px, transparent 1px);
  background-size: 40px 40px;
  z-index: 1;
}.sv-scene-root-scene-02 .retro-window{
  position: relative;
  z-index: 10;
  width: 320px;
  height: 480px;
  background: #C0C0C0;
  border: 3px solid #FFFFFF;
  border-right-color: #808080;
  border-bottom-color: #808080;
  box-shadow: 10px 10px 0px rgba(0, 0, 0, 0.3);
  display: flex;
  flex-direction: column;
  opacity: 0;
  transform: translateY(20px);
  transition: all 0.6s cubic-bezier(0.23, 1, 0.32, 1);
}.sv-scene-root-scene-02 .retro-window[data-step-active="true"]{
  opacity: 1;
  transform: translateY(0);
}.sv-scene-root-scene-02 .window-header{
  background: linear-gradient(90deg, #000080, #1084D0);
  padding: 4px 8px;
  display: flex;
  justify-content: space-between;
  align-items: center;
  color: #FFF;
  font-family: var(--sv-font-mono);
  font-size: 12px;
  font-weight: bold;
}.sv-scene-root-scene-02 .win-btn{
  width: 16px;
  height: 14px;
  background: #C0C0C0;
  border: 1px solid #FFF;
  border-right-color: #000;
  border-bottom-color: #000;
  color: #000;
  font-size: 10px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  margin-left: 2px;
}.sv-scene-root-scene-02 .window-body{
  flex-grow: 1;
  background: #000;
  margin: 4px;
  padding: 15px;
  position: relative;
  overflow: hidden;
  color: #FFF;
  font-family: var(--sv-font-mono);
  border: 2px inset #808080;
}.sv-scene-root-scene-02 .terminal-text{
  font-size: 14px;
  line-height: 1.6;
  white-space: pre-wrap;
  color: #F53501;
}.sv-scene-root-scene-02 .hero-penguin-container{
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  opacity: 0;
  transform: scale(0.5);
  transition: all 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275);
}.sv-scene-root-scene-02 .hero-penguin-container[data-step-active="true"]{
  opacity: 1;
  transform: scale(1);
}.sv-scene-root-scene-02 .penguin-icon{
  font-size: 120px;
  color: #FFF;
  z-index: 5;
}.sv-scene-root-scene-02 .scarf-accent{
  position: absolute;
  width: 60px;
  height: 10px;
  background: #F53501;
  top: 60%;
  left: 50%;
  transform: translateX(-50%);
  border-radius: 5px;
  box-shadow: 0 0 15px #F53501;
  z-index: 6;
}.sv-scene-root-scene-02 .penguin-glow{
  position: absolute;
  width: 140px;
  height: 140px;
  background: #F53501;
  filter: blur(40px);
  opacity: 0.4;
  border-radius: 50%;
}.sv-scene-root-scene-02 .bubble-burst-layer{
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 20;
}.sv-scene-root-scene-02 .chat-bubble{
  position: absolute;
  padding: 8px 12px;
  background: #FFF;
  border: 1px solid #F53501;
  border-radius: 12px;
  font-size: 12px;
  color: #1B1B1B;
  opacity: 0;
  animation: bubble-float 2s ease-out forwards;
}@keyframes bubble-float{
  0% { transform: translate(0, 0) scale(0); opacity: 0; }
  20% { opacity: 1; }
  100% { transform: translate(var(--tx), var(--ty)) scale(1); opacity: 0; }
}.sv-scene-root-scene-02 .window-status{
  padding: 4px 8px;
  font-size: 10px;
  color: #000;
  background: #C0C0C0;
  border-top: 1px solid #808080;
  display: flex;
  justify-content: space-between;
}.sv-scene-root-scene-02 .blink-dot{ color: #F53501; animation: blink 1s infinite; }@keyframes blink{ 50% { opacity: 0; } }.sv-scene-root-scene-02 .scene-tag{
  position: absolute;
  top: 10%;
  left: 40px;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  opacity: 0;
  transition: all 0.8s ease;
}.sv-scene-root-scene-02 .scene-tag[data-step-active="true"]{ opacity: 1; }.sv-scene-root-scene-02 .tag-year{
  font-family: var(--sv-font-display-cn);
  font-size: 42px;
  color: #F53501;
  line-height: 1;
}.sv-scene-root-scene-02 .tag-line{
  width: 100px;
  height: 2px;
  background: #F53501;
  margin: 10px 0;
}.sv-scene-root-scene-02 .tag-event{
  font-family: var(--sv-font-mono);
  font-size: 12px;
  color: #FFF;
  letter-spacing: 4px;
}

/* scene-03 */
.sv-scene-root-scene-03 .s-scene-03-wrapper{
  position: relative;
  width: 100%;
  height: 100%;
  background-color: #1B1B1B;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  font-family: var(--sv-font-body-cn);
}.sv-scene-root-scene-03 .tech-grid{
  position: absolute;
  inset: 0;
  background-image: linear-gradient(rgba(255, 255, 255, 0.05) 1px, transparent 1px), linear-gradient(90deg, rgba(255, 255, 255, 0.05) 1px, transparent 1px);
  background-size: 30px 30px;
  z-index: 1;
}.sv-scene-root-scene-03 /* Step 0: Chart Animation */
.chart-container{
  position: absolute;
  bottom: 0;
  width: 100%;
  height: 50%;
  z-index: 2;
  opacity: 0;
  transition: opacity 1s ease;
}.sv-scene-root-scene-03 .chart-container[data-step-active="true"]{
  opacity: 1;
}.sv-scene-root-scene-03 .chart-path{
  stroke-dasharray: 1000;
  stroke-dashoffset: 1000;
}.sv-scene-root-scene-03 .chart-container[data-step-active="true"] .chart-path{
  animation: chart-draw 2.5s cubic-bezier(0.4, 0, 0.2, 1) forwards;
}@keyframes chart-draw{
  to { stroke-dashoffset: 0; }
}.sv-scene-root-scene-03 .stat-label{
  position: absolute;
  left: 20px;
  top: 20px;
  color: #F53501;
  font-family: var(--sv-font-mono);
  font-size: 12px;
  letter-spacing: 2px;
  border-left: 2px solid #F53501;
  padding-left: 8px;
}.sv-scene-root-scene-03 /* Step 1: Coin Hero */
.coin-hero{
  position: relative;
  z-index: 10;
  display: flex;
  flex-direction: column;
  align-items: center;
  opacity: 0;
  transform: translateY(-50px) scale(0.8);
  transition: all 0.8s cubic-bezier(0.34, 1.56, 0.64, 1);
}.sv-scene-root-scene-03 .coin-hero[data-step-active="true"]{
  opacity: 1;
  transform: translateY(0) scale(1);
}.sv-scene-root-scene-03 .coin-3d{
  width: 180px;
  height: 180px;
  perspective: 1000px;
  margin-bottom: 24px;
}.sv-scene-root-scene-03 .coin-face{
  width: 100%;
  height: 100%;
  background: linear-gradient(135deg, #FFD700 0%, #B8860B 50%, #FFD700 100%);
  border-radius: 50%;
  padding: 10px;
  box-shadow: 0 20px 40px rgba(0,0,0,0.5), inset 0 0 20px rgba(255,255,255,0.5);
  animation: coin-spin 6s infinite linear;
}.sv-scene-root-scene-03 .coin-inner{
  width: 100%;
  height: 100%;
  border: 4px solid rgba(0,0,0,0.1);
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  position: relative;
  overflow: hidden;
}.sv-scene-root-scene-03 .stock-symbol{
  font-family: var(--sv-font-display-cn);
  font-size: 24px;
  color: #5C4033;
  font-weight: 900;
  text-shadow: 1px 1px 0px rgba(255,255,255,0.3);
}.sv-scene-root-scene-03 .coin-reflection{
  position: absolute;
  top: -50%;
  left: -50%;
  width: 200%;
  height: 200%;
  background: linear-gradient(45deg, transparent 45%, rgba(255,255,255,0.6) 50%, transparent 55%);
  animation: reflect 3s infinite ease-in-out;
}@keyframes reflect{ 0% { transform: translate(-20%, -20%); } 100% { transform: translate(20%, 20%); } }@keyframes coin-spin{ 0% { transform: rotateY(0deg); } 100% { transform: rotateY(360deg); } }.sv-scene-root-scene-03 .main-title{
  font-family: var(--sv-font-display-cn);
  font-size: 32px;
  color: #FFFFFF;
  text-transform: uppercase;
  letter-spacing: 2px;
}.sv-scene-root-scene-03 /* Step 2: Biz Grid */
.business-grid{
  position: absolute;
  bottom: 12%;
  width: 90%;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 12px;
  z-index: 15;
}.sv-scene-root-scene-03 .biz-card{
  background: rgba(255, 255, 255, 0.03);
  border: 1px solid rgba(255, 255, 255, 0.1);
  padding: 16px 8px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  opacity: 0;
  transform: translateY(20px);
  transition: all 0.5s ease;
  transition-delay: calc(var(--i) * 0.15s);
}.sv-scene-root-scene-03 .business-grid[data-step-active="true"] .biz-card{
  opacity: 1;
  transform: translateY(0);
  border-color: rgba(245, 53, 1, 0.3);
}.sv-scene-root-scene-03 .biz-icon{
  font-size: 28px;
  color: #F53501;
}.sv-scene-root-scene-03 .biz-name{
  font-family: var(--sv-font-display-cn);
  font-size: 16px;
  color: #FFFFFF;
}.sv-scene-root-scene-03 .marker-line{
  position: absolute;
  left: 5%;
  width: 90%;
  height: 1px;
  background: rgba(245, 53, 1, 0.2);
  z-index: 5;
}.sv-scene-root-scene-03 .marker-line.top{ top: 20%; }.sv-scene-root-scene-03 .marker-line.bottom{ bottom: 20%; }

/* scene-04 */
.sv-scene-root-scene-04 .s-scene-04-wrapper{
  position: relative;
  width: 100%;
  height: 100%;
  background-color: #1B1B1B;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  font-family: var(--sv-font-body-cn);
}.sv-scene-root-scene-04 .tech-grid{
  position: absolute;
  inset: 0;
  background-image: linear-gradient(rgba(255, 255, 255, 0.05) 1px, transparent 1px), linear-gradient(90deg, rgba(255, 255, 255, 0.05) 1px, transparent 1px);
  background-size: 40px 40px;
  z-index: 1;
}.sv-scene-root-scene-04 .wechat-wash{
  position: absolute;
  inset: 0;
  background: radial-gradient(circle at center, rgba(7, 193, 96, 0.2) 0%, transparent 70%);
  opacity: 0;
  transition: opacity 1.2s ease;
  z-index: 2;
}.sv-scene-root-scene-04 .wechat-wash[data-step-active="true"]{ opacity: 1; }.sv-scene-root-scene-04 .hero-container{
  position: relative;
  z-index: 10;
  text-align: center;
  opacity: 0;
  transform: scale(0.9);
  transition: all 0.8s cubic-bezier(0.175, 0.885, 0.32, 1.275);
}.sv-scene-root-scene-04 .hero-container[data-step-active="true"]{
  opacity: 1;
  transform: scale(1);
}.sv-scene-root-scene-04 .year-tag{
  font-family: var(--sv-font-mono);
  font-size: 14px;
  color: #F53501;
  background: rgba(245, 53, 1, 0.1);
  padding: 2px 10px;
  border: 1px solid #F53501;
  display: inline-block;
  margin-bottom: 20px;
  letter-spacing: 2px;
}.sv-scene-root-scene-04 .logo-box{
  position: relative;
  width: 120px;
  height: 120px;
  margin: 0 auto 30px;
}.sv-scene-root-scene-04 .main-logo{
  font-size: 120px;
  color: #07C160;
  filter: drop-shadow(0 0 20px rgba(7, 193, 96, 0.4));
}.sv-scene-root-scene-04 .logo-glow{
  position: absolute;
  inset: -20px;
  background: radial-gradient(circle, rgba(7, 193, 96, 0.15) 0%, transparent 70%);
  animation: logo-pulse 3s infinite ease-in-out;
}.sv-scene-root-scene-04 .main-title{
  font-family: var(--sv-font-display-cn);
  font-size: 48px;
  color: #FFFFFF;
  margin: 0;
  letter-spacing: 4px;
}.sv-scene-root-scene-04 /* Step 1: Ripple */
.ripple-system{
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-end;
  padding-bottom: 25%;
  z-index: 5;
  opacity: 0;
  transition: opacity 0.5s ease;
}.sv-scene-root-scene-04 .ripple-system[data-step-active="true"]{ opacity: 1; }.sv-scene-root-scene-04 .ripple-circle{
  position: absolute;
  top: 50%;
  left: 50%;
  border: 1px solid rgba(7, 193, 96, 0.3);
  border-radius: 50%;
  transform: translate(-50%, -50%) scale(0);
}.sv-scene-root-scene-04 .ripple-system[data-step-active="true"] .ripple-circle{
  animation: ripple-out 4s infinite linear;
}.sv-scene-root-scene-04 .c1{ width: 300px; height: 300px; animation-delay: 0s; }.sv-scene-root-scene-04 .c2{ width: 300px; height: 300px; animation-delay: 1.3s; }.sv-scene-root-scene-04 .c3{ width: 300px; height: 300px; animation-delay: 2.6s; }.sv-scene-root-scene-04 .connection-text{
  font-family: var(--sv-font-display-cn);
  color: #07C160;
  font-size: 20px;
  letter-spacing: 8px;
  text-transform: uppercase;
  opacity: 0.8;
}.sv-scene-root-scene-04 /* Step 2: Ecosystem */
.ecosystem-radial{
  position: absolute;
  inset: 0;
  z-index: 15;
  pointer-events: none;
}.sv-scene-root-scene-04 .eco-item{
  position: absolute;
  top: 50%;
  left: 50%;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  opacity: 0;
  transition: all 0.8s cubic-bezier(0.34, 1.56, 0.64, 1);
  --dist: 160px;
}.sv-scene-root-scene-04 .ecosystem-radial[data-step-active="true"] .eco-item{
  opacity: 1;
  transform: translate(calc(cos(var(--angle)) * var(--dist) - 50%), calc(sin(var(--angle)) * var(--dist) - 50%));
}.sv-scene-root-scene-04 .eco-icon-wrapper{
  width: 60px;
  height: 60px;
  background: rgba(255, 255, 255, 0.05);
  border: 1px solid rgba(7, 193, 96, 0.4);
  border-radius: 12px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 32px;
  color: #07C160;
  box-shadow: 0 10px 20px rgba(0,0,0,0.3);
  animation: float-breathing 4s infinite ease-in-out;
}.sv-scene-root-scene-04 .eco-label{
  font-size: 14px;
  color: #FFFFFF;
  white-space: nowrap;
  font-weight: 600;
  text-shadow: 0 2px 4px rgba(0,0,0,0.5);
}.sv-scene-root-scene-04 /* Decor Frames */
.focus-frame-top, .sv-scene-root-scene-04 .focus-frame-bottom{
  position: absolute;
  left: 10%;
  width: 80%;
  height: 20px;
  border: 1px solid rgba(245, 53, 1, 0.2);
  z-index: 5;
}.sv-scene-root-scene-04 .focus-frame-top{
  top: 5%;
  border-bottom: none;
}.sv-scene-root-scene-04 .focus-frame-bottom{
  bottom: 5%;
  border-top: none;
}@keyframes logo-pulse{ 0%, 100% { transform: scale(1); opacity: 0.15; } 50% { transform: scale(1.1); opacity: 0.3; } }@keyframes ripple-out{ from { transform: translate(-50%, -50%) scale(0.5); opacity: 1; } to { transform: translate(-50%, -50%) scale(2.5); opacity: 0; } }@keyframes float-breathing{
  0%, 100% { transform: translateY(0); } 
  50% { transform: translateY(-10px); }
}

/* scene-05 */
.sv-scene-root-scene-05 .s-scene-05-wrapper{
  position: relative;
  width: 100%;
  height: 100%;
  background-color: #1B1B1B;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  font-family: var(--sv-font-body-cn);
}.sv-scene-root-scene-05 .tech-grid{
  position: absolute;
  inset: 0;
  background-image: linear-gradient(rgba(255, 255, 255, 0.03) 1px, transparent 1px), linear-gradient(90deg, rgba(255, 255, 255, 0.03) 1px, transparent 1px);
  background-size: 30px 30px;
  z-index: 1;
}.sv-scene-root-scene-05 .blue-neural-wash{
  position: absolute;
  inset: 0;
  background: radial-gradient(circle at 50% 50%, rgba(10, 17, 40, 0.8) 0%, transparent 100%);
  opacity: 0;
  transition: opacity 1.5s ease;
  z-index: 2;
}.sv-scene-root-scene-05 .blue-neural-wash[data-step-active="true"]{ opacity: 1; }.sv-scene-root-scene-05 .neural-net-container{
  position: absolute;
  inset: 0;
  z-index: 3;
  opacity: 0;
  transition: opacity 1s ease;
}.sv-scene-root-scene-05 .neural-net-container[data-step-active="true"]{ opacity: 0.6; }.sv-scene-root-scene-05 .neural-svg{
  width: 100%;
  height: 100%;
  filter: drop-shadow(0 0 5px rgba(245, 53, 1, 0.4));
}.sv-scene-root-scene-05 /* Step 1: Cloud Ring */
.cloud-progress-container{
  position: absolute;
  top: 15%;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 15px;
  opacity: 0;
  transform: translateY(-50px);
  transition: all 0.8s cubic-bezier(0.23, 1, 0.32, 1);
  z-index: 10;
}.sv-scene-root-scene-05 .cloud-progress-container[data-step-active="true"]{
  opacity: 1;
  transform: translateY(0);
}.sv-scene-root-scene-05 .frosted-ring{
  position: relative;
  width: 100px;
  height: 100px;
  background: rgba(255, 255, 255, 0.05);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  border-radius: 50%;
  border: 1px solid rgba(255, 255, 255, 0.1);
  display: flex;
  align-items: center;
  justify-content: center;
}.sv-scene-root-scene-05 .ring-spinner{
  position: absolute;
  inset: -5px;
  border: 2px solid transparent;
  border-top: 2px solid #F53501;
  border-radius: 50%;
  animation: spin 2s linear infinite;
}.sv-scene-root-scene-05 .cloud-icon{
  font-size: 40px;
  color: #FFFFFF;
  filter: drop-shadow(0 0 10px rgba(255, 255, 255, 0.3));
}.sv-scene-root-scene-05 .status-label{
  font-family: var(--sv-font-mono);
  color: #F53501;
  font-size: 10px;
  letter-spacing: 2px;
  font-weight: bold;
}.sv-scene-root-scene-05 /* Step 2: Hero Text */
.hero-content-area{
  position: relative;
  z-index: 20;
  text-align: center;
  opacity: 0;
  transform: scale(0.9);
  transition: all 1s cubic-bezier(0.34, 1.56, 0.64, 1);
}.sv-scene-root-scene-05 .hero-content-area[data-step-active="true"]{
  opacity: 1;
  transform: scale(1);
}.sv-scene-root-scene-05 .vision-title{
  font-family: var(--sv-font-display-cn);
  font-size: 72px;
  color: #FFFFFF;
  margin: 0;
  line-height: 1.2;
  display: flex;
  flex-direction: column;
}.sv-scene-root-scene-05 .glow-text{
  text-shadow: 0 0 20px rgba(255, 255, 255, 0.2);
}.sv-scene-root-scene-05 .glow-text.highlight{
  color: #F53501;
  text-shadow: 0 0 30px rgba(245, 53, 1, 0.6);
  border: 2px solid #F53501;
  padding: 0 10px;
  margin-top: 10px;
}.sv-scene-root-scene-05 .tech-tag-line{
  font-family: var(--sv-font-mono);
  color: rgba(255, 255, 255, 0.4);
  font-size: 14px;
  letter-spacing: 6px;
  margin-top: 20px;
}.sv-scene-root-scene-05 .particle-emitter{
  position: absolute;
  inset: -100px;
  pointer-events: none;
}.sv-scene-root-scene-05 /* Decor elements */
.frame-bracket{
  position: absolute;
  width: 20px;
  height: 20px;
  border: 2px solid #F53501;
  z-index: 50;
}.sv-scene-root-scene-05 .tl{ top: 40px; left: 40px; border-right: none; border-bottom: none; }.sv-scene-root-scene-05 .tr{ top: 40px; right: 40px; border-left: none; border-bottom: none; }.sv-scene-root-scene-05 .bl{ bottom: 40px; left: 40px; border-right: none; border-top: none; }.sv-scene-root-scene-05 .br{ bottom: 40px; right: 40px; border-left: none; border-top: none; }.sv-scene-root-scene-05 .data-stream-side{
  position: absolute;
  right: 10px;
  top: 50%;
  transform: rotate(90deg) translate(-50%, 0);
  font-family: var(--sv-font-mono);
  font-size: 10px;
  color: rgba(245, 53, 1, 0.5);
  letter-spacing: 2px;
}@keyframes spin{ from { transform: rotate(0deg); } to { transform: rotate(360deg); } }.sv-scene-root-scene-05 .particle{
  position: absolute;
  background: #F53501;
  width: 2px;
  height: 2px;
  border-radius: 50%;
  animation: float-up var(--d) linear infinite;
  opacity: 0;
}@keyframes float-up{
  0% { transform: translateY(0); opacity: 0; }
  20% { opacity: 0.8; }
  100% { transform: translateY(-150px); opacity: 0; }
}

/* scene-06 */
.sv-scene-root-scene-06 .s-scene-06-wrapper{
  position: relative;
  width: 100%;
  height: 100%;
  background-color: #1B1B1B;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  transition: transform 1.2s cubic-bezier(0.7, 0, 0.3, 1), opacity 1.2s ease;
}.sv-scene-root-scene-06 .s-scene-06-wrapper[data-closing="true"]{
  transform: scale(0.9);
  opacity: 0;
}.sv-scene-root-scene-06 .bg-base{
  position: absolute;
  inset: 0;
  background: #1B1B1B;
  z-index: 1;
}.sv-scene-root-scene-06 .bg-gradient-overlay{
  position: absolute;
  inset: 0;
  background: radial-gradient(circle at 50% 50%, #FFFFFF 0%, #E6F0FF 40%, #B8D4FF 100%);
  opacity: 0;
  transition: opacity 1.5s cubic-bezier(0.4, 0, 0.2, 1);
  z-index: 2;
}.sv-scene-root-scene-06 .bg-gradient-overlay[data-step-active="true"]{
  opacity: 1;
}.sv-scene-root-scene-06 .tech-grid-closing{
  position: absolute;
  inset: 0;
  background-image: linear-gradient(rgba(245, 53, 1, 0.05) 1px, transparent 1px), linear-gradient(90deg, rgba(245, 53, 1, 0.05) 1px, transparent 1px);
  background-size: 40px 40px;
  z-index: 3;
  opacity: 0.3;
}.sv-scene-root-scene-06 .slogan-container{
  position: relative;
  z-index: 10;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  opacity: 0;
  transform: translateY(30px);
  transition: all 1s cubic-bezier(0.23, 1, 0.32, 1);
}.sv-scene-root-scene-06 .slogan-container[data-step-active="true"]{
  opacity: 1;
  transform: translateY(0);
}.sv-scene-root-scene-06 .slogan-line{
  overflow: hidden;
}.sv-scene-root-scene-06 .slogan-text{
  display: block;
  font-family: var(--sv-font-display-cn);
  font-size: 48px;
  color: #1B1B1B;
  letter-spacing: 4px;
  line-height: 1.2;
}.sv-scene-root-scene-06 .slogan-text.highlight{
  color: #F53501;
}.sv-scene-root-scene-06 .slogan-divider{
  width: 40px;
  height: 4px;
  background: #F53501;
  margin: 20px 0;
}.sv-scene-root-scene-06 .anniversary-footer{
  position: absolute;
  bottom: 12%;
  display: flex;
  flex-direction: column;
  align-items: center;
  z-index: 10;
  opacity: 0;
  transform: translateY(20px);
  transition: all 0.8s ease-out;
}.sv-scene-root-scene-06 .anniversary-footer[data-step-active="true"]{
  opacity: 1;
  transform: translateY(0);
}.sv-scene-root-scene-06 .logo-wrap{
  display: flex;
  align-items: center;
  gap: 15px;
  margin-bottom: 10px;
}.sv-scene-root-scene-06 .tencent-logo{
  font-size: 32px;
  color: #0052D9;
}.sv-scene-root-scene-06 .logo-divider{
  width: 1px;
  height: 20px;
  background: rgba(0, 0, 0, 0.2);
}.sv-scene-root-scene-06 .anniversary-badge{
  display: flex;
  flex-direction: column;
  align-items: flex-start;
}.sv-scene-root-scene-06 .anniversary-badge .num{
  font-family: var(--sv-font-mono);
  font-weight: 900;
  font-size: 24px;
  line-height: 1;
  color: #1B1B1B;
}.sv-scene-root-scene-06 .anniversary-badge .text{
  font-family: var(--sv-font-mono);
  font-size: 8px;
  letter-spacing: 1px;
  color: #666;
}.sv-scene-root-scene-06 .brand-subtext{
  font-family: var(--sv-font-mono);
  font-size: 10px;
  color: rgba(0, 0, 0, 0.4);
  letter-spacing: 2px;
}.sv-scene-root-scene-06 /* Tech Decors */
.frame-bracket{
  position: absolute;
  width: 15px;
  height: 15px;
  border: 1.5px solid #F53501;
  z-index: 20;
  opacity: 0.5;
}.sv-scene-root-scene-06 .tl{ top: 40px; left: 40px; border-right: none; border-bottom: none; }.sv-scene-root-scene-06 .tr{ top: 40px; right: 40px; border-left: none; border-bottom: none; }.sv-scene-root-scene-06 .bl{ bottom: 40px; left: 40px; border-right: none; border-top: none; }.sv-scene-root-scene-06 .br{ bottom: 40px; right: 40px; border-left: none; border-top: none; }.sv-scene-root-scene-06 .scan-line{
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 2px;
  background: linear-gradient(90deg, transparent, #F53501, transparent);
  z-index: 5;
  opacity: 0.1;
  animation: scan 4s linear infinite;
}@keyframes scan{ 0% { top: 0; } 100% { top: 100%; } }
  </style>
</head>
<body data-orientation="portrait">
  <div id="app">
    <div id="viewport">
      <div id="stage">

<section class="scene" data-scene-id="scene-01">
  <div class="sv-scene-root sv-scene-root-scene-01" data-scene-root-id="scene-01">
<div class="s-scene-01-wrapper">
  <div class="tech-grid"></div>
  <div class="fluid-bg"></div>
  
  <div class="focus-frame" data-step="0">
    <div class="corner top-left"></div>
    <div class="corner top-right"></div>
    <div class="corner bottom-left"></div>
    <div class="corner bottom-right"></div>
  </div>

  <div class="main-content">
    <div class="logo-container" data-step="0">
      <iconify-icon icon="simple-icons:tencent" class="tencent-logo"></iconify-icon>
      <div class="logo-glow"></div>
    </div>

    <div class="text-glass-panel" data-step="1">
      <h1 class="shimmer-text">腾讯：连接未来的25年</h1>
      <div class="accent-bar"></div>
    </div>
  </div>

  <div class="tech-metadata" data-step="0">
    <div class="meta-item">
      <span class="label">EST.</span>
      <span class="value">1998</span>
    </div>
    <div class="meta-line"></div>
    <div class="meta-item">
      <span class="label">GLOBAL</span>
      <span class="value">CONNECT</span>
    </div>
  </div>
</div>
  </div>
</section>

<section class="scene" data-scene-id="scene-02">
  <div class="sv-scene-root sv-scene-root-scene-02" data-scene-root-id="scene-02">
<div class="s-scene-02-wrapper">
  <div class="tech-grid"></div>
  
  <!-- Step 0: Retro Window -->
  <div class="retro-window" data-step="0">
    <div class="window-header">
      <div class="header-title">OICQ_SETUP.EXE</div>
      <div class="header-controls">
        <span class="win-btn">_</span>
        <span class="win-btn">□</span>
        <span class="win-btn close">×</span>
      </div>
    </div>
    <div class="window-body">
      <div class="terminal-cursor"></div>
      <div class="terminal-text" id="terminal-typing-area"></div>
      
      <!-- Step 2: Penguin Hero -->
      <div class="hero-penguin-container" data-step="2">
        <div class="penguin-glow"></div>
        <iconify-icon icon="simple-icons:tencentqq" class="penguin-icon"></iconify-icon>
        <div class="scarf-accent"></div>
      </div>
    </div>
    <div class="window-status">
      <span>Status: Initializing...</span>
      <span class="blink-dot">●</span>
    </div>
  </div>

  <!-- Step 2: Bursting Bubbles Area -->
  <div class="bubble-burst-layer" data-step="2">
    <!-- Generated by JS -->
  </div>

  <!-- Scene Metadata -->
  <div class="scene-tag" data-step="0">
    <span class="tag-year">1999</span>
    <div class="tag-line"></div>
    <span class="tag-event">LAUNCH EVENT</span>
  </div>
</div>
  </div>
</section>

<section class="scene" data-scene-id="scene-03">
  <div class="sv-scene-root sv-scene-root-scene-03" data-scene-root-id="scene-03">
<div class="s-scene-03-wrapper">
  <div class="tech-grid"></div>
  
  <!-- Step 0: Background Growth Chart -->
  <div class="chart-container" data-step="0">
    <svg viewBox="0 0 400 800" class="growth-svg">
      <defs>
        <linearGradient id="lineGrad" x1="0%" y1="0%" x2="0%" y2="100%">
          <stop offset="0%" stop-color="#F53501" stop-opacity="1" />
          <stop offset="100%" stop-color="#F53501" stop-opacity="0.1" />
        </linearGradient>
      </defs>
      <path class="chart-path" d="M0,600 L100,550 L200,580 L300,450 L400,300" fill="none" stroke="#F53501" stroke-width="4" />
      <path class="chart-fill" d="M0,600 L100,550 L200,580 L300,450 L400,300 L400,800 L0,800 Z" fill="url(#lineGrad)" opacity="0.1" />
    </svg>
    <div class="stat-label">IPO GROWTH</div>
  </div>

  <!-- Step 1: Golden Coin -->
  <div class="coin-hero" data-step="1">
    <div class="coin-3d">
      <div class="coin-face">
        <div class="coin-inner">
          <span class="stock-symbol">HK.0700</span>
          <div class="coin-reflection"></div>
        </div>
      </div>
    </div>
    <h1 class="main-title">2004 香港上市</h1>
  </div>

  <!-- Step 2: Grid Cards -->
  <div class="business-grid" data-step="2">
    <div class="biz-card" style="--i:0">
      <iconify-icon icon="lucide:layout-template" class="biz-icon"></iconify-icon>
      <span class="biz-name">门户</span>
    </div>
    <div class="biz-card" style="--i:1">
      <iconify-icon icon="lucide:gamepad-2" class="biz-icon"></iconify-icon>
      <span class="biz-name">游戏</span>
    </div>
    <div class="biz-card" style="--i:2">
      <iconify-icon icon="lucide:layout-grid" class="biz-icon"></iconify-icon>
      <span class="biz-name">空间</span>
    </div>
  </div>

  <div class="marker-line top"></div>
  <div class="marker-line bottom"></div>
</div>
  </div>
</section>

<section class="scene" data-scene-id="scene-04">
  <div class="sv-scene-root sv-scene-root-scene-04" data-scene-root-id="scene-04">
<div class="s-scene-04-wrapper">
  <div class="tech-grid"></div>
  
  <!-- Background Green Wash -->
  <div class="wechat-wash" data-step="0"></div>

  <!-- Step 0: Central Logo & Year -->
  <div class="hero-container" data-step="0">
    <div class="year-tag">2011</div>
    <div class="logo-box">
      <iconify-icon icon="simple-icons:wechat" class="main-logo"></iconify-icon>
      <div class="logo-glow"></div>
    </div>
    <h1 class="main-title">微信诞生</h1>
  </div>

  <!-- Step 1: Connection Ripples -->
  <div class="ripple-system" data-step="1">
    <div class="ripple-circle c1"></div>
    <div class="ripple-circle c2"></div>
    <div class="ripple-circle c3"></div>
    <div class="connection-text">连接一切</div>
  </div>

  <!-- Step 2: Ecosystem Icons -->
  <div class="ecosystem-radial" data-step="2">
    <div class="eco-item p-pay" style="--angle: -120deg">
      <div class="eco-icon-wrapper">
        <iconify-icon icon="ant-design:wechat-pay-outlined"></iconify-icon>
      </div>
      <span class="eco-label">移动支付</span>
    </div>
    <div class="eco-item p-mini" style="--angle: 0deg">
      <div class="eco-icon-wrapper">
        <iconify-icon icon="lucide:component"></iconify-icon>
      </div>
      <span class="eco-label">小程序</span>
    </div>
    <div class="eco-item p-moments" style="--angle: 120deg">
      <div class="eco-icon-wrapper">
        <iconify-icon icon="lucide:aperture"></iconify-icon>
      </div>
      <span class="eco-label">朋友圈</span>
    </div>
  </div>

  <!-- Decor elements -->
  <div class="focus-frame-top"></div>
  <div class="focus-frame-bottom"></div>
</div>
  </div>
</section>

<section class="scene" data-scene-id="scene-05">
  <div class="sv-scene-root sv-scene-root-scene-05" data-scene-root-id="scene-05">
<div class="s-scene-05-wrapper">
  <div class="tech-grid"></div>
  
  <!-- Step 0: Neural Network & Star Blue Wash -->
  <div class="blue-neural-wash" data-step="0"></div>
  <div class="neural-net-container" data-step="0">
    <svg class="neural-svg" viewBox="0 0 400 800">
      <g class="nodes">
        <circle cx="100" cy="200" r="3" fill="#F53501" />
        <circle cx="300" cy="250" r="3" fill="#F53501" />
        <circle cx="200" cy="400" r="4" fill="#F53501" />
        <circle cx="150" cy="600" r="3" fill="#F53501" />
        <circle cx="250" cy="550" r="3" fill="#F53501" />
      </g>
      <g class="links" stroke="rgba(245, 53, 1, 0.3)" stroke-width="1">
        <line x1="100" y1="200" x2="200" y2="400" />
        <line x1="300" y1="250" x2="200" y2="400" />
        <line x1="200" y1="400" x2="150" y2="600" />
        <line x1="200" y1="400" x2="250" y2="550" />
      </g>
    </svg>
  </div>

  <!-- Step 1: Cloud Frosted Progress Bar -->
  <div class="cloud-progress-container" data-step="1">
    <div class="frosted-ring">
      <div class="ring-spinner"></div>
      <iconify-icon icon="lucide:cloud" class="cloud-icon"></iconify-icon>
    </div>
    <div class="status-label">CLOUD COMPUTING</div>
  </div>

  <!-- Step 2: Hero Text & Particles -->
  <div class="hero-content-area" data-step="2">
    <div class="particle-emitter" id="particles-js-05"></div>
    <h1 class="vision-title">
      <span class="glow-text">科技</span><span class="glow-text highlight">向善</span>
    </h1>
    <div class="tech-tag-line">TECH FOR GOOD</div>
  </div>

  <!-- Layout Brackets (Tech Blogger Style) -->
  <div class="frame-bracket tl"></div>
  <div class="frame-bracket tr"></div>
  <div class="frame-bracket bl"></div>
  <div class="frame-bracket br"></div>
  <div class="data-stream-side">01001 / AI / CLOUD / VISION</div>
</div>
  </div>
</section>

<section class="scene" data-scene-id="scene-06">
  <div class="sv-scene-root sv-scene-root-scene-06" data-scene-root-id="scene-06">
<div class="s-scene-06-wrapper">
  <!-- Background Layers -->
  <div class="bg-base"></div>
  <div class="bg-gradient-overlay" data-step="0"></div>
  <div class="tech-grid-closing"></div>

  <!-- Step 1: Hero Slogan -->
  <div class="slogan-container" data-step="1">
    <div class="slogan-line">
      <span class="slogan-text">用户为本</span>
    </div>
    <div class="slogan-divider"></div>
    <div class="slogan-line">
      <span class="slogan-text highlight">科技向善</span>
    </div>
  </div>

  <!-- Step 2: Anniversary Logo & Closing -->
  <div class="anniversary-footer" data-step="2">
    <div class="logo-wrap">
      <iconify-icon icon="simple-icons:tencent" class="tencent-logo"></iconify-icon>
      <div class="logo-divider"></div>
      <div class="anniversary-badge">
        <span class="num">25</span>
        <span class="text">ANNIVERSARY</span>
      </div>
    </div>
    <div class="brand-subtext">VALUE FOR USERS, TECH FOR GOOD</div>
  </div>

  <!-- Style Brackets (Persistence) -->
  <div class="frame-bracket tl"></div>
  <div class="frame-bracket tr"></div>
  <div class="frame-bracket bl"></div>
  <div class="frame-bracket br"></div>

  <!-- Scanning Line -->
  <div class="scan-line"></div>
</div>
  </div>
</section>
      </div>
    </div>
  </div>
  <script id="__SVGVIDEO_META__" type="application/json">{"version":2,"format":"json","title":"腾讯公司发展历程：连接的演变","fullScript":"从1998年的深圳，到如今影响全球的互联网巨头，腾讯走过了一条不平凡的道路。一切始于那只戴着红围巾的小企鹅。1999年，OICQ正式上线，开启了中国人的即时通讯时代。2004年，腾讯在香港挂牌上市，业务版图迅速扩张，从门户网站、在线游戏，到社交网络。2011年是关键的转折点，微信诞生了。这不仅是一个聊天工具，更是连接一切的开始，彻底改变了我们的移动生活方式。进入新时代，腾讯提出了“科技向善”的愿景。从消费互联网全面拥抱产业互联网，深耕AI、云服务与前沿科技。深耕消费互联网，拥抱产业互联网，这，就是腾讯。连接未来，无限可能。","visualStyleId":"kejibozhu","orientation":"portrait","scenes":[{"id":"scene-01","title":"开篇：连接的起源","steps":2,"status":"success","prompt":"竖屏9:16布局。背景采用腾讯品牌深蓝色渐变，带有微弱的流体动态效果。Step 0：中心位置淡入发光的腾讯Logo，伴随轻微的弹簧缩放。Step 1：Logo下方出现文字‘腾讯：连接未来的25年’，使用毛玻璃质感底色，文字带Shimmer扫光特效。","scriptText":"从1998年的深圳，到如今影响全球的互联网巨头，腾讯走过了一条不平凡的道路。","audioObjectKey":"sv-video/v1/u_1aa5e8b3-d1b1-4121-b796-bb5ac9fcd45a/p_5640993b-edd4-480b-89f0-36cd4325fb5e/s_scene-01/v_yunxi/a_81ca1825_1777091614616_dre9l9.mp3","audioDurationMs":7596,"voiceName":"yunxi","relatedSceneIds":["scene-02"]},{"id":"scene-02","title":"1999：企鹅诞生","steps":3,"status":"success","prompt":"模拟复古电脑视窗风格。Step 0：居中展示一个90年代风格的灰色终端窗口。Step 1：窗口内逐字打字显示‘Connecting... OICQ’。Step 2：窗口中心弹出一只经典的红围巾企鹅SVG图标，背景出现无数个气泡对话框向四周扩散，象征通讯的爆发。","scriptText":"一切始于那只戴着红围巾的小企鹅。1999年，OICQ正式上线，开启了中国人的即时通讯时代。","audioObjectKey":"sv-video/v1/u_1aa5e8b3-d1b1-4121-b796-bb5ac9fcd45a/p_5640993b-edd4-480b-89f0-36cd4325fb5e/s_scene-02/v_yunxi/a_cd39a1b5_1777091931404_n3tdd6.mp3","audioDurationMs":9612,"voiceName":"yunxi","relatedSceneIds":["scene-01","scene-03"]},{"id":"scene-03","title":"2004：香港上市","steps":3,"status":"success","prompt":"金色与蓝色的商业视觉风格。Step 0：背景出现向上增长的平滑折线图，颜色为亮蓝色。Step 1：屏幕中央降落一枚金色的港币符号或‘HK.0700’字样的硬币，带有金属反光。Step 2：四周环绕浮现‘门户’、‘游戏’、‘空间’的卡片，采用网格布局，依次淡入。","scriptText":"2004年，腾讯在香港挂牌上市，业务版图迅速扩张，从门户网站、在线游戏，到社交网络。","audioObjectKey":"sv-video/v1/u_1aa5e8b3-d1b1-4121-b796-bb5ac9fcd45a/p_5640993b-edd4-480b-89f0-36cd4325fb5e/s_scene-03/v_yunxi/a_19bb2e6d_1777091688930_k2tzxc.mp3","audioDurationMs":9576,"voiceName":"yunxi","relatedSceneIds":["scene-02","scene-04"]},{"id":"scene-04","title":"2011：微信时代","steps":3,"status":"success","prompt":"极简绿色视觉风格。Step 0：全屏背景转为微信绿，中心出现经典的双气泡Logo。Step 1：Logo产生强烈的涟漪波动效果，象征‘连接一切’。Step 2：屏幕上下浮现出支付图标、小程序图标和朋友圈图标，呈现放射状排列，所有元素做悬浮呼吸动画。","scriptText":"2011年是关键的转折点，微信诞生了。这不仅是一个聊天工具，更是连接一切的开始，彻底改变了我们的移动生活方式。","audioObjectKey":"sv-video/v1/u_1aa5e8b3-d1b1-4121-b796-bb5ac9fcd45a/p_5640993b-edd4-480b-89f0-36cd4325fb5e/s_scene-04/v_yunxi/a_3b1d16e7_1777091725610_mago9s.mp3","audioDurationMs":10512,"voiceName":"yunxi","relatedSceneIds":["scene-03","scene-05"]},{"id":"scene-05","title":"产业互联网：科技向善","steps":3,"status":"success","prompt":"高科技感与未来感。Step 0：背景变为深邃的星空蓝，出现AI神经网络的动态连线。Step 1：上方降下一个代表‘云端’的毛玻璃圆环进度条，不停旋转。Step 2：中心浮现‘科技向善’四个大字，文字边缘带有发光描边，并有光粒子从下往上漂浮。","scriptText":"进入新时代，腾讯提出了“科技向善”的愿景。从消费互联网全面拥抱产业互联网，深耕AI、云服务与前沿科技。","audioObjectKey":"sv-video/v1/u_1aa5e8b3-d1b1-4121-b796-bb5ac9fcd45a/p_5640993b-edd4-480b-89f0-36cd4325fb5e/s_scene-05/v_yunxi/a_8d34a6fe_1777091763843_484ysn.mp3","audioDurationMs":11196,"voiceName":"yunxi","relatedSceneIds":["scene-04","scene-06"]},{"id":"scene-06","title":"总结：连接未来","steps":2,"status":"success","prompt":"简洁有力的品牌收尾。Step 0：背景回归纯净白蓝渐变。Step 1：腾讯最新的品牌Slogan‘用户为本，科技向善’在屏幕黄金分割位置垂直居中显示。Step 2：底部浮现25周年标识，整体画面向内收缩淡出。","scriptText":"深耕消费互联网，拥抱产业互联网，这，就是腾讯。连接未来，无限可能。","audioObjectKey":"sv-video/v1/u_1aa5e8b3-d1b1-4121-b796-bb5ac9fcd45a/p_5640993b-edd4-480b-89f0-36cd4325fb5e/s_scene-06/v_yunxi/a_76cc86c3_1777091799141_uh6sgl.mp3","audioDurationMs":8388,"voiceName":"yunxi","relatedSceneIds":["scene-05"]}]}</script>
  <script id="__SVGVIDEO_SCENE_DATA__" type="application/json">[{"id":"scene-01","title":"开篇：连接的起源","steps":2,"html":"<div class=\"s-scene-01-wrapper\">\n  <div class=\"tech-grid\"></div>\n  <div class=\"fluid-bg\"></div>\n  \n  <div class=\"focus-frame\" data-step=\"0\">\n    <div class=\"corner top-left\"></div>\n    <div class=\"corner top-right\"></div>\n    <div class=\"corner bottom-left\"></div>\n    <div class=\"corner bottom-right\"></div>\n  </div>\n\n  <div class=\"main-content\">\n    <div class=\"logo-container\" data-step=\"0\">\n      <iconify-icon icon=\"simple-icons:tencent\" class=\"tencent-logo\"></iconify-icon>\n      <div class=\"logo-glow\"></div>\n    </div>\n\n    <div class=\"text-glass-panel\" data-step=\"1\">\n      <h1 class=\"shimmer-text\">腾讯：连接未来的25年</h1>\n      <div class=\"accent-bar\"></div>\n    </div>\n  </div>\n\n  <div class=\"tech-metadata\" data-step=\"0\">\n    <div class=\"meta-item\">\n      <span class=\"label\">EST.</span>\n      <span class=\"value\">1998</span>\n    </div>\n    <div class=\"meta-line\"></div>\n    <div class=\"meta-item\">\n      <span class=\"label\">GLOBAL</span>\n      <span class=\"value\">CONNECT</span>\n    </div>\n  </div>\n</div>","css":".s-scene-01-wrapper {\n  position: relative;\n  width: 100%;\n  height: 100%;\n  background-color: #1B1B1B;\n  overflow: hidden;\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  justify-content: center;\n  font-family: var(--sv-font-body-cn);\n  color: #FFFFFF;\n}\n\n.tech-grid {\n  position: absolute;\n  inset: 0;\n  background-image: \n    linear-gradient(to right, rgba(255, 255, 255, 0.03) 1px, transparent 1px),\n    linear-gradient(to bottom, rgba(255, 255, 255, 0.03) 1px, transparent 1px);\n  background-size: 40px 40px;\n  z-index: 1;\n}\n\n.fluid-bg {\n  position: absolute;\n  inset: 0;\n  background: radial-gradient(circle at 50% 50%, rgba(245, 53, 1, 0.05) 0%, transparent 70%);\n  filter: blur(40px);\n  animation: fluid-pulse 8s ease-in-out infinite alternate;\n  z-index: 1;\n}\n\n@keyframes fluid-pulse {\n  from { transform: scale(1) translate(0, 0); }\n  to { transform: scale(1.2) translate(5%, 5%); }\n}\n\n.focus-frame {\n  position: absolute;\n  width: 280px;\n  height: 280px;\n  z-index: 2;\n  transition: all 0.6s cubic-bezier(0.34, 1.56, 0.64, 1);\n  opacity: 0;\n  transform: scale(1.2);\n}\n.focus-frame[data-step-active=\"true\"] {\n  opacity: 1;\n  transform: scale(1);\n}\n\n.corner {\n  position: absolute;\n  width: 20px;\n  height: 20px;\n  border: 2px solid #F53501;\n}\n.top-left { top: 0; left: 0; border-right: 0; border-bottom: 0; }\n.top-right { top: 0; right: 0; border-left: 0; border-bottom: 0; }\n.bottom-left { bottom: 0; left: 0; border-right: 0; border-top: 0; }\n.bottom-right { bottom: 0; right: 0; border-left: 0; border-top: 0; }\n\n.main-content {\n  position: relative;\n  z-index: 10;\n  text-align: center;\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  gap: 40px;\n}\n\n.logo-container {\n  position: relative;\n  width: 120px;\n  height: 120px;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  opacity: 0;\n  transition: all 0.8s cubic-bezier(0.175, 0.885, 0.32, 1.275);\n}\n.logo-container[data-step-active=\"true\"] {\n  opacity: 1;\n  transform: scale(1.1);\n}\n\n.tencent-logo {\n  font-size: 80px;\n  color: #FFFFFF;\n  filter: drop-shadow(0 0 20px rgba(245, 53, 1, 0.3));\n}\n\n.logo-glow {\n  position: absolute;\n  width: 100%;\n  height: 100%;\n  background: #F53501;\n  filter: blur(40px);\n  opacity: 0.15;\n  z-index: -1;\n}\n\n.text-glass-panel {\n  padding: 16px 24px;\n  background: rgba(255, 255, 255, 0.05);\n  backdrop-filter: blur(12px);\n  border: 1px solid rgba(255, 255, 255, 0.1);\n  border-radius: 4px;\n  opacity: 0;\n  transform: translateY(20px);\n  transition: all 0.6s ease-out;\n}\n.text-glass-panel[data-step-active=\"true\"] {\n  opacity: 1;\n  transform: translateY(0);\n}\n\n.shimmer-text {\n  font-family: var(--sv-font-display-cn);\n  font-size: 24px;\n  font-weight: 900;\n  margin: 0;\n  background: linear-gradient(90deg, #FFFFFF 0%, #F53501 50%, #FFFFFF 100%);\n  background-size: 200% auto;\n  -webkit-background-clip: text;\n  -webkit-text-fill-color: transparent;\n  animation: shimmer 3s linear infinite;\n}\n\n@keyframes shimmer {\n  to { background-position: 200% center; }\n}\n\n.accent-bar {\n  width: 40px;\n  height: 3px;\n  background: #F53501;\n  margin: 8px auto 0;\n}\n\n.tech-metadata {\n  position: absolute;\n  bottom: 60px;\n  display: flex;\n  align-items: center;\n  gap: 20px;\n  opacity: 0;\n  transition: opacity 1s ease;\n}\n.tech-metadata[data-step-active=\"true\"] {\n  opacity: 0.6;\n}\n\n.meta-item {\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n}\n\n.meta-item .label {\n  font-family: var(--sv-font-mono);\n  font-size: 10px;\n  color: #F53501;\n  letter-spacing: 2px;\n}\n\n.meta-item .value {\n  font-family: var(--sv-font-mono);\n  font-size: 14px;\n  font-weight: bold;\n}\n\n.meta-line {\n  width: 1px;\n  height: 30px;\n  background: rgba(255, 255, 255, 0.2);\n}","js":"window.SceneLifecycle.register('scene-01', {\n  enter() {\n    const sceneRoot = document.querySelector('[data-scene-id=\"scene-01\"]');\n    if (sceneRoot) {\n      window.__SVGVIDEO_REFRESH_ICONS(sceneRoot);\n    }\n  },\n  step(stepIndex) {\n    const sceneRoot = document.querySelector('[data-scene-id=\"scene-01\"]');\n    if (!sceneRoot) return;\n\n    const elements = sceneRoot.querySelectorAll('[data-step]');\n    elements.forEach(el => {\n      const stepValue = parseInt(el.getAttribute('data-step'));\n      el.setAttribute('data-step-active', stepIndex >= stepValue ? 'true' : 'false');\n    });\n  },\n  leave() {}\n});"},{"id":"scene-02","title":"1999：企鹅诞生","steps":3,"html":"<div class=\"s-scene-02-wrapper\">\n  <div class=\"tech-grid\"></div>\n  \n  <!-- Step 0: Retro Window -->\n  <div class=\"retro-window\" data-step=\"0\">\n    <div class=\"window-header\">\n      <div class=\"header-title\">OICQ_SETUP.EXE</div>\n      <div class=\"header-controls\">\n        <span class=\"win-btn\">_</span>\n        <span class=\"win-btn\">□</span>\n        <span class=\"win-btn close\">×</span>\n      </div>\n    </div>\n    <div class=\"window-body\">\n      <div class=\"terminal-cursor\"></div>\n      <div class=\"terminal-text\" id=\"terminal-typing-area\"></div>\n      \n      <!-- Step 2: Penguin Hero -->\n      <div class=\"hero-penguin-container\" data-step=\"2\">\n        <div class=\"penguin-glow\"></div>\n        <iconify-icon icon=\"simple-icons:tencentqq\" class=\"penguin-icon\"></iconify-icon>\n        <div class=\"scarf-accent\"></div>\n      </div>\n    </div>\n    <div class=\"window-status\">\n      <span>Status: Initializing...</span>\n      <span class=\"blink-dot\">●</span>\n    </div>\n  </div>\n\n  <!-- Step 2: Bursting Bubbles Area -->\n  <div class=\"bubble-burst-layer\" data-step=\"2\">\n    <!-- Generated by JS -->\n  </div>\n\n  <!-- Scene Metadata -->\n  <div class=\"scene-tag\" data-step=\"0\">\n    <span class=\"tag-year\">1999</span>\n    <div class=\"tag-line\"></div>\n    <span class=\"tag-event\">LAUNCH EVENT</span>\n  </div>\n</div>","css":".s-scene-02-wrapper {\n  position: relative;\n  width: 100%;\n  height: 100%;\n  background-color: #1B1B1B;\n  overflow: hidden;\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  justify-content: center;\n  font-family: var(--sv-font-body-cn);\n}\n\n.tech-grid {\n  position: absolute;\n  inset: 0;\n  background-image: linear-gradient(rgba(255, 255, 255, 0.03) 1px, transparent 1px), linear-gradient(90deg, rgba(255, 255, 255, 0.03) 1px, transparent 1px);\n  background-size: 40px 40px;\n  z-index: 1;\n}\n\n.retro-window {\n  position: relative;\n  z-index: 10;\n  width: 320px;\n  height: 480px;\n  background: #C0C0C0;\n  border: 3px solid #FFFFFF;\n  border-right-color: #808080;\n  border-bottom-color: #808080;\n  box-shadow: 10px 10px 0px rgba(0, 0, 0, 0.3);\n  display: flex;\n  flex-direction: column;\n  opacity: 0;\n  transform: translateY(20px);\n  transition: all 0.6s cubic-bezier(0.23, 1, 0.32, 1);\n}\n.retro-window[data-step-active=\"true\"] {\n  opacity: 1;\n  transform: translateY(0);\n}\n\n.window-header {\n  background: linear-gradient(90deg, #000080, #1084D0);\n  padding: 4px 8px;\n  display: flex;\n  justify-content: space-between;\n  align-items: center;\n  color: #FFF;\n  font-family: var(--sv-font-mono);\n  font-size: 12px;\n  font-weight: bold;\n}\n\n.win-btn {\n  width: 16px;\n  height: 14px;\n  background: #C0C0C0;\n  border: 1px solid #FFF;\n  border-right-color: #000;\n  border-bottom-color: #000;\n  color: #000;\n  font-size: 10px;\n  display: inline-flex;\n  align-items: center;\n  justify-content: center;\n  margin-left: 2px;\n}\n\n.window-body {\n  flex-grow: 1;\n  background: #000;\n  margin: 4px;\n  padding: 15px;\n  position: relative;\n  overflow: hidden;\n  color: #FFF;\n  font-family: var(--sv-font-mono);\n  border: 2px inset #808080;\n}\n\n.terminal-text {\n  font-size: 14px;\n  line-height: 1.6;\n  white-space: pre-wrap;\n  color: #F53501;\n}\n\n.hero-penguin-container {\n  position: absolute;\n  inset: 0;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  opacity: 0;\n  transform: scale(0.5);\n  transition: all 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275);\n}\n.hero-penguin-container[data-step-active=\"true\"] {\n  opacity: 1;\n  transform: scale(1);\n}\n\n.penguin-icon {\n  font-size: 120px;\n  color: #FFF;\n  z-index: 5;\n}\n\n.scarf-accent {\n  position: absolute;\n  width: 60px;\n  height: 10px;\n  background: #F53501;\n  top: 60%;\n  left: 50%;\n  transform: translateX(-50%);\n  border-radius: 5px;\n  box-shadow: 0 0 15px #F53501;\n  z-index: 6;\n}\n\n.penguin-glow {\n  position: absolute;\n  width: 140px;\n  height: 140px;\n  background: #F53501;\n  filter: blur(40px);\n  opacity: 0.4;\n  border-radius: 50%;\n}\n\n.bubble-burst-layer {\n  position: absolute;\n  inset: 0;\n  pointer-events: none;\n  z-index: 20;\n}\n\n.chat-bubble {\n  position: absolute;\n  padding: 8px 12px;\n  background: #FFF;\n  border: 1px solid #F53501;\n  border-radius: 12px;\n  font-size: 12px;\n  color: #1B1B1B;\n  opacity: 0;\n  animation: bubble-float 2s ease-out forwards;\n}\n\n@keyframes bubble-float {\n  0% { transform: translate(0, 0) scale(0); opacity: 0; }\n  20% { opacity: 1; }\n  100% { transform: translate(var(--tx), var(--ty)) scale(1); opacity: 0; }\n}\n\n.window-status {\n  padding: 4px 8px;\n  font-size: 10px;\n  color: #000;\n  background: #C0C0C0;\n  border-top: 1px solid #808080;\n  display: flex;\n  justify-content: space-between;\n}\n\n.blink-dot { color: #F53501; animation: blink 1s infinite; }\n@keyframes blink { 50% { opacity: 0; } }\n\n.scene-tag {\n  position: absolute;\n  top: 10%;\n  left: 40px;\n  display: flex;\n  flex-direction: column;\n  align-items: flex-start;\n  opacity: 0;\n  transition: all 0.8s ease;\n}\n.scene-tag[data-step-active=\"true\"] { opacity: 1; }\n\n.tag-year {\n  font-family: var(--sv-font-display-cn);\n  font-size: 42px;\n  color: #F53501;\n  line-height: 1;\n}\n\n.tag-line {\n  width: 100px;\n  height: 2px;\n  background: #F53501;\n  margin: 10px 0;\n}\n\n.tag-event {\n  font-family: var(--sv-font-mono);\n  font-size: 12px;\n  color: #FFF;\n  letter-spacing: 4px;\n}","js":"window.SceneLifecycle.register('scene-02', {\n  enter() {\n    const sceneRoot = document.querySelector('[data-scene-id=\"scene-02\"]');\n    if (sceneRoot) {\n      window.__SVGVIDEO_REFRESH_ICONS(sceneRoot);\n    }\n  },\n  step(stepIndex) {\n    const sceneRoot = document.querySelector('[data-scene-id=\"scene-02\"]');\n    if (!sceneRoot) return;\n\n    const elements = sceneRoot.querySelectorAll('[data-step]');\n    elements.forEach(el => {\n      const stepValue = parseInt(el.getAttribute('data-step'));\n      el.setAttribute('data-step-active', stepIndex >= stepValue ? 'true' : 'false');\n    });\n\n    // Handle Typing Effect in Step 1\n    if (stepIndex === 1) {\n      const typingArea = sceneRoot.querySelector('#terminal-typing-area');\n      if (typingArea && !typingArea.getAttribute('data-started')) {\n        typingArea.setAttribute('data-started', 'true');\n        const text = 'Connecting...\\nRemote server found.\\nProtocol: OICQ v1.0\\nStatus: ONLINE';\n        let i = 0;\n        const interval = setInterval(() => {\n          typingArea.textContent = text.slice(0, i) + (i % 2 === 0 ? '_' : ' ');\n          i++;\n          if (i > text.length) {\n            clearInterval(interval);\n            typingArea.textContent = text;\n          }\n        }, 50);\n      }\n    }\n\n    // Handle Bubble Burst in Step 2\n    if (stepIndex === 2) {\n      const layer = sceneRoot.querySelector('.bubble-burst-layer');\n      if (layer && layer.children.length === 0) {\n        const msgs = ['你好', 'OICQ', '1999', 'Hello', 'Ping', 'Online', 'Connected'];\n        for (let i = 0; i < 20; i++) {\n          const b = document.createElement('div');\n          b.className = 'chat-bubble';\n          b.textContent = msgs[Math.floor(Math.random() * msgs.length)];\n          const angle = Math.random() * Math.PI * 2;\n          const dist = 150 + Math.random() * 200;\n          b.style.setProperty('--tx', `${Math.cos(angle) * dist}px`);\n          b.style.setProperty('--ty', `${Math.sin(angle) * dist}px`);\n          b.style.left = '50%';\n          b.style.top = '50%';\n          b.style.animationDelay = `${Math.random() * 0.5}s`;\n          layer.appendChild(b);\n        }\n      }\n    }\n  },\n  leave() {}\n});"},{"id":"scene-03","title":"2004：香港上市","steps":3,"html":"<div class=\"s-scene-03-wrapper\">\n  <div class=\"tech-grid\"></div>\n  \n  <!-- Step 0: Background Growth Chart -->\n  <div class=\"chart-container\" data-step=\"0\">\n    <svg viewBox=\"0 0 400 800\" class=\"growth-svg\">\n      <defs>\n        <linearGradient id=\"lineGrad\" x1=\"0%\" y1=\"0%\" x2=\"0%\" y2=\"100%\">\n          <stop offset=\"0%\" stop-color=\"#F53501\" stop-opacity=\"1\" />\n          <stop offset=\"100%\" stop-color=\"#F53501\" stop-opacity=\"0.1\" />\n        </linearGradient>\n      </defs>\n      <path class=\"chart-path\" d=\"M0,600 L100,550 L200,580 L300,450 L400,300\" fill=\"none\" stroke=\"#F53501\" stroke-width=\"4\" />\n      <path class=\"chart-fill\" d=\"M0,600 L100,550 L200,580 L300,450 L400,300 L400,800 L0,800 Z\" fill=\"url(#lineGrad)\" opacity=\"0.1\" />\n    </svg>\n    <div class=\"stat-label\">IPO GROWTH</div>\n  </div>\n\n  <!-- Step 1: Golden Coin -->\n  <div class=\"coin-hero\" data-step=\"1\">\n    <div class=\"coin-3d\">\n      <div class=\"coin-face\">\n        <div class=\"coin-inner\">\n          <span class=\"stock-symbol\">HK.0700</span>\n          <div class=\"coin-reflection\"></div>\n        </div>\n      </div>\n    </div>\n    <h1 class=\"main-title\">2004 香港上市</h1>\n  </div>\n\n  <!-- Step 2: Grid Cards -->\n  <div class=\"business-grid\" data-step=\"2\">\n    <div class=\"biz-card\" style=\"--i:0\">\n      <iconify-icon icon=\"lucide:layout-template\" class=\"biz-icon\"></iconify-icon>\n      <span class=\"biz-name\">门户</span>\n    </div>\n    <div class=\"biz-card\" style=\"--i:1\">\n      <iconify-icon icon=\"lucide:gamepad-2\" class=\"biz-icon\"></iconify-icon>\n      <span class=\"biz-name\">游戏</span>\n    </div>\n    <div class=\"biz-card\" style=\"--i:2\">\n      <iconify-icon icon=\"lucide:layout-grid\" class=\"biz-icon\"></iconify-icon>\n      <span class=\"biz-name\">空间</span>\n    </div>\n  </div>\n\n  <div class=\"marker-line top\"></div>\n  <div class=\"marker-line bottom\"></div>\n</div>","css":".s-scene-03-wrapper {\n  position: relative;\n  width: 100%;\n  height: 100%;\n  background-color: #1B1B1B;\n  overflow: hidden;\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  justify-content: center;\n  font-family: var(--sv-font-body-cn);\n}\n\n.tech-grid {\n  position: absolute;\n  inset: 0;\n  background-image: linear-gradient(rgba(255, 255, 255, 0.05) 1px, transparent 1px), linear-gradient(90deg, rgba(255, 255, 255, 0.05) 1px, transparent 1px);\n  background-size: 30px 30px;\n  z-index: 1;\n}\n\n/* Step 0: Chart Animation */\n.chart-container {\n  position: absolute;\n  bottom: 0;\n  width: 100%;\n  height: 50%;\n  z-index: 2;\n  opacity: 0;\n  transition: opacity 1s ease;\n}\n.chart-container[data-step-active=\"true\"] {\n  opacity: 1;\n}\n.chart-path {\n  stroke-dasharray: 1000;\n  stroke-dashoffset: 1000;\n}\n.chart-container[data-step-active=\"true\"] .chart-path {\n  animation: chart-draw 2.5s cubic-bezier(0.4, 0, 0.2, 1) forwards;\n}\n@keyframes chart-draw {\n  to { stroke-dashoffset: 0; }\n}\n\n.stat-label {\n  position: absolute;\n  left: 20px;\n  top: 20px;\n  color: #F53501;\n  font-family: var(--sv-font-mono);\n  font-size: 12px;\n  letter-spacing: 2px;\n  border-left: 2px solid #F53501;\n  padding-left: 8px;\n}\n\n/* Step 1: Coin Hero */\n.coin-hero {\n  position: relative;\n  z-index: 10;\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  opacity: 0;\n  transform: translateY(-50px) scale(0.8);\n  transition: all 0.8s cubic-bezier(0.34, 1.56, 0.64, 1);\n}\n.coin-hero[data-step-active=\"true\"] {\n  opacity: 1;\n  transform: translateY(0) scale(1);\n}\n\n.coin-3d {\n  width: 180px;\n  height: 180px;\n  perspective: 1000px;\n  margin-bottom: 24px;\n}\n\n.coin-face {\n  width: 100%;\n  height: 100%;\n  background: linear-gradient(135deg, #FFD700 0%, #B8860B 50%, #FFD700 100%);\n  border-radius: 50%;\n  padding: 10px;\n  box-shadow: 0 20px 40px rgba(0,0,0,0.5), inset 0 0 20px rgba(255,255,255,0.5);\n  animation: coin-spin 6s infinite linear;\n}\n\n.coin-inner {\n  width: 100%;\n  height: 100%;\n  border: 4px solid rgba(0,0,0,0.1);\n  border-radius: 50%;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  position: relative;\n  overflow: hidden;\n}\n\n.stock-symbol {\n  font-family: var(--sv-font-display-cn);\n  font-size: 24px;\n  color: #5C4033;\n  font-weight: 900;\n  text-shadow: 1px 1px 0px rgba(255,255,255,0.3);\n}\n\n.coin-reflection {\n  position: absolute;\n  top: -50%;\n  left: -50%;\n  width: 200%;\n  height: 200%;\n  background: linear-gradient(45deg, transparent 45%, rgba(255,255,255,0.6) 50%, transparent 55%);\n  animation: reflect 3s infinite ease-in-out;\n}\n\n@keyframes reflect { 0% { transform: translate(-20%, -20%); } 100% { transform: translate(20%, 20%); } }\n@keyframes coin-spin { 0% { transform: rotateY(0deg); } 100% { transform: rotateY(360deg); } }\n\n.main-title {\n  font-family: var(--sv-font-display-cn);\n  font-size: 32px;\n  color: #FFFFFF;\n  text-transform: uppercase;\n  letter-spacing: 2px;\n}\n\n/* Step 2: Biz Grid */\n.business-grid {\n  position: absolute;\n  bottom: 12%;\n  width: 90%;\n  display: grid;\n  grid-template-columns: repeat(3, 1fr);\n  gap: 12px;\n  z-index: 15;\n}\n\n.biz-card {\n  background: rgba(255, 255, 255, 0.03);\n  border: 1px solid rgba(255, 255, 255, 0.1);\n  padding: 16px 8px;\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  gap: 8px;\n  opacity: 0;\n  transform: translateY(20px);\n  transition: all 0.5s ease;\n  transition-delay: calc(var(--i) * 0.15s);\n}\n\n.business-grid[data-step-active=\"true\"] .biz-card {\n  opacity: 1;\n  transform: translateY(0);\n  border-color: rgba(245, 53, 1, 0.3);\n}\n\n.biz-icon {\n  font-size: 28px;\n  color: #F53501;\n}\n\n.biz-name {\n  font-family: var(--sv-font-display-cn);\n  font-size: 16px;\n  color: #FFFFFF;\n}\n\n.marker-line {\n  position: absolute;\n  left: 5%;\n  width: 90%;\n  height: 1px;\n  background: rgba(245, 53, 1, 0.2);\n  z-index: 5;\n}\n.marker-line.top { top: 20%; }\n.marker-line.bottom { bottom: 20%; }","js":"window.SceneLifecycle.register('scene-03', {\n  enter() {\n    const sceneRoot = document.querySelector('[data-scene-id=\"scene-03\"]');\n    if (sceneRoot) {\n      window.__SVGVIDEO_REFRESH_ICONS(sceneRoot);\n    }\n  },\n  step(stepIndex) {\n    const sceneRoot = document.querySelector('[data-scene-id=\"scene-03\"]');\n    if (!sceneRoot) return;\n\n    const elements = sceneRoot.querySelectorAll('[data-step]');\n    elements.forEach(el => {\n      const stepValue = parseInt(el.getAttribute('data-step'));\n      el.setAttribute('data-step-active', stepIndex >= stepValue ? 'true' : 'false');\n    });\n\n    // Visual impact when coin lands at step 1\n    if (stepIndex === 1) {\n      const coin = sceneRoot.querySelector('.coin-hero');\n      if (coin) {\n        coin.animate([\n          { transform: 'scale(1.2) translateY(-20px)' },\n          { transform: 'scale(1) translateY(0)' }\n        ], { duration: 500, easing: 'cubic-bezier(0.175, 0.885, 0.32, 1.275)' });\n      }\n    }\n  },\n  leave() {}\n});"},{"id":"scene-04","title":"2011：微信时代","steps":3,"html":"<div class=\"s-scene-04-wrapper\">\n  <div class=\"tech-grid\"></div>\n  \n  <!-- Background Green Wash -->\n  <div class=\"wechat-wash\" data-step=\"0\"></div>\n\n  <!-- Step 0: Central Logo & Year -->\n  <div class=\"hero-container\" data-step=\"0\">\n    <div class=\"year-tag\">2011</div>\n    <div class=\"logo-box\">\n      <iconify-icon icon=\"simple-icons:wechat\" class=\"main-logo\"></iconify-icon>\n      <div class=\"logo-glow\"></div>\n    </div>\n    <h1 class=\"main-title\">微信诞生</h1>\n  </div>\n\n  <!-- Step 1: Connection Ripples -->\n  <div class=\"ripple-system\" data-step=\"1\">\n    <div class=\"ripple-circle c1\"></div>\n    <div class=\"ripple-circle c2\"></div>\n    <div class=\"ripple-circle c3\"></div>\n    <div class=\"connection-text\">连接一切</div>\n  </div>\n\n  <!-- Step 2: Ecosystem Icons -->\n  <div class=\"ecosystem-radial\" data-step=\"2\">\n    <div class=\"eco-item p-pay\" style=\"--angle: -120deg\">\n      <div class=\"eco-icon-wrapper\">\n        <iconify-icon icon=\"ant-design:wechat-pay-outlined\"></iconify-icon>\n      </div>\n      <span class=\"eco-label\">移动支付</span>\n    </div>\n    <div class=\"eco-item p-mini\" style=\"--angle: 0deg\">\n      <div class=\"eco-icon-wrapper\">\n        <iconify-icon icon=\"lucide:component\"></iconify-icon>\n      </div>\n      <span class=\"eco-label\">小程序</span>\n    </div>\n    <div class=\"eco-item p-moments\" style=\"--angle: 120deg\">\n      <div class=\"eco-icon-wrapper\">\n        <iconify-icon icon=\"lucide:aperture\"></iconify-icon>\n      </div>\n      <span class=\"eco-label\">朋友圈</span>\n    </div>\n  </div>\n\n  <!-- Decor elements -->\n  <div class=\"focus-frame-top\"></div>\n  <div class=\"focus-frame-bottom\"></div>\n</div>","css":".s-scene-04-wrapper {\n  position: relative;\n  width: 100%;\n  height: 100%;\n  background-color: #1B1B1B;\n  overflow: hidden;\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  justify-content: center;\n  font-family: var(--sv-font-body-cn);\n}\n\n.tech-grid {\n  position: absolute;\n  inset: 0;\n  background-image: linear-gradient(rgba(255, 255, 255, 0.05) 1px, transparent 1px), linear-gradient(90deg, rgba(255, 255, 255, 0.05) 1px, transparent 1px);\n  background-size: 40px 40px;\n  z-index: 1;\n}\n\n.wechat-wash {\n  position: absolute;\n  inset: 0;\n  background: radial-gradient(circle at center, rgba(7, 193, 96, 0.2) 0%, transparent 70%);\n  opacity: 0;\n  transition: opacity 1.2s ease;\n  z-index: 2;\n}\n.wechat-wash[data-step-active=\"true\"] { opacity: 1; }\n\n.hero-container {\n  position: relative;\n  z-index: 10;\n  text-align: center;\n  opacity: 0;\n  transform: scale(0.9);\n  transition: all 0.8s cubic-bezier(0.175, 0.885, 0.32, 1.275);\n}\n.hero-container[data-step-active=\"true\"] {\n  opacity: 1;\n  transform: scale(1);\n}\n\n.year-tag {\n  font-family: var(--sv-font-mono);\n  font-size: 14px;\n  color: #F53501;\n  background: rgba(245, 53, 1, 0.1);\n  padding: 2px 10px;\n  border: 1px solid #F53501;\n  display: inline-block;\n  margin-bottom: 20px;\n  letter-spacing: 2px;\n}\n\n.logo-box {\n  position: relative;\n  width: 120px;\n  height: 120px;\n  margin: 0 auto 30px;\n}\n\n.main-logo {\n  font-size: 120px;\n  color: #07C160;\n  filter: drop-shadow(0 0 20px rgba(7, 193, 96, 0.4));\n}\n\n.logo-glow {\n  position: absolute;\n  inset: -20px;\n  background: radial-gradient(circle, rgba(7, 193, 96, 0.15) 0%, transparent 70%);\n  animation: logo-pulse 3s infinite ease-in-out;\n}\n\n.main-title {\n  font-family: var(--sv-font-display-cn);\n  font-size: 48px;\n  color: #FFFFFF;\n  margin: 0;\n  letter-spacing: 4px;\n}\n\n/* Step 1: Ripple */\n.ripple-system {\n  position: absolute;\n  top: 50%;\n  left: 50%;\n  transform: translate(-50%, -50%);\n  width: 100%;\n  height: 100%;\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  justify-content: flex-end;\n  padding-bottom: 25%;\n  z-index: 5;\n  opacity: 0;\n  transition: opacity 0.5s ease;\n}\n.ripple-system[data-step-active=\"true\"] { opacity: 1; }\n\n.ripple-circle {\n  position: absolute;\n  top: 50%;\n  left: 50%;\n  border: 1px solid rgba(7, 193, 96, 0.3);\n  border-radius: 50%;\n  transform: translate(-50%, -50%) scale(0);\n}\n.ripple-system[data-step-active=\"true\"] .ripple-circle {\n  animation: ripple-out 4s infinite linear;\n}\n.c1 { width: 300px; height: 300px; animation-delay: 0s; }\n.c2 { width: 300px; height: 300px; animation-delay: 1.3s; }\n.c3 { width: 300px; height: 300px; animation-delay: 2.6s; }\n\n.connection-text {\n  font-family: var(--sv-font-display-cn);\n  color: #07C160;\n  font-size: 20px;\n  letter-spacing: 8px;\n  text-transform: uppercase;\n  opacity: 0.8;\n}\n\n/* Step 2: Ecosystem */\n.ecosystem-radial {\n  position: absolute;\n  inset: 0;\n  z-index: 15;\n  pointer-events: none;\n}\n\n.eco-item {\n  position: absolute;\n  top: 50%;\n  left: 50%;\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  gap: 12px;\n  opacity: 0;\n  transition: all 0.8s cubic-bezier(0.34, 1.56, 0.64, 1);\n  --dist: 160px;\n}\n\n.ecosystem-radial[data-step-active=\"true\"] .eco-item {\n  opacity: 1;\n  transform: translate(calc(cos(var(--angle)) * var(--dist) - 50%), calc(sin(var(--angle)) * var(--dist) - 50%));\n}\n\n.eco-icon-wrapper {\n  width: 60px;\n  height: 60px;\n  background: rgba(255, 255, 255, 0.05);\n  border: 1px solid rgba(7, 193, 96, 0.4);\n  border-radius: 12px;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  font-size: 32px;\n  color: #07C160;\n  box-shadow: 0 10px 20px rgba(0,0,0,0.3);\n  animation: float-breathing 4s infinite ease-in-out;\n}\n\n.eco-label {\n  font-size: 14px;\n  color: #FFFFFF;\n  white-space: nowrap;\n  font-weight: 600;\n  text-shadow: 0 2px 4px rgba(0,0,0,0.5);\n}\n\n/* Decor Frames */\n.focus-frame-top, .focus-frame-bottom {\n  position: absolute;\n  left: 10%;\n  width: 80%;\n  height: 20px;\n  border: 1px solid rgba(245, 53, 1, 0.2);\n  z-index: 5;\n}\n.focus-frame-top {\n  top: 5%;\n  border-bottom: none;\n}\n.focus-frame-bottom {\n  bottom: 5%;\n  border-top: none;\n}\n\n@keyframes logo-pulse { 0%, 100% { transform: scale(1); opacity: 0.15; } 50% { transform: scale(1.1); opacity: 0.3; } }\n@keyframes ripple-out { from { transform: translate(-50%, -50%) scale(0.5); opacity: 1; } to { transform: translate(-50%, -50%) scale(2.5); opacity: 0; } }\n@keyframes float-breathing {\n  0%, 100% { transform: translateY(0); } \n  50% { transform: translateY(-10px); }\n}","js":"window.SceneLifecycle.register('scene-04', {\n  enter() {\n    const sceneRoot = document.querySelector('[data-scene-id=\"scene-04\"]');\n    if (sceneRoot) {\n      window.__SVGVIDEO_REFRESH_ICONS(sceneRoot);\n    }\n  },\n  step(stepIndex) {\n    const sceneRoot = document.querySelector('[data-scene-id=\"scene-04\"]');\n    if (!sceneRoot) return;\n\n    const elements = sceneRoot.querySelectorAll('[data-step]');\n    elements.forEach(el => {\n      const stepValue = parseInt(el.getAttribute('data-step'));\n      el.setAttribute('data-step-active', stepIndex >= stepValue ? 'true' : 'false');\n    });\n\n    // Special impact for the logo in Step 0\n    if (stepIndex === 0) {\n      const logoBox = sceneRoot.querySelector('.logo-box');\n      if (logoBox) {\n        logoBox.animate([\n          { transform: 'scale(0.5) rotate(-10deg)', opacity: 0 },\n          { transform: 'scale(1.1) rotate(5deg)', opacity: 1 },\n          { transform: 'scale(1) rotate(0deg)', opacity: 1 }\n        ], { duration: 1000, easing: 'cubic-bezier(0.34, 1.56, 0.64, 1)' });\n      }\n    }\n\n    // Visual burst effect when ripples/connection starts at Step 1\n    if (stepIndex === 1) {\n      const wash = sceneRoot.querySelector('.wechat-wash');\n      if (wash) {\n        wash.animate([\n          { background: 'radial-gradient(circle at center, rgba(7, 193, 96, 0.6) 0%, transparent 10%)' },\n          { background: 'radial-gradient(circle at center, rgba(7, 193, 96, 0.2) 0%, transparent 70%)' }\n        ], { duration: 800 });\n      }\n    }\n  },\n  leave() {}\n});"},{"id":"scene-05","title":"产业互联网：科技向善","steps":3,"html":"<div class=\"s-scene-05-wrapper\">\n  <div class=\"tech-grid\"></div>\n  \n  <!-- Step 0: Neural Network & Star Blue Wash -->\n  <div class=\"blue-neural-wash\" data-step=\"0\"></div>\n  <div class=\"neural-net-container\" data-step=\"0\">\n    <svg class=\"neural-svg\" viewBox=\"0 0 400 800\">\n      <g class=\"nodes\">\n        <circle cx=\"100\" cy=\"200\" r=\"3\" fill=\"#F53501\" />\n        <circle cx=\"300\" cy=\"250\" r=\"3\" fill=\"#F53501\" />\n        <circle cx=\"200\" cy=\"400\" r=\"4\" fill=\"#F53501\" />\n        <circle cx=\"150\" cy=\"600\" r=\"3\" fill=\"#F53501\" />\n        <circle cx=\"250\" cy=\"550\" r=\"3\" fill=\"#F53501\" />\n      </g>\n      <g class=\"links\" stroke=\"rgba(245, 53, 1, 0.3)\" stroke-width=\"1\">\n        <line x1=\"100\" y1=\"200\" x2=\"200\" y2=\"400\" />\n        <line x1=\"300\" y1=\"250\" x2=\"200\" y2=\"400\" />\n        <line x1=\"200\" y1=\"400\" x2=\"150\" y2=\"600\" />\n        <line x1=\"200\" y1=\"400\" x2=\"250\" y2=\"550\" />\n      </g>\n    </svg>\n  </div>\n\n  <!-- Step 1: Cloud Frosted Progress Bar -->\n  <div class=\"cloud-progress-container\" data-step=\"1\">\n    <div class=\"frosted-ring\">\n      <div class=\"ring-spinner\"></div>\n      <iconify-icon icon=\"lucide:cloud\" class=\"cloud-icon\"></iconify-icon>\n    </div>\n    <div class=\"status-label\">CLOUD COMPUTING</div>\n  </div>\n\n  <!-- Step 2: Hero Text & Particles -->\n  <div class=\"hero-content-area\" data-step=\"2\">\n    <div class=\"particle-emitter\" id=\"particles-js-05\"></div>\n    <h1 class=\"vision-title\">\n      <span class=\"glow-text\">科技</span><span class=\"glow-text highlight\">向善</span>\n    </h1>\n    <div class=\"tech-tag-line\">TECH FOR GOOD</div>\n  </div>\n\n  <!-- Layout Brackets (Tech Blogger Style) -->\n  <div class=\"frame-bracket tl\"></div>\n  <div class=\"frame-bracket tr\"></div>\n  <div class=\"frame-bracket bl\"></div>\n  <div class=\"frame-bracket br\"></div>\n  <div class=\"data-stream-side\">01001 / AI / CLOUD / VISION</div>\n</div>","css":".s-scene-05-wrapper {\n  position: relative;\n  width: 100%;\n  height: 100%;\n  background-color: #1B1B1B;\n  overflow: hidden;\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  justify-content: center;\n  font-family: var(--sv-font-body-cn);\n}\n\n.tech-grid {\n  position: absolute;\n  inset: 0;\n  background-image: linear-gradient(rgba(255, 255, 255, 0.03) 1px, transparent 1px), linear-gradient(90deg, rgba(255, 255, 255, 0.03) 1px, transparent 1px);\n  background-size: 30px 30px;\n  z-index: 1;\n}\n\n.blue-neural-wash {\n  position: absolute;\n  inset: 0;\n  background: radial-gradient(circle at 50% 50%, rgba(10, 17, 40, 0.8) 0%, transparent 100%);\n  opacity: 0;\n  transition: opacity 1.5s ease;\n  z-index: 2;\n}\n.blue-neural-wash[data-step-active=\"true\"] { opacity: 1; }\n\n.neural-net-container {\n  position: absolute;\n  inset: 0;\n  z-index: 3;\n  opacity: 0;\n  transition: opacity 1s ease;\n}\n.neural-net-container[data-step-active=\"true\"] { opacity: 0.6; }\n\n.neural-svg {\n  width: 100%;\n  height: 100%;\n  filter: drop-shadow(0 0 5px rgba(245, 53, 1, 0.4));\n}\n\n/* Step 1: Cloud Ring */\n.cloud-progress-container {\n  position: absolute;\n  top: 15%;\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  gap: 15px;\n  opacity: 0;\n  transform: translateY(-50px);\n  transition: all 0.8s cubic-bezier(0.23, 1, 0.32, 1);\n  z-index: 10;\n}\n.cloud-progress-container[data-step-active=\"true\"] {\n  opacity: 1;\n  transform: translateY(0);\n}\n\n.frosted-ring {\n  position: relative;\n  width: 100px;\n  height: 100px;\n  background: rgba(255, 255, 255, 0.05);\n  backdrop-filter: blur(8px);\n  -webkit-backdrop-filter: blur(8px);\n  border-radius: 50%;\n  border: 1px solid rgba(255, 255, 255, 0.1);\n  display: flex;\n  align-items: center;\n  justify-content: center;\n}\n\n.ring-spinner {\n  position: absolute;\n  inset: -5px;\n  border: 2px solid transparent;\n  border-top: 2px solid #F53501;\n  border-radius: 50%;\n  animation: spin 2s linear infinite;\n}\n\n.cloud-icon {\n  font-size: 40px;\n  color: #FFFFFF;\n  filter: drop-shadow(0 0 10px rgba(255, 255, 255, 0.3));\n}\n\n.status-label {\n  font-family: var(--sv-font-mono);\n  color: #F53501;\n  font-size: 10px;\n  letter-spacing: 2px;\n  font-weight: bold;\n}\n\n/* Step 2: Hero Text */\n.hero-content-area {\n  position: relative;\n  z-index: 20;\n  text-align: center;\n  opacity: 0;\n  transform: scale(0.9);\n  transition: all 1s cubic-bezier(0.34, 1.56, 0.64, 1);\n}\n.hero-content-area[data-step-active=\"true\"] {\n  opacity: 1;\n  transform: scale(1);\n}\n\n.vision-title {\n  font-family: var(--sv-font-display-cn);\n  font-size: 72px;\n  color: #FFFFFF;\n  margin: 0;\n  line-height: 1.2;\n  display: flex;\n  flex-direction: column;\n}\n\n.glow-text {\n  text-shadow: 0 0 20px rgba(255, 255, 255, 0.2);\n}\n\n.glow-text.highlight {\n  color: #F53501;\n  text-shadow: 0 0 30px rgba(245, 53, 1, 0.6);\n  border: 2px solid #F53501;\n  padding: 0 10px;\n  margin-top: 10px;\n}\n\n.tech-tag-line {\n  font-family: var(--sv-font-mono);\n  color: rgba(255, 255, 255, 0.4);\n  font-size: 14px;\n  letter-spacing: 6px;\n  margin-top: 20px;\n}\n\n.particle-emitter {\n  position: absolute;\n  inset: -100px;\n  pointer-events: none;\n}\n\n/* Decor elements */\n.frame-bracket {\n  position: absolute;\n  width: 20px;\n  height: 20px;\n  border: 2px solid #F53501;\n  z-index: 50;\n}\n.tl { top: 40px; left: 40px; border-right: none; border-bottom: none; }\n.tr { top: 40px; right: 40px; border-left: none; border-bottom: none; }\n.bl { bottom: 40px; left: 40px; border-right: none; border-top: none; }\n.br { bottom: 40px; right: 40px; border-left: none; border-top: none; }\n\n.data-stream-side {\n  position: absolute;\n  right: 10px;\n  top: 50%;\n  transform: rotate(90deg) translate(-50%, 0);\n  font-family: var(--sv-font-mono);\n  font-size: 10px;\n  color: rgba(245, 53, 1, 0.5);\n  letter-spacing: 2px;\n}\n\n@keyframes spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }\n\n.particle {\n  position: absolute;\n  background: #F53501;\n  width: 2px;\n  height: 2px;\n  border-radius: 50%;\n  animation: float-up var(--d) linear infinite;\n  opacity: 0;\n}\n\n@keyframes float-up {\n  0% { transform: translateY(0); opacity: 0; }\n  20% { opacity: 0.8; }\n  100% { transform: translateY(-150px); opacity: 0; }\n}","js":"window.SceneLifecycle.register('scene-05', {\n  enter() {\n    const root = document.querySelector('[data-scene-id=\"scene-05\"]');\n    if (root) {\n      window.__SVGVIDEO_REFRESH_ICONS(root);\n      this.createParticles(root);\n    }\n  },\n  step(index) {\n    const root = document.querySelector('[data-scene-id=\"scene-05\"]');\n    if (!root) return;\n\n    const elements = root.querySelectorAll('[data-step]');\n    elements.forEach(el => {\n      const stepValue = parseInt(el.getAttribute('data-step'));\n      el.setAttribute('data-step-active', index >= stepValue ? 'true' : 'false');\n    });\n\n    if (index === 0) {\n      const svgLines = root.querySelectorAll('.links line');\n      svgLines.forEach((line, i) => {\n        line.animate([\n          { strokeDasharray: '0 500', strokeDashoffset: '0' },\n          { strokeDasharray: '500 500', strokeDashoffset: '0' }\n        ], { duration: 1500, delay: i * 200, fill: 'forwards' });\n      });\n    }\n\n    if (index === 2) {\n      const title = root.querySelector('.vision-title');\n      title.animate([\n        { filter: 'brightness(1) blur(0px)' },\n        { filter: 'brightness(2) blur(2px)' },\n        { filter: 'brightness(1) blur(0px)' }\n      ], { duration: 1000, easing: 'ease-out' });\n    }\n  },\n  createParticles(root) {\n    const container = root.querySelector('#particles-js-05');\n    if (!container) return;\n    container.innerHTML = '';\n    for (let i = 0; i < 20; i++) {\n      const p = document.createElement('div');\n      p.className = 'particle';\n      p.style.left = Math.random() * 100 + '%';\n      p.style.bottom = '0px';\n      p.style.setProperty('--d', (2 + Math.random() * 3) + 's');\n      p.style.animationDelay = Math.random() * 2 + 's';\n      container.appendChild(p);\n    }\n  },\n  leave() {}\n});"},{"id":"scene-06","title":"总结：连接未来","steps":2,"html":"<div class=\"s-scene-06-wrapper\">\n  <!-- Background Layers -->\n  <div class=\"bg-base\"></div>\n  <div class=\"bg-gradient-overlay\" data-step=\"0\"></div>\n  <div class=\"tech-grid-closing\"></div>\n\n  <!-- Step 1: Hero Slogan -->\n  <div class=\"slogan-container\" data-step=\"1\">\n    <div class=\"slogan-line\">\n      <span class=\"slogan-text\">用户为本</span>\n    </div>\n    <div class=\"slogan-divider\"></div>\n    <div class=\"slogan-line\">\n      <span class=\"slogan-text highlight\">科技向善</span>\n    </div>\n  </div>\n\n  <!-- Step 2: Anniversary Logo & Closing -->\n  <div class=\"anniversary-footer\" data-step=\"2\">\n    <div class=\"logo-wrap\">\n      <iconify-icon icon=\"simple-icons:tencent\" class=\"tencent-logo\"></iconify-icon>\n      <div class=\"logo-divider\"></div>\n      <div class=\"anniversary-badge\">\n        <span class=\"num\">25</span>\n        <span class=\"text\">ANNIVERSARY</span>\n      </div>\n    </div>\n    <div class=\"brand-subtext\">VALUE FOR USERS, TECH FOR GOOD</div>\n  </div>\n\n  <!-- Style Brackets (Persistence) -->\n  <div class=\"frame-bracket tl\"></div>\n  <div class=\"frame-bracket tr\"></div>\n  <div class=\"frame-bracket bl\"></div>\n  <div class=\"frame-bracket br\"></div>\n\n  <!-- Scanning Line -->\n  <div class=\"scan-line\"></div>\n</div>","css":".s-scene-06-wrapper {\n  position: relative;\n  width: 100%;\n  height: 100%;\n  background-color: #1B1B1B;\n  overflow: hidden;\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  justify-content: center;\n  transition: transform 1.2s cubic-bezier(0.7, 0, 0.3, 1), opacity 1.2s ease;\n}\n\n.s-scene-06-wrapper[data-closing=\"true\"] {\n  transform: scale(0.9);\n  opacity: 0;\n}\n\n.bg-base {\n  position: absolute;\n  inset: 0;\n  background: #1B1B1B;\n  z-index: 1;\n}\n\n.bg-gradient-overlay {\n  position: absolute;\n  inset: 0;\n  background: radial-gradient(circle at 50% 50%, #FFFFFF 0%, #E6F0FF 40%, #B8D4FF 100%);\n  opacity: 0;\n  transition: opacity 1.5s cubic-bezier(0.4, 0, 0.2, 1);\n  z-index: 2;\n}\n\n.bg-gradient-overlay[data-step-active=\"true\"] {\n  opacity: 1;\n}\n\n.tech-grid-closing {\n  position: absolute;\n  inset: 0;\n  background-image: linear-gradient(rgba(245, 53, 1, 0.05) 1px, transparent 1px), linear-gradient(90deg, rgba(245, 53, 1, 0.05) 1px, transparent 1px);\n  background-size: 40px 40px;\n  z-index: 3;\n  opacity: 0.3;\n}\n\n.slogan-container {\n  position: relative;\n  z-index: 10;\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  text-align: center;\n  opacity: 0;\n  transform: translateY(30px);\n  transition: all 1s cubic-bezier(0.23, 1, 0.32, 1);\n}\n\n.slogan-container[data-step-active=\"true\"] {\n  opacity: 1;\n  transform: translateY(0);\n}\n\n.slogan-line {\n  overflow: hidden;\n}\n\n.slogan-text {\n  display: block;\n  font-family: var(--sv-font-display-cn);\n  font-size: 48px;\n  color: #1B1B1B;\n  letter-spacing: 4px;\n  line-height: 1.2;\n}\n\n.slogan-text.highlight {\n  color: #F53501;\n}\n\n.slogan-divider {\n  width: 40px;\n  height: 4px;\n  background: #F53501;\n  margin: 20px 0;\n}\n\n.anniversary-footer {\n  position: absolute;\n  bottom: 12%;\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  z-index: 10;\n  opacity: 0;\n  transform: translateY(20px);\n  transition: all 0.8s ease-out;\n}\n\n.anniversary-footer[data-step-active=\"true\"] {\n  opacity: 1;\n  transform: translateY(0);\n}\n\n.logo-wrap {\n  display: flex;\n  align-items: center;\n  gap: 15px;\n  margin-bottom: 10px;\n}\n\n.tencent-logo {\n  font-size: 32px;\n  color: #0052D9;\n}\n\n.logo-divider {\n  width: 1px;\n  height: 20px;\n  background: rgba(0, 0, 0, 0.2);\n}\n\n.anniversary-badge {\n  display: flex;\n  flex-direction: column;\n  align-items: flex-start;\n}\n\n.anniversary-badge .num {\n  font-family: var(--sv-font-mono);\n  font-weight: 900;\n  font-size: 24px;\n  line-height: 1;\n  color: #1B1B1B;\n}\n\n.anniversary-badge .text {\n  font-family: var(--sv-font-mono);\n  font-size: 8px;\n  letter-spacing: 1px;\n  color: #666;\n}\n\n.brand-subtext {\n  font-family: var(--sv-font-mono);\n  font-size: 10px;\n  color: rgba(0, 0, 0, 0.4);\n  letter-spacing: 2px;\n}\n\n/* Tech Decors */\n.frame-bracket {\n  position: absolute;\n  width: 15px;\n  height: 15px;\n  border: 1.5px solid #F53501;\n  z-index: 20;\n  opacity: 0.5;\n}\n.tl { top: 40px; left: 40px; border-right: none; border-bottom: none; }\n.tr { top: 40px; right: 40px; border-left: none; border-bottom: none; }\n.bl { bottom: 40px; left: 40px; border-right: none; border-top: none; }\n.br { bottom: 40px; right: 40px; border-left: none; border-top: none; }\n\n.scan-line {\n  position: absolute;\n  top: 0;\n  left: 0;\n  width: 100%;\n  height: 2px;\n  background: linear-gradient(90deg, transparent, #F53501, transparent);\n  z-index: 5;\n  opacity: 0.1;\n  animation: scan 4s linear infinite;\n}\n\n@keyframes scan { 0% { top: 0; } 100% { top: 100%; } }","js":"window.SceneLifecycle.register('scene-06', {\n  enter() {\n    const root = document.querySelector('[data-scene-id=\"scene-06\"]');\n    if (root) {\n      window.__SVGVIDEO_REFRESH_ICONS(root);\n      root.removeAttribute('data-closing');\n    }\n  },\n  step(index) {\n    const root = document.querySelector('[data-scene-id=\"scene-06\"]');\n    if (!root) return;\n\n    const elements = root.querySelectorAll('[data-step]');\n    elements.forEach(el => {\n      const stepValue = parseInt(el.getAttribute('data-step'));\n      el.setAttribute('data-step-active', index >= stepValue ? 'true' : 'false');\n    });\n\n    // Handle Step 2 special closing animation\n    if (index === 2) {\n      setTimeout(() => {\n        // After showing the logo for a while, trigger the shrink fade-out\n        // This mimics the '向内收缩淡出' requirement\n        // In a real flow, this might be triggered by the timeline manager\n        // root.setAttribute('data-closing', 'true');\n      }, 2000);\n    }\n\n    // Visual reinforcement for Step 0 color shift\n    if (index === 0) {\n      const brackets = root.querySelectorAll('.frame-bracket');\n      brackets.forEach(b => b.style.borderColor = '#0052D9'); // Shift theme to brand blue\n    }\n  },\n  leave() {\n    const root = document.querySelector('[data-scene-id=\"scene-06\"]');\n    if (root) root.setAttribute('data-closing', 'true');\n  }\n});"}]</script>
  <script defer src="https://unpkg.com/lucide@latest"></script>
  <script defer src="https://code.iconify.design/iconify-icon/2.3.0/iconify-icon.min.js"></script>
  <script>
    (function () {
      var state = { sceneId: 'scene-02', step: 0, direction: 1 };
      var previousSceneId = null;
      var exitCleanupTimer = null;
      var isPlaying = false;
      var playTimer = null;
      var progressRaf = null;
      var STEP_INTERVAL_MS = 1400;
      var SCENE_FINISH_PAUSE_MS = 2000;
      var autoplayFromFirstScene = false;
      var progressWrap = document.getElementById('sv-progress-wrap');
      var progressFill = document.getElementById('sv-progress-fill');
      var progressThumb = document.getElementById('sv-progress-thumb');
      var playBtn = document.getElementById('sv-play-btn');
      var expandBtn = document.getElementById('sv-expand-btn');
      var statusText = document.getElementById('sv-status-text');
      var sceneButtonList = Array.prototype.slice.call(document.querySelectorAll('[data-panel-scene-id]'));
      var sceneRetryButtonList = Array.prototype.slice.call(document.querySelectorAll('[data-retry-scene-id]'));
      var editorOverlay = document.getElementById('sv-editor-overlay');
      var editorBackdrop = document.getElementById('sv-editor-backdrop');
      var editorSheet = document.getElementById('sv-editor-sheet');
      var editorResizeHandle = document.getElementById('sv-editor-resize-handle');
      var editorCloseBtn = document.getElementById('sv-editor-close-btn');
      var editorSaveBtn = document.getElementById('sv-editor-save-btn');
      var editorList = document.getElementById('sv-editor-list');
      var editorSaveTip = document.getElementById('sv-editor-save-tip');
      var editorTipTimer = null;
      var isEditorOpen = false;
      var isEditorDirty = false;
      var editorHeightVh = 50;
      var hostCaps = { canRetry: false, canEditPanel: false, locale: 'zh', theme: 'dark' };
      var metaJson = document.getElementById('__SVGVIDEO_META__');
      var meta = { scenes: [] };
      try {
        meta = JSON.parse((metaJson && metaJson.textContent) || '{}');
      } catch (e) {
        meta = { scenes: [] };
      }
      var metaSceneMap = {};
      var metaScenes = Array.isArray(meta.scenes) ? meta.scenes : [];
      metaScenes.forEach(function (scene) {
        if (scene && typeof scene.id === 'string' && scene.id) metaSceneMap[scene.id] = scene;
      });
      function refreshFrameworkIcons(root) {
        var refreshRoot = root || document;
        try {
          if (window.lucide && typeof window.lucide.createIcons === 'function') {
            window.lucide.createIcons({ root: refreshRoot });
          }
        } catch (iconErr) {
          console.warn('[svg-video] lucide refresh failed', iconErr);
        }
      }
      window.__SVGVIDEO_REFRESH_ICONS = refreshFrameworkIcons;
      var sceneModels = Array.prototype.slice.call(document.querySelectorAll('#stage > [data-scene-id]')).map(function (node, index) {
        var sceneId = node.getAttribute('data-scene-id') || ('scene-' + (index + 1));
        var metaScene = metaSceneMap[sceneId] || {};
        var sceneTitle = typeof metaScene.title === 'string' && metaScene.title ? metaScene.title : ('分镜' + (index + 1));
        var sceneSteps = Math.max(1, Number(metaScene.steps) || 1);
        var sceneStatus = typeof metaScene.status === 'string' ? metaScene.status : 'pending';
        var scenePrompt = typeof metaScene.prompt === 'string' ? metaScene.prompt : '';
        var sceneScript = typeof metaScene.scriptText === 'string' ? metaScene.scriptText : '';
        var sceneAudioObjectKey = typeof metaScene.audioObjectKey === 'string' ? metaScene.audioObjectKey : '';
        var sceneAudioDurationMs = Number(metaScene.audioDurationMs) > 0 ? Number(metaScene.audioDurationMs) : 0;
        var sceneVoiceName = typeof metaScene.voiceName === 'string' ? metaScene.voiceName : '';
        var relatedSceneIds = Array.isArray(metaScene.relatedSceneIds) ? metaScene.relatedSceneIds : [];
        return {
          id: sceneId,
          title: sceneTitle,
          steps: sceneSteps,
          status: sceneStatus,
          prompt: scenePrompt,
          scriptText: sceneScript,
          audioObjectKey: sceneAudioObjectKey,
          audioDurationMs: sceneAudioDurationMs,
          voiceName: sceneVoiceName,
          relatedSceneIds: relatedSceneIds
        };
      });
      if (!sceneModels.length && metaScenes.length) {
        sceneModels = metaScenes.map(function (scene, index) {
          return {
            id: scene.id || ('scene-' + (index + 1)),
            title: scene.title || ('分镜' + (index + 1)),
            steps: Math.max(1, Number(scene.steps) || 1),
            status: typeof scene.status === 'string' ? scene.status : 'pending',
            prompt: typeof scene.prompt === 'string' ? scene.prompt : '',
            scriptText: typeof scene.scriptText === 'string' ? scene.scriptText : '',
            audioObjectKey: typeof scene.audioObjectKey === 'string' ? scene.audioObjectKey : '',
            audioDurationMs: Number(scene.audioDurationMs) > 0 ? Number(scene.audioDurationMs) : 0,
            voiceName: typeof scene.voiceName === 'string' ? scene.voiceName : '',
            relatedSceneIds: Array.isArray(scene.relatedSceneIds) ? scene.relatedSceneIds : []
          };
        });
      }
      function i18nText(zh, en) {
        return hostCaps.locale === 'en' ? en : zh;
      }
      function applyI18nTexts() {
        var toolbarTitleEl = document.getElementById('sv-toolbar-title');
        var titleEl = document.getElementById('sv-editor-header-title');
        if (toolbarTitleEl) toolbarTitleEl.textContent = i18nText('分镜面板', 'Storyboard');
        if (titleEl) titleEl.textContent = i18nText('分镜详情编辑', 'Storyboard details');
        if (expandBtn) {
          var expandText = i18nText('展开分镜详情', 'Open storyboard details');
          expandBtn.setAttribute('aria-label', expandText);
          expandBtn.setAttribute('title', expandText);
        }
        if (editorSaveBtn) editorSaveBtn.textContent = isEditorDirty ? i18nText('保存*', 'Save*') : i18nText('保存', 'Save');
        if (editorCloseBtn) editorCloseBtn.textContent = i18nText('关闭', 'Close');
        if (editorSaveTip) editorSaveTip.textContent = i18nText('已保存', 'Saved');
      }
      function clampEditorHeightVh(value) {
        return Math.max(32, Math.min(80, Number(value) || 50));
      }
      function applyEditorHeight(vh) {
        editorHeightVh = clampEditorHeightVh(vh);
        document.documentElement.style.setProperty('--sv-editor-height', editorHeightVh + 'vh');
      }
      function persistMetaToScript() {
        meta.title = typeof meta.title === 'string' ? meta.title : '';
        meta.version = meta.version || 1;
        meta.scenes = sceneModels.map(function (scene) {
          return {
            id: scene.id,
            title: scene.title || '',
            steps: Math.max(1, Number(scene.steps) || 1),
            status: scene.status || 'pending',
            prompt: scene.prompt || '',
            scriptText: scene.scriptText || '',
            audioObjectKey: scene.audioObjectKey || '',
            audioDurationMs: Number(scene.audioDurationMs) > 0 ? Number(scene.audioDurationMs) : 0,
            voiceName: scene.voiceName || '',
            relatedSceneIds: Array.isArray(scene.relatedSceneIds) ? scene.relatedSceneIds : []
          };
        });
        if (metaJson) metaJson.textContent = JSON.stringify(meta);
      }
      function showSavedTip() {
        if (!editorSaveTip) return;
        if (editorTipTimer) {
          clearTimeout(editorTipTimer);
          editorTipTimer = null;
        }
        editorSaveTip.classList.add('is-visible');
        editorTipTimer = setTimeout(function () {
          editorSaveTip.classList.remove('is-visible');
        }, 1200);
      }
      function markEditorDirty() {
        isEditorDirty = true;
        applyI18nTexts();
      }
      function clearEditorDirty() {
        isEditorDirty = false;
        applyI18nTexts();
      }
      function getStatusLabel(status) {
        if (status === 'success') return i18nText('已生成', 'Done');
        if (status === 'generating') return i18nText('生成中', 'Generating');
        if (status === 'failed') return i18nText('失败', 'Failed');
        return i18nText('未生成', 'Pending');
      }
      function getStatusClass(status) {
        if (status === 'generating') return 'is-generating';
        if (status === 'success') return 'is-success';
        return 'is-failed';
      }
      function getRetryButtonInnerHtml(isGenerating) {
        if (isGenerating) return '<span class="scene-chip-retry-spinner"></span>';
        return '<svg class="scene-chip-retry-icon" viewBox="0 0 1024 1024" width="16" height="16" fill="currentColor"><path d="M698.569 531.322a39.825 39.825 0 0 0-23.898 7.949c-17.702 13.216-21.34 38.28-8.124 55.982l95.716 128.212c7.854 10.521 19.893 16.073 32.084 16.073a39.825 39.825 0 0 0 23.898-7.949c17.702-13.216 21.34-38.28 8.124-55.982l-95.716-128.212c-7.853-10.521-19.892-16.074-32.084-16.073z" /><path d="M922.498 563.818a39.825 39.825 0 0 0-23.898 7.949l-128.212 95.716c-17.703 13.216-21.34 38.28-8.124 55.982 7.854 10.521 19.893 16.073 32.084 16.073a39.825 39.825 0 0 0 23.898-7.949l128.212-95.716c17.702-13.216 21.34-38.28 8.124-55.982-7.854-10.521-19.893-16.074-32.084-16.073zM229.652 282.148a39.825 39.825 0 0 0-23.898 7.949c-17.702 13.216-21.34 38.28-8.124 55.982l95.716 128.212c7.854 10.521 19.893 16.073 32.084 16.073a39.825 39.825 0 0 0 23.898-7.949c17.702-13.216 21.34-38.28 8.124-55.982l-95.716-128.212c-7.854-10.521-19.892-16.074-32.084-16.073z" /><path d="M229.652 282.148a39.825 39.825 0 0 0-23.898 7.949L77.542 385.813c-17.702 13.216-21.34 38.28-8.124 55.982 7.854 10.521 19.893 16.073 32.084 16.073a39.825 39.825 0 0 0 23.898-7.949l128.213-95.716c17.702-13.216 21.34-38.28 8.124-55.982-7.855-10.521-19.893-16.073-32.085-16.073z" /><path d="M614.968 763.65c-44.13 18.171-94.005 25.121-146.168 16.968C353.747 762.636 261.364 670.253 243.381 555.2c-9.681-61.936 1.932-120.646 28.337-170.344l-58.657-58.657c-40.139 64.442-60.11 142.746-50.806 226.152 17.993 161.306 148.088 291.401 309.394 309.394 62.041 6.92 121.259-2.357 174.048-24.083 26.535-10.921 33.625-45.203 13.335-65.493l-0.098-0.098c-11.555-11.555-28.854-14.643-43.966-8.421zM409.033 260.35c44.131-18.171 94.007-25.122 146.169-16.968 115.051 17.985 207.431 110.365 225.416 225.416 9.682 61.935-1.931 120.646-28.337 170.346l58.658 58.658c40.139-64.443 60.109-142.747 50.805-226.155-17.993-161.305-148.087-291.399-309.391-309.391-62.042-6.92-121.26 2.356-174.05 24.082-26.535 10.921-33.625 45.203-13.335 65.493l0.099 0.099c11.555 11.554 28.854 14.642 43.966 8.42z" /></svg>';
      }
      function setSceneStatus(sceneId, status) {
        var index = getSceneIndexById(sceneId);
        if (index < 0) return;
        sceneModels[index].status = status;
        persistMetaToScript();
        var statusClass = getStatusClass(status);
        var chipWrap = document.querySelector('[data-chip-scene-id="' + sceneId + '"]');
        if (chipWrap) {
          chipWrap.classList.remove('is-success', 'is-failed', 'is-generating');
          chipWrap.classList.add(statusClass);
          var chipBtn = chipWrap.querySelector('.scene-chip');
          if (chipBtn) {
            chipBtn.classList.remove('is-success', 'is-failed', 'is-generating');
            chipBtn.classList.add(statusClass);
          }
          var retryBtn = chipWrap.querySelector('.scene-chip-retry');
          if (retryBtn) {
            retryBtn.innerHTML = getRetryButtonInnerHtml(status === 'generating');
            if (status === 'generating') retryBtn.setAttribute('disabled', 'disabled');
            else retryBtn.removeAttribute('disabled');
          }
        }
        renderStatus();
      }
      function requestSceneRetry(sceneId) {
        if (!hostCaps.canRetry) return;
        var idx = getSceneIndexById(sceneId);
        if (idx < 0) return;
        var scene = sceneModels[idx];
        setSceneStatus(sceneId, 'generating');
        focusScene(sceneId);
        if (window.parent && window.parent !== window) {
          window.parent.postMessage(
            { type: '__SVGVIDEO_RETRY_SCENE', sceneId: sceneId, prompt: scene.prompt || '' },
            '*'
          );
        }
      }
      function renderEditorList() {
        if (!editorList) return;
        editorList.innerHTML = '';
        var fragment = document.createDocumentFragment();
        sceneModels.forEach(function (scene, index) {
          var item = document.createElement('section');
          item.className = 'sv-editor-item';
          item.setAttribute('data-editor-scene-id', scene.id);

          var head = document.createElement('div');
          head.className = 'sv-editor-item-head';

          var jumpBtn = document.createElement('button');
          jumpBtn.type = 'button';
          jumpBtn.className = 'sv-editor-item-title';
          jumpBtn.textContent = i18nText('分镜', 'Scene') + (index + 1) + ' · ' + (scene.title || '');
          jumpBtn.addEventListener('click', function () {
            focusScene(scene.id);
          });
          head.appendChild(jumpBtn);

          var statusWrap = document.createElement('div');
          statusWrap.className = 'sv-editor-status-wrap';
          var status = document.createElement('span');
          status.className = 'sv-editor-status';
          status.textContent = getStatusLabel(scene.status);
          statusWrap.appendChild(status);
          var retryBtn = document.createElement('button');
          retryBtn.type = 'button';
          retryBtn.className = 'scene-chip-retry sv-editor-retry';
          retryBtn.setAttribute('aria-label', i18nText('重新生成当前分镜', 'Retry this scene'));
          retryBtn.setAttribute('title', i18nText('重新生成当前分镜', 'Retry this scene'));
          retryBtn.innerHTML = getRetryButtonInnerHtml(scene.status === 'generating');
          if (scene.status === 'generating' || !hostCaps.canRetry) retryBtn.setAttribute('disabled', 'disabled');
          retryBtn.addEventListener('click', function (event) {
            event.preventDefault();
            event.stopPropagation();
            requestSceneRetry(scene.id);
            if (isEditorOpen) renderEditorList();
          });
          statusWrap.appendChild(retryBtn);
          head.appendChild(statusWrap);

          item.appendChild(head);

          var scriptField = document.createElement('label');
          scriptField.className = 'sv-editor-field';
          var scriptLabel = document.createElement('span');
          scriptLabel.className = 'sv-editor-label';
          scriptLabel.textContent = i18nText('脚本', 'Script');
          var scriptInput = document.createElement('div');
          scriptInput.className = 'sv-editor-script-view';
          scriptInput.setAttribute('data-field-scene-id', scene.id);
          scriptInput.textContent = scene.scriptText || '';
          scriptField.appendChild(scriptLabel);
          scriptField.appendChild(scriptInput);
          item.appendChild(scriptField);

          var promptField = document.createElement('label');
          promptField.className = 'sv-editor-field';
          var promptLabel = document.createElement('span');
          promptLabel.className = 'sv-editor-label';
          promptLabel.textContent = i18nText('提示词', 'Prompt');
          var promptInput = document.createElement('textarea');
          promptInput.className = 'sv-editor-textarea';
          promptInput.setAttribute('data-field-scene-id', scene.id);
          promptInput.setAttribute('data-field', 'prompt');
          promptInput.value = scene.prompt || '';
          promptInput.addEventListener('click', function (event) {
            event.stopPropagation();
          });
          promptInput.addEventListener('input', function () {
            scene.prompt = promptInput.value;
            markEditorDirty();
          });
          promptField.appendChild(promptLabel);
          promptField.appendChild(promptInput);
          item.appendChild(promptField);
          item.addEventListener('click', function () {
            focusScene(scene.id);
          });

          fragment.appendChild(item);
        });
        editorList.appendChild(fragment);
        highlightActiveEditorItem();
      }
      function highlightActiveEditorItem() {
        if (!editorList) return;
        var itemList = Array.prototype.slice.call(editorList.querySelectorAll('[data-editor-scene-id]'));
        itemList.forEach(function (item) {
          if (item.getAttribute('data-editor-scene-id') === state.sceneId) item.classList.add('is-active');
          else item.classList.remove('is-active');
        });
      }
      function openEditor() {
        if (!hostCaps.canEditPanel) return;
        if (!editorOverlay) return;
        renderEditorList();
        applyEditorHeight(editorHeightVh);
        editorOverlay.classList.add('is-open');
        editorOverlay.setAttribute('aria-hidden', 'false');
        document.body.classList.add('sv-editor-open');
        isEditorOpen = true;
      }
      function closeEditor() {
        if (!editorOverlay) return;
        editorOverlay.classList.remove('is-open');
        editorOverlay.setAttribute('aria-hidden', 'true');
        document.body.classList.remove('sv-editor-open');
        isEditorOpen = false;
      }
      function saveEditor() {
        persistMetaToScript();
        clearEditorDirty();
        showSavedTip();
        if (window.parent && window.parent !== window) {
          window.parent.postMessage({ type: '__SVGVIDEO_META_UPDATED', meta: meta }, '*');
        }
      }
      function getTotalSteps() {
        return sceneModels.reduce(function (sum, scene) { return sum + Math.max(1, Number(scene.steps) || 1); }, 0);
      }
      function getSceneIndexById(sceneId) {
        for (var i = 0; i < sceneModels.length; i++) {
          if (sceneModels[i].id === sceneId) return i;
        }
        return -1;
      }
      function getSceneInitialStep(sceneId) {
        var index = getSceneIndexById(sceneId);
        if (index < 0) return 0;
        var maxSteps = Math.max(1, Number(sceneModels[index].steps) || 1);
        return Math.max(0, maxSteps - 1);
      }
      function focusScene(sceneId) {
        var index = getSceneIndexById(sceneId);
        if (index < 0) return;
        var maxSteps = Math.max(1, Number(sceneModels[index].steps) || 1);
        stopPlayback();
        window.__SVGVIDEO_SET_STATE(sceneId, 0, 1);
        if (maxSteps > 1) {
          setTimeout(function () {
            window.__SVGVIDEO_SET_STATE(sceneId, Math.max(0, maxSteps - 1), 1);
          }, 90);
        }
      }
      function getProgressPercent(sceneId, step) {
        var total = getTotalSteps();
        if (!total) return 0;
        var index = getSceneIndexById(sceneId);
        if (index < 0) return 0;
        var completed = 0;
        for (var i = 0; i < index; i++) completed += Math.max(1, Number(sceneModels[i].steps) || 1);
        var maxSteps = Math.max(1, Number(sceneModels[index].steps) || 1);
        var localStep = Math.max(0, Math.min(maxSteps - 1, Number(step) || 0));
        return Math.max(0, Math.min(100, ((completed + localStep) / total) * 100));
      }
      function updateProgressUi(percent) {
        var pct = Math.max(0, Math.min(100, percent || 0));
        if (progressFill) progressFill.style.width = pct + '%';
        if (progressThumb) progressThumb.style.left = pct + '%';
      }
      function stopProgressAnimation() {
        if (progressRaf) {
          cancelAnimationFrame(progressRaf);
          progressRaf = null;
        }
      }
      function startProgressSegment(fromPercent, toPercent, durationMs) {
        stopProgressAnimation();
        var from = Math.max(0, Math.min(100, Number(fromPercent) || 0));
        var to = Math.max(0, Math.min(100, Number(toPercent) || 0));
        var duration = Math.max(1, Number(durationMs) || 1);
        if (duration <= 1 || to <= from) {
          updateProgressUi(to);
          return;
        }
        var startAt = performance.now();
        var tick = function (now) {
          if (!isPlaying) {
            stopProgressAnimation();
            return;
          }
          var elapsed = now - startAt;
          var ratio = Math.max(0, Math.min(1, elapsed / duration));
          var current = from + (to - from) * ratio;
          updateProgressUi(current);
          if (ratio < 1) {
            progressRaf = requestAnimationFrame(tick);
          } else {
            stopProgressAnimation();
          }
        };
        progressRaf = requestAnimationFrame(tick);
      }
      function getPlaybackSegment(sceneIndex, step) {
        var scene = sceneModels[sceneIndex];
        if (!scene) return null;
        var currentPercent = getProgressPercent(scene.id, step);
        var maxSteps = Math.max(1, Number(scene.steps) || 1);
        if (step < maxSteps - 1) {
          return {
            from: currentPercent,
            to: getProgressPercent(scene.id, step + 1),
            duration: STEP_INTERVAL_MS
          };
        }
        if (sceneIndex < sceneModels.length - 1) {
          var nextScene = sceneModels[sceneIndex + 1];
          var nextPercent = nextScene ? getProgressPercent(nextScene.id, 0) : currentPercent;
          return {
            from: currentPercent,
            to: nextPercent,
            duration: SCENE_FINISH_PAUSE_MS
          };
        }
        return {
          from: currentPercent,
          to: 100,
          duration: STEP_INTERVAL_MS
        };
      }
      function renderStatus() {
        if (!statusText) return;
        var done = sceneModels.filter(function (scene) { return scene.status === 'success'; }).length;
        var pending = sceneModels.filter(function (scene) { return scene.status === 'pending' || scene.status === 'generating'; }).length;
        var failed = sceneModels.filter(function (scene) { return scene.status === 'failed'; }).length;
        statusText.textContent = '已生成 ' + done + '/' + sceneModels.length + ' · 待生成 ' + pending + ' · 失败 ' + failed;
      }
      function updatePlayButton() {
        if (!playBtn) return;
        playBtn.innerHTML = isPlaying
          ? '<svg viewBox="0 0 24 24" width="14" height="14" fill="currentColor"><rect x="6" y="5" width="4" height="14" rx="1"></rect><rect x="14" y="5" width="4" height="14" rx="1"></rect></svg>'
          : '<svg viewBox="0 0 24 24" width="14" height="14" fill="currentColor"><path d="M7 5v14l11-7z"></path></svg>';
        playBtn.setAttribute('aria-label', isPlaying ? '暂停播放' : '播放分镜');
        playBtn.setAttribute('title', isPlaying ? '暂停播放' : '播放分镜');
      }
      function applyHostCaps() {
        if (hostCaps.canRetry) document.body.classList.add('sv-host-can-retry');
        else document.body.classList.remove('sv-host-can-retry');
        if (hostCaps.canEditPanel) document.body.classList.add('sv-host-can-edit-panel');
        else document.body.classList.remove('sv-host-can-edit-panel');
        if (hostCaps.theme === 'light') document.body.classList.add('sv-theme-light');
        else document.body.classList.remove('sv-theme-light');
      }
      function highlightActiveSceneButton() {
        sceneButtonList.forEach(function (button) {
          var targetId = button.getAttribute('data-panel-scene-id');
          if (targetId === state.sceneId) button.classList.add('is-active');
          else button.classList.remove('is-active');
        });
      }
      function stopPlayback() {
        if (playTimer) {
          clearTimeout(playTimer);
          playTimer = null;
        }
        stopProgressAnimation();
        isPlaying = false;
        updatePlayButton();
      }
      function startPlayback() {
        if (!sceneModels.length) return;
        stopPlayback();
        var sceneIndex = Math.max(0, getSceneIndexById(state.sceneId));
        var step = 0;
        var applyPlaybackState = function (direction) {
          var scene = sceneModels[sceneIndex];
          if (!scene) return false;
          window.__SVGVIDEO_SET_STATE(scene.id, step, direction);
          updateProgressUi(getProgressPercent(scene.id, step));
          var segment = getPlaybackSegment(sceneIndex, step);
          if (segment) startProgressSegment(segment.from, segment.to, segment.duration);
          return true;
        };
        var scheduleAdvance = function (delay) {
          playTimer = setTimeout(function () {
            var currentScene = sceneModels[sceneIndex];
            if (!currentScene) {
              stopPlayback();
              return;
            }
            var maxSteps = Math.max(1, Number(currentScene.steps) || 1);
            if (step < maxSteps - 1) {
              step += 1;
              applyPlaybackState(1);
              scheduleAdvance(STEP_INTERVAL_MS);
              return;
            }
            if (sceneIndex < sceneModels.length - 1) {
              playTimer = setTimeout(function () {
                sceneIndex += 1;
                step = 0;
                if (!applyPlaybackState(1)) {
                  stopPlayback();
                  return;
                }
                scheduleAdvance(STEP_INTERVAL_MS);
              }, SCENE_FINISH_PAUSE_MS);
              return;
            }
            stopPlayback();
          }, delay);
        };
        isPlaying = true;
        updatePlayButton();
        if (!applyPlaybackState(1)) {
          stopPlayback();
          return;
        }
        scheduleAdvance(STEP_INTERVAL_MS);
      }
      window.SceneLifecycle = {
        handlers: {},
        register: function (sceneId, handler) { this.handlers[sceneId] = handler || {}; }
      };

      (function () {
        var sceneDataJson = document.getElementById('__SVGVIDEO_SCENE_DATA__');
        var runtimeScenes = [];
        try {
          runtimeScenes = JSON.parse((sceneDataJson && sceneDataJson.textContent) || '[]');
        } catch (_e) {
          runtimeScenes = [];
        }
        runtimeScenes.forEach(function (scene) {
          if (!scene || typeof scene.id !== 'string') return;
          var js = typeof scene.js === 'string' ? scene.js.trim() : '';
          if (!js) return;
          try {
            var factory = new Function(js);
            factory.call(window);
          } catch (err) {
            console.error('[svg-video] scene runtime failed', scene.id, err);
          }
        });
        refreshFrameworkIcons(document);
      })();

      function applyStepVisibility(sceneEl) {
        var stepNodes = sceneEl.querySelectorAll('[data-step]');
        stepNodes.forEach(function (node) {
          var minStep = Number(node.getAttribute('data-step') || '0');
          var visible = state.step >= minStep;
          node.style.opacity = visible ? '1' : '0';
          node.style.visibility = visible ? 'visible' : 'hidden';
          node.style.pointerEvents = visible ? 'auto' : 'none';
          node.style.transform = visible ? 'translateY(0)' : 'translateY(8px)';
          node.style.filter = visible ? 'blur(0px)' : 'blur(4px)';
        });
      }
      function applySceneState() {
        var scenes = document.querySelectorAll('#stage > [data-scene-id]');
        var currentSceneEl = null;
        var previousSceneEl = null;
        scenes.forEach(function (sceneEl) {
          var sceneId = sceneEl.getAttribute('data-scene-id');
          if (sceneId === state.sceneId) currentSceneEl = sceneEl;
          if (sceneId === previousSceneId) previousSceneEl = sceneEl;
        });
        var direction = state.direction >= 0 ? 1 : -1;
        document.documentElement.style.setProperty('--scene-enter-offset', direction > 0 ? '56px' : '-56px');
        document.documentElement.style.setProperty('--scene-exit-offset', direction > 0 ? '-56px' : '56px');
        if (exitCleanupTimer) {
          clearTimeout(exitCleanupTimer);
          exitCleanupTimer = null;
        }
        scenes.forEach(function (sceneEl) {
          if (sceneEl !== currentSceneEl && sceneEl !== previousSceneEl) {
            sceneEl.classList.remove('scene-active');
            sceneEl.classList.remove('scene-exit');
          }
        });
        if (previousSceneEl && previousSceneEl !== currentSceneEl) {
          previousSceneEl.classList.remove('scene-active');
          previousSceneEl.classList.add('scene-exit');
        }
        if (currentSceneEl) {
          currentSceneEl.classList.remove('scene-exit');
          currentSceneEl.classList.add('scene-active');
          applyStepVisibility(currentSceneEl);
        }
        if (previousSceneEl && previousSceneEl !== currentSceneEl) {
          exitCleanupTimer = setTimeout(function () { previousSceneEl.classList.remove('scene-exit'); }, 620);
        }
        if (previousSceneId !== state.sceneId) {
          if (previousSceneId && window.SceneLifecycle.handlers[previousSceneId] && typeof window.SceneLifecycle.handlers[previousSceneId].leave === 'function') {
            try { window.SceneLifecycle.handlers[previousSceneId].leave(); } catch (e) {}
          }
          if (window.SceneLifecycle.handlers[state.sceneId] && typeof window.SceneLifecycle.handlers[state.sceneId].enter === 'function') {
            try { window.SceneLifecycle.handlers[state.sceneId].enter(); } catch (e) {}
          }
        }
        if (window.SceneLifecycle.handlers[state.sceneId] && typeof window.SceneLifecycle.handlers[state.sceneId].step === 'function') {
          try { window.SceneLifecycle.handlers[state.sceneId].step(state.step); } catch (e) {}
        }
        if (currentSceneEl) {
          refreshFrameworkIcons(currentSceneEl);
        }
        previousSceneId = state.sceneId;
        highlightActiveSceneButton();
        highlightActiveEditorItem();
        updateProgressUi(getProgressPercent(state.sceneId, state.step));
      }
      window.__SVGVIDEO_SET_STATE = function (sceneId, step, direction) {
        if (typeof sceneId === 'string' && sceneId) {
          if (typeof direction === 'number' && Number.isFinite(direction)) {
            state.direction = direction >= 0 ? 1 : -1;
          } else {
            var nextIndex = getSceneIndexById(sceneId);
            var prevIndex = getSceneIndexById(state.sceneId);
            if (nextIndex !== -1 && prevIndex !== -1) state.direction = nextIndex >= prevIndex ? 1 : -1;
          }
          state.sceneId = sceneId;
        }
        if (typeof step === 'number' && Number.isFinite(step)) state.step = Math.max(0, Math.floor(step));
        applySceneState();
      };
      if (state.sceneId && getSceneIndexById(state.sceneId) === -1 && sceneModels.length) {
        state.sceneId = sceneModels[0].id;
      }
      if (playBtn) {
        playBtn.addEventListener('click', function () {
          if (isPlaying) stopPlayback();
          else startPlayback();
        });
      }
      if (expandBtn) {
        expandBtn.addEventListener('click', function () {
          if (isEditorOpen) closeEditor();
          else openEditor();
        });
      }
      if (editorCloseBtn) {
        editorCloseBtn.addEventListener('click', closeEditor);
      }
      if (editorBackdrop) {
        editorBackdrop.addEventListener('click', closeEditor);
      }
      if (editorSaveBtn) {
        editorSaveBtn.addEventListener('click', saveEditor);
      }
      if (editorResizeHandle && editorSheet) {
        editorResizeHandle.addEventListener('mousedown', function (event) {
          event.preventDefault();
          var startY = event.clientY;
          var startHeightPx = (window.innerHeight * editorHeightVh) / 100;
          var onMove = function (moveEvent) {
            var delta = startY - moveEvent.clientY;
            var nextHeightPx = startHeightPx + delta;
            var nextHeightVh = (nextHeightPx / Math.max(1, window.innerHeight)) * 100;
            applyEditorHeight(nextHeightVh);
          };
          var onUp = function () {
            window.removeEventListener('mousemove', onMove);
            window.removeEventListener('mouseup', onUp);
          };
          window.addEventListener('mousemove', onMove);
          window.addEventListener('mouseup', onUp);
        });
      }
      sceneButtonList.forEach(function (button) {
        button.addEventListener('click', function () {
          var sceneId = button.getAttribute('data-panel-scene-id');
          if (!sceneId) return;
          focusScene(sceneId);
        });
      });
      sceneRetryButtonList.forEach(function (button) {
        button.addEventListener('click', function (event) {
          event.preventDefault();
          event.stopPropagation();
          var sceneId = button.getAttribute('data-retry-scene-id');
          if (!sceneId) return;
          requestSceneRetry(sceneId);
        });
      });
      if (progressWrap) {
        progressWrap.addEventListener('click', function (event) {
          var rect = progressWrap.getBoundingClientRect();
          var pct = Math.max(0, Math.min(100, ((event.clientX - rect.left) / rect.width) * 100));
          var totalSteps = getTotalSteps();
          var targetStep = Math.round((pct / 100) * totalSteps);
          var accumulated = 0;
          for (var i = 0; i < sceneModels.length; i++) {
            var sceneSteps = Math.max(1, Number(sceneModels[i].steps) || 1);
            if (accumulated + sceneSteps > targetStep) {
              var localStep = Math.max(0, Math.min(sceneSteps - 1, targetStep - accumulated));
              stopPlayback();
              window.__SVGVIDEO_SET_STATE(sceneModels[i].id, localStep, 1);
              updateProgressUi(pct);
              return;
            }
            accumulated += sceneSteps;
          }
        });
      }
      window.addEventListener('message', function (event) {
        var data = event && event.data;
        if (!data || typeof data.type !== 'string') return;
        if (data.type === '__SVGVIDEO_SET_STATE') {
          window.__SVGVIDEO_SET_STATE(data.sceneId, data.step, data.direction);
          return;
        }
        if (data.type === '__SVGVIDEO_PLAY') {
          startPlayback();
          return;
        }
        if (data.type === '__SVGVIDEO_STOP') {
          stopPlayback();
          return;
        }
        if (data.type === '__SVGVIDEO_SET_HOST_CAPS') {
          hostCaps.canRetry = Boolean(data.canRetry);
          hostCaps.canEditPanel = Boolean(data.canEditPanel);
          hostCaps.locale = data.locale === 'en' ? 'en' : 'zh';
          hostCaps.theme = data.theme === 'light' ? 'light' : 'dark';
          applyI18nTexts();
          applyHostCaps();
          if (isEditorOpen) renderEditorList();
          return;
        }
        if (data.type === '__SVGVIDEO_SET_SCENE_STATUS') {
          var sceneId = typeof data.sceneId === 'string' ? data.sceneId : '';
          var status = typeof data.status === 'string' ? data.status : '';
          if (sceneId && status) {
            setSceneStatus(sceneId, status);
            if (isEditorOpen) renderEditorList();
          }
          return;
        }
        if (data.type === '__SVGVIDEO_SET_RECORDING') {
          if (data && data.active) {
            document.body.classList.add('sv-recording');
            // 如果指定了初始分镜，无过渡动画地跳转过去，避免录制开头残留旧分镜画面
            if (data.sceneId) {
              var noTransStyle = document.createElement('style');
              noTransStyle.id = 'sv-no-transition';
              noTransStyle.textContent = '.scene, [data-step] { transition: none !important; }';
              document.head.appendChild(noTransStyle);
              window.__SVGVIDEO_SET_STATE(data.sceneId, 0, 1);
              // 强制 reflow 后恢复过渡动画
              void document.body.offsetHeight;
              requestAnimationFrame(function () {
                var s = document.getElementById('sv-no-transition');
                if (s) s.remove();
              });
            }
          } else {
            document.body.classList.remove('sv-recording');
          }
          return;
        }
        if (data.type === '__SVGVIDEO_SET_META' && data.meta && typeof data.meta === 'object') {
          meta = data.meta;
          var freshScenes = Array.isArray(meta.scenes) ? meta.scenes : [];
          freshScenes.forEach(function (scene) {
            var idx = getSceneIndexById(scene.id);
            if (idx < 0) return;
            sceneModels[idx].prompt = typeof scene.prompt === 'string' ? scene.prompt : '';
            sceneModels[idx].scriptText = typeof scene.scriptText === 'string' ? scene.scriptText : '';
            sceneModels[idx].audioObjectKey = typeof scene.audioObjectKey === 'string' ? scene.audioObjectKey : '';
            sceneModels[idx].audioDurationMs = Number(scene.audioDurationMs) > 0 ? Number(scene.audioDurationMs) : 0;
            sceneModels[idx].voiceName = typeof scene.voiceName === 'string' ? scene.voiceName : '';
          });
          persistMetaToScript();
          if (isEditorOpen) renderEditorList();
        }
      });
      document.addEventListener('keydown', function (event) {
        if (event.key === 'Escape' && isEditorOpen) closeEditor();
      });
      window.__SVGVIDEO_PLAY = startPlayback;
      window.__SVGVIDEO_STOP = stopPlayback;
      window.__SVGVIDEO_OPEN_EDITOR = openEditor;
      window.__SVGVIDEO_CLOSE_EDITOR = closeEditor;
      applyEditorHeight(editorHeightVh);
      renderStatus();
      updatePlayButton();
      applyHostCaps();
      clearEditorDirty();
      applyI18nTexts();
      applySceneState();
      if (autoplayFromFirstScene && sceneModels.length) {
        state.sceneId = sceneModels[0].id;
        state.step = 0;
        state.direction = 1;
        applySceneState();
        startPlayback();
      }
    })();
  </script>
</body>
</html>25:{"id":"5640993b-edd4-480b-89f0-36cd4325fb5e","exportName":"$undefined","title":"The Development History of Tencent: The Evolution of Connection","description":"Explaining the development history of Tencent","category":"html-video","tags":"$26","likes":1,"favorites":0,"views":210,"sourceType":"HTMLVideo","componentSource":"$27","isLiked":false,"isFavorited":false}
29:[]
2a:T3a04,<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>3D Glass Text Animation</title>
<style>
    /* 基础重置与全屏居中 */
    body, html {
        margin: 0;
        padding: 0;
        width: 100%;
        height: 100%;
        background-color: #f0f4f8; /* 亮色主题底色 */
        display: flex;
        justify-content: center;
        align-items: center;
        overflow: hidden;
        font-family: 'Impact', 'Arial Black', sans-serif;
        touch-action: none; /* 阻止移动端默认滚动，便于拖拽 */
    }

    /* 响应式缩放容器 */
    .scale-wrapper {
        position: relative;
        width: 1200px;
        height: 700px;
        /* 默认居中，利用 JS 动态计算缩放比例保持画面完整 */
        transform-origin: center center;
    }

    /* 背景层：亮色渐变与光斑 */
    .bg-layer {
        position: absolute;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        z-index: 1;
        border-radius: 16px;
        overflow: hidden;
        background: radial-gradient(circle at 50% 50%, #ffffff 0%, #e2eaf4 100%);
        box-shadow: 0 20px 60px rgba(150, 170, 200, 0.2);
    }

    /* 3D 场景容器：开启透视 */
    .scene {
        position: absolute;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        z-index: 2;
        perspective: 1500px;
        display: flex;
        justify-content: center;
        align-items: center;
    }

    /* 旋转组：承载所有 3D 图层 */
    .text-group {
        position: relative;
        width: 100%;
        height: 100%;
        transform-style: preserve-3d;
        /* 初始角度将在 JS 中设置并更新 */
        cursor: grab;
    }
    
    .text-group:active {
        cursor: grabbing;
    }

    /* 每一层 SVG 的统一样式 */
    .slice {
        position: absolute;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        display: flex;
        justify-content: center;
        align-items: center;
        pointer-events: none; /* 避免遮挡鼠标事件 */
    }

    .slice svg {
        width: 100%;
        height: 100%;
        overflow: visible;
    }

    /* 中文副标题排版 */
    .subtitle-container {
        position: absolute;
        bottom: 80px;
        width: 100%;
        text-align: center;
        z-index: 3;
        pointer-events: none;
    }

    .subtitle {
        font-family: 'PingFang SC', 'Microsoft YaHei', sans-serif;
        font-size: 20px;
        font-weight: 300;
        color: #798da3;
        letter-spacing: 16px;
        text-transform: uppercase;
        opacity: 0;
        transform: translateY(20px);
        animation: fadeUp 1.5s cubic-bezier(0.2, 0.8, 0.2, 1) 1s forwards;
        text-shadow: 0 2px 4px rgba(255, 255, 255, 0.8);
    }

    /* 装饰线 */
    .deco-line {
        width: 60px;
        height: 1px;
        background: #aab8c8;
        margin: 15px auto 0;
        opacity: 0;
        animation: expandLine 1s cubic-bezier(0.2, 0.8, 0.2, 1) 1.5s forwards;
    }

    @keyframes fadeUp {
        to { opacity: 1; transform: translateY(0); }
    }
    @keyframes expandLine {
        to { opacity: 0.6; width: 120px; }
    }

    /* 交互提示 */
    .hint {
        position: absolute;
        top: 40px;
        width: 100%;
        text-align: center;
        font-family: 'PingFang SC', sans-serif;
        font-size: 14px;
        color: #aab8c8;
        letter-spacing: 2px;
        z-index: 3;
        opacity: 0;
        animation: fadeInOut 4s ease-in-out 2s forwards;
        pointer-events: none;
    }
    @keyframes fadeInOut {
        0% { opacity: 0; }
        20% { opacity: 0.8; }
        80% { opacity: 0.8; }
        100% { opacity: 0; }
    }
</style>
</head>
<body>

<div class="scale-wrapper" id="scaleWrapper">
    <!-- 1. 背景层 (SVG 光斑与网格) -->
    <div class="bg-layer">
        <svg width="100%" height="100%" viewBox="0 0 1200 700" preserveAspectRatio="xMidYMid slice">
            <defs>
                <filter id="superBlur" x="-50%" y="-50%" width="200%" height="200%">
                    <feGaussianBlur stdDeviation="80" />
                </filter>
                <pattern id="grid" width="40" height="40" patternUnits="userSpaceOnUse">
                    <path d="M 40 0 L 0 0 0 40" fill="none" stroke="rgba(150, 170, 200, 0.1)" stroke-width="1"/>
                </pattern>
            </defs>
            
            <!-- 极简网格 -->
            <rect width="100%" height="100%" fill="url(#grid)" />

            <!-- 缓慢漂浮的玻璃态光斑 -->
            <g filter="url(#superBlur)">
                <circle cx="300" cy="200" r="150" fill="#a1c4fd" opacity="0.5">
                    <animate attributeName="cx" values="300; 500; 300" dur="15s" ease="ease-in-out" repeatCount="indefinite"/>
                    <animate attributeName="cy" values="200; 400; 200" dur="18s" ease="ease-in-out" repeatCount="indefinite"/>
                </circle>
                <circle cx="900" cy="500" r="180" fill="#c2e9fb" opacity="0.6">
                    <animate attributeName="cx" values="900; 700; 900" dur="20s" ease="ease-in-out" repeatCount="indefinite"/>
                    <animate attributeName="cy" values="500; 300; 500" dur="16s" ease="ease-in-out" repeatCount="indefinite"/>
                </circle>
                <circle cx="600" cy="350" r="250" fill="#fdfbfb" opacity="0.8">
                    <animate attributeName="r" values="250; 300; 250" dur="12s" ease="ease-in-out" repeatCount="indefinite"/>
                </circle>
            </g>
        </svg>
    </div>

    <!-- 交互提示 -->
    <div class="hint">拖拽以自由旋转视角</div>

    <!-- 2. 核心 3D 文本场景 -->
    <div class="scene" id="sceneContainer">
        <div class="text-group" id="textGroup">
            <!-- JS 将动态生成多层 SVG 挤出 3D 玻璃厚度 -->
        </div>
    </div>

    <!-- 3. 前景字幕排版 -->
    <div class="subtitle-container">
        <div class="subtitle">全 矢 量 玻 璃 态 空 间 渲 染</div>
        <div class="deco-line"></div>
    </div>
</div>

<script>
    /**
     * 1. 响应式缩放逻辑
     * 保持 1200x700 的比例，在浏览器窗口中完美居中缩放
     */
    const wrapper = document.getElementById('scaleWrapper');
    const TARGET_WIDTH = 1200;
    const TARGET_HEIGHT = 700;

    function handleResize() {
        const winW = window.innerWidth;
        const winH = window.innerHeight;
        const scaleX = winW / TARGET_WIDTH;
        const scaleY = winH / TARGET_HEIGHT;
        // 留出一点边距，取最小缩放比
        const scale = Math.min(scaleX, scaleY) * 0.95; 
        wrapper.style.transform = `scale(${scale})`;
    }
    window.addEventListener('resize', handleResize);
    handleResize(); // 初始化调用

    /**
     * 2. 生成 3D 玻璃文本层
     * 核心思想：利用 DOM 叠层 + CSS translateZ 模拟 3D 实体的厚度 (Isometric / Extrusion)
     * 不使用 Canvas，纯 SVG 高清渲染
     */
    const textGroup = document.getElementById('textGroup');
    const textContent = "SvgAnimate";
    const LAYERS = 50; // 厚度层数，层数越多越平滑，但影响性能，50是甜点值
    const DEPTH_STEP = 1.2; // 每层 Z 轴间距 (px)
    
    // 预定义 SVG 滤镜和渐变资源，只在第一层生成一次供后续复用
    const defsHTML = `
        <defs>
            <!-- 玻璃高光流光渐变 -->
            <linearGradient id="shineGrad" x1="-100%" y1="0%" x2="0%" y2="0%">
                <stop offset="0%" stop-color="rgba(255,255,255,0)" />
                <stop offset="30%" stop-color="rgba(255,255,255,0)" />
                <stop offset="50%" stop-color="rgba(255,255,255,0.9)" />
                <stop offset="70%" stop-color="rgba(255,255,255,0)" />
                <stop offset="100%" stop-color="rgba(255,255,255,0)" />
                <animate attributeName="x1" values="-150%; 250%" dur="4s" repeatCount="indefinite" />
                <animate attributeName="x2" values="-50%; 350%" dur="4s" repeatCount="indefinite" />
            </linearGradient>

            <!-- 基础玻璃表面渐变 -->
            <linearGradient id="glassSurface" x1="0%" y1="0%" x2="0%" y2="100%">
                <stop offset="0%" stop-color="rgba(255,255,255,0.8)" />
                <stop offset="100%" stop-color="rgba(230,240,255,0.3)" />
            </linearGradient>

            <!-- 底部阴影滤镜 -->
            <filter id="dropShadow" x="-20%" y="-20%" width="140%" height="140%">
                <feGaussianBlur in="SourceAlpha" stdDeviation="15" result="blur" />
                <feOffset dx="0" dy="20" result="offsetBlur" />
                <feComponentTransfer>
                    <feFuncA type="linear" slope="0.15" />
                </feComponentTransfer>
                <feMerge> 
                    <feMergeNode />
                    <feMergeNode in="SourceGraphic" />
                </feMerge>
            </filter>
        </defs>
    `;

    let htmlBuffer = '';

    // A. 生成底部投影层 (最远离相机的层)
    htmlBuffer += `
        <div class="slice" style="transform: translateZ(${(-LAYERS/2 * DEPTH_STEP) - 40}px);">
            <svg viewBox="0 0 1200 700">
                <text x="50%" y="50%" text-anchor="middle" dominant-baseline="middle" 
                      font-size="170" font-weight="900" letter-spacing="4px" font-style="italic"
                      fill="#8a9eb5" filter="blur(25px)" opacity="0.6">
                    ${textContent}
                </text>
            </svg>
        </div>
    `;

    // B. 循环生成玻璃厚度体 (挤出侧面)
    for (let i = 0; i <= LAYERS; i++) {
        const z = (i - LAYERS / 2) * DEPTH_STEP;
        const isTop = (i === LAYERS);
        const isBottom = (i === 0);
        
        // 样式计算逻辑：营造玻璃的清透与边缘折射感
        let fill, stroke, strokeWidth, filter, additionalElements;
        
        if (isTop) {
            // 正面：清晰的表面，带有流光和基础半透明填充
            fill = "url(#glassSurface)";
            stroke = "rgba(255, 255, 255, 1)"; // 锐利的白色边缘
            strokeWidth = "2";
            filter = "none";
            // 顶层附加流光层
            additionalElements = `
                <text x="50%" y="50%" text-anchor="middle" dominant-baseline="middle" 
                      font-size="170" font-weight="900" letter-spacing="4px" font-style="italic"
                      fill="url(#shineGrad)" pointer-events="none">
                    ${textContent}
                </text>
            `;
        } else if (isBottom) {
            // 底面：稍暗，封底
            fill = "rgba(200, 215, 235, 0.4)";
            stroke = "rgba(255, 255, 255, 0.5)";
            strokeWidth = "1";
            filter = "none";
            additionalElements = "";
        } else {
            // 侧边厚度层：极低透明度交替叠加，模拟毛玻璃内部浑浊与折射
            const opacity = 0.04;
            // 细微交替颜色制造条纹质感（类似亚克力切割边缘）
            const color = i % 2 === 0 ? `rgba(255, 255, 255, ${opacity})` : `rgba(180, 210, 255, ${opacity})`;
            fill = color;
            stroke = `rgba(200, 220, 255, ${opacity * 1.5})`;
            strokeWidth = "1.5";
            filter = "none";
            additionalElements = "";
        }

        htmlBuffer += `
            <div class="slice" style="transform: translateZ(${z}px);">
                <svg viewBox="0 0 1200 700">
                    ${i === 0 ? defsHTML : ''} <!-- 只注入一次 defs -->
                    <text x="50%" y="50%" text-anchor="middle" dominant-baseline="middle" 
                          font-size="170" font-weight="900" letter-spacing="4px" font-style="italic"
                          fill="${fill}" stroke="${stroke}" stroke-width="${strokeWidth}"
                          stroke-linejoin="round">
                        ${textContent}
                    </text>
                    ${additionalElements}
                </svg>
            </div>
        `;
    }

    // 渲染到 DOM
    textGroup.innerHTML = htmlBuffer;


    /**
     * 3. 动画与交互逻辑 (核心主循环)
     * 使用唯一的 requestAnimationFrame 处理自动旋转和平滑拖拽过渡
     */
    const sceneContainer = document.getElementById('sceneContainer');
    
    // 旋转状态变量
    let currentRotateX = -15; // 初始微微俯视
    let currentRotateY = 30;  // 初始偏转角度
    let targetRotateX = -15;
    let targetRotateY = 30;
    
    // 交互状态
    let isDragging = false;
    let startX = 0;
    let startY = 0;
    let autoRotate = true;
    let time = 0;

    // 监听拖拽事件 (Pointer Events 兼容鼠标和触摸)
    sceneContainer.addEventListener('pointerdown', (e) => {
        isDragging = true;
        autoRotate = false; // 交互时暂停自动旋转
        startX = e.clientX;
        startY = e.clientY;
        sceneContainer.setPointerCapture(e.pointerId);
    });

    sceneContainer.addEventListener('pointermove', (e) => {
        if (!isDragging) return;
        
        const deltaX = e.clientX - startX;
        const deltaY = e.clientY - startY;
        
        // 调整灵敏度
        targetRotateY += deltaX * 0.4;
        targetRotateX -= deltaY * 0.4;
        
        // 限制 X 轴翻转，防止倒置，保持最佳观感
        targetRotateX = Math.max(-60, Math.min(60, targetRotateX));
        
        startX = e.clientX;
        startY = e.clientY;
    });

    sceneContainer.addEventListener('pointerup', (e) => {
        isDragging = false;
        sceneContainer.releasePointerCapture(e.pointerId);
        // 设置一个延时恢复自动旋转
        setTimeout(() => {
            if (!isDragging) autoRotate = true;
        }, 2000);
    });

    // 唯一主循环
    function renderLoop() {
        if (autoRotate) {
            // 自动缓慢旋转：利用正弦函数做优雅的摇摆，而不是死板的单向旋转
            time += 0.005;
            targetRotateY += 0.2; // 持续围绕 Y 轴旋转
            targetRotateX = -15 + Math.sin(time) * 10; // X 轴轻微呼吸摇摆
        }

        // 平滑插值 (Lerp) 实现惯性顺滑感
        currentRotateX += (targetRotateX - currentRotateX) * 0.1;
        currentRotateY += (targetRotateY - currentRotateY) * 0.1;

        // 应用 3D 变换
        textGroup.style.transform = `rotateX(${currentRotateX}deg) rotateY(${currentRotateY}deg)`;

        requestAnimationFrame(renderLoop);
    }

    // 启动动画循环
    renderLoop();

</script>
</body>
</html>28:{"id":"0ecaed6b-2393-4bff-89fc-e62dee3a32c4","exportName":"$undefined","title":"Create a 3D text animation using three.js with a sharp, angular font","description":"Create a 3D text animation using three.js, displaying the letters 'SvgAnimate' in a sharp, angular font. The scene features a bright theme with glass-textured text. It rotates slowly in 3D automatically.","category":"text-svg-animation","tags":"$29","likes":0,"favorites":0,"views":312,"sourceType":"SvgAnimation","componentSource":"$2a","isLiked":false,"isFavorited":false}
2c:[]
2d:T3bbd,<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>Tracert 命令运行逻辑动画</title>
    <style>
        :root {
            --bg-color: #0a0e14;
            --panel-color: rgba(20, 26, 35, 0.9);
            --accent-blue: #00d2ff;
            --accent-green: #00ff9d;
            --accent-red: #ff4b2b;
            --text-main: #e0e6ed;
            --text-dim: #718096;
            --font-mono: 'Fira Code', 'JetBrains Mono', 'Courier New', monospace;
        }

        body, html {
            margin: 0;
            padding: 0;
            width: 100%;
            height: 100%;
            background-color: var(--bg-color);
            color: var(--text-main);
            font-family: 'PingFang SC', 'Microsoft YaHei', sans-serif;
            overflow: hidden;
            display: flex;
            justify-content: center;
            align-items: center;
        }

        #container {
            position: relative;
            width: 1200px;
            height: 700px;
            background: var(--bg-color);
            box-shadow: 0 0 50px rgba(0,0,0,0.5);
            border: 1px solid #2d3748;
            overflow: hidden;
        }

        .svg-layer {
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
        }

        /* 终端窗口样式 */
        #terminal {
            position: absolute;
            bottom: 30px;
            left: 50%;
            transform: translateX(-50%);
            width: 1100px;
            height: 180px;
            background: var(--panel-color);
            border: 1px solid #4a5568;
            border-radius: 8px;
            padding: 15px;
            font-family: var(--font-mono);
            font-size: 14px;
            color: var(--accent-green);
            box-shadow: 0 10px 30px rgba(0,0,0,0.5);
            overflow-y: auto;
            scrollbar-width: thin;
        }

        .cursor {
            display: inline-block;
            width: 8px;
            height: 15px;
            background: var(--accent-green);
            animation: blink 1s infinite;
            vertical-align: middle;
        }

        @keyframes blink {
            50% { opacity: 0; }
        }

        /* 顶部标题栏 */
        #header {
            position: absolute;
            top: 20px;
            left: 40px;
            display: flex;
            flex-direction: column;
        }

        #header h1 {
            margin: 0;
            font-size: 24px;
            letter-spacing: 2px;
            color: var(--accent-blue);
            text-transform: uppercase;
        }

        #header p {
            margin: 5px 0 0 0;
            font-size: 14px;
            color: var(--text-dim);
        }

        .step-label {
            font-size: 16px;
            font-weight: bold;
            fill: var(--text-main);
        }

        .node-label {
            font-size: 12px;
            fill: var(--text-dim);
            text-anchor: middle;
        }

        .ttl-badge {
            font-family: var(--font-mono);
            font-weight: bold;
            font-size: 14px;
        }

        /* 科技感动画线条 */
        .path-line {
            fill: none;
            stroke: #2d3748;
            stroke-width: 2;
            stroke-dasharray: 8 4;
        }

        .path-active {
            stroke: var(--accent-blue);
            stroke-width: 3;
            stroke-dasharray: 1000;
            stroke-dashoffset: 1000;
        }

        @keyframes dash {
            to { stroke-dashoffset: 0; }
        }
    </style>
</head>
<body>

<div id="container">
    <!-- 背景装饰网格 -->
    <svg class="svg-layer" viewBox="0 0 1200 700">
        <defs>
            <pattern id="grid" width="40" height="40" patternUnits="userSpaceOnUse">
                <path d="M 40 0 L 0 0 0 40" fill="none" stroke="#1a202c" stroke-width="1"/>
            </pattern>
            <filter id="glow">
                <feGaussianBlur stdDeviation="3" result="coloredBlur"/>
                <feMerge>
                    <feMergeNode in="coloredBlur"/>
                    <feMergeNode in="SourceGraphic"/>
                </feMerge>
            </filter>
            
            <!-- 图标定义 -->
            <symbol id="icon-pc" viewBox="0 0 24 24">
                <path fill="currentColor" d="M20,18H4V4H20M20,2H4C2.89,2 2,2.89 2,4V16A2,2 0 0,0 4,18H9L7.5,20.5V22H16.5V20.5L15,18H20A2,2 0 0,0 22,16V4C22,2.89 21.1,2 20,2Z" />
            </symbol>
            <symbol id="icon-router" viewBox="0 0 24 24">
                <path fill="currentColor" d="M20,13H4V11H20M20,17H4V15H20M20,7H4V5H20M22,3H2V21H22V3Z" />
            </symbol>
            <symbol id="icon-server" viewBox="0 0 24 24">
                <path fill="currentColor" d="M19,15H5V13H19M19,19H5V17H19M19,11H5V9H19M5,7V5H19V7H5M21,3H3V21H21V3Z" />
            </symbol>
        </defs>
        <rect width="100%" height="100%" fill="url(#grid)" />

        <!-- 节点连线 -->
        <g id="connections"></g>

        <!-- 节点 -->
        <g id="nodes">
            <!-- 源主机 -->
            <g transform="translate(100, 320)" id="node-source">
                <use href="#icon-pc" x="-25" y="-25" width="50" height="50" style="color: var(--accent-blue); filter: url(#glow);"/>
                <text y="45" class="node-label">源主机 (Client)</text>
                <text y="60" class="node-label" style="font-size:10px">192.168.1.10</text>
            </g>

            <!-- 路由器 1 -->
            <g transform="translate(350, 320)" id="node-r1">
                <use href="#icon-router" x="-20" y="-20" width="40" height="40" style="color: var(--text-dim);"/>
                <text y="40" class="node-label">跃点 1</text>
                <text y="55" class="node-label" style="font-size:10px">10.0.0.1</text>
            </g>

            <!-- 路由器 2 -->
            <g transform="translate(600, 320)" id="node-r2">
                <use href="#icon-router" x="-20" y="-20" width="40" height="40" style="color: var(--text-dim);"/>
                <text y="40" class="node-label">跃点 2</text>
                <text y="55" class="node-label" style="font-size:10px">20.0.0.5</text>
            </g>

            <!-- 路由器 3 -->
            <g transform="translate(850, 320)" id="node-r3">
                <use href="#icon-router" x="-20" y="-20" width="40" height="40" style="color: var(--text-dim);"/>
                <text y="40" class="node-label">跃点 3</text>
                <text y="55" class="node-label" style="font-size:10px">30.0.0.8</text>
            </g>

            <!-- 目标服务器 -->
            <g transform="translate(1100, 320)" id="node-target">
                <use href="#icon-server" x="-25" y="-25" width="50" height="50" style="color: var(--accent-green);"/>
                <text y="45" class="node-label">目标服务器</text>
                <text y="60" class="node-label" style="font-size:10px">8.8.8.8</text>
            </g>
        </g>

        <!-- 动画元素：数据包 -->
        <g id="packet-layer"></g>

        <!-- 步骤提示文字 -->
        <text id="step-description" x="600" y="80" text-anchor="middle" class="step-label" fill="var(--accent-blue)">准备执行 Tracert 命令...</text>
    </svg>

    <div id="header">
        <h1>Tracert 运行逻辑可视化</h1>
        <p>ICMP 生存时间 (TTL) 机制演示</p>
    </div>

    <div id="terminal">
        <div>C:\Users\Admin&gt; <span id="command-text"></span><span class="cursor"></span></div>
        <div id="log-content"></div>
    </div>
</div>

<script>
    const CONFIG = {
        nodes: [
            { id: 'source', x: 100, y: 320, ip: '192.168.1.10' },
            { id: 'r1', x: 350, y: 320, ip: '10.0.0.1' },
            { id: 'r2', x: 600, y: 320, ip: '20.0.0.5' },
            { id: 'r3', x: 850, y: 320, ip: '30.0.0.8' },
            { id: 'target', x: 1100, y: 320, ip: '8.8.8.8' }
        ],
        packetColor: '#00d2ff',
        errorColor: '#ff4b2b',
        successColor: '#00ff9d'
    };

    const terminal = document.getElementById('log-content');
    const stepDesc = document.getElementById('step-description');
    const packetLayer = document.getElementById('packet-layer');
    const commandText = document.getElementById('command-text');

    async function sleep(ms) {
        return new Promise(resolve => setTimeout(resolve, ms));
    }

    function addLog(text, color = 'var(--accent-green)') {
        const div = document.createElement('div');
        div.style.color = color;
        div.style.marginTop = '2px';
        div.innerHTML = text;
        terminal.appendChild(div);
        terminal.scrollTop = terminal.scrollHeight;
    }

    async function typeCommand(text) {
        for (let i = 0; i <= text.length; i++) {
            commandText.textContent = text.slice(0, i);
            await sleep(50);
        }
        await sleep(500);
    }

    function createPacket(x, y, ttl, color) {
        const g = document.createElementNS('http://www.w3.org/2000/svg', 'g');
        g.setAttribute('transform', `translate(${x}, ${y})`);
        
        const circle = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
        circle.setAttribute('r', '8');
        circle.setAttribute('fill', color);
        circle.setAttribute('filter', 'url(#glow)');
        
        const text = document.createElementNS('http://www.w3.org/2000/svg', 'text');
        text.setAttribute('y', '-15');
        text.setAttribute('text-anchor', 'middle');
        text.setAttribute('class', 'ttl-badge');
        text.setAttribute('fill', color);
        text.textContent = `TTL=${ttl}`;
        
        g.appendChild(circle);
        g.appendChild(text);
        packetLayer.appendChild(g);
        return g;
    }

    async function movePacket(packet, startNodeIdx, endNodeIdx, duration) {
        const start = CONFIG.nodes[startNodeIdx];
        const end = CONFIG.nodes[endNodeIdx];
        const startTime = performance.now();

        return new Promise(resolve => {
            function animate(time) {
                let progress = (time - startTime) / duration;
                if (progress > 1) progress = 1;
                
                const currentX = start.x + (end.x - start.x) * progress;
                const currentY = start.y + (end.y - start.y) * progress;
                packet.setAttribute('transform', `translate(${currentX}, ${currentY})`);
                
                if (progress < 1) {
                    requestAnimationFrame(animate);
                } else {
                    resolve();
                }
            }
            requestAnimationFrame(animate);
        });
    }

    async function runTracert() {
        // 1. 初始化
        await typeCommand('tracert 8.8.8.8');
        addLog('通过最多 30 个跃点跟踪到 8.8.8.8 的路由:', 'var(--text-main)');
        await sleep(1000);

        // 循环每一跳
        for (let hop = 1; hop <= 4; hop++) {
            const isTarget = hop === 4;
            const ttl = hop;
            stepDesc.textContent = `步骤 ${hop}: 发送 TTL=${ttl} 的数据包`;
            stepDesc.setAttribute('fill', CONFIG.packetColor);

            // 发送包
            const p = createPacket(CONFIG.nodes[0].x, CONFIG.nodes[0].y, ttl, CONFIG.packetColor);
            
            // 沿途经过路由器
            for (let currentPos = 0; currentPos < hop; currentPos++) {
                await movePacket(p, currentPos, currentPos + 1, 600);
                if (currentPos + 1 < hop) {
                    // TTL 递减动画
                    const newTTL = ttl - (currentPos + 1);
                    p.querySelector('text').textContent = `TTL=${newTTL}`;
                    // 闪烁一下表示处理
                    p.querySelector('circle').setAttribute('fill', '#fff');
                    await sleep(100);
                    p.querySelector('circle').setAttribute('fill', CONFIG.packetColor);
                }
            }

            // 到达目标点（TTL变为0或到达目的地）
            await sleep(300);
            
            if (!isTarget) {
                // 路由器返回 ICMP Time Exceeded
                stepDesc.textContent = `跃点 ${hop} (${CONFIG.nodes[hop].ip}): TTL 耗尽，返回 ICMP Time Exceeded`;
                stepDesc.setAttribute('fill', CONFIG.errorColor);
                p.querySelector('circle').setAttribute('fill', CONFIG.errorColor);
                p.querySelector('text').textContent = 'Time Exceeded';
                p.querySelector('text').setAttribute('fill', CONFIG.errorColor);
                
                // 高亮当前路由器
                const routerIcon = document.querySelector(`#node-r${hop} use`);
                routerIcon.style.color = CONFIG.packetColor;
                routerIcon.style.filter = 'url(#glow)';

                await sleep(500);
                // 返回源主机
                await movePacket(p, hop, 0, 800);
                addLog(`&nbsp;&nbsp;${hop}&nbsp;&nbsp;&nbsp;&nbsp;1 ms&nbsp;&nbsp;&nbsp;&nbsp;1 ms&nbsp;&nbsp;&nbsp;&nbsp;1 ms&nbsp;&nbsp;${CONFIG.nodes[hop].ip}`);
            } else {
                // 到达目标服务器返回 ICMP Echo Reply
                stepDesc.textContent = `目标已到达: 8.8.8.8 返回 ICMP Echo Reply`;
                stepDesc.setAttribute('fill', CONFIG.successColor);
                p.querySelector('circle').setAttribute('fill', CONFIG.successColor);
                p.querySelector('text').textContent = 'Echo Reply';
                p.querySelector('text').setAttribute('fill', CONFIG.successColor);

                const targetIcon = document.querySelector(`#node-target use`);
                targetIcon.style.color = CONFIG.successColor;
                targetIcon.style.filter = 'url(#glow)';

                await sleep(500);
                await movePacket(p, 4, 0, 800);
                addLog(`&nbsp;&nbsp;${hop}&nbsp;&nbsp;&nbsp;&nbsp;2 ms&nbsp;&nbsp;&nbsp;&nbsp;1 ms&nbsp;&nbsp;&nbsp;&nbsp;2 ms&nbsp;&nbsp;${CONFIG.nodes[hop].ip}`);
            }

            packetLayer.removeChild(p);
            await sleep(800);
        }

        stepDesc.textContent = "跟踪完成。所有路径节点已识别。";
        stepDesc.setAttribute('fill', CONFIG.successColor);
        addLog('<br>跟踪完成。', 'var(--text-main)');
        
        await sleep(5000);
        location.reload(); // 循环演示
    }

    // 初始化连线
    function initLines() {
        const connGroup = document.getElementById('connections');
        for (let i = 0; i < CONFIG.nodes.length - 1; i++) {
            const line = document.createElementNS('http://www.w3.org/2000/svg', 'line');
            line.setAttribute('x1', CONFIG.nodes[i].x);
            line.setAttribute('y1', CONFIG.nodes[i].y);
            line.setAttribute('x2', CONFIG.nodes[i+1].x);
            line.setAttribute('y2', CONFIG.nodes[i+1].y);
            line.setAttribute('class', 'path-line');
            connGroup.appendChild(line);
        }
    }

    window.onload = () => {
        initLines();
        runTracert();
    };
</script>

</body>
</html>2b:{"id":"6da4394f-610e-4e1f-92c7-d6ecff10f250","exportName":"$undefined","title":"Create the execution logic of a tracert command, presenting each step with animation","description":"Create the execution logic of a tracert command, presenting each step with animation","category":"flowchart-animation","tags":"$2c","likes":0,"favorites":0,"views":3955,"sourceType":"SvgAnimation","componentSource":"$2d","isLiked":false,"isFavorited":false}
2f:[]
30:T4f1f,<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Minimalist Billiards</title>
    <style>
        @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap');

        :root {
            --bg-color: #042F23;
            --table-color: #064E3B;
            --text-primary: #FFFFFF;
            --text-secondary: #9CA3AF;
            --accent: #FACC15;
            --danger: #EF4444;
        }

        body, html {
            margin: 0;
            padding: 0;
            width: 100%;
            height: 100%;
            overflow: hidden;
            background-color: var(--bg-color);
            font-family: 'Inter', system-ui, -apple-system, sans-serif;
            cursor: none;
            touch-action: none;
        }

        #game-container {
            width: 100vw;
            height: 100vh;
            display: flex;
            justify-content: center;
            align-items: center;
            position: relative;
        }

        svg {
            width: 100%;
            height: 100%;
            display: block;
        }

        /* 文本无法选中 */
        text {
            user-select: none;
            pointer-events: none;
        }

        /* 动画类 */
        .fade-out {
            opacity: 0;
            transition: opacity 0.5s ease-out;
        }

        .score-popup {
            font-weight: bold;
            font-size: 24px;
            fill: var(--accent);
            animation: floatUp 1s ease-out forwards;
        }

        @keyframes floatUp {
            0% { transform: translateY(0) scale(0.5); opacity: 1; }
            50% { transform: translateY(-20px) scale(1.2); opacity: 1; }
            100% { transform: translateY(-40px) scale(1); opacity: 0; }
        }
    </style>
</head>
<body>

<div id="game-container">
    <svg id="game-svg" viewBox="0 0 1200 700" preserveAspectRatio="xMidYMid meet">
        <defs>
            <!-- 桌布阴影 -->
            <filter id="inner-shadow">
                <feOffset dx="0" dy="0"/>
                <feGaussianBlur stdDeviation="15" result="offset-blur"/>
                <feComposite operator="out" in="SourceGraphic" in2="offset-blur" result="inverse"/>
                <feFlood flood-color="black" flood-opacity="0.5" result="color"/>
                <feComposite operator="in" in="color" in2="inverse" result="shadow"/>
                <feComposite operator="over" in="shadow" in2="SourceGraphic"/>
            </filter>

            <!-- 球体立体高光滤镜与渐变 -->
            <radialGradient id="ball-highlight" cx="30%" cy="30%" r="70%">
                <stop offset="0%" stop-color="#ffffff" stop-opacity="0.6"/>
                <stop offset="50%" stop-color="#ffffff" stop-opacity="0"/>
                <stop offset="100%" stop-color="#000000" stop-opacity="0.4"/>
            </radialGradient>

            <filter id="drop-shadow" x="-20%" y="-20%" width="150%" height="150%">
                <feDropShadow dx="3" dy="5" stdDeviation="3" flood-color="#000" flood-opacity="0.4"/>
            </filter>

            <filter id="glow" x="-20%" y="-20%" width="140%" height="140%">
                <feGaussianBlur stdDeviation="5" result="blur" />
                <feComposite in="SourceGraphic" in2="blur" operator="over" />
            </filter>
        </defs>

        <!-- 台球桌外框 -->
        <rect x="40" y="40" width="1120" height="620" rx="30" fill="#111827" />
        <rect x="50" y="50" width="1100" height="600" rx="20" fill="#1F2937" />
        <!-- 台球桌布 -->
        <rect x="70" y="70" width="1060" height="560" rx="10" fill="#064E3B" filter="url(#inner-shadow)" />
        
        <!-- 发球线 -->
        <line x1="300" y1="70" x2="300" y2="630" stroke="#047857" stroke-width="2" opacity="0.5" />
        <circle cx="300" cy="350" r="40" fill="none" stroke="#047857" stroke-width="2" opacity="0.5" />

        <!-- 球袋洞口 -->
        <g id="pockets-layer">
            <circle cx="80" cy="80" r="28" fill="#030712" />
            <circle cx="600" cy="70" r="25" fill="#030712" />
            <circle cx="1120" cy="80" r="28" fill="#030712" />
            <circle cx="80" cy="620" r="28" fill="#030712" />
            <circle cx="600" cy="630" r="25" fill="#030712" />
            <circle cx="1120" cy="620" r="28" fill="#030712" />
        </g>

        <!-- 瞄准辅助层 -->
        <g id="aim-layer" style="display: none;">
            <!-- 预测虚线 -->
            <line id="aim-line" x1="0" y1="0" x2="0" y2="0" stroke="#FFFFFF" stroke-width="1.5" stroke-dasharray="6 4" opacity="0.6" />
            <!-- 力度指示圈 -->
            <circle id="power-ring" cx="0" cy="0" r="0" fill="none" stroke="#FACC15" stroke-width="2" opacity="0.8" />
            <!-- 球杆 -->
            <g id="cue-stick">
                <rect x="0" y="-3" width="400" height="6" fill="#D1D5DB" rx="2" />
                <rect x="0" y="-3" width="20" height="6" fill="#3B82F6" rx="2" /> <!-- 杆头 -->
                <rect x="250" y="-4" width="150" height="8" fill="#1F2937" rx="2" /> <!-- 握把 -->
            </g>
        </g>

        <!-- 球体渲染层 -->
        <g id="balls-layer"></g>

        <!-- UI 层 (瑞士风格排版) -->
        <g id="ui-layer">
            <!-- 左上角标题 -->
            <text x="40" y="30" fill="#9CA3AF" font-size="12" font-weight="700" letter-spacing="2">PROJECT: BILLIARDS</text>
            <text x="210" y="30" fill="#4B5563" font-size="12" font-weight="400">/ 动效与物理驱动</text>
            
            <!-- 右上角分数 -->
            <text x="1160" y="30" fill="#9CA3AF" font-size="12" font-weight="700" letter-spacing="2" text-anchor="end">SCORE</text>
            <text id="score-text" x="1160" y="65" fill="#FFFFFF" font-size="36" font-weight="700" text-anchor="end" font-variant-numeric="tabular-nums">000</text>

            <!-- 底部状态栏 -->
            <text x="40" y="685" fill="#9CA3AF" font-size="10" font-weight="700" letter-spacing="1">STATUS</text>
            <text id="status-text" x="100" y="685" fill="#FACC15" font-size="10" font-weight="700" letter-spacing="1">AWAITING BREAK</text>

            <!-- 操作提示 -->
            <text x="1160" y="685" fill="#6B7280" font-size="10" font-weight="400" text-anchor="end">拖拽白球调整角度与力度发球</text>
        </g>

        <!-- 进球特效层 -->
        <g id="fx-layer"></g>

        <!-- 交互光标 -->
        <g id="cursor-group" pointer-events="none">
            <circle cx="0" cy="0" r="6" fill="none" stroke="#FFFFFF" stroke-width="1.5" />
            <circle cx="0" cy="0" r="1" fill="#FACC15" />
        </g>
    </svg>
</div>

<script>
    /**
     * 极简 2D 物理引擎与台球逻辑
     */
    const svg = document.getElementById('game-svg');
    const ballsLayer = document.getElementById('balls-layer');
    const aimLayer = document.getElementById('aim-layer');
    const aimLine = document.getElementById('aim-line');
    const cueStick = document.getElementById('cue-stick');
    const powerRing = document.getElementById('power-ring');
    const scoreText = document.getElementById('score-text');
    const statusText = document.getElementById('status-text');
    const fxLayer = document.getElementById('fx-layer');
    const cursorGroup = document.getElementById('cursor-group');

    // 物理与几何常量
    const TABLE_BOUNDS = { left: 70, right: 1130, top: 70, bottom: 630 };
    const POCKETS = [
        { x: 80, y: 80 }, { x: 600, y: 70 }, { x: 1120, y: 80 },
        { x: 80, y: 620 }, { x: 600, y: 630 }, { x: 1120, y: 620 }
    ];
    const POCKET_RADIUS = 30;
    const BALL_RADIUS = 14;
    const FRICTION = 0.988; // 摩擦力衰减
    const RESTITUTION = 0.92; // 碰撞弹性系数
    const MAX_POWER = 35; // 最大击球速度
    const MIN_VELOCITY = 0.05; // 停止阈值

    let balls = [];
    let score = 0;
    let gameState = 'IDLE'; // IDLE, AIMING, ROLLING
    let dragStart = { x: 0, y: 0 };
    let dragCurrent = { x: 0, y: 0 };
    let cueBall = null;

    // 颜色配置
    const COLORS = [
        '#FACC15', '#3B82F6', '#EF4444', '#8B5CF6', 
        '#F97316', '#10B981', '#881337', '#111827' // 黑球最后
    ];

    /**
     * 初始化球局
     */
    function initBalls() {
        balls = [];
        ballsLayer.innerHTML = '';

        // 白球
        cueBall = createBall('cue', 300, 350, '#FFFFFF');
        
        // 摆放目标球 (三角形阵列)
        const startX = 850;
        const startY = 350;
        const rowSpacing = BALL_RADIUS * Math.sqrt(3) + 1;
        const colSpacing = BALL_RADIUS * 2 + 1;

        let colorIdx = 0;
        let ballId = 1;

        for (let row = 0; row < 5; row++) {
            for (let col = 0; col <= row; col++) {
                const x = startX + row * rowSpacing;
                const y = startY - (row * colSpacing) / 2 + col * colSpacing;
                
                // 黑球放在中间特定位置 (第3排中间)
                let color = '#EF4444'; // 默认红球
                if (row === 2 && col === 1) {
                    color = '#111827'; // 黑球
                } else {
                    color = COLORS[colorIdx % (COLORS.length - 1)];
                    colorIdx++;
                }

                createBall(`target-${ballId++}`, x, y, color);
            }
        }
    }

    function createBall(id, x, y, color) {
        const ball = {
            id, x, y, vx: 0, vy: 0, mass: 1, radius: BALL_RADIUS, color, active: true
        };
        balls.push(ball);

        const g = document.createElementNS('http://www.w3.org/2000/svg', 'g');
        g.setAttribute('id', id);
        g.setAttribute('transform', `translate(${x}, ${y})`);
        
        // 投影阴影
        const shadow = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
        shadow.setAttribute('r', BALL_RADIUS);
        shadow.setAttribute('fill', 'none');
        shadow.setAttribute('filter', 'url(#drop-shadow)');
        
        // 基础颜色
        const circle = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
        circle.setAttribute('r', BALL_RADIUS);
        circle.setAttribute('fill', color);
        
        // 立体高光
        const highlight = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
        highlight.setAttribute('r', BALL_RADIUS);
        highlight.setAttribute('fill', 'url(#ball-highlight)');

        g.appendChild(shadow);
        g.appendChild(circle);
        g.appendChild(highlight);
        ballsLayer.appendChild(g);

        ball.element = g;
        return ball;
    }

    /**
     * 物理更新核心
     */
    function updatePhysics() {
        let isMoving = false;

        // 1. 更新位置与摩擦力
        for (let ball of balls) {
            if (!ball.active) continue;

            ball.x += ball.vx;
            ball.y += ball.vy;

            ball.vx *= FRICTION;
            ball.vy *= FRICTION;

            if (Math.abs(ball.vx) < MIN_VELOCITY) ball.vx = 0;
            if (Math.abs(ball.vy) < MIN_VELOCITY) ball.vy = 0;

            if (ball.vx !== 0 || ball.vy !== 0) isMoving = true;

            // 2. 边界碰撞检测
            if (ball.x - ball.radius < TABLE_BOUNDS.left) {
                ball.x = TABLE_BOUNDS.left + ball.radius;
                ball.vx *= -RESTITUTION;
            } else if (ball.x + ball.radius > TABLE_BOUNDS.right) {
                ball.x = TABLE_BOUNDS.right - ball.radius;
                ball.vx *= -RESTITUTION;
            }

            if (ball.y - ball.radius < TABLE_BOUNDS.top) {
                ball.y = TABLE_BOUNDS.top + ball.radius;
                ball.vy *= -RESTITUTION;
            } else if (ball.y + ball.radius > TABLE_BOUNDS.bottom) {
                ball.y = TABLE_BOUNDS.bottom - ball.radius;
                ball.vy *= -RESTITUTION;
            }
        }

        // 3. 球与球之间的碰撞 (使用动量守恒)
        for (let i = 0; i < balls.length; i++) {
            for (let j = i + 1; j < balls.length; j++) {
                const b1 = balls[i];
                const b2 = balls[j];
                if (!b1.active || !b2.active) continue;

                const dx = b2.x - b1.x;
                const dy = b2.y - b1.y;
                const dist = Math.hypot(dx, dy);
                const minDist = b1.radius + b2.radius;

                if (dist < minDist) {
                    // 分离重叠
                    const overlap = (minDist - dist) / 2;
                    const nx = dx / dist;
                    const ny = dy / dist;

                    b1.x -= overlap * nx;
                    b1.y -= overlap * ny;
                    b2.x += overlap * nx;
                    b2.y += overlap * ny;

                    // 相对速度
                    const kx = b1.vx - b2.vx;
                    const ky = b1.vy - b2.vy;

                    // 沿法线的冲量
                    const p = 2 * (nx * kx + ny * ky) / (b1.mass + b2.mass);

                    // 应用冲量与弹性系数
                    b1.vx -= p * b2.mass * nx * RESTITUTION;
                    b1.vy -= p * b2.mass * ny * RESTITUTION;
                    b2.vx += p * b1.mass * nx * RESTITUTION;
                    b2.vy += p * b1.mass * ny * RESTITUTION;
                }
            }
        }

        // 4. 进袋检测
        for (let ball of balls) {
            if (!ball.active) continue;
            for (let pocket of POCKETS) {
                const dist = Math.hypot(ball.x - pocket.x, ball.y - pocket.y);
                if (dist < POCKET_RADIUS) {
                    handlePocketIn(ball, pocket);
                    break;
                }
            }
        }

        // 状态切换
        if (gameState === 'ROLLING' && !isMoving) {
            gameState = 'IDLE';
            statusText.textContent = 'READY / 等待击球';
            statusText.setAttribute('fill', '#3B82F6');
            
            // 检查白球是否进袋
            if (!cueBall.active) {
                resetCueBall();
            }
        }
    }

    function handlePocketIn(ball, pocket) {
        ball.active = false;
        ball.vx = 0;
        ball.vy = 0;
        
        // 视觉效果：球缩小消失
        ball.element.style.transition = 'all 0.3s ease-in';
        ball.element.setAttribute('transform', `translate(${pocket.x}, ${pocket.y}) scale(0)`);
        
        if (ball.id !== 'cue') {
            const points = ball.color === '#111827' ? 50 : 10;
            updateScore(points);
            showFloatingText(`+${points}`, pocket.x, pocket.y - 20);
        } else {
            showFloatingText("FOUL", pocket.x, pocket.y - 20, '#EF4444');
            updateScore(-20);
        }
    }

    function resetCueBall() {
        cueBall.x = 300;
        cueBall.y = 350;
        cueBall.vx = 0;
        cueBall.vy = 0;
        cueBall.active = true;
        cueBall.element.style.transition = 'none';
        cueBall.element.setAttribute('transform', `translate(${cueBall.x}, ${cueBall.y}) scale(1)`);
    }

    function updateScore(delta) {
        score = Math.max(0, score + delta);
        scoreText.textContent = score.toString().padStart(3, '0');
    }

    function showFloatingText(text, x, y, color = '#FACC15') {
        const textEl = document.createElementNS('http://www.w3.org/2000/svg', 'text');
        textEl.setAttribute('x', x);
        textEl.setAttribute('y', y);
        textEl.setAttribute('class', 'score-popup');
        textEl.setAttribute('text-anchor', 'middle');
        textEl.setAttribute('fill', color);
        textEl.textContent = text;
        fxLayer.appendChild(textEl);
        
        setTimeout(() => {
            if (fxLayer.contains(textEl)) fxLayer.removeChild(textEl);
        }, 1000);
    }

    /**
     * 渲染同步
     */
    function render() {
        for (let ball of balls) {
            if (ball.active) {
                ball.element.setAttribute('transform', `translate(${ball.x}, ${ball.y})`);
            }
        }
    }

    /**
     * 交互逻辑 (鼠标/触摸)
     */
    function getMousePos(e) {
        const pt = svg.createSVGPoint();
        pt.x = e.clientX || (e.touches && e.touches[0].clientX);
        pt.y = e.clientY || (e.touches && e.touches[0].clientY);
        return pt.matrixTransform(svg.getScreenCTM().inverse());
    }

    svg.addEventListener('pointermove', (e) => {
        const pos = getMousePos(e);
        
        // 更新光标
        cursorGroup.setAttribute('transform', `translate(${pos.x}, ${pos.y})`);

        if (gameState === 'AIMING') {
            dragCurrent = pos;
            updateAimingVisuals();
        }
    });

    svg.addEventListener('pointerdown', (e) => {
        if (gameState !== 'IDLE' || !cueBall.active) return;
        
        const pos = getMousePos(e);
        // 判断是否点在白球附近
        const distToCue = Math.hypot(pos.x - cueBall.x, pos.y - cueBall.y);
        
        if (distToCue < BALL_RADIUS * 4) {
            gameState = 'AIMING';
            dragStart = pos;
            dragCurrent = pos;
            aimLayer.style.display = 'block';
            statusText.textContent = 'AIMING / 蓄力中...';
            statusText.setAttribute('fill', '#FACC15');
            svg.setPointerCapture(e.pointerId);
        }
    });

    svg.addEventListener('pointerup', (e) => {
        if (gameState === 'AIMING') {
            gameState = 'ROLLING';
            aimLayer.style.display = 'none';
            svg.releasePointerCapture(e.pointerId);
            statusText.textContent = 'ROLLING / 运动中';
            statusText.setAttribute('fill', '#10B981');

            // 计算拉力向量并发球
            const dx = dragStart.x - dragCurrent.x;
            const dy = dragStart.y - dragCurrent.y;
            const distance = Math.hypot(dx, dy);
            
            if (distance > 5) { // 最小击球力度防误触
                const power = Math.min(distance * 0.15, MAX_POWER);
                const angle = Math.atan2(dy, dx);
                
                cueBall.vx = Math.cos(angle) * power;
                cueBall.vy = Math.sin(angle) * power;
            } else {
                gameState = 'IDLE';
                statusText.textContent = 'READY / 等待击球';
            }
        }
    });

    function updateAimingVisuals() {
        const dx = dragStart.x - dragCurrent.x;
        const dy = dragStart.y - dragCurrent.y;
        const distance = Math.hypot(dx, dy);
        const angle = Math.atan2(dy, dx);
        
        // 限制最大拉力视觉
        const visualDist = Math.min(distance, 200);
        
        // 预测线 (白球出发方向)
        aimLine.setAttribute('x1', cueBall.x);
        aimLine.setAttribute('y1', cueBall.y);
        aimLine.setAttribute('x2', cueBall.x + Math.cos(angle) * 500);
        aimLine.setAttribute('y2', cueBall.y + Math.sin(angle) * 500);

        // 力度圈
        powerRing.setAttribute('cx', cueBall.x);
        powerRing.setAttribute('cy', cueBall.y);
        powerRing.setAttribute('r', BALL_RADIUS + visualDist * 0.2);
        const powerRatio = visualDist / 200;
        powerRing.setAttribute('stroke', powerRatio > 0.8 ? '#EF4444' : (powerRatio > 0.4 ? '#F97316' : '#FACC15'));

        // 球杆 (在白球反方向，且根据力度后退)
        const cueAngleDeg = angle * (180 / Math.PI) + 180;
        const cueOffset = BALL_RADIUS + 5 + visualDist * 0.5;
        cueStick.setAttribute('transform', `translate(${cueBall.x}, ${cueBall.y}) rotate(${cueAngleDeg}) translate(${cueOffset}, 0)`);
    }

    /**
     * 主循环
     */
    function gameLoop() {
        updatePhysics();
        render();
        requestAnimationFrame(gameLoop);
    }

    // 启动
    initBalls();
    gameLoop();

    // 防止默认拖拽和右键菜单影响体验
    document.addEventListener('contextmenu', e => e.preventDefault());
    document.addEventListener('dragstart', e => e.preventDefault());

</script>
</body>
</html>2e:{"id":"437e921a-803b-49f8-ac19-10593eef6e75","exportName":"$undefined","title":"Develop a simple billiard animation where users can serve via mouse and adjust the angle","description":"Develop a simple billiard animation where users can serve via mouse and adjust the angle and power","category":"svg-mini-game","tags":"$2f","likes":1,"favorites":0,"views":4699,"sourceType":"SvgAnimation","componentSource":"$30","isLiked":false,"isFavorited":false}
32:T599,Create a high-quality isometric animation based on SVG code. Two minimalist modern smartphones are positioned in the center of the screen, tilted at a 30-degree angle with rounded edges and a matte texture. The left phone screen features a soft mint green UI representing the sender, while the right phone screen features a serene sky blue UI with a simplified digital wallet icon representing the receiver. The core of the animation is a bright, flat-designed gold coin with subtle edge highlights. At the start, the coin pops out from the center of the left screen with a spring easing effect and a slight vertical displacement. It follows an elegant parabolic trajectory made of dynamic flowing dashed lines in translucent deep cyan, creating an anisotropic flow sensation in the direction of movement. Upon touching the right phone screen, the digital wallet icon produces a subtle scaling 'breathing' or ripple effect to simulate the 'received' feedback, and the coin smoothly shrinks and disappears into the wallet opening. The overall visual style strictly adheres to Flat Design, using no complex shadows and relying on color brightness for layering. The background is a very light off-white (#F5F7FA), creating a clean, professional fintech atmosphere. The animation is set to a 2-3 second seamless loop, using Ease-in-out easing functions between keyframes to ensure smooth and natural movement of the coin and UI feedback.33:[]
34:T3bcd,<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Isometric Crypto Transfer Animation</title>
    <style>
        :root {
            --bg-color: #F5F7FA;
            --mint-primary: #34D399;
            --mint-light: #D1FAE5;
            --sky-primary: #38BDF8;
            --sky-light: #E0F2FE;
            --text-main: #334155;
            --text-sub: #94A3B8;
            --phone-edge: #CBD5E1;
            --phone-face: #FFFFFF;
        }

        body, html {
            margin: 0;
            padding: 0;
            width: 100%;
            height: 100%;
            background-color: var(--bg-color);
            display: flex;
            align-items: center;
            justify-content: center;
            font-family: 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
            overflow: hidden;
        }

        #animation-container {
            width: 100%;
            height: 100%;
            max-width: 1200px;
            max-height: 700px;
            position: relative;
        }

        svg {
            width: 100%;
            height: 100%;
            display: block;
            user-select: none;
        }

        /* 轨迹虚线流动动画 */
        @keyframes dashFlow {
            from { stroke-dashoffset: 24; }
            to { stroke-dashoffset: 0; }
        }

        .trajectory-line {
            stroke-dasharray: 12 12;
            animation: dashFlow 0.8s linear infinite;
        }

        /* 提升渲染质量 */
        * {
            shape-rendering: geometricPrecision;
            text-rendering: optimizeLegibility;
        }
    </style>
</head>
<body>

<div id="animation-container">
    <svg viewBox="0 0 1200 700" preserveAspectRatio="xMidYMid meet">
        <defs>
            <!-- 手机主体阴影 -->
            <filter id="phone-shadow" x="-20%" y="-20%" width="150%" height="150%">
                <feDropShadow dx="0" dy="24" stdDeviation="16" flood-color="#64748B" flood-opacity="0.15" />
            </filter>

            <!-- 金币发光效果 -->
            <filter id="coin-glow" x="-50%" y="-50%" width="200%" height="200%">
                <feGaussianBlur stdDeviation="6" result="blur" />
                <feComposite in="SourceGraphic" in2="blur" operator="over" />
            </filter>

            <!-- 渐变定义 -->
            <linearGradient id="mint-grad" x1="0%" y1="0%" x2="100%" y2="100%">
                <stop offset="0%" stop-color="#6EE7B7" />
                <stop offset="100%" stop-color="#10B981" />
            </linearGradient>
            
            <linearGradient id="sky-grad" x1="0%" y1="0%" x2="100%" y2="100%">
                <stop offset="0%" stop-color="#7DD3FC" />
                <stop offset="100%" stop-color="#0284C7" />
            </linearGradient>
        </defs>

        <!-- 背景装饰网格 (极简) -->
        <g opacity="0.4" stroke="#E2E8F0" stroke-width="1">
            <line x1="0" y1="350" x2="1200" y2="350" stroke-dasharray="4 8" />
            <line x1="600" y1="0" x2="600" y2="700" stroke-dasharray="4 8" />
        </g>

        <!-- 抛物线轨迹 (在手机和金币底层) -->
        <path id="fly-path" class="trajectory-line" 
              d="M 320,480 C 320,180 720,80 920,220" 
              fill="none" stroke="rgba(14, 116, 144, 0.3)" stroke-width="4" stroke-linecap="round" />

        <!-- ================= 左侧手机 (发送端) ================= -->
        <g id="left-phone" transform="translate(320, 480)">
            <!-- 侧边厚度层 (向下偏移产生3D感) -->
            <g transform="translate(0, 18)">
                <g transform="matrix(0.866, 0.5, -0.866, 0.5, 0, 0)">
                    <rect x="-85" y="-175" width="170" height="350" rx="28" fill="var(--phone-edge)" filter="url(#phone-shadow)"/>
                </g>
            </g>
            
            <!-- 顶面层 (屏幕与面板) -->
            <g transform="matrix(0.866, 0.5, -0.866, 0.5, 0, 0)">
                <!-- 手机外壳 -->
                <rect x="-85" y="-175" width="170" height="350" rx="28" fill="var(--phone-face)" />
                <!-- 屏幕内框 -->
                <rect x="-75" y="-165" width="150" height="330" rx="20" fill="#F8FAFC" />
                
                <!-- UI 界面: 薄荷绿主题 -->
                <g transform="translate(0, -70)">
                    <!-- 顶部背景 -->
                    <path d="M -75,-95 L 75,-95 L 75,30 Q 0,60 -75,30 Z" fill="var(--mint-light)" opacity="0.6"/>
                    
                    <!-- 标题 -->
                    <text x="0" y="-60" fill="var(--mint-primary)" font-size="14" font-weight="bold" text-anchor="middle" letter-spacing="1">资产转账</text>
                    
                    <!-- 金额显示区 -->
                    <rect x="-55" y="-35" width="110" height="50" rx="12" fill="#FFFFFF" opacity="0.9" />
                    <text x="0" y="-15" fill="var(--text-sub)" font-size="10" text-anchor="middle">发送金额 (CNY)</text>
                    <text x="0" y="5" fill="var(--text-main)" font-size="16" font-weight="bold" text-anchor="middle">¥ 10,000</text>
                    
                    <!-- 骨架屏装饰线条 -->
                    <rect x="-55" y="45" width="110" height="8" rx="4" fill="#E2E8F0" />
                    <rect x="-55" y="65" width="80" height="8" rx="4" fill="#E2E8F0" />
                    <rect x="-55" y="85" width="95" height="8" rx="4" fill="#E2E8F0" />
                </g>

                <!-- 发送按钮 (带交互动画) -->
                <g id="send-btn-group" transform="translate(0, 110)">
                    <rect x="-60" y="-22" width="120" height="44" rx="22" fill="url(#mint-grad)" />
                    <text x="0" y="5" fill="#FFFFFF" font-size="14" font-weight="bold" text-anchor="middle" letter-spacing="2">确认发送</text>
                </g>
            </g>
        </g>

        <!-- ================= 右侧手机 (接收端) ================= -->
        <g id="right-phone" transform="translate(920, 220)">
            <!-- 侧边厚度层 -->
            <g transform="translate(0, 18)">
                <g transform="matrix(0.866, 0.5, -0.866, 0.5, 0, 0)">
                    <rect x="-85" y="-175" width="170" height="350" rx="28" fill="var(--phone-edge)" filter="url(#phone-shadow)"/>
                </g>
            </g>
            
            <!-- 顶面层 -->
            <g transform="matrix(0.866, 0.5, -0.866, 0.5, 0, 0)">
                <!-- 手机外壳 -->
                <rect x="-85" y="-175" width="170" height="350" rx="28" fill="var(--phone-face)" />
                <!-- 屏幕内框 -->
                <rect x="-75" y="-165" width="150" height="330" rx="20" fill="#F0F9FF" />
                
                <!-- UI 界面: 天空蓝主题 -->
                <text x="0" y="-120" fill="var(--sky-primary)" font-size="14" font-weight="bold" text-anchor="middle" letter-spacing="1">数字钱包</text>
                
                <rect x="-60" y="-90" width="120" height="6" rx="3" fill="#BAE6FD" opacity="0.5" />
                <rect x="-40" y="-75" width="80" height="6" rx="3" fill="#BAE6FD" opacity="0.5" />

                <!-- 入账涟漪特效圈 -->
                <circle id="wallet-ripple-1" cx="0" cy="10" r="0" fill="none" stroke="var(--sky-primary)" stroke-width="4" opacity="0" />
                <circle id="wallet-ripple-2" cx="0" cy="10" r="0" fill="none" stroke="var(--sky-primary)" stroke-width="2" opacity="0" />

                <!-- 钱包图标核心组件 -->
                <g id="wallet-icon-group" transform="translate(0, 10)">
                    <!-- 钱包后盖 -->
                    <rect x="-44" y="-28" width="88" height="56" rx="10" fill="#7DD3FC" />
                    <!-- 内部卡片露出 -->
                    <rect x="-30" y="-36" width="60" height="20" rx="4" fill="#FFFFFF" opacity="0.8" />
                    <rect x="-20" y="-30" width="40" height="4" rx="2" fill="#E2E8F0" />
                    <!-- 钱包前盖 -->
                    <rect x="-44" y="-8" width="88" height="36" rx="10" fill="url(#sky-grad)" />
                    <!-- 钱包扣子 -->
                    <circle cx="0" cy="-8" r="7" fill="#FFFFFF" />
                    <circle cx="0" cy="-8" r="3" fill="#0284C7" />
                    <!-- 钱包开口缝隙标识 -->
                    <path d="M -30,-8 L -15,-8 M 15,-8 L 30,-8" stroke="#0284C7" stroke-width="2" stroke-linecap="round" opacity="0.4" />
                </g>

                <!-- 底部状态文本 -->
                <rect x="-50" y="100" width="100" height="30" rx="15" fill="#FFFFFF" opacity="0.8" />
                <text id="receive-status" x="0" y="120" fill="var(--text-sub)" font-size="12" font-weight="bold" text-anchor="middle">等待接收...</text>
            </g>
        </g>

        <!-- ================= 飞行的金币 ================= -->
        <!-- 放置在最顶层，不受等距投影矩阵影响，保持正视扁平感 -->
        <g id="coin-master" transform="translate(320, 480)">
            <g id="coin-scaler" transform="scale(0)">
                <g filter="url(#coin-glow)">
                    <!-- 金币厚度/阴影 -->
                    <circle cx="0" cy="4" r="26" fill="#D97706" />
                    <!-- 金币主面 -->
                    <circle cx="0" cy="0" r="26" fill="#FBBF24" />
                    <!-- 边缘高光 -->
                    <path d="M -20,0 A 20 20 0 0 1 20,0" fill="none" stroke="#FDE68A" stroke-width="3" stroke-linecap="round" />
                    <!-- 内部图案 (极简菱形/星芒代表数字资产) -->
                    <path d="M 0,-14 L 6,0 L 0,14 L -6,0 Z" fill="#FFFBEB" opacity="0.9" />
                    <path d="M -14,0 L 0,-6 L 14,0 L 0,6 Z" fill="#FFFBEB" opacity="0.9" />
                </g>
            </g>
        </g>
    </svg>
</div>

<script>
    document.addEventListener("DOMContentLoaded", () => {
        // 获取DOM元素
        const path = document.getElementById('fly-path');
        const coinMaster = document.getElementById('coin-master');
        const coinScaler = document.getElementById('coin-scaler');
        const walletGroup = document.getElementById('wallet-icon-group');
        const ripple1 = document.getElementById('wallet-ripple-1');
        const ripple2 = document.getElementById('wallet-ripple-2');
        const sendBtn = document.getElementById('send-btn-group');
        const statusText = document.getElementById('receive-status');
        
        const pathLength = path.getTotalLength();
        const DURATION = 2800; // 动画总时长 (ms)
        let startTime = null;

        // 缓动函数定义
        // Quartic Ease In Out (平滑加速后减速)
        const easeInOutQuart = (t) => t < 0.5 ? 8 * t * t * t * t : 1 - Math.pow(-2 * t + 2, 4) / 2;
        
        // Elastic Ease Out (弹簧效果)
        const easeOutElastic = (t) => {
            const c4 = (2 * Math.PI) / 3;
            return t === 0 ? 0 : t === 1 ? 1 : Math.pow(2, -10 * t) * Math.sin((t * 10 - 0.75) * c4) + 1;
        };

        // Cubic Ease In (加速缩小)
        const easeInCubic = (t) => t * t * t;

        // 核心动画主循环 (严禁叠加，仅启动一次)
        function renderLoop(timestamp) {
            if (!startTime) startTime = timestamp;
            const elapsed = (timestamp - startTime) % DURATION;
            const t = elapsed / DURATION; // 全局进度 0.0 ~ 1.0

            // --------------------------------------------------
            // 阶段规划:
            // 0.00 - 0.10: 按钮按下，金币弹簧弹出
            // 0.10 - 0.85: 金币沿抛物线飞行
            // 0.85 - 0.95: 金币到达，缩入钱包，钱包呼吸，涟漪扩散
            // 0.95 - 1.00: 状态重置，静默等待下一轮
            // --------------------------------------------------

            // 1. 发送按钮交互 (0.0 - 0.1)
            let btnScale = 1;
            if (t < 0.1) {
                const btnT = t / 0.1;
                btnScale = 1 - Math.sin(btnT * Math.PI) * 0.08; // 轻微按压反弹
            }
            sendBtn.setAttribute('transform', `translate(0, 110) scale(${btnScale})`);

            // 2. 金币位置计算 (0.1 - 0.85)
            let moveT = 0;
            if (t > 0.1 && t < 0.85) {
                const normT = (t - 0.1) / 0.75;
                moveT = easeInOutQuart(normT);
            } else if (t >= 0.85) {
                moveT = 1;
            }
            
            // 获取路径上的坐标
            const point = path.getPointAtLength(moveT * pathLength);
            coinMaster.setAttribute('transform', `translate(${point.x}, ${point.y})`);

            // 3. 金币缩放与弹簧效果
            let coinScale = 0;
            if (t < 0.1) {
                // 弹簧弹出
                coinScale = easeOutElastic(t / 0.1);
            } else if (t >= 0.1 && t < 0.85) {
                // 飞行中保持原大小
                coinScale = 1;
            } else if (t >= 0.85 && t < 0.95) {
                // 缩入钱包消失
                const shrinkT = (t - 0.85) / 0.1;
                coinScale = 1 - easeInCubic(shrinkT);
            }
            coinScaler.setAttribute('transform', `scale(${coinScale})`);

            // 4. 钱包接收反馈 (0.85 - 0.95)
            let walletScale = 1;
            let rScale = 0;
            let rOp = 0;

            if (t >= 0.82 && t < 0.98) {
                // 稍微提前一点点触发钱包呼吸，让衔接更自然
                const wT = (t - 0.82) / 0.16;
                walletScale = 1 + Math.sin(wT * Math.PI) * 0.12; // 放大再缩小
                
                // 涟漪效果
                if (t >= 0.85) {
                    const ripT = (t - 0.85) / 0.1;
                    rScale = ripT;
                    rOp = 1 - ripT;
                }
            }
            walletGroup.setAttribute('transform', `translate(0, 10) scale(${walletScale})`);
            
            ripple1.setAttribute('r', rScale * 60);
            ripple1.setAttribute('opacity', rOp);
            
            // 第二层涟漪稍有延迟
            let r2Scale = 0, r2Op = 0;
            if (t >= 0.87 && t < 0.97) {
                const rip2T = (t - 0.87) / 0.1;
                r2Scale = rip2T;
                r2Op = 1 - rip2T;
            }
            ripple2.setAttribute('r', r2Scale * 45);
            ripple2.setAttribute('opacity', r2Op * 0.8);

            // 5. 状态文字更新
            if (t > 0.85 && t < 0.98) {
                statusText.textContent = "入账成功 +10,000";
                statusText.setAttribute('fill', 'var(--mint-primary)');
            } else {
                statusText.textContent = "等待接收...";
                statusText.setAttribute('fill', 'var(--text-sub)');
            }

            // 循环调用
            requestAnimationFrame(renderLoop);
        }

        // 启动唯一动画主循环
        requestAnimationFrame(renderLoop);
    });
</script>
</body>
</html>31:{"id":"b5fa6a71-3a69-41e6-aad0-fb7d6a9b6530","exportName":"$undefined","title":"Create a high-quality isometric view based on SVG code","description":"$32","category":"product-demo-animation","tags":"$33","likes":2,"favorites":0,"views":4400,"sourceType":"SvgAnimation","componentSource":"$34","isLiked":false,"isFavorited":false}
36:[]
37:T85d5,<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>马年起飞 - 坦克大战2026 (加强版)</title>
    <!-- 引入像素字体 -->
    <link href="https://fonts.googleapis.com/css2?family=Press+Start+2P&family=ZCOOL+KuaiLe&display=swap" rel="stylesheet">
    <!-- 引入烟花库 -->
    <script src="https://cdn.jsdelivr.net/npm/canvas-confetti@1.6.0/dist/confetti.browser.min.js"></script>
    <style>
        :root {
            --bg-color: #000000;
            --wall-color: #b53120;
            --wall-stroke: #802015;
            --text-inactive: #4a4a4a;
            --text-active: #f7d010;
            --player-color: #f7d010;
            --enemy-color: #cccccc;
        }

        body, html {
            margin: 0;
            padding: 0;
            width: 100%;
            height: 100%;
            background-color: var(--bg-color);
            overflow: hidden;
            font-family: 'Press Start 2P', 'ZCOOL KuaiLe', cursive, monospace;
            user-select: none;
        }

        #game-container {
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            width: 1200px;
            height: 700px;
            background-color: #050505;
            box-shadow: 0 0 20px rgba(0, 0, 0, 0.8);
            border: 4px solid #333;
        }

        /* UI Overlay */
        .ui-layer {
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            pointer-events: none;
            display: flex;
            flex-direction: column;
            align-items: center;
            z-index: 10;
        }

        .header-hud {
            margin-top: 20px;
            display: flex;
            gap: 40px;
            align-items: center;
        }

        /* 通用HUD容器样式 (血条和文字共用) */
        .hud-panel {
            display: flex;
            align-items: center;
            gap: 15px;
            background: rgba(0, 0, 0, 0.6);
            padding: 10px 20px;
            border: 2px solid #555;
            border-radius: 4px;
            box-shadow: 0 4px 0 rgba(0,0,0,0.5);
        }

        .char-container {
            display: flex;
            gap: 20px;
        }

        .char-slot {
            font-size: 32px;
            color: var(--text-inactive);
            text-shadow: 2px 2px 0px #000;
            transition: all 0.3s ease;
            position: relative;
        }

        .char-slot.active {
            color: var(--text-active);
            text-shadow: 0 0 10px var(--text-active), 4px 4px 0px #b53120;
            transform: scale(1.2);
            animation: bounce 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275);
        }

        /* HP Bar Styles */
        .hp-label {
            color: #f7d010;
            font-size: 20px;
            text-shadow: 2px 2px 0 #000;
        }

        .hp-bar-bg {
            width: 150px;
            height: 20px;
            background: #333;
            border: 2px solid #fff;
            position: relative;
        }

        .hp-bar-fill {
            width: 100%;
            height: 100%;
            background: #2ecc71;
            transition: width 0.2s ease-out, background-color 0.2s;
        }

        @keyframes bounce {
            0% { transform: scale(1); }
            50% { transform: scale(1.5); }
            100% { transform: scale(1.2); }
        }

        /* SVG Game Layer */
        svg {
            width: 100%;
            height: 100%;
            display: block;
        }

        /* Start/End Screen */
        .overlay-screen {
            position: absolute;
            top: 0; left: 0; width: 100%; height: 100%;
            background: rgba(0,0,0,0.85);
            display: flex;
            flex-direction: column;
            justify-content: center;
            align-items: center;
            z-index: 20;
            color: white;
            text-align: center;
        }

        .hidden { display: none !important; }

        h1 { color: #b53120; text-shadow: 4px 4px 0 #fff; font-size: 60px; margin-bottom: 20px; }
        p { font-size: 18px; line-height: 1.6; color: #ccc; margin-bottom: 40px; }
        .btn {
            background: #b53120;
            color: white;
            border: 4px solid white;
            padding: 15px 40px;
            font-size: 24px;
            font-family: inherit;
            cursor: pointer;
            text-transform: uppercase;
            box-shadow: 6px 6px 0px rgba(255,255,255,0.2);
            pointer-events: auto;
        }
        .btn:hover { background: #d94230; transform: translate(-2px, -2px); box-shadow: 8px 8px 0px rgba(255,255,255,0.2); }
        .btn:active { transform: translate(2px, 2px); box-shadow: 2px 2px 0px rgba(255,255,255,0.2); }

    </style>
</head>
<body>

<div id="game-container">
    <!-- Game SVG -->
    <svg id="game-svg" viewBox="0 0 1200 700" preserveAspectRatio="xMidYMid slice">
        <defs>
            <!-- Brick Pattern -->
            <pattern id="brick-pattern" x="0" y="0" width="20" height="20" patternUnits="userSpaceOnUse">
                <rect width="20" height="20" fill="#b53120"/>
                <path d="M0 10 H20 M10 0 V10 M10 10 V20" stroke="#802015" stroke-width="2"/>
                <rect x="2" y="2" width="16" height="6" fill="#d44530"/>
                <rect x="2" y="12" width="6" height="6" fill="#d44530"/>
                <rect x="12" y="12" width="6" height="6" fill="#d44530"/>
            </pattern>
            
            <!-- Glow Filter for Red Enemies -->
            <filter id="red-glow" x="-50%" y="-50%" width="200%" height="200%">
                <feGaussianBlur in="SourceGraphic" stdDeviation="4" result="blur" />
                <feColorMatrix in="blur" type="matrix" values="
                    0 0 0 0 1
                    0 0 0 0 0
                    0 0 0 0 0
                    0 0 0 1 0" result="redBlur" />
                <feMerge>
                    <feMergeNode in="redBlur" />
                    <feMergeNode in="SourceGraphic" />
                </feMerge>
            </filter>

            <!-- Gold Shine Filter -->
            <filter id="gold-shine">
                <feSpecularLighting result="spec" specularConstant="1.2" specularExponent="20" lighting-color="#fff">
                    <fePointLight x="10" y="10" z="20"/>
                </feSpecularLighting>
                <feComposite in="spec" in2="SourceAlpha" operator="in" result="specOut"/>
                <feComposite in="SourceGraphic" in2="specOut" operator="arithmetic" k1="0" k2="1" k3="1" k4="0"/>
            </filter>
        </defs>

        <g id="layer-ground">
            <rect width="1200" height="700" fill="#111"/>
            <!-- Grid Lines -->
            <path d="M0 0" stroke="#222" stroke-width="1" id="grid-lines"/>
        </g>
        
        <g id="layer-base"></g>
        <g id="layer-walls"></g>
        <g id="layer-items"></g> <!-- Layer for PowerUps -->
        <g id="layer-bullets"></g>
        <g id="layer-enemies"></g>
        <g id="layer-player"></g>
        <g id="layer-fx"></g>
    </svg>

    <!-- UI Overlay -->
    <div class="ui-layer">
        <div class="header-hud">
            <!-- HP Bar -->
            <div class="hud-panel">
                <div class="hp-label">1P HP</div>
                <div class="hp-bar-bg">
                    <div id="player-hp-bar" class="hp-bar-fill"></div>
                </div>
            </div>
            <!-- Text Collection (Now with border) -->
            <div class="hud-panel">
                <div class="char-container">
                    <div class="char-slot" id="char-0">马</div>
                    <div class="char-slot" id="char-1">年</div>
                    <div class="char-slot" id="char-2">起</div>
                    <div class="char-slot" id="char-3">飞</div>
                </div>
            </div>
        </div>
    </div>

    <!-- Start Screen -->
    <div id="start-screen" class="overlay-screen">
        <h1>TANK 2026</h1>
        <p>方向键移动 / 自动射击<br>击毁<span style="color:#ff4444; text-shadow:0 0 5px red">红色发光坦克</span>点亮文字<br>击毁<span style="color:#ffd700">金色坦克</span>获取超级武器<br>集齐 "马年起飞" 召唤烟花</p>
        <button class="btn" onclick="startGame()">开始游戏</button>
    </div>

    <!-- Win Screen -->
    <div id="win-screen" class="overlay-screen hidden">
        <h1 style="color:#f7d010">胜利!</h1>
        <p>2026 马年大吉<br>所有字符已点亮</p>
        <button class="btn" onclick="location.reload()">再玩一次</button>
    </div>

    <!-- Fail Screen -->
    <div id="fail-screen" class="overlay-screen hidden">
        <h1 style="color:#e74c3c">任务失败</h1>
        <p id="fail-reason">堡垒已沦陷</p>
        <button class="btn" onclick="startGame()">重新挑战</button>
    </div>
</div>

<script>
    /**
     * Constants & Config
     */
    const CONFIG = {
        width: 1200,
        height: 700,
        gridSize: 40,
        playerSpeed: 3,
        enemySpeed: 1.5,
        bulletSpeed: 7,
        fireRate: 400, // ms
        enemyFireRate: 1500,
        maxEnemies: 5,
        spawnInterval: 2000,
        chars: ['马', '年', '起', '飞'],
        colors: {
            player: '#f7d010',
            enemyNormal: '#cccccc', 
            enemyRed: '#e74c3c', 
            enemyGold: '#ffd700',
        },
        maxPlayerHp: 3
    };

    /**
     * Game State
     */
    const state = {
        isRunning: false,
        lastTime: 0,
        keys: { ArrowUp: false, ArrowDown: false, ArrowLeft: false, ArrowRight: false },
        player: null,
        enemies: [],
        bullets: [],
        walls: [],
        items: [], // PowerUps
        progress: [false, false, false, false],
        enemySpawnTimer: 0,
        frameCount: 0,
        base: null
    };

    /**
     * Map Data "2026"
     */
    function generateMap() {
        const map = [];
        const offsetX = 5; 
        const offsetY = 3;

        // Helper to add pixel block
        const addBlock = (x, y) => map.push({ x: (x + offsetX) * CONFIG.gridSize, y: (y + offsetY) * CONFIG.gridSize, w: CONFIG.gridSize, h: CONFIG.gridSize, hp: 1 });

        // Digit 2
        const draw2 = (ox) => {
            [0,1,2,3].forEach(i => addBlock(ox+i, 0)); 
            addBlock(ox+3, 1); addBlock(ox+3, 2);
            [0,1,2,3].forEach(i => addBlock(ox+i, 3)); 
            addBlock(ox, 4); addBlock(ox, 5);
            [0,1,2,3].forEach(i => addBlock(ox+i, 6)); 
        };

        // Digit 0
        const draw0 = (ox) => {
            [0,1,2,3].forEach(i => addBlock(ox+i, 0)); 
            [0,1,2,3].forEach(i => addBlock(ox+i, 6)); 
            [1,2,3,4,5].forEach(i => { addBlock(ox, i); addBlock(ox+3, i); }); 
        };

        // Digit 6
        const draw6 = (ox) => {
            [0,1,2,3].forEach(i => addBlock(ox+i, 0)); 
            [1,2].forEach(i => addBlock(ox, i)); 
            [0,1,2,3].forEach(i => addBlock(ox+i, 3)); 
            [4,5].forEach(i => { addBlock(ox, i); addBlock(ox+3, i); }); 
            [0,1,2,3].forEach(i => addBlock(ox+i, 6)); 
        };

        draw2(0);
        draw0(5);
        draw2(10);
        draw6(15);

        // Base Protection Walls
        const bx = 15; 
        const by = 16;
        
        const baseWalls = [
            {x: bx-1, y: by},   // Left
            {x: bx-1, y: by-1}, // Top-Left
            {x: bx, y: by-1},   // Top
            {x: bx+1, y: by-1}, // Top-Right
            {x: bx+1, y: by}    // Right
        ];

        baseWalls.forEach(p => {
            map.push({ x: p.x * CONFIG.gridSize, y: p.y * CONFIG.gridSize, w: CONFIG.gridSize, h: CONFIG.gridSize, hp: 1 });
        });

        // Random scattered blocks
        for(let i=0; i<12; i++) {
            let rx = Math.floor(Math.random() * 28);
            let ry = Math.floor(Math.random() * 15);
            if (ry > 10 && rx > 10 && rx < 20) continue;
            let exists = map.some(b => b.x === rx * CONFIG.gridSize && b.y === ry * CONFIG.gridSize);
            if(!exists) map.push({ x: rx * CONFIG.gridSize, y: ry * CONFIG.gridSize, w: CONFIG.gridSize, h: CONFIG.gridSize, hp: 1 });
        }

        return map;
    }

    /**
     * SVG Helpers
     */
    const svgNS = "http://www.w3.org/2000/svg";
    const layers = {
        ground: document.getElementById('layer-ground'),
        base: document.getElementById('layer-base'),
        walls: document.getElementById('layer-walls'),
        items: document.getElementById('layer-items'),
        bullets: document.getElementById('layer-bullets'),
        enemies: document.getElementById('layer-enemies'),
        player: document.getElementById('layer-player'),
        fx: document.getElementById('layer-fx')
    };

    function createRect(x, y, w, h, fill, parent) {
        const rect = document.createElementNS(svgNS, 'rect');
        rect.setAttribute('x', x);
        rect.setAttribute('y', y);
        rect.setAttribute('width', w);
        rect.setAttribute('height', h);
        rect.setAttribute('fill', fill);
        if(parent) parent.appendChild(rect);
        return rect;
    }

    function createTankSVG(type) {
        const g = document.createElementNS(svgNS, 'g');
        let mainColor = CONFIG.colors.enemyNormal;
        let isPlayer = type === 'player';
        
        if (isPlayer) mainColor = CONFIG.colors.player;
        else if (type === 'red') mainColor = CONFIG.colors.enemyRed;
        else if (type === 'gold') mainColor = CONFIG.colors.enemyGold;

        // Apply filters
        if (type === 'red') g.setAttribute('filter', 'url(#red-glow)');
        if (type === 'gold') g.setAttribute('filter', 'url(#gold-shine)');

        // Tracks
        createRect(0, 0, 10, 36, '#222', g);
        createRect(30, 0, 10, 36, '#222', g);
        
        // Body
        createRect(5, 5, 30, 26, mainColor, g);
        
        // Turret
        const turret = createRect(12, 10, 16, 16, isPlayer ? '#fff' : '#444', g);
        turret.setAttribute('opacity', '0.6');

        // Gun
        createRect(18, -4, 4, 18, '#111', g); 
        
        // Detail
        if(isPlayer) createRect(16, 14, 8, 8, '#b53120', g);
        if(type === 'red') createRect(16, 14, 8, 8, '#fff', g); // Core for red tank

        return g;
    }

    function createEagleSVG(x, y) {
        const g = document.createElementNS(svgNS, 'g');
        g.setAttribute('transform', `translate(${x}, ${y})`);
        createRect(0, 0, 40, 40, '#000', g);
        const eagleColor = '#bdc3c7';
        createRect(4, 12, 32, 8, eagleColor, g);
        createRect(4, 8, 8, 4, eagleColor, g);
        createRect(28, 8, 8, 4, eagleColor, g);
        createRect(16, 4, 8, 8, eagleColor, g);
        createRect(22, 6, 4, 2, '#e74c3c', g);
        createRect(12, 20, 16, 12, eagleColor, g);
        createRect(12, 32, 4, 4, '#f39c12', g);
        createRect(24, 32, 4, 4, '#f39c12', g);
        createRect(18, 24, 4, 4, '#b53120', g);
        layers.base.appendChild(g);
        return g;
    }

    function createWeaponSVG(x, y) {
        const g = document.createElementNS(svgNS, 'g');
        g.setAttribute('transform', `translate(${x}, ${y})`);
        
        // Star/Weapon shape
        const star = document.createElementNS(svgNS, 'polygon');
        star.setAttribute('points', '20,2 25,14 38,14 28,22 32,34 20,26 8,34 12,22 2,14 15,14');
        star.setAttribute('fill', '#f7d010');
        star.setAttribute('stroke', '#fff');
        star.setAttribute('stroke-width', '2');
        
        // Animation
        const anim = document.createElementNS(svgNS, 'animateTransform');
        anim.setAttribute('attributeName', 'transform');
        anim.setAttribute('type', 'scale');
        anim.setAttribute('values', '0.8;1.2;0.8');
        anim.setAttribute('dur', '1s');
        anim.setAttribute('repeatCount', 'indefinite');
        anim.setAttribute('additive', 'sum');
        
        // Center pivot for scale
        const center = document.createElementNS(svgNS, 'animateTransform');
        center.setAttribute('attributeName', 'transform');
        center.setAttribute('type', 'translate');
        center.setAttribute('values', '20 20');
        center.setAttribute('additive', 'sum');
        
        const uncenter = document.createElementNS(svgNS, 'animateTransform');
        uncenter.setAttribute('attributeName', 'transform');
        uncenter.setAttribute('type', 'translate');
        uncenter.setAttribute('values', '-20 -20');
        uncenter.setAttribute('additive', 'sum');

        star.appendChild(center);
        star.appendChild(anim);
        star.appendChild(uncenter);
        
        g.appendChild(star);
        layers.items.appendChild(g);
        return g;
    }

    /**
     * Classes
     */
    class Base {
        constructor() {
            this.x = 600; 
            this.y = 640; 
            this.w = 40;
            this.h = 40;
            this.active = true;
            this.el = createEagleSVG(this.x, this.y);
        }

        destroy() {
            if (!this.active) return;
            this.active = false;
            if (this.el) {
                this.el.innerHTML = ''; 
                createRect(0, 0, 40, 40, '#000', this.el);
                createRect(5, 5, 10, 10, '#555', this.el);
                createRect(20, 25, 15, 10, '#555', this.el);
                createExplosion(this.x + 20, this.y + 20, 60);
            }
        }
    }

    class PowerUp {
        constructor(x, y) {
            this.x = x;
            this.y = y;
            this.w = 40;
            this.h = 40;
            this.active = true;
            this.el = createWeaponSVG(x, y);
        }
        
        collect() {
            this.active = false;
            if(this.el.parentNode) this.el.parentNode.removeChild(this.el);
        }
    }

    class Tank {
        constructor(x, y, type) {
            this.x = x;
            this.y = y;
            this.w = 40;
            this.h = 40;
            this.dir = 0; // 0:Up, 1:Right, 2:Down, 3:Left
            this.type = type; // 'player', 'normal', 'red', 'gold'
            this.isPlayer = (type === 'player');
            this.speed = this.isPlayer ? CONFIG.playerSpeed : CONFIG.enemySpeed;
            this.lastFire = 0;
            this.hp = this.isPlayer ? CONFIG.maxPlayerHp : (type === 'gold' ? 2 : 1); // Gold tanks tougher
            
            // Player stats
            this.fireRate = this.isPlayer ? CONFIG.fireRate : CONFIG.enemyFireRate;
            this.bulletSpeed = CONFIG.bulletSpeed;
            this.isPoweredUp = false;

            this.el = createTankSVG(type);
            this.el.setAttribute('transform', `translate(${x},${y})`);
            
            (this.isPlayer ? layers.player : layers.enemies).appendChild(this.el);
        }

        powerUp() {
            if (!this.isPlayer) return;
            this.isPoweredUp = true;
            // Enhance stats: 3x firepower (speed) and fire rate
            this.fireRate = CONFIG.fireRate / 3; 
            this.bulletSpeed = CONFIG.bulletSpeed * 2.5; // 3x might be too fast for collision detection
            
            // Visual feedback
            this.el.querySelector('rect[width="4"]').setAttribute('fill', '#ff0000'); // Red gun
            createExplosion(this.x+20, this.y+20, 50); // Effect
        }

        update() {
            let dx = 0, dy = 0;
            let moved = false;

            if (this.isPlayer) {
                if (state.keys.ArrowUp) { dy = -this.speed; this.dir = 0; moved = true; }
                else if (state.keys.ArrowDown) { dy = this.speed; this.dir = 2; moved = true; }
                else if (state.keys.ArrowLeft) { dx = -this.speed; this.dir = 3; moved = true; }
                else if (state.keys.ArrowRight) { dx = this.speed; this.dir = 1; moved = true; }
                
                const now = Date.now();
                if (now - this.lastFire > this.fireRate) {
                    this.fire();
                    this.lastFire = now;
                }
            } else {
                // AI
                const moveDirs = [{x:0, y:-1}, {x:1, y:0}, {x:0, y:1}, {x:-1, y:0}];
                dx = moveDirs[this.dir].x * this.speed;
                dy = moveDirs[this.dir].y * this.speed;
                moved = true;

                if (Math.random() < 0.02) this.dir = Math.floor(Math.random() * 4);
                
                const now = Date.now();
                if (now - this.lastFire > this.fireRate) {
                    this.fire();
                    this.lastFire = now + Math.random() * 500;
                }
            }

            let nx = this.x + dx;
            let ny = this.y + dy;

            if (nx < 0) nx = 0;
            if (ny < 0) ny = 0;
            if (nx > CONFIG.width - this.w) nx = CONFIG.width - this.w;
            if (ny > CONFIG.height - this.h) ny = CONFIG.height - this.h;

            const hitWall = checkWallCollision(nx, ny, this.w, this.h);
            const hitOther = checkTankCollision(this, nx, ny);
            const hitBase = state.base && state.base.active && intersect(nx, ny, this.w, this.h, state.base.x, state.base.y, state.base.w, state.base.h);

            if (!hitWall && !hitOther && !hitBase) {
                this.x = nx;
                this.y = ny;
            } else if (!this.isPlayer) {
                this.dir = Math.floor(Math.random() * 4);
            }

            const rot = this.dir * 90;
            this.el.setAttribute('transform', `translate(${this.x + 20}, ${this.y + 20}) rotate(${rot}) translate(-20, -20)`);
        }

        fire() {
            const bx = this.x + 20 - 4; 
            const by = this.y + 20 - 4;
            let vx = 0, vy = 0;
            if (this.dir === 0) { vy = -this.bulletSpeed; }
            if (this.dir === 1) { vx = this.bulletSpeed; }
            if (this.dir === 2) { vy = this.bulletSpeed; }
            if (this.dir === 3) { vx = -this.bulletSpeed; }
            
            const startX = bx + vx * 2;
            const startY = by + vy * 2;

            state.bullets.push(new Bullet(startX, startY, vx, vy, this.isPlayer));
        }

        destroy() {
            if (this.el.parentNode) this.el.parentNode.removeChild(this.el);
            createExplosion(this.x + 20, this.y + 20, 40);

            // Special Logic based on Type
            if (this.type === 'red') {
                handleTextUnlock();
            } else if (this.type === 'gold') {
                spawnWeapon();
            }
        }
    }

    class Bullet {
        constructor(x, y, vx, vy, isPlayer) {
            this.x = x;
            this.y = y;
            this.vx = vx;
            this.vy = vy;
            this.w = 8;
            this.h = 8;
            this.isPlayer = isPlayer;
            this.dead = false;
            
            this.el = document.createElementNS(svgNS, 'circle');
            this.el.setAttribute('r', 4);
            this.el.setAttribute('fill', isPlayer ? '#fff' : '#ff0000');
            layers.bullets.appendChild(this.el);
        }

        update() {
            this.x += this.vx;
            this.y += this.vy;

            if (this.x < 0 || this.x > CONFIG.width || this.y < 0 || this.y > CONFIG.height) {
                this.dead = true;
            }

            const wallIdx = getWallCollisionIndex(this.x, this.y, this.w, this.h);
            if (wallIdx !== -1) {
                this.dead = true;
                state.walls[wallIdx].hp--;
                if(state.walls[wallIdx].hp <= 0) {
                    const w = state.walls[wallIdx];
                    if(w.el.parentNode) w.el.parentNode.removeChild(w.el);
                    createExplosion(w.x + w.w/2, w.y + w.h/2, 20);
                    state.walls.splice(wallIdx, 1);
                }
            }

            if (state.base && state.base.active) {
                if (intersect(this.x, this.y, this.w, this.h, state.base.x, state.base.y, state.base.w, state.base.h)) {
                    this.dead = true;
                    state.base.destroy();
                    gameFail("堡垒已沦陷");
                    return;
                }
            }

            if (this.isPlayer) {
                for (let i = 0; i < state.enemies.length; i++) {
                    const e = state.enemies[i];
                    if (intersect(this.x, this.y, this.w, this.h, e.x, e.y, e.w, e.h)) {
                        this.dead = true;
                        e.hp--;
                        if (e.hp <= 0) {
                            e.destroy();
                            state.enemies.splice(i, 1);
                        } else {
                            // Hit effect
                            createExplosion(this.x, this.y, 10);
                        }
                        break;
                    }
                }
            } else {
                if (state.player && intersect(this.x, this.y, this.w, this.h, state.player.x, state.player.y, state.player.w, state.player.h)) {
                    this.dead = true;
                    state.player.hp--;
                    updateHpUI();
                    createExplosion(this.x, this.y, 10);
                    
                    if (state.player.hp <= 0) {
                        state.player.destroy();
                        state.player = null;
                        gameFail("坦克被摧毁");
                    }
                }
            }

            if (this.dead) {
                if (this.el.parentNode) this.el.parentNode.removeChild(this.el);
            } else {
                this.el.setAttribute('cx', this.x + 4);
                this.el.setAttribute('cy', this.y + 4);
            }
        }
    }

    /**
     * Logic Helpers
     */
    function intersect(x1, y1, w1, h1, x2, y2, w2, h2) {
        return x1 < x2 + w2 && x1 + w1 > x2 && y1 < y2 + h2 && y1 + h1 > y2;
    }

    function checkWallCollision(x, y, w, h) {
        for (let wall of state.walls) {
            if (intersect(x, y, w, h, wall.x, wall.y, wall.w, wall.h)) return true;
        }
        return false;
    }

    function getWallCollisionIndex(x, y, w, h) {
        for (let i = 0; i < state.walls.length; i++) {
            let wall = state.walls[i];
            if (intersect(x, y, w, h, wall.x, wall.y, wall.w, wall.h)) return i;
        }
        return -1;
    }

    function checkTankCollision(self, x, y) {
        for (let e of state.enemies) {
            if (e === self) continue;
            if (intersect(x, y, self.w, self.h, e.x, e.y, e.w, e.h)) return true;
        }
        if (state.player && self !== state.player) {
            if (intersect(x, y, self.w, self.h, state.player.x, state.player.y, state.player.w, state.player.h)) return true;
        }
        return false;
    }

    function createExplosion(x, y, size) {
        const exp = document.createElementNS(svgNS, 'circle');
        exp.setAttribute('cx', x);
        exp.setAttribute('cy', y);
        exp.setAttribute('r', 1);
        exp.setAttribute('fill', 'orange');
        exp.setAttribute('stroke', 'yellow');
        exp.setAttribute('stroke-width', 2);
        layers.fx.appendChild(exp);

        exp.style.transition = 'all 0.3s ease-out';
        setTimeout(() => {
            exp.setAttribute('r', size);
            exp.setAttribute('opacity', 0);
        }, 10);
        setTimeout(() => {
            if(exp.parentNode) exp.parentNode.removeChild(exp);
        }, 310);
    }

    function handleTextUnlock() {
        const remaining = state.progress.map((v, i) => v ? -1 : i).filter(i => i !== -1);
        
        if (remaining.length > 0) {
            const idx = remaining[Math.floor(Math.random() * remaining.length)];
            state.progress[idx] = true;
            const el = document.getElementById(`char-${idx}`);
            el.classList.add('active');
            
            if (state.progress.every(v => v)) {
                gameWin();
            }
        }
    }

    function spawnWeapon() {
        let attempts = 0;
        let x, y;
        let valid = false;
        
        while(attempts < 50 && !valid) {
            x = Math.floor(Math.random() * (CONFIG.width - 40));
            y = Math.floor(Math.random() * (CONFIG.height - 40));
            
            // Align to grid roughly
            x = Math.floor(x / 20) * 20;
            y = Math.floor(y / 20) * 20;

            // Check collisions
            if (!checkWallCollision(x, y, 40, 40) && 
                !intersect(x, y, 40, 40, state.base.x, state.base.y, state.base.w, state.base.h)) {
                valid = true;
            }
            attempts++;
        }

        if (valid) {
            state.items.push(new PowerUp(x, y));
        }
    }

    function updateHpUI() {
        const bar = document.getElementById('player-hp-bar');
        const pct = state.player ? (state.player.hp / CONFIG.maxPlayerHp) * 100 : 0;
        bar.style.width = `${pct}%`;
        
        if (pct > 60) bar.style.backgroundColor = '#2ecc71';
        else if (pct > 30) bar.style.backgroundColor = '#f1c40f';
        else bar.style.backgroundColor = '#e74c3c';
    }

    /**
     * Main Game Loop
     */
    function startGame() {
        document.getElementById('start-screen').classList.add('hidden');
        document.getElementById('win-screen').classList.add('hidden');
        document.getElementById('fail-screen').classList.add('hidden');
        
        // Reset Entities
        state.enemies.forEach(e => { if(e.el.parentNode) e.el.parentNode.removeChild(e.el); });
        state.bullets.forEach(b => { if(b.el.parentNode) b.el.parentNode.removeChild(b.el); });
        state.walls.forEach(w => { if(w.el.parentNode) w.el.parentNode.removeChild(w.el); });
        state.items.forEach(i => { if(i.el.parentNode) i.el.parentNode.removeChild(i.el); });
        if (state.base && state.base.el.parentNode) state.base.el.parentNode.removeChild(state.base.el);
        
        state.enemies = [];
        state.bullets = [];
        state.walls = [];
        state.items = [];
        state.progress = [false, false, false, false];
        state.isRunning = true;
        
        document.querySelectorAll('.char-slot').forEach(el => el.classList.remove('active'));

        const mapData = generateMap();
        mapData.forEach(b => {
            const el = createRect(b.x, b.y, b.w, b.h, 'url(#brick-pattern)', layers.walls);
            el.setAttribute('stroke', '#000');
            state.walls.push({ ...b, el });
        });

        state.base = new Base();

        if(state.player && state.player.el.parentNode) state.player.el.parentNode.removeChild(state.player.el);
        state.player = new Tank(480, 640, 'player');
        updateHpUI();

        state.lastTime = Date.now();
        requestAnimationFrame(loop);
    }

    function gameWin() {
        state.isRunning = false;
        document.getElementById('win-screen').classList.remove('hidden');
        
        const end = Date.now() + 5000;
        const colors = ['#bb0000', '#f7d010', '#ffffff'];

        (function frame() {
            confetti({
                particleCount: 3,
                angle: 60,
                spread: 55,
                origin: { x: 0 },
                colors: colors
            });
            confetti({
                particleCount: 3,
                angle: 120,
                spread: 55,
                origin: { x: 1 },
                colors: colors
            });

            if (Date.now() < end) {
                requestAnimationFrame(frame);
            }
        }());
    }

    function gameFail(reason) {
        state.isRunning = false;
        document.getElementById('fail-reason').innerText = reason;
        document.getElementById('fail-screen').classList.remove('hidden');
    }

    function loop() {
        if (!state.isRunning) return;

        const now = Date.now();
        state.lastTime = now;

        // Spawn Enemies
        if (state.enemies.length < CONFIG.maxEnemies && now > state.enemySpawnTimer) {
            const spawnPoints = [{x:0,y:0}, {x:CONFIG.width-40, y:0}, {x:0, y:CONFIG.height-40}];
            const pt = spawnPoints[Math.floor(Math.random()*spawnPoints.length)];
            
            if (!checkTankCollision({w:40, h:40}, pt.x, pt.y)) {
                // Determine Type
                const rand = Math.random();
                let type = 'normal';
                if (rand < 0.15) type = 'gold'; // 15% Gold
                else if (rand < 0.45) type = 'red'; // 30% Red (Text carrier)
                
                state.enemies.push(new Tank(pt.x, pt.y, type));
                state.enemySpawnTimer = now + CONFIG.spawnInterval;
            }
        }

        // Update Entities
        if (state.player) {
            state.player.update();
            
            // Check Item Collection
            for (let i = state.items.length - 1; i >= 0; i--) {
                const item = state.items[i];
                if (intersect(state.player.x, state.player.y, state.player.w, state.player.h, item.x, item.y, item.w, item.h)) {
                    item.collect();
                    state.items.splice(i, 1);
                    state.player.powerUp();
                }
            }
        }

        state.enemies.forEach(e => e.update());
        
        for (let i = state.bullets.length - 1; i >= 0; i--) {
            state.bullets[i].update();
            if (state.bullets[i].dead) {
                state.bullets.splice(i, 1);
            }
        }

        requestAnimationFrame(loop);
    }

    /**
     * Input Handling
     */
    window.addEventListener('keydown', (e) => {
        if (state.keys.hasOwnProperty(e.code)) state.keys[e.code] = true;
    });

    window.addEventListener('keyup', (e) => {
        if (state.keys.hasOwnProperty(e.code)) state.keys[e.code] = false;
    });

</script>
</body>
</html>35:{"id":"474864d6-3140-47ba-a0ce-87ee6ea1d384","exportName":"$undefined","title":"Add a border like a health bar to the characters 'Year of the Horse Takeoff'. It should light up after being attacked.","description":"Add a border like a health bar to the characters 'Year of the Horse Takeoff'. Add a red glow effect to enemy tanks that light up the text after being attacked to distinguish them from regular enemy tanks. Randomly add some golden tanks; after being attacked, power-up weapons will appear at random locations on the screen (avoiding overlap with buildings). When the player's tank picks them up, firepower and fire rate are tripled. Do not change anything else.","category":"svg-mini-game","tags":"$36","likes":0,"favorites":0,"views":5415,"sourceType":"SvgAnimation","componentSource":"$37","isLiked":false,"isFavorited":false}
39:[]
3a:T6d2f,<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Global Expedition: Cape to Cape</title>
    <script src="https://cdn.jsdelivr.net/npm/d3@7"></script>
    <script src="https://cdn.jsdelivr.net/npm/topojson-client@3"></script>
    <script src="https://unpkg.com/lucide@latest"></script>
    <style>
        :root {
            --bg-color: #0F172A;
            --primary: #38BDF8;
            --secondary: #94A3B8;
            --accent: #FBBF24;
            --danger: #EF4444;
            --grid: rgba(56, 189, 248, 0.1);
            --font-main: 'Montserrat', 'PingFang SC', sans-serif;
            --font-mono: 'Courier New', monospace;
        }

        body, html {
            margin: 0;
            padding: 0;
            width: 100%;
            height: 100%;
            background-color: var(--bg-color);
            overflow: hidden;
            font-family: var(--font-main);
            color: var(--primary);
            display: flex;
            justify-content: center;
            align-items: center;
        }

        #container {
            position: relative;
            width: 100vw;
            height: 100vh;
            display: flex;
            justify-content: center;
            align-items: center;
            background: radial-gradient(circle at center, #1e293b 0%, #0f172a 100%);
        }

        svg {
            width: 100%;
            height: 100%;
            max-width: 1200px;
            max-height: 700px;
            filter: drop-shadow(0 0 30px rgba(0,0,0,0.5));
        }

        /* UI Elements */
        .hud-text {
            font-family: var(--font-mono);
            text-transform: uppercase;
            letter-spacing: 1px;
            pointer-events: none;
        }

        .title-group text {
            fill: #F8FAFC;
            text-shadow: 0 0 10px rgba(56, 189, 248, 0.5);
        }

        .panel-bg {
            fill: rgba(15, 23, 42, 0.85);
            stroke: var(--primary);
            stroke-width: 1;
            stroke-opacity: 0.5;
        }

        .marker-label {
            font-size: 12px;
            font-weight: bold;
            fill: var(--accent);
            text-shadow: 0 2px 4px rgba(0,0,0,0.8);
            opacity: 0;
            transition: opacity 0.3s;
        }

        .marker-point {
            fill: var(--accent);
            stroke: #fff;
            stroke-width: 2;
            cursor: pointer;
        }

        .pulse-ring {
            fill: none;
            stroke: var(--accent);
            stroke-width: 1;
            opacity: 0;
            transform-origin: center;
        }

        /* Loading Screen */
        #loader {
            position: absolute;
            top: 0; left: 0; width: 100%; height: 100%;
            background: var(--bg-color);
            display: flex;
            flex-direction: column;
            justify-content: center;
            align-items: center;
            z-index: 100;
            transition: opacity 0.8s ease-out;
        }
        .loader-bar {
            width: 200px;
            height: 2px;
            background: #1e293b;
            margin-top: 20px;
            position: relative;
            overflow: hidden;
        }
        .loader-progress {
            position: absolute;
            left: 0; top: 0; bottom: 0;
            width: 0%;
            background: var(--primary);
            box-shadow: 0 0 10px var(--primary);
            transition: width 0.2s;
        }

        /* Animations */
        @keyframes blink { 0%, 100% { opacity: 1; } 50% { opacity: 0.3; } }
        .blink { animation: blink 2s infinite; }
        
        @keyframes rotate-ui { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }
        .rotating { animation: rotate-ui 10s linear infinite; transform-origin: center; }

    </style>
</head>
<body>

    <div id="loader">
        <div style="color: var(--primary); font-family: var(--font-mono); letter-spacing: 4px;">SYSTEM INITIALIZING</div>
        <div class="loader-bar"><div class="loader-progress" id="load-bar"></div></div>
    </div>

    <div id="container">
        <svg id="scene" viewBox="0 0 1200 700" preserveAspectRatio="xMidYMid meet">
            <defs>
                <!-- Filters & Gradients -->
                <radialGradient id="oceanGradient" cx="50%" cy="50%" r="50%" fx="25%" fy="25%">
                    <stop offset="0%" stop-color="#334155" />
                    <stop offset="100%" stop-color="#020617" />
                </radialGradient>
                
                <filter id="glow-blue" x="-50%" y="-50%" width="200%" height="200%">
                    <feGaussianBlur stdDeviation="4" result="blur" />
                    <feComposite in="SourceGraphic" in2="blur" operator="over" />
                </filter>

                <filter id="glow-gold" x="-50%" y="-50%" width="200%" height="200%">
                    <feGaussianBlur stdDeviation="3" result="blur" />
                    <feMerge>
                        <feMergeNode in="blur" />
                        <feMergeNode in="SourceGraphic" />
                    </feMerge>
                </filter>

                <mask id="globeMask">
                    <circle cx="600" cy="350" r="250" fill="white" />
                </mask>

                <pattern id="gridPattern" width="40" height="40" patternUnits="userSpaceOnUse">
                    <path d="M 40 0 L 0 0 0 40" fill="none" stroke="var(--grid)" stroke-width="0.5"/>
                </pattern>
            </defs>

            <!-- Background Elements -->
            <g id="stars-bg"></g>
            <rect width="100%" height="100%" fill="url(#gridPattern)" opacity="0.3" pointer-events="none"/>

            <!-- Main Globe Group -->
            <g id="globe-group">
                <!-- Atmosphere Glow -->
                <circle cx="600" cy="350" r="255" fill="#38BDF8" opacity="0.1" filter="url(#glow-blue)" />
                
                <!-- Ocean -->
                <circle cx="600" cy="350" r="250" fill="url(#oceanGradient)" stroke="#475569" stroke-width="1" />

                <!-- Map Content (Clipped to Sphere) -->
                <g id="geo-content">
                    <path id="graticule" fill="none" stroke="#475569" stroke-width="0.5" opacity="0.3" />
                    <path id="land" fill="#1e293b" stroke="#64748b" stroke-width="0.5" />
                    
                    <!-- Route Lines -->
                    <path id="route-bg" fill="none" stroke="#38BDF8" stroke-width="1" opacity="0.3" stroke-dasharray="2 4" />
                    <path id="route-active" fill="none" stroke="#FBBF24" stroke-width="2.5" stroke-linecap="round" filter="url(#glow-gold)" />
                    
                    <!-- Markers Container -->
                    <g id="markers-layer"></g>

                    <!-- Moving Target Indicator -->
                    <g id="target-cursor" style="display: none;">
                        <circle r="6" fill="none" stroke="#FBBF24" stroke-width="2" />
                        <circle r="12" fill="none" stroke="#FBBF24" stroke-width="1" opacity="0.5" />
                        <line x1="-15" y1="0" x2="15" y2="0" stroke="#FBBF24" stroke-width="1" />
                        <line x1="0" y1="-15" x2="0" y2="15" stroke="#FBBF24" stroke-width="1" />
                    </g>
                </g>
                
                <!-- Globe Shadow (Night side) -->
                <path id="night-shadow" fill="#020617" opacity="0.4" style="mix-blend-mode: multiply;" pointer-events="none" />
            </g>

            <!-- HUD / UI Overlay -->
            <g class="hud-ui">
                <!-- Top Header -->
                <g transform="translate(600, 60)">
                    <text text-anchor="middle" font-size="24" font-weight="800" fill="#F8FAFC" letter-spacing="4">GLOBAL EXPEDITION</text>
                    <text y="25" text-anchor="middle" font-size="12" fill="#94A3B8" letter-spacing="2">TRANS-CONTINENTAL TRAJECTORY // 跨大陆征途</text>
                    <line x1="-200" y1="40" x2="200" y2="40" stroke="url(#oceanGradient)" stroke-width="1" />
                    <rect x="-150" y="39" width="300" height="2" fill="#38BDF8" opacity="0.5" />
                </g>

                <!-- Left Panel: Location Info -->
                <g transform="translate(50, 150)">
                    <path d="M0,0 L240,0 L260,20 L260,120 L20,120 L0,100 Z" class="panel-bg" />
                    <rect x="0" y="0" width="4" height="40" fill="#FBBF24" />
                    
                    <text x="20" y="30" class="hud-text" font-size="10" fill="#94A3B8">CURRENT LOCATION / 当前位置</text>
                    <text id="ui-location-name" x="20" y="55" class="hud-text" font-size="18" font-weight="bold" fill="#F8FAFC">INITIALIZING...</text>
                    
                    <text x="20" y="85" class="hud-text" font-size="10" fill="#94A3B8">COORDINATES / 坐标</text>
                    <text id="ui-coords" x="20" y="100" class="hud-text" font-size="14" fill="#38BDF8">00°00' N, 00°00' E</text>
                </g>

                <!-- Right Panel: Mission Status -->
                <g transform="translate(890, 150)">
                    <path d="M20,0 L260,0 L260,100 L240,120 L0,120 L0,20 Z" class="panel-bg" />
                    <rect x="256" y="0" width="4" height="40" fill="#38BDF8" />

                    <text x="240" y="30" text-anchor="end" class="hud-text" font-size="10" fill="#94A3B8">MISSION PROGRESS / 进程</text>
                    <text id="ui-progress" x="240" y="60" text-anchor="end" class="hud-text" font-size="32" font-weight="bold" fill="#F8FAFC">0%</text>
                    
                    <rect x="40" y="80" width="200" height="4" fill="#1e293b" />
                    <rect id="ui-progress-bar" x="40" y="80" width="0" height="4" fill="#FBBF24" filter="url(#glow-gold)" />
                </g>

                <!-- Bottom Panel: Waypoints Log -->
                <g transform="translate(300, 620)">
                    <line x1="0" y1="0" x2="600" y2="0" stroke="#334155" stroke-width="1" />
                    <circle cx="0" cy="0" r="3" fill="#38BDF8" />
                    <circle cx="600" cy="0" r="3" fill="#38BDF8" />
                    
                    <text x="300" y="25" text-anchor="middle" class="hud-text blink" font-size="12" fill="#FBBF24">
                        <tspan id="ui-status-msg">WAITING FOR SATELLITE LINK...</tspan>
                    </text>
                </g>

                <!-- Decorative Compass -->
                <g transform="translate(1100, 600)" opacity="0.6">
                    <circle r="40" fill="none" stroke="#38BDF8" stroke-width="1" stroke-dasharray="4 4" />
                    <circle r="30" fill="none" stroke="#94A3B8" stroke-width="0.5" />
                    <path d="M0,-35 L5,-5 L35,0 L5,5 L0,35 L-5,5 L-35,0 L-5,-5 Z" fill="#1e293b" stroke="#38BDF8" stroke-width="1" class="rotating" />
                    <text x="0" y="-50" text-anchor="middle" font-size="10" fill="#38BDF8" font-family="monospace">N</text>
                </g>
            </g>
        </svg>
    </div>

    <script>
        // --- Configuration ---
        const CONFIG = {
            width: 1200,
            height: 700,
            globeRadius: 250,
            center: [600, 350],
            speed: 0.0015, // Animation speed
            pauseOnWaypoint: 60, // Frames to pause at key locations
            waypoints: [
                { name: "好望角 (起点)", en: "Cape of Good Hope", coords: [18.47, -34.35], type: "start" },
                { name: "坦桑尼亚", en: "Tanzania", coords: [34.88, -6.36], type: "pass" },
                { name: "开罗", en: "Cairo, Egypt", coords: [31.23, 30.04], type: "major" },
                { name: "德黑兰", en: "Tehran, Iran", coords: [51.38, 35.68], type: "pass" },
                { name: "中亚腹地", en: "Central Asia", coords: [65.00, 42.00], type: "pass" },
                { name: "新西伯利亚", en: "Novosibirsk", coords: [82.93, 55.00], type: "major" },
                { name: "雅库茨克", en: "Yakutsk", coords: [129.73, 62.03], type: "pass" },
                { name: "白令海峡", en: "Bering Strait", coords: [-169.00, 65.50], type: "critical" }, // Crossing point
                { name: "安克雷奇", en: "Anchorage", coords: [-149.90, 61.21], type: "major" },
                { name: "温哥华", en: "Vancouver", coords: [-123.12, 49.28], type: "pass" },
                { name: "巴拿马城", en: "Panama City", coords: [-79.51, 8.98], type: "major" },
                { name: "利马", en: "Lima, Peru", coords: [-77.04, -12.04], type: "pass" },
                { name: "合恩角 (终点)", en: "Cape Horn", coords: [-67.29, -55.98], type: "end" }
            ]
        };

        // --- Globals ---
        let projection, pathGenerator;
        let routePoints = [];
        let currentProgress = 0;
        let isPaused = false;
        let pauseTimer = 0;
        let animationId;
        let landFeature, graticuleFeature;
        
        // --- Initialization ---
        async function init() {
            const loaderBar = document.getElementById('load-bar');
            
            // 1. Setup D3 Projection
            projection = d3.geoOrthographic()
                .scale(CONFIG.globeRadius)
                .translate(CONFIG.center)
                .clipAngle(90);

            pathGenerator = d3.geoPath().projection(projection);

            // 2. Generate Stars
            generateStars();
            loaderBar.style.width = "30%";

            // 3. Load Map Data
            try {
                const worldData = await d3.json('https://cdn.jsdelivr.net/npm/world-atlas@2/countries-110m.json');
                landFeature = topojson.feature(worldData, worldData.objects.land);
                graticuleFeature = d3.geoGraticule()();
                loaderBar.style.width = "70%";
            } catch (e) {
                console.error("Failed to load map data", e);
                document.getElementById('ui-status-msg').textContent = "DATA LINK FAILED";
                return;
            }

            // 4. Calculate Route
            calculateRoute();
            loaderBar.style.width = "100%";

            // 5. Render Static Elements
            renderStaticMap();
            renderMarkers();

            // 6. Start Animation
            setTimeout(() => {
                document.getElementById('loader').style.opacity = 0;
                setTimeout(() => document.getElementById('loader').remove(), 800);
                animate();
            }, 500);

            // 7. Interaction
            setupInteraction();
        }

        function generateStars() {
            const starGroup = d3.select("#stars-bg");
            for(let i=0; i<300; i++) {
                starGroup.append("circle")
                    .attr("cx", Math.random() * CONFIG.width)
                    .attr("cy", Math.random() * CONFIG.height)
                    .attr("r", Math.random() * 1.5)
                    .attr("fill", "#fff")
                    .attr("opacity", Math.random() * 0.8);
            }
        }

        function calculateRoute() {
            // Create a dense array of points along the waypoints using Great Circle interpolation
            routePoints = [];
            for (let i = 0; i < CONFIG.waypoints.length - 1; i++) {
                const start = CONFIG.waypoints[i].coords;
                const end = CONFIG.waypoints[i+1].coords;
                const interpolator = d3.geoInterpolate(start, end);
                
                // Distance determines number of steps for constant speed
                const dist = d3.geoDistance(start, end);
                const steps = Math.max(20, Math.floor(dist * 200)); 

                for (let t = 0; t < steps; t++) {
                    routePoints.push(interpolator(t / steps));
                }
                // Add waypoint index marker to the point data for UI triggers
                routePoints[routePoints.length - 1].waypointIndex = i + 1;
            }
            routePoints.push(CONFIG.waypoints[CONFIG.waypoints.length - 1].coords);
        }

        function renderStaticMap() {
            d3.select("#land").datum(landFeature).attr("d", pathGenerator);
            d3.select("#graticule").datum(graticuleFeature).attr("d", pathGenerator);
            
            // Full Route Background (faint)
            const routeLineString = { type: "LineString", coordinates: routePoints };
            d3.select("#route-bg").datum(routeLineString).attr("d", pathGenerator);
        }

        function renderMarkers() {
            const container = d3.select("#markers-layer");
            
            CONFIG.waypoints.forEach((wp, index) => {
                const g = container.append("g")
                    .attr("class", "marker-group")
                    .attr("id", `marker-${index}`);

                // Pulse Ring
                g.append("circle")
                    .attr("class", "pulse-ring")
                    .attr("r", 8);

                // Center Point
                g.append("circle")
                    .attr("class", "marker-point")
                    .attr("r", index === 0 || index === CONFIG.waypoints.length-1 ? 5 : 3)
                    .attr("fill", wp.type === 'critical' ? '#EF4444' : '#FBBF24');

                // Label
                g.append("text")
                    .attr("class", "marker-label")
                    .attr("x", 10)
                    .attr("y", 4)
                    .text(wp.name);
            });
        }

        // --- Main Animation Loop ---
        function animate() {
            if (!isPaused) {
                // Handle Pausing at Waypoints
                if (pauseTimer > 0) {
                    pauseTimer--;
                    updateGlobeRotation(); // Keep rotating or holding? Let's hold slightly or drift.
                    requestAnimationFrame(animate);
                    return;
                }

                // Advance Progress
                currentProgress += CONFIG.speed;
                if (currentProgress >= 1) {
                    currentProgress = 1;
                    isPaused = true; // End of journey
                    document.getElementById('ui-status-msg').textContent = "MISSION ACCOMPLISHED / 抵达终点";
                    document.getElementById('ui-status-msg').parentElement.classList.remove('blink');
                }

                updateScene();
            }
            requestAnimationFrame(animate);
        }

        function updateScene() {
            const totalPoints = routePoints.length;
            const currentIndex = Math.floor(currentProgress * (totalPoints - 1));
            const currentPos = routePoints[currentIndex];

            // 1. Rotate Globe to center on current position
            // Smoothly interpolate rotation
            const targetRotate = [-currentPos[0], -currentPos[1]];
            const currentRotate = projection.rotate();
            
            // Simple easing for rotation
            const newRotate = [
                currentRotate[0] + (targetRotate[0] - currentRotate[0]) * 0.1,
                currentRotate[1] + (targetRotate[1] - currentRotate[1]) * 0.1
            ];
            projection.rotate(newRotate);

            // 2. Update Map Paths
            d3.select("#land").attr("d", pathGenerator);
            d3.select("#graticule").attr("d", pathGenerator);
            d3.select("#route-bg").attr("d", pathGenerator);

            // 3. Draw Active Route (from start to current)
            const activeRoute = {
                type: "LineString",
                coordinates: routePoints.slice(0, currentIndex + 1)
            };
            d3.select("#route-active").datum(activeRoute).attr("d", pathGenerator);

            // 4. Update Markers Visibility & Position
            updateMarkers(newRotate);

            // 5. Update Target Cursor
            const cursor = d3.select("#target-cursor");
            const projectedPos = projection(currentPos);
            
            // Check if point is visible (not on back of globe)
            const centerPos = projection.invert(CONFIG.center);
            const dist = d3.geoDistance(currentPos, centerPos);
            
            if (dist < 1.57) { // < 90 degrees approx
                cursor.style("display", "block")
                      .attr("transform", `translate(${projectedPos[0]}, ${projectedPos[1]})`);
            } else {
                cursor.style("display", "none");
            }

            // 6. Update UI
            updateUI(currentPos, currentIndex);

            // 7. Check for Waypoint Arrival
            if (currentPos.waypointIndex !== undefined) {
                // We hit a waypoint node
                const wpIndex = currentPos.waypointIndex;
                // Only pause if we haven't processed this index recently (simple debounce logic implied by linear progress)
                // To avoid multiple pauses, we remove the property or check a flag. 
                // Since we regenerate points, let's just use the fact that we are AT the index.
                // But float math is tricky. 
                
                // Better approach: Check distance to actual waypoints
                const nearestWP = CONFIG.waypoints.find(wp => d3.geoDistance(wp.coords, currentPos) < 0.005);
                if (nearestWP && !nearestWP.visited) {
                    nearestWP.visited = true;
                    pauseTimer = CONFIG.pauseOnWaypoint;
                    document.getElementById('ui-status-msg').textContent = `ARRIVED AT: ${nearestWP.en}`;
                    
                    // Trigger marker pulse
                    const markerSel = d3.select(`#marker-${CONFIG.waypoints.indexOf(nearestWP)}`);
                    markerSel.select(".pulse-ring")
                        .transition().duration(1000)
                        .attr("r", 20).style("opacity", 0)
                        .on("end", function() { d3.select(this).attr("r", 8).style("opacity", 1); });
                }
            }
        }

        function updateMarkers(rotation) {
            const centerPos = projection.invert(CONFIG.center);

            CONFIG.waypoints.forEach((wp, i) => {
                const dist = d3.geoDistance(wp.coords, centerPos);
                const isVisible = dist < 1.57; // 90 degrees
                const el = d3.select(`#marker-${i}`);
                
                if (isVisible) {
                    const pos = projection(wp.coords);
                    el.style("display", "block")
                      .attr("transform", `translate(${pos[0]}, ${pos[1]})`);
                    
                    // Show label if close to center or active
                    if (dist < 0.5 || wp.visited) {
                        el.select(".marker-label").style("opacity", 1);
                    } else {
                        el.select(".marker-label").style("opacity", 0.5);
                    }
                } else {
                    el.style("display", "none");
                }
            });
        }

        function updateUI(pos, idx) {
            // Coordinates
            const lat = pos[1];
            const lon = pos[0];
            const latStr = Math.abs(lat).toFixed(2) + "° " + (lat >= 0 ? "N" : "S");
            const lonStr = Math.abs(lon).toFixed(2) + "° " + (lon >= 0 ? "E" : "W");
            document.getElementById('ui-coords').textContent = `${latStr}, ${lonStr}`;

            // Progress
            const pct = Math.floor(currentProgress * 100);
            document.getElementById('ui-progress').textContent = pct + "%";
            document.getElementById('ui-progress-bar').setAttribute("width", pct * 2);

            // Location Name (Find nearest or generic)
            // Already handled by waypoint arrival, but for "In transit":
            if (pauseTimer === 0) {
                // Find previous waypoint
                let prevName = CONFIG.waypoints[0].name;
                for(let i=0; i<CONFIG.waypoints.length; i++) {
                    if (CONFIG.waypoints[i].visited) prevName = CONFIG.waypoints[i].name;
                }
                document.getElementById('ui-location-name').textContent = `EN ROUTE: ${prevName} -> ...`;
                document.getElementById('ui-status-msg').textContent = "TRACKING SATELLITE FEED...";
            } else {
                 // Paused at location
                 const currentWP = CONFIG.waypoints.find(wp => wp.visited && d3.geoDistance(wp.coords, pos) < 0.01);
                 if(currentWP) document.getElementById('ui-location-name').textContent = currentWP.name;
            }
        }

        function updateGlobeRotation() {
            // Helper to keep rotation smooth even when paused
            // (Optional: add subtle idle rotation)
        }

        // --- Interaction ---
        function setupInteraction() {
            let drag = false;
            let lastX, lastY;
            const svg = document.getElementById('scene');

            svg.addEventListener('pointerdown', (e) => {
                drag = true;
                lastX = e.clientX;
                lastY = e.clientY;
                isPaused = true; // Pause animation on drag
                svg.setPointerCapture(e.pointerId);
                document.getElementById('ui-status-msg').textContent = "MANUAL OVERRIDE ENGAGED";
            });

            svg.addEventListener('pointermove', (e) => {
                if (!drag) return;
                const dx = e.clientX - lastX;
                const dy = e.clientY - lastY;
                
                const rotate = projection.rotate();
                const k = 0.25; // sensitivity
                projection.rotate([rotate[0] + dx * k, rotate[1] - dy * k]);
                
                lastX = e.clientX;
                lastY = e.clientY;
                
                // Manual update of static elements only (path doesn't grow)
                d3.select("#land").attr("d", pathGenerator);
                d3.select("#graticule").attr("d", pathGenerator);
                d3.select("#route-bg").attr("d", pathGenerator);
                
                // Re-calculate active route visual for new angle
                const currentIndex = Math.floor(currentProgress * (routePoints.length - 1));
                const activeRoute = { type: "LineString", coordinates: routePoints.slice(0, currentIndex + 1) };
                d3.select("#route-active").datum(activeRoute).attr("d", pathGenerator);
                
                updateMarkers();
                
                // Hide cursor during manual drag usually, or update it
                const currentPos = routePoints[currentIndex];
                const projPos = projection(currentPos);
                const centerPos = projection.invert(CONFIG.center);
                if (d3.geoDistance(currentPos, centerPos) < 1.57) {
                    d3.select("#target-cursor")
                        .style("display", "block")
                        .attr("transform", `translate(${projPos[0]}, ${projPos[1]})`);
                } else {
                    d3.select("#target-cursor").style("display", "none");
                }
            });

            svg.addEventListener('pointerup', (e) => {
                drag = false;
                isPaused = false; // Resume
                svg.releasePointerCapture(e.pointerId);
                document.getElementById('ui-status-msg').textContent = "RESUMING AUTO-NAVIGATION...";
            });
        }

        // Run
        init();

    </script>
</body>
</html>38:{"id":"a534989e-ce9b-492e-bc4d-cebf4130eb20","exportName":"$undefined","title":"A person sets off from the Cape of Good Hope on Earth, passes through the African continent and the Asian continent, and crosses the Bering Sea","description":"A person sets off from the Cape of Good Hope on Earth, passes through the African continent and the Asian continent, enters the American continent through the Bering Strait, and finally arrives at the southernmost tip of South America from North America, presenting a full route map and marking some key locations.","category":"svg-map-animation","tags":"$39","likes":0,"favorites":0,"views":5787,"sourceType":"SvgAnimation","componentSource":"$3a","isLiked":false,"isFavorited":false}
3c:[]
3d:T5026,<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>四川旅游数据可视化</title>
    <script src="https://d3js.org/d3.v7.min.js"></script>
    <style>
        :root {
            --bg-dark: #0f172a;
            --bg-panel: rgba(30, 41, 59, 0.7);
            --primary: #38bdf8; /* Sky blue */
            --accent: #00f2ff; /* Cyan glow */
            --gold: #ffd700;   /* Gold trace */
            --text-main: #f1f5f9;
            --text-sub: #94a3b8;
            --grid-color: rgba(56, 189, 248, 0.1);
        }

        body {
            margin: 0;
            padding: 0;
            width: 100vw;
            height: 100vh;
            background-color: var(--bg-dark);
            background-image: 
                radial-gradient(circle at 50% 50%, #1e293b 0%, #0f172a 100%);
            font-family: 'PingFang SC', 'Microsoft YaHei', 'Helvetica Neue', sans-serif;
            overflow: hidden;
            color: var(--text-main);
            user-select: none;
        }

        /* Container for Aspect Ratio */
        #container {
            position: relative;
            width: 100vw;
            height: 100vh;
            display: flex;
            justify-content: center;
            align-items: center;
        }

        /* SVG Canvas */
        svg {
            width: 100%;
            height: 100%;
            max-width: 1200px;
            max-height: 700px;
            filter: drop-shadow(0 0 20px rgba(0,0,0,0.5));
        }

        /* Background Grid */
        .bg-grid {
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background-image: 
                linear-gradient(var(--grid-color) 1px, transparent 1px),
                linear-gradient(90deg, var(--grid-color) 1px, transparent 1px);
            background-size: 40px 40px;
            pointer-events: none;
            z-index: -1;
            opacity: 0.3;
        }

        /* Map Styles */
        .province {
            fill: rgba(51, 65, 85, 0.3);
            stroke: rgba(148, 163, 184, 0.3);
            stroke-width: 0.5px;
            transition: fill 0.3s ease;
        }

        .province:hover {
            fill: rgba(56, 189, 248, 0.2);
        }

        .sichuan-base {
            fill: rgba(15, 23, 42, 0.8);
            stroke: var(--primary);
            stroke-width: 1px;
            opacity: 0; /* Hidden initially, shown after zoom */
        }

        .golden-trace {
            fill: none;
            stroke: var(--gold);
            stroke-width: 2.5px;
            stroke-linecap: round;
            stroke-linejoin: round;
            filter: url(#glow-gold);
            pointer-events: none;
        }

        /* Marker Styles */
        .marker-group {
            cursor: pointer;
        }

        .marker-ring {
            fill: none;
            stroke: var(--accent);
            stroke-width: 1.5px;
            opacity: 0;
        }

        .marker-core {
            fill: #fff;
            filter: url(#glow-cyan);
            opacity: 0;
        }

        .marker-line {
            stroke: var(--accent);
            stroke-width: 1px;
            stroke-dasharray: 2, 2;
            opacity: 0;
        }

        .marker-text {
            fill: var(--text-main);
            font-size: 14px;
            font-weight: bold;
            text-shadow: 0 2px 4px rgba(0,0,0,0.8);
            opacity: 0;
            pointer-events: none;
        }

        .marker-text-bg {
            fill: rgba(15, 23, 42, 0.8);
            opacity: 0;
        }

        /* UI Overlay */
        .ui-overlay {
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            pointer-events: none;
            max-width: 1200px;
            max-height: 700px;
            margin: auto;
        }

        .header {
            position: absolute;
            top: 30px;
            left: 30px;
            opacity: 0;
            transform: translateY(-20px);
            animation: slideIn 1s forwards 0.5s;
        }

        .header h1 {
            margin: 0;
            font-size: 32px;
            letter-spacing: 4px;
            background: linear-gradient(90deg, #fff, var(--accent));
            -webkit-background-clip: text;
            -webkit-text-fill-color: transparent;
            text-shadow: 0 0 15px rgba(0, 242, 255, 0.4);
        }

        .header .subtitle {
            font-size: 12px;
            color: var(--text-sub);
            letter-spacing: 2px;
            margin-top: 5px;
            display: block;
        }

        .info-card {
            position: absolute;
            bottom: 40px;
            right: 40px;
            width: 280px;
            background: rgba(15, 23, 42, 0.85);
            backdrop-filter: blur(10px);
            border: 1px solid rgba(56, 189, 248, 0.3);
            border-left: 4px solid var(--gold);
            padding: 20px;
            border-radius: 4px;
            transform: translateX(50px);
            opacity: 0;
            transition: all 0.5s cubic-bezier(0.23, 1, 0.32, 1);
            box-shadow: 0 10px 40px rgba(0,0,0,0.6);
        }

        .info-card.active {
            transform: translateX(0);
            opacity: 1;
        }

        .info-card h2 {
            margin: 0 0 8px 0;
            color: var(--gold);
            font-size: 20px;
            display: flex;
            align-items: center;
            justify-content: space-between;
        }

        .info-card .type-tag {
            font-size: 10px;
            background: rgba(255, 215, 0, 0.15);
            color: var(--gold);
            padding: 2px 6px;
            border-radius: 2px;
            border: 1px solid rgba(255, 215, 0, 0.3);
        }

        .info-card p {
            margin: 0;
            font-size: 13px;
            line-height: 1.6;
            color: #cbd5e1;
        }

        /* Loading Spinner */
        #loader {
            position: absolute;
            top: 0; left: 0; right: 0; bottom: 0;
            background: #020617;
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            z-index: 1000;
            transition: opacity 0.8s;
        }

        .spinner-ring {
            width: 60px;
            height: 60px;
            border: 2px solid transparent;
            border-top-color: var(--accent);
            border-radius: 50%;
            animation: spin 1s linear infinite;
        }
        
        .spinner-text {
            margin-top: 20px;
            color: var(--accent);
            font-size: 12px;
            letter-spacing: 3px;
            animation: blink 1.5s infinite;
        }

        @keyframes spin { 100% { transform: rotate(360deg); } }
        @keyframes blink { 50% { opacity: 0.5; } }
        @keyframes slideIn { to { opacity: 1; transform: translateY(0); } }

    </style>
</head>
<body>

    <div id="loader">
        <div class="spinner-ring"></div>
        <div class="spinner-text">SYSTEM INITIALIZING...</div>
    </div>

    <div id="container">
        <div class="bg-grid"></div>
        
        <svg id="main-svg" viewBox="0 0 1200 700" preserveAspectRatio="xMidYMid meet">
            <defs>
                <filter id="glow-gold" x="-50%" y="-50%" width="200%" height="200%">
                    <feGaussianBlur stdDeviation="3" result="coloredBlur"/>
                    <feMerge>
                        <feMergeNode in="coloredBlur"/>
                        <feMergeNode in="SourceGraphic"/>
                    </feMerge>
                </filter>
                <filter id="glow-cyan" x="-50%" y="-50%" width="200%" height="200%">
                    <feGaussianBlur stdDeviation="2" result="coloredBlur"/>
                    <feMerge>
                        <feMergeNode in="coloredBlur"/>
                        <feMergeNode in="SourceGraphic"/>
                    </feMerge>
                </filter>
            </defs>
            
            <g id="map-group">
                <!-- Provinces will be drawn here -->
            </g>
            <g id="sichuan-highlight-group"></g>
            <g id="trace-group"></g>
            <g id="markers-group"></g>
        </svg>

        <div class="ui-overlay">
            <div class="header">
                <h1>巴蜀胜地</h1>
                <span class="subtitle">SICHUAN TOURISM INTELLIGENCE MAP</span>
            </div>

            <div id="info-panel" class="info-card">
                <h2><span id="info-title">中国</span> <span id="info-tag" class="type-tag">REGION</span></h2>
                <p id="info-desc">正在加载地理数据，请稍候...</p>
            </div>
        </div>
    </div>

    <script>
        // Data & Configuration
        const CONFIG = {
            width: 1200,
            height: 700,
            geoUrl: 'https://gist.githubusercontent.com/zhulinpinyu/8e18d57b3b18fb74e776/raw/efbb74cfea53decb1fe7d6bf279fd351c28c4810/china_simplify.json',
            sichuanName: '四川',
            // Manual offsets to prevent overlap: [x, y] relative to dot
            spots: [
                {
                    name: "九寨沟",
                    coord: [103.918, 33.263],
                    type: "自然遗产",
                    desc: "童话世界，人间仙境。翠海、叠瀑、彩林、雪峰、藏情、蓝冰，六绝驰名中外。",
                    offset: [30, -30] // Top Right
                },
                {
                    name: "三星堆",
                    coord: [104.200, 30.950],
                    type: "古蜀文明",
                    desc: "沉睡数千年，一醒惊天下。青铜神树、纵目面具，揭示了古蜀文明的神秘面纱。",
                    offset: [40, -10] // Right
                },
                {
                    name: "峨眉山",
                    coord: [103.322, 29.568],
                    type: "佛教名山",
                    desc: "普贤菩萨道场，地势陡峭，风景秀丽，素有'峨眉天下秀'之称。",
                    offset: [-60, 20] // Bottom Left (Away from Leshan)
                },
                {
                    name: "乐山大佛",
                    coord: [103.770, 29.540],
                    type: "世界遗产",
                    desc: "山是一尊佛，佛是一座山。中国最大的一尊摩崖石刻造像。",
                    offset: [40, 30] // Bottom Right (Away from Emei)
                },
                {
                    name: "贡嘎山",
                    coord: [101.960, 29.580],
                    type: "蜀山之王",
                    desc: "海拔7556米，四川省最高峰。周围林立着145座海拔五六千米的冰峰。",
                    offset: [-50, -30] // Top Left
                },
                {
                    name: "稻城亚丁",
                    coord: [100.340, 28.530],
                    type: "最后净土",
                    desc: "水蓝色星球上的最后一片净土。仙乃日、央迈勇、夏诺多吉三座神山巍然耸立。",
                    offset: [-40, 20] // Bottom Left
                }
            ]
        };

        // State
        let svg, projection, pathGen;
        let mapGroup, traceGroup, markersGroup, highlightGroup;
        let geoData, sichuanFeature;

        // Initialize
        document.addEventListener('DOMContentLoaded', () => {
            initSystem();
        });

        function initSystem() {
            svg = d3.select("#main-svg");
            mapGroup = d3.select("#map-group");
            highlightGroup = d3.select("#sichuan-highlight-group");
            traceGroup = d3.select("#trace-group");
            markersGroup = d3.select("#markers-group");

            loadData();
        }

        function loadData() {
            fetch(CONFIG.geoUrl)
                .then(res => {
                    if (!res.ok) throw new Error("Network response was not ok");
                    return res.json();
                })
                .then(data => {
                    geoData = data;
                    // Find Sichuan
                    sichuanFeature = geoData.features.find(f => f.properties.name.indexOf(CONFIG.sichuanName) > -1);
                    
                    if (!sichuanFeature) throw new Error("Sichuan data not found");

                    renderInitialMap();
                    
                    // Hide loader
                    const loader = document.getElementById('loader');
                    loader.style.opacity = 0;
                    setTimeout(() => loader.remove(), 800);

                    // Start Animation Sequence
                    setTimeout(runAnimationSequence, 1000);
                })
                .catch(err => {
                    console.error(err);
                    document.querySelector('.spinner-text').textContent = "DATA ERROR - CHECK CONSOLE";
                    document.querySelector('.spinner-ring').style.borderColor = "red";
                });
        }

        function renderInitialMap() {
            // Initial Projection: Fit whole China to canvas
            projection = d3.geoMercator().fitSize([CONFIG.width, CONFIG.height], geoData);
            pathGen = d3.geoPath().projection(projection);

            // Draw all provinces
            mapGroup.selectAll("path")
                .data(geoData.features)
                .enter()
                .append("path")
                .attr("d", pathGen)
                .attr("class", "province")
                .attr("id", d => `prov-${d.properties.id}`);
        }

        function runAnimationSequence() {
            // Sequence:
            // 1. Zoom to Sichuan
            // 2. Highlight & Gold Trace
            // 3. Show Markers one by one

            zoomToSichuan(() => {
                highlightSichuan(() => {
                    showMarkers();
                });
            });
        }

        function zoomToSichuan(callback) {
            // Calculate bounds of Sichuan
            const bounds = pathGen.bounds(sichuanFeature);
            const dx = bounds[1][0] - bounds[0][0];
            const dy = bounds[1][1] - bounds[0][1];
            const x = (bounds[0][0] + bounds[1][0]) / 2;
            const y = (bounds[0][1] + bounds[1][1]) / 2;
            
            // Determine scale (add padding)
            const scale = 0.65 / Math.max(dx / CONFIG.width, dy / CONFIG.height);
            
            // Target ViewBox
            const targetWidth = CONFIG.width / scale;
            const targetHeight = CONFIG.height / scale;
            const targetX = x - targetWidth / 2;
            const targetY = y - targetHeight / 2;

            // Update UI
            updateInfo("定位中...", "NAVIGATION", "正在锁定目标区域：四川省 (Sichuan Province)");

            // Animate ViewBox
            svg.transition()
                .duration(2000)
                .ease(d3.easeCubicInOut)
                .attr("viewBox", `${targetX} ${targetY} ${targetWidth} ${targetHeight}`)
                .on("end", callback);

            // Fade out other provinces slightly during zoom to focus attention
            mapGroup.selectAll("path")
                .transition().duration(2000)
                .style("opacity", 0.2);
        }

        function highlightSichuan(callback) {
            // 1. Draw Sichuan Background (Darker/Distinct)
            highlightGroup.append("path")
                .datum(sichuanFeature)
                .attr("d", pathGen)
                .attr("class", "sichuan-base")
                .transition().duration(1000)
                .style("opacity", 1);

            updateInfo("四川省", "SICHUAN", "天府之国，沃野千里。准备展示核心旅游资源。");

            // 2. Gold Trace Animation
            const pathNode = traceGroup.append("path")
                .datum(sichuanFeature)
                .attr("d", pathGen)
                .attr("class", "golden-trace");
            
            const totalLength = pathNode.node().getTotalLength();

            pathNode
                .attr("stroke-dasharray", totalLength + " " + totalLength)
                .attr("stroke-dashoffset", totalLength)
                .transition()
                .duration(2500)
                .ease(d3.easeLinear)
                .attr("stroke-dashoffset", 0)
                .on("end", callback);
        }

        function showMarkers() {
            let delay = 0;
            const interval = 1200;

            CONFIG.spots.forEach((spot, i) => {
                setTimeout(() => {
                    createMarker(spot, i);
                }, delay);
                delay += interval;
            });
        }

        function createMarker(spot, index) {
            const [lon, lat] = spot.coord;
            const [x, y] = projection([lon, lat]);
            
            const g = markersGroup.append("g")
                .attr("class", "marker-group")
                .attr("transform", `translate(${x}, ${y})`)
                .on("click", () => updateInfo(spot.name, spot.type, spot.desc));

            // 1. Ring Expansion
            g.append("circle")
                .attr("class", "marker-ring")
                .attr("r", 0)
                .transition()
                .duration(800)
                .ease(d3.easeBackOut)
                .attr("r", 6)
                .style("opacity", 1);

            // Continuous pulse for ring
            startPulse(g.select(".marker-ring"));

            // 2. Core Dot
            g.append("circle")
                .attr("class", "marker-core")
                .attr("r", 0)
                .transition()
                .delay(200)
                .duration(500)
                .attr("r", 2.5)
                .style("opacity", 1);

            // 3. Leader Line
            const [offsetX, offsetY] = spot.offset;
            const line = g.append("line")
                .attr("class", "marker-line")
                .attr("x1", 0).attr("y1", 0)
                .attr("x2", 0).attr("y2", 0) // Start at center
                .transition()
                .delay(400)
                .duration(600)
                .attr("x2", offsetX)
                .attr("y2", offsetY)
                .style("opacity", 0.8);

            // 4. Text Label
            // Background rect for readability (optional, simple text shadow used instead)
            
            const textAnchor = offsetX > 0 ? "start" : "end";
            const textX = offsetX + (offsetX > 0 ? 5 : -5);
            const textY = offsetY + 5; // vertical align fix

            g.append("text")
                .attr("class", "marker-text")
                .attr("x", textX)
                .attr("y", textY)
                .attr("text-anchor", textAnchor)
                .text(spot.name)
                .transition()
                .delay(800)
                .duration(500)
                .style("opacity", 1);

            // Update Info Card automatically
            updateInfo(spot.name, spot.type, spot.desc);
        }

        function startPulse(selection) {
            selection.transition()
                .duration(1500)
                .attr("r", 12)
                .style("stroke-opacity", 0)
                .on("end", function() {
                    d3.select(this)
                        .attr("r", 6)
                        .style("stroke-opacity", 1);
                    startPulse(d3.select(this));
                });
        }

        function updateInfo(title, tag, desc) {
            const card = document.getElementById('info-panel');
            const titleEl = document.getElementById('info-title');
            const tagEl = document.getElementById('info-tag');
            const descEl = document.getElementById('info-desc');

            // Visual feedback
            card.style.borderColor = 'var(--gold)';
            setTimeout(() => card.style.borderColor = 'rgba(56, 189, 248, 0.3)', 300);

            if (!card.classList.contains('active')) card.classList.add('active');

            titleEl.innerText = title;
            tagEl.innerText = tag;
            descEl.innerText = desc;
        }

    </script>
</body>
</html>3b:{"id":"1ff45342-b007-4d5b-851d-75ccd72e7486","exportName":"$undefined","title":"The scene starts with a map of China, then the camera zooms in on Sichuan, with a golden light circling Sichuan Province.","description":"The scene starts with a map of China, then the camera zooms in on Sichuan, with a golden light circling the outline of Sichuan Province. Then, the following tourist attractions are marked in Sichuan in sequence: Jiuzhaigou, Mount Emei, Leshan Giant Buddha, Sanxingdui, Mount Gongga, and Daocheng Yading. The overall animation style should be cool. Be careful to adjust the positions of the names of each tourist attraction so they do not overlap.","category":"svg-map-animation","tags":"$3c","likes":1,"favorites":0,"views":5652,"sourceType":"SvgAnimation","componentSource":"$3d","isLiked":false,"isFavorited":false}
3f:[]
40:T4c36,<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>地球往事：三体三部曲全景导览</title>
    <!-- Core Dependencies -->
    <script src="https://cdn.tailwindcss.com"></script>
    <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" rel="stylesheet">
    <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;600;700&family=Noto+Sans+SC:wght@300;400;700&display=swap" rel="stylesheet">
    
    <!-- Design System Override -->
    <script>
        tailwind.config = {
            theme: {
                extend: {
                    fontFamily: { 
                        sans: ['Inter', 'Noto Sans SC', 'sans-serif'],
                        mono: ['Fira Code', 'monospace']
                    },
                    colors: {
                        space: {
                            950: '#020617',
                            900: '#0f172a',
                            800: '#1e293b',
                        },
                        trisolaris: '#ff4d4d',
                        earth: '#3b82f6',
                        forest: '#10b981'
                    },
                    animation: {
                        'pulse-slow': 'pulse 4s cubic-bezier(0.4, 0, 0.6, 1) infinite',
                        'float': 'float 6s ease-in-out infinite',
                    },
                    keyframes: {
                        float: {
                            '0%, 100%': { transform: 'translateY(0)' },
                            '50%': { transform: 'translateY(-20px)' },
                        }
                    }
                }
            }
        }
    </script>
    <style>
        body { 
            background-color: #020617;
            color: #e2e8f0;
            overflow-x: hidden;
        }
        .glass-card {
            background: rgba(30, 41, 59, 0.7);
            backdrop-filter: blur(12px);
            border: 1px solid rgba(255, 255, 255, 0.1);
        }
        .star-bg {
            position: fixed;
            top: 0; left: 0; width: 100%; height: 100%;
            z-index: -1;
            background: radial-gradient(circle at center, #1e293b 0%, #020617 100%);
        }
        .glow-text {
            text-shadow: 0 0 15px rgba(59, 130, 246, 0.5);
        }
        .book-tab.active {
            border-bottom: 3px solid #3b82f6;
            color: #3b82f6;
        }
        ::-webkit-scrollbar { width: 4px; }
        ::-webkit-scrollbar-thumb { background: #334155; border-radius: 10px; }
    </style>
</head>
<body class="font-sans">
    <div class="star-bg" id="starfield"></div>

    <!-- Navigation -->
    <nav class="sticky top-0 z-50 glass-card border-b border-white/10 px-6 py-4">
        <div class="max-w-7xl mx-auto flex justify-between items-center">
            <div class="flex items-center space-x-3">
                <div class="w-10 h-10 bg-blue-600 rounded-full flex items-center justify-center animate-pulse">
                    <i class="fas fa-atom text-white"></i>
                </div>
                <span class="text-xl font-bold tracking-tighter uppercase">Remembrance of Earth's Past</span>
            </div>
            <div class="hidden md:flex space-x-8 text-sm font-medium">
                <a href="#overview" class="hover:text-blue-400 transition">概览</a>
                <a href="#trilogy" class="hover:text-blue-400 transition">三部曲</a>
                <a href="#concepts" class="hover:text-blue-400 transition">核心概念</a>
                <a href="#characters" class="hover:text-blue-400 transition">关键人物</a>
            </div>
        </div>
    </nav>

    <!-- Hero Section -->
    <header class="relative h-[80vh] flex items-center justify-center overflow-hidden">
        <div class="absolute inset-0 z-0">
            <img src="https://images.unsplash.com/photo-1462331940025-496dfbfc7564?auto=format&fit=crop&w=2000&q=80" 
                 class="w-full h-full object-cover opacity-30 scale-110" alt="Cosmos">
        </div>
        <div class="relative z-10 text-center px-4 max-w-4xl">
            <h1 class="text-5xl md:text-8xl font-bold mb-6 glow-text tracking-tight">三体</h1>
            <p class="text-xl md:text-2xl text-slate-300 font-light leading-relaxed mb-8">
                “给时光以生命，给岁月以文明。” —— 刘慈欣史诗级科幻巨作
            </p>
            <div class="flex flex-wrap justify-center gap-4">
                <button onclick="document.getElementById('trilogy').scrollIntoView({behavior:'smooth'})" class="px-8 py-3 bg-blue-600 hover:bg-blue-700 rounded-full font-semibold transition transform hover:scale-105">
                    开启探索
                </button>
                <button class="px-8 py-3 bg-white/10 hover:bg-white/20 rounded-full font-semibold backdrop-blur-md transition">
                    了解作者
                </button>
            </div>
        </div>
        <!-- Floating Elements -->
        <div class="absolute bottom-10 left-1/2 -translate-x-1/2 animate-bounce text-slate-500">
            <i class="fas fa-chevron-down text-2xl"></i>
        </div>
    </header>

    <main class="max-w-7xl mx-auto px-6 py-20 space-y-32">
        
        <!-- Trilogy Section -->
        <section id="trilogy">
            <div class="flex flex-col md:flex-row justify-between items-end mb-12 gap-4">
                <div>
                    <h2 class="text-3xl font-bold mb-2">三部曲全景</h2>
                    <p class="text-slate-400">从红岸基地到宇宙的终结</p>
                </div>
                <div class="flex space-x-2 glass-card p-1 rounded-xl">
                    <button onclick="switchBook(1)" id="btn-1" class="book-tab active px-6 py-2 rounded-lg transition">I. 三体</button>
                    <button onclick="switchBook(2)" id="btn-2" class="book-tab px-6 py-2 rounded-lg transition">II. 黑暗森林</button>
                    <button onclick="switchBook(3)" id="btn-3" class="book-tab px-6 py-2 rounded-lg transition">III. 死神永生</button>
                </div>
            </div>

            <div id="book-content" class="grid md:grid-cols-2 gap-12 items-center min-h-[400px]">
                <!-- Content injected by JS -->
            </div>
        </section>

        <!-- Core Concepts Grid -->
        <section id="concepts">
            <div class="text-center mb-16">
                <h2 class="text-3xl font-bold mb-4">核心宇宙法则</h2>
                <div class="h-1 w-20 bg-blue-600 mx-auto"></div>
            </div>
            <div class="grid md:grid-cols-3 gap-8">
                <div class="glass-card p-8 rounded-2xl hover:border-blue-500/50 transition group">
                    <div class="w-14 h-14 bg-blue-500/20 rounded-lg flex items-center justify-center mb-6 group-hover:scale-110 transition">
                        <i class="fas fa-tree text-2xl text-blue-400"></i>
                    </div>
                    <h3 class="text-xl font-bold mb-4">黑暗森林理论</h3>
                    <p class="text-slate-400 leading-relaxed">宇宙就是一座黑暗森林，每个文明都是带枪的猎人。他人即地狱，任何暴露自己存在的文明都将很快被消灭。</p>
                </div>
                <div class="glass-card p-8 rounded-2xl hover:border-trisolaris/50 transition group">
                    <div class="w-14 h-14 bg-red-500/20 rounded-lg flex items-center justify-center mb-6 group-hover:scale-110 transition">
                        <i class="fas fa-microchip text-2xl text-red-400"></i>
                    </div>
                    <h3 class="text-xl font-bold mb-4">智子 (Sophon)</h3>
                    <p class="text-slate-400 leading-relaxed">三体人将质子改造为智能计算机，锁死了人类的基础物理研究，并实现了对地球的实时全方位监控。</p>
                </div>
                <div class="glass-card p-8 rounded-2xl hover:border-forest/50 transition group">
                    <div class="w-14 h-14 bg-emerald-500/20 rounded-lg flex items-center justify-center mb-6 group-hover:scale-110 transition">
                        <i class="fas fa-layer-group text-2xl text-emerald-400"></i>
                    </div>
                    <h3 class="text-xl font-bold mb-4">维度打击</h3>
                    <p class="text-slate-400 leading-relaxed">高级文明使用“二向箔”等武器将目标空间降维，从三维坍缩至二维，是宇宙中最彻底的毁灭方式。</p>
                </div>
            </div>
        </section>

        <!-- Characters Section -->
        <section id="characters">
            <h2 class="text-3xl font-bold mb-12">关键见证者</h2>
            <div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6">
                <!-- Character Card 1 -->
                <div class="glass-card rounded-2xl overflow-hidden group">
                    <div class="h-48 overflow-hidden">
                        <img src="https://images.unsplash.com/photo-1534447677768-be436bb09401?auto=format&fit=crop&w=800&q=80" class="w-full h-full object-cover group-hover:scale-110 transition duration-500" alt="Ye Wenjie">
                    </div>
                    <div class="p-6">
                        <h4 class="text-xl font-bold mb-1">叶文洁</h4>
                        <p class="text-blue-400 text-sm mb-4">统帅 / 启蒙者</p>
                        <p class="text-slate-400 text-sm">红岸基地的天体物理学家，在绝望中向宇宙发出了第一声呼唤。</p>
                    </div>
                </div>
                <!-- Character Card 2 -->
                <div class="glass-card rounded-2xl overflow-hidden group">
                    <div class="h-48 overflow-hidden">
                        <img src="https://images.unsplash.com/photo-1506318137071-a8e063b4bcc0?auto=format&fit=crop&w=800&q=80" class="w-full h-full object-cover group-hover:scale-110 transition duration-500" alt="Luo Ji">
                    </div>
                    <div class="p-6">
                        <h4 class="text-xl font-bold mb-1">罗辑</h4>
                        <p class="text-blue-400 text-sm mb-4">面壁者 / 执剑人</p>
                        <p class="text-slate-400 text-sm">领悟了黑暗森林法则，以一人之力威慑三体文明，守护地球半个世纪。</p>
                    </div>
                </div>
                <!-- Character Card 3 -->
                <div class="glass-card rounded-2xl overflow-hidden group">
                    <div class="h-48 overflow-hidden">
                        <img src="https://images.unsplash.com/photo-1446776811953-b23d57bd21aa?auto=format&fit=crop&w=800&q=80" class="w-full h-full object-cover group-hover:scale-110 transition duration-500" alt="Cheng Xin">
                    </div>
                    <div class="p-6">
                        <h4 class="text-xl font-bold mb-1">程心</h4>
                        <p class="text-blue-400 text-sm mb-4">第二任执剑人</p>
                        <p class="text-slate-400 text-sm">代表了人类的母性与仁慈，在宇宙终极的抉择面前，她选择了爱。</p>
                    </div>
                </div>
                <!-- Character Card 4 -->
                <div class="glass-card rounded-2xl overflow-hidden group">
                    <div class="h-48 overflow-hidden">
                        <img src="https://images.unsplash.com/photo-1451187580459-43490279c0fa?auto=format&fit=crop&w=800&q=80" class="w-full h-full object-cover group-hover:scale-110 transition duration-500" alt="Zhang Beihai">
                    </div>
                    <div class="p-6">
                        <h4 class="text-xl font-bold mb-1">章北海</h4>
                        <p class="text-blue-400 text-sm mb-4">增援未来特遣军</p>
                        <p class="text-slate-400 text-sm">坚定的逃亡主义者，为人类文明在星际间保留了最后的火种。</p>
                    </div>
                </div>
            </div>
        </section>

        <!-- Interactive Quote Section -->
        <section class="relative py-20 rounded-3xl overflow-hidden">
            <div class="absolute inset-0 bg-gradient-to-r from-blue-900/40 to-purple-900/40 z-0"></div>
            <div class="relative z-10 text-center px-6">
                <i class="fas fa-quote-left text-4xl text-blue-500 mb-8"></i>
                <h2 id="dynamic-quote" class="text-2xl md:text-4xl font-light italic leading-relaxed max-w-4xl mx-auto mb-8">
                    失去人性，失去很多；失去兽性，失去一切。
                </h2>
                <button onclick="refreshQuote()" class="text-blue-400 hover:text-blue-300 transition flex items-center mx-auto space-x-2">
                    <i class="fas fa-sync-alt"></i> <span>换一句名言</span>
                </button>
            </div>
        </section>

    </main>

    <!-- Footer -->
    <footer class="border-t border-white/10 py-12 bg-space-950">
        <div class="max-w-7xl mx-auto px-6 flex flex-col md:flex-row justify-between items-center gap-8">
            <div class="text-center md:text-left">
                <div class="text-2xl font-bold mb-2">地球往事</div>
                <p class="text-slate-500 text-sm">致敬刘慈欣，致敬伟大的想象力。</p>
            </div>
            <div class="flex space-x-6 text-2xl text-slate-400">
                <a href="#" class="hover:text-white transition"><i class="fab fa-github"></i></a>
                <a href="#" class="hover:text-white transition"><i class="fab fa-twitter"></i></a>
                <a href="#" class="hover:text-white transition"><i class="fas fa-envelope"></i></a>
            </div>
            <div class="text-slate-500 text-sm">
                &copy; 2023 三体迷社区. 仅供学习交流使用.
            </div>
        </div>
    </footer>

    <!-- Application Logic -->
    <script>
        const books = {
            1: {
                title: "三体 I",
                subtitle: "地球往事",
                desc: "文化大革命期间，红岸基地的一名天体物理学家叶文洁向宇宙发出了寻找外星文明的信息。四光年外的三体文明收到了信息，并决定入侵地球。人类的命运从此改变。",
                tags: ["红岸基地", "三体游戏", "智子", "古筝行动"],
                img: "https://images.unsplash.com/photo-1614728894747-a83421e2b9c9?auto=format&fit=crop&w=800&q=80",
                color: "text-blue-400"
            },
            2: {
                title: "三体 II",
                subtitle: "黑暗森林",
                desc: "三体舰队向地球进发，智子封锁了人类科技。人类启动“面壁计划”，选出四位面壁者利用三体人思维透明的弱点进行反击。罗辑最终发现了宇宙的黑暗森林法则。",
                tags: ["面壁计划", "水滴", "黑暗森林", "摇篮系统"],
                img: "https://images.unsplash.com/photo-1446776811953-b23d57bd21aa?auto=format&fit=crop&w=800&q=80",
                color: "text-emerald-400"
            },
            3: {
                title: "三体 III",
                subtitle: "死神永生",
                desc: "威慑纪元结束，人类与三体文明的博弈进入最终阶段。程心作为执剑人面临终极抉择。宇宙的真相逐渐揭开：这是一场跨越维度的、永无止境的战争。",
                tags: ["阶梯计划", "掩体计划", "二向箔", "小宇宙"],
                img: "https://images.unsplash.com/photo-1451187580459-43490279c0fa?auto=format&fit=crop&w=800&q=80",
                color: "text-red-400"
            }
        };

        const quotes = [
            "失去人性，失去很多；失去兽性，失去一切。",
            "给时光以生命，给岁月以文明。",
            "弱小和无知不是生存的障碍，傲慢才是。",
            "你的无畏来源于无知。",
            "我消灭你，与你何干？",
            "这是人类的落日。",
            "前进！前进！！不择手段地前进！！！"
        ];

        function switchBook(id) {
            const book = books[id];
            const container = document.getElementById('book-content');
            
            // Update tabs
            document.querySelectorAll('.book-tab').forEach(btn => btn.classList.remove('active'));
            document.getElementById(`btn-${id}`).classList.add('active');

            // Animate content change
            container.style.opacity = 0;
            setTimeout(() => {
                container.innerHTML = `
                    <div class="space-y-6 animate-fade-in">
                        <h3 class="text-4xl font-bold ${book.color}">${book.title}</h3>
                        <h4 class="text-2xl text-slate-300">${book.subtitle}</h4>
                        <p class="text-slate-400 leading-relaxed text-lg">${book.desc}</p>
                        <div class="flex flex-wrap gap-2">
                            ${book.tags.map(tag => `<span class="px-3 py-1 bg-white/5 border border-white/10 rounded-full text-xs text-slate-300">${tag}</span>`).join('')}
                        </div>
                    </div>
                    <div class="relative group">
                        <div class="absolute -inset-4 bg-gradient-to-r from-blue-500 to-purple-600 rounded-2xl blur opacity-20 group-hover:opacity-40 transition"></div>
                        <img src="${book.img}" class="relative rounded-2xl shadow-2xl w-full h-80 object-cover" alt="${book.title}">
                    </div>
                `;
                container.style.opacity = 1;
            }, 200);
        }

        function refreshQuote() {
            const quoteEl = document.getElementById('dynamic-quote');
            const randomQuote = quotes[Math.floor(Math.random() * quotes.length)];
            quoteEl.style.opacity = 0;
            setTimeout(() => {
                quoteEl.innerText = randomQuote;
                quoteEl.style.opacity = 1;
            }, 300);
        }

        // Starfield Animation
        function initStars() {
            const starfield = document.getElementById('starfield');
            const count = 150;
            for (let i = 0; i < count; i++) {
                const star = document.createElement('div');
                const size = Math.random() * 2 + 'px';
                star.style.position = 'absolute';
                star.style.width = size;
                star.style.height = size;
                star.style.backgroundColor = 'white';
                star.style.top = Math.random() * 100 + '%';
                star.style.left = Math.random() * 100 + '%';
                star.style.opacity = Math.random();
                star.style.borderRadius = '50%';
                if (Math.random() > 0.8) {
                    star.classList.add('animate-pulse-slow');
                }
                starfield.appendChild(star);
            }
        }

        // Initialize
        window.onload = () => {
            switchBook(1);
            initStars();
            console.log('Three-Body View Loaded');
        };
    </script>
</body>
</html>3e:{"id":"c5e463ce-a951-4780-a3b1-8b6b8dd13633","exportName":"$undefined","title":"Explain the Three-Body Problem series","description":"Explain the Three-Body Problem series","category":"dynamic-view-webpage","tags":"$3f","likes":2,"favorites":0,"views":6316,"sourceType":"DynamicView","componentSource":"$40","isLiked":false,"isFavorited":false}
1d:["$1e","$22","$25","$28","$2b","$2e","$31","$35","$38","$3b","$3e"]
2:["$","$Lf",null,{"locale":"en","initialPage":"gallery","initialTemplates":[{"id":"62574c6d-8ac1-43ff-8ab8-d4a1a16f8fc4","exportName":"$undefined","title":"Linux Kernel Super Vulnerability: Copy Fail In-depth Analysis","description":"$10","category":"html-video","tags":[],"likes":0,"favorites":0,"views":213,"sourceType":"HTMLVideo","componentSource":"$11","isLiked":false,"isFavorited":false},{"id":"27b578da-9595-4751-9334-8710c8e8c7b7","exportName":"$undefined","title":"Demonstrating the basic syntax and principles of Python exception handling","description":"Demonstrating the basic syntax and principles of Python exception handling","category":"custom","tags":[],"likes":1,"favorites":0,"views":427,"sourceType":"SvgAnimation","componentSource":"$12","isLiked":false,"isFavorited":false},{"id":"5640993b-edd4-480b-89f0-36cd4325fb5e","exportName":"$undefined","title":"The Development History of Tencent: The Evolution of Connection","description":"Explaining the development history of Tencent","category":"html-video","tags":[],"likes":1,"favorites":0,"views":210,"sourceType":"HTMLVideo","componentSource":"$13","isLiked":false,"isFavorited":false},{"id":"0ecaed6b-2393-4bff-89fc-e62dee3a32c4","exportName":"$undefined","title":"Create a 3D text animation using three.js with a sharp, angular font","description":"Create a 3D text animation using three.js, displaying the letters 'SvgAnimate' in a sharp, angular font. The scene features a bright theme with glass-textured text. It rotates slowly in 3D automatically.","category":"text-svg-animation","tags":[],"likes":0,"favorites":0,"views":312,"sourceType":"SvgAnimation","componentSource":"$14","isLiked":false,"isFavorited":false},{"id":"6da4394f-610e-4e1f-92c7-d6ecff10f250","exportName":"$undefined","title":"Create the execution logic of a tracert command, presenting each step with animation","description":"Create the execution logic of a tracert command, presenting each step with animation","category":"flowchart-animation","tags":[],"likes":0,"favorites":0,"views":3955,"sourceType":"SvgAnimation","componentSource":"$15","isLiked":false,"isFavorited":false},{"id":"437e921a-803b-49f8-ac19-10593eef6e75","exportName":"$undefined","title":"Develop a simple billiard animation where users can serve via mouse and adjust the angle","description":"Develop a simple billiard animation where users can serve via mouse and adjust the angle and power","category":"svg-mini-game","tags":[],"likes":1,"favorites":0,"views":4699,"sourceType":"SvgAnimation","componentSource":"$16","isLiked":false,"isFavorited":false},{"id":"b5fa6a71-3a69-41e6-aad0-fb7d6a9b6530","exportName":"$undefined","title":"Create a high-quality isometric view based on SVG code","description":"$17","category":"product-demo-animation","tags":[],"likes":2,"favorites":0,"views":4400,"sourceType":"SvgAnimation","componentSource":"$18","isLiked":false,"isFavorited":false},{"id":"474864d6-3140-47ba-a0ce-87ee6ea1d384","exportName":"$undefined","title":"Add a border like a health bar to the characters 'Year of the Horse Takeoff'. It should light up after being attacked.","description":"Add a border like a health bar to the characters 'Year of the Horse Takeoff'. Add a red glow effect to enemy tanks that light up the text after being attacked to distinguish them from regular enemy tanks. Randomly add some golden tanks; after being attacked, power-up weapons will appear at random locations on the screen (avoiding overlap with buildings). When the player's tank picks them up, firepower and fire rate are tripled. Do not change anything else.","category":"svg-mini-game","tags":[],"likes":0,"favorites":0,"views":5415,"sourceType":"SvgAnimation","componentSource":"$19","isLiked":false,"isFavorited":false},{"id":"a534989e-ce9b-492e-bc4d-cebf4130eb20","exportName":"$undefined","title":"A person sets off from the Cape of Good Hope on Earth, passes through the African continent and the Asian continent, and crosses the Bering Sea","description":"A person sets off from the Cape of Good Hope on Earth, passes through the African continent and the Asian continent, enters the American continent through the Bering Strait, and finally arrives at the southernmost tip of South America from North America, presenting a full route map and marking some key locations.","category":"svg-map-animation","tags":[],"likes":0,"favorites":0,"views":5787,"sourceType":"SvgAnimation","componentSource":"$1a","isLiked":false,"isFavorited":false},{"id":"1ff45342-b007-4d5b-851d-75ccd72e7486","exportName":"$undefined","title":"The scene starts with a map of China, then the camera zooms in on Sichuan, with a golden light circling Sichuan Province.","description":"The scene starts with a map of China, then the camera zooms in on Sichuan, with a golden light circling the outline of Sichuan Province. Then, the following tourist attractions are marked in Sichuan in sequence: Jiuzhaigou, Mount Emei, Leshan Giant Buddha, Sanxingdui, Mount Gongga, and Daocheng Yading. The overall animation style should be cool. Be careful to adjust the positions of the names of each tourist attraction so they do not overlap.","category":"svg-map-animation","tags":[],"likes":1,"favorites":0,"views":5652,"sourceType":"SvgAnimation","componentSource":"$1b","isLiked":false,"isFavorited":false},{"id":"c5e463ce-a951-4780-a3b1-8b6b8dd13633","exportName":"$undefined","title":"Explain the Three-Body Problem series","description":"Explain the Three-Body Problem series","category":"dynamic-view-webpage","tags":[],"likes":2,"favorites":0,"views":6316,"sourceType":"DynamicView","componentSource":"$1c","isLiked":false,"isFavorited":false}],"initialFeaturedTemplates":"$1d","initialTotal":34,"initialFeaturedTotal":34,"initialCategoryFilter":"featured","initialPageNumber":1,"initialSearchTerm":"","initialPageSize":11}]
