Prechádzať zdrojové kódy

chore: 滔达福项目PPT全套生成工作产物(slides/images/svg/pptx/脚本)

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
asus 1 mesiac pred
rodič
commit
6baa638362
100 zmenil súbory, kde vykonal 16824 pridanie a 0 odobranie
  1. BIN
      ppt-output/backup_presentation_20260806_0710.pptx
  2. 6547 0
      ppt-output/dom-to-svg.bundle.js
  3. BIN
      ppt-output/images/slide_01_0.png
  4. BIN
      ppt-output/images/slide_03_0.png
  5. BIN
      ppt-output/images/slide_04_0.png
  6. BIN
      ppt-output/images/slide_05_0.png
  7. BIN
      ppt-output/images/slide_06_2.png
  8. BIN
      ppt-output/images/slide_08_0.png
  9. BIN
      ppt-output/images/slide_10_1.png
  10. BIN
      ppt-output/images/slide_11_0.png
  11. BIN
      ppt-output/images/slide_13_2.png
  12. BIN
      ppt-output/images/slide_14_2.png
  13. BIN
      ppt-output/images/slide_15_0.png
  14. BIN
      ppt-output/images/slide_16_0.png
  15. BIN
      ppt-output/images/slide_17_1.png
  16. BIN
      ppt-output/images/slide_18_1.png
  17. BIN
      ppt-output/images/slide_19_0.png
  18. BIN
      ppt-output/images/slide_20_2.png
  19. BIN
      ppt-output/images/slide_21_0.png
  20. BIN
      ppt-output/images/slide_22_1.png
  21. BIN
      ppt-output/images/slide_23_0.png
  22. BIN
      ppt-output/images/slide_24_1.png
  23. BIN
      ppt-output/images/slide_25_0.png
  24. BIN
      ppt-output/images/slide_26_1_robot.png
  25. BIN
      ppt-output/images/slide_26_2_glasses.png
  26. BIN
      ppt-output/images/slide_26_3_wristband.png
  27. BIN
      ppt-output/images/slide_26_4_earbuds.png
  28. BIN
      ppt-output/images/slide_26_5_cook.png
  29. BIN
      ppt-output/images/slide_28_2.png
  30. 360 0
      ppt-output/outline.json
  31. 3491 0
      ppt-output/planning.json
  32. 84 0
      ppt-output/planning/planning1.json
  33. 156 0
      ppt-output/planning/planning10.json
  34. 73 0
      ppt-output/planning/planning11.json
  35. 190 0
      ppt-output/planning/planning12.json
  36. 164 0
      ppt-output/planning/planning13.json
  37. 167 0
      ppt-output/planning/planning14.json
  38. 73 0
      ppt-output/planning/planning15.json
  39. 141 0
      ppt-output/planning/planning16.json
  40. 157 0
      ppt-output/planning/planning17.json
  41. 124 0
      ppt-output/planning/planning18.json
  42. 73 0
      ppt-output/planning/planning19.json
  43. 77 0
      ppt-output/planning/planning2.json
  44. 148 0
      ppt-output/planning/planning20.json
  45. 156 0
      ppt-output/planning/planning21.json
  46. 181 0
      ppt-output/planning/planning22.json
  47. 73 0
      ppt-output/planning/planning23.json
  48. 198 0
      ppt-output/planning/planning24.json
  49. 163 0
      ppt-output/planning/planning25.json
  50. 164 0
      ppt-output/planning/planning26.json
  51. 74 0
      ppt-output/planning/planning3.json
  52. 169 0
      ppt-output/planning/planning4.json
  53. 173 0
      ppt-output/planning/planning5.json
  54. 155 0
      ppt-output/planning/planning6.json
  55. 159 0
      ppt-output/planning/planning7.json
  56. 73 0
      ppt-output/planning/planning8.json
  57. 119 0
      ppt-output/planning/planning9.json
  58. BIN
      ppt-output/png/slide-1.png
  59. BIN
      ppt-output/png/slide-10.png
  60. BIN
      ppt-output/png/slide-11.png
  61. BIN
      ppt-output/png/slide-12.png
  62. BIN
      ppt-output/png/slide-13.png
  63. BIN
      ppt-output/png/slide-14.png
  64. BIN
      ppt-output/png/slide-15.png
  65. BIN
      ppt-output/png/slide-16.png
  66. BIN
      ppt-output/png/slide-17.png
  67. BIN
      ppt-output/png/slide-18.png
  68. BIN
      ppt-output/png/slide-19.png
  69. BIN
      ppt-output/png/slide-2.png
  70. BIN
      ppt-output/png/slide-20.png
  71. BIN
      ppt-output/png/slide-21.png
  72. BIN
      ppt-output/png/slide-22.png
  73. BIN
      ppt-output/png/slide-23.png
  74. BIN
      ppt-output/png/slide-24.png
  75. BIN
      ppt-output/png/slide-25.png
  76. BIN
      ppt-output/png/slide-26.png
  77. BIN
      ppt-output/png/slide-3.png
  78. BIN
      ppt-output/png/slide-4.png
  79. BIN
      ppt-output/png/slide-5.png
  80. BIN
      ppt-output/png/slide-6.png
  81. BIN
      ppt-output/png/slide-7.png
  82. BIN
      ppt-output/png/slide-8.png
  83. BIN
      ppt-output/png/slide-9.png
  84. BIN
      ppt-output/presentation.pptx
  85. BIN
      ppt-output/presentation_new.pptx
  86. 240 0
      ppt-output/preview.html
  87. 112 0
      ppt-output/runtime/gen_images.py
  88. 626 0
      ppt-output/runtime/gen_planning.py
  89. 190 0
      ppt-output/runtime/html-spec.md
  90. 23 0
      ppt-output/runtime/image_list.txt
  91. 29 0
      ppt-output/runtime/interview-qa.txt
  92. 41 0
      ppt-output/runtime/prompt-interview.md
  93. 20 0
      ppt-output/runtime/requirements-interview.txt
  94. 18 0
      ppt-output/runtime/split_planning.py
  95. 17 0
      ppt-output/runtime/view_planning.py
  96. 216 0
      ppt-output/slides/slide-1.html
  97. 318 0
      ppt-output/slides/slide-10.html
  98. 202 0
      ppt-output/slides/slide-11.html
  99. 366 0
      ppt-output/slides/slide-12.html
  100. 524 0
      ppt-output/slides/slide-13.html

BIN
ppt-output/backup_presentation_20260806_0710.pptx


+ 6547 - 0
ppt-output/dom-to-svg.bundle.js

@@ -0,0 +1,6547 @@
+(() => {
+  var __create = Object.create;
+  var __defProp = Object.defineProperty;
+  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
+  var __getOwnPropNames = Object.getOwnPropertyNames;
+  var __getProtoOf = Object.getPrototypeOf;
+  var __hasOwnProp = Object.prototype.hasOwnProperty;
+  var __commonJS = (cb, mod) => function __require() {
+    try {
+      return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
+    } catch (e) {
+      throw mod = 0, e;
+    }
+  };
+  var __copyProps = (to, from, except, desc) => {
+    if (from && typeof from === "object" || typeof from === "function") {
+      for (let key of __getOwnPropNames(from))
+        if (!__hasOwnProp.call(to, key) && key !== except)
+          __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
+    }
+    return to;
+  };
+  var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
+    // If the importer is in node compatibility mode or this is not an ESM
+    // file that has been converted to a CommonJS file using a Babel-
+    // compatible transform (i.e. "__esModule" has not been set), then set
+    // "default" to the CommonJS "module.exports" for node compatibility.
+    isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
+    mod
+  ));
+
+  // node_modules/picocolors/picocolors.browser.js
+  var require_picocolors_browser = __commonJS({
+    "node_modules/picocolors/picocolors.browser.js"(exports, module) {
+      var x = String;
+      var create = function() {
+        return { isColorSupported: false, reset: x, bold: x, dim: x, italic: x, underline: x, inverse: x, hidden: x, strikethrough: x, black: x, red: x, green: x, yellow: x, blue: x, magenta: x, cyan: x, white: x, gray: x, bgBlack: x, bgRed: x, bgGreen: x, bgYellow: x, bgBlue: x, bgMagenta: x, bgCyan: x, bgWhite: x, blackBright: x, redBright: x, greenBright: x, yellowBright: x, blueBright: x, magentaBright: x, cyanBright: x, whiteBright: x, bgBlackBright: x, bgRedBright: x, bgGreenBright: x, bgYellowBright: x, bgBlueBright: x, bgMagentaBright: x, bgCyanBright: x, bgWhiteBright: x };
+      };
+      module.exports = create();
+      module.exports.createColors = create;
+    }
+  });
+
+  // (disabled):node_modules/postcss/lib/terminal-highlight
+  var require_terminal_highlight = __commonJS({
+    "(disabled):node_modules/postcss/lib/terminal-highlight"() {
+    }
+  });
+
+  // node_modules/postcss/lib/css-syntax-error.js
+  var require_css_syntax_error = __commonJS({
+    "node_modules/postcss/lib/css-syntax-error.js"(exports, module) {
+      "use strict";
+      var pico = require_picocolors_browser();
+      var terminalHighlight = require_terminal_highlight();
+      var CssSyntaxError2 = class _CssSyntaxError extends Error {
+        constructor(message, line, column, source, file, plugin2) {
+          super(message);
+          this.name = "CssSyntaxError";
+          this.reason = message;
+          if (file) {
+            this.file = file;
+          }
+          if (source) {
+            this.source = source;
+          }
+          if (plugin2) {
+            this.plugin = plugin2;
+          }
+          if (typeof line !== "undefined" && typeof column !== "undefined") {
+            if (typeof line === "number") {
+              this.line = line;
+              this.column = column;
+            } else {
+              this.line = line.line;
+              this.column = line.column;
+              this.endLine = column.line;
+              this.endColumn = column.column;
+            }
+          }
+          this.setMessage();
+          if (Error.captureStackTrace) {
+            Error.captureStackTrace(this, _CssSyntaxError);
+          }
+        }
+        setMessage() {
+          this.message = this.plugin ? this.plugin + ": " : "";
+          this.message += this.file ? this.file : "<css input>";
+          if (typeof this.line !== "undefined") {
+            this.message += ":" + this.line + ":" + this.column;
+          }
+          this.message += ": " + this.reason;
+        }
+        showSourceCode(color) {
+          if (!this.source) return "";
+          let css = this.source;
+          if (color == null) color = pico.isColorSupported;
+          let aside = (text) => text;
+          let mark = (text) => text;
+          let highlight = (text) => text;
+          if (color) {
+            let { bold, gray, red } = pico.createColors(true);
+            mark = (text) => bold(red(text));
+            aside = (text) => gray(text);
+            if (terminalHighlight) {
+              highlight = (text) => terminalHighlight(text);
+            }
+          }
+          let lines = css.split(/\r?\n/);
+          let start = Math.max(this.line - 3, 0);
+          let end = Math.min(this.line + 2, lines.length);
+          let maxWidth = String(end).length;
+          return lines.slice(start, end).map((line, index) => {
+            let number = start + 1 + index;
+            let gutter = " " + (" " + number).slice(-maxWidth) + " | ";
+            if (number === this.line) {
+              if (line.length > 160) {
+                let padding = 20;
+                let subLineStart = Math.max(0, this.column - padding);
+                let subLineEnd = Math.max(
+                  this.column + padding,
+                  this.endColumn + padding
+                );
+                let subLine = line.slice(subLineStart, subLineEnd);
+                let spacing2 = aside(gutter.replace(/\d/g, " ")) + line.slice(0, Math.min(this.column - 1, padding - 1)).replace(/[^\t]/g, " ");
+                return mark(">") + aside(gutter) + highlight(subLine) + "\n " + spacing2 + mark("^");
+              }
+              let spacing = aside(gutter.replace(/\d/g, " ")) + line.slice(0, this.column - 1).replace(/[^\t]/g, " ");
+              return mark(">") + aside(gutter) + highlight(line) + "\n " + spacing + mark("^");
+            }
+            return " " + aside(gutter) + highlight(line);
+          }).join("\n");
+        }
+        toString() {
+          let code = this.showSourceCode();
+          if (code) {
+            code = "\n\n" + code + "\n";
+          }
+          return this.name + ": " + this.message + code;
+        }
+      };
+      module.exports = CssSyntaxError2;
+      CssSyntaxError2.default = CssSyntaxError2;
+    }
+  });
+
+  // node_modules/postcss/lib/stringifier.js
+  var require_stringifier = __commonJS({
+    "node_modules/postcss/lib/stringifier.js"(exports, module) {
+      "use strict";
+      var STYLE_TAG = /(<)(\/?style\b)/gi;
+      var COMMENT_OPEN = /(<)(!--)/g;
+      var AT_NAME_END = /[\t\n\f\r "#'()/;[\\\]{}]/;
+      function escapeHTMLInCSS(str) {
+        if (typeof str !== "string") return str;
+        if (!str.includes("<")) return str;
+        return str.replace(STYLE_TAG, "\\3c $2").replace(COMMENT_OPEN, "\\3c $2");
+      }
+      var DEFAULT_RAW = {
+        after: "\n",
+        beforeClose: "\n",
+        beforeComment: "\n",
+        beforeDecl: "\n",
+        beforeOpen: " ",
+        beforeRule: "\n",
+        colon: ": ",
+        commentLeft: " ",
+        commentRight: " ",
+        emptyBody: "",
+        indent: "    ",
+        semicolon: false
+      };
+      function capitalize(str) {
+        return str[0].toUpperCase() + str.slice(1);
+      }
+      function atruleStart(str, node) {
+        let name = "@" + node.name;
+        let params = node.params ? str.rawValue(node, "params") : "";
+        let afterName = node.raws.afterName;
+        if (typeof afterName === "undefined") {
+          afterName = params ? " " : "";
+        } else if (afterName === "" && params && !AT_NAME_END.test(params[0])) {
+          afterName = " ";
+        }
+        return name + afterName + params;
+      }
+      function pushBody(str, stack, node) {
+        let nodes = node.nodes;
+        let last = nodes.length - 1;
+        while (last > 0) {
+          if (nodes[last].type !== "comment") break;
+          last -= 1;
+        }
+        let semicolon = str.raw(node, "semicolon");
+        let isDocument = node.type === "document";
+        for (let i = nodes.length - 1; i >= 0; i--) {
+          let child = nodes[i];
+          let childSemicolon = last !== i || semicolon;
+          if (!childSemicolon && i < nodes.length - 1 && (child.type === "atrule" && !child.nodes || child.type === "decl" && child.prop.startsWith("--"))) {
+            childSemicolon = true;
+          }
+          stack.push({
+            document: isDocument,
+            node: child,
+            semicolon: childSemicolon
+          });
+        }
+      }
+      function pushBlock(str, stack, node, start) {
+        let between = str.raw(node, "between", "beforeOpen");
+        str.builder(escapeHTMLInCSS(start + between) + "{", node, "start");
+        let hasNodes = node.nodes && node.nodes.length;
+        let close = () => {
+          let after = hasNodes ? str.raw(node, "after") : str.raw(node, "after", "emptyBody");
+          if (after) str.builder(escapeHTMLInCSS(after));
+          str.builder("}", node, "end");
+          if (node.type === "rule" && node.raws.ownSemicolon) {
+            str.builder(escapeHTMLInCSS(node.raws.ownSemicolon), node, "end");
+          }
+        };
+        if (hasNodes) {
+          stack.push(close);
+          pushBody(str, stack, node);
+        } else {
+          close();
+        }
+      }
+      var Stringifier = class _Stringifier {
+        constructor(builder) {
+          this.builder = builder;
+        }
+        atrule(node, semicolon) {
+          let start = atruleStart(this, node);
+          if (node.nodes) {
+            this.block(node, start);
+          } else {
+            let end = (node.raws.between || "") + (semicolon ? ";" : "");
+            this.builder(escapeHTMLInCSS(start + end), node);
+          }
+        }
+        beforeAfter(node, detect) {
+          let value;
+          if (node.type === "decl") {
+            value = this.raw(node, null, "beforeDecl");
+          } else if (node.type === "comment") {
+            value = this.raw(node, null, "beforeComment");
+          } else if (detect === "before") {
+            value = this.raw(node, null, "beforeRule");
+          } else {
+            value = this.raw(node, null, "beforeClose");
+          }
+          let buf = node.parent;
+          let depth = 0;
+          while (buf && buf.type !== "root") {
+            depth += 1;
+            buf = buf.parent;
+          }
+          if (value.includes("\n")) {
+            let indent = this.raw(node, null, "indent");
+            if (indent.length) {
+              for (let step = 0; step < depth; step++) value += indent;
+            }
+          }
+          return value;
+        }
+        block(node, start) {
+          let between = this.raw(node, "between", "beforeOpen");
+          this.builder(escapeHTMLInCSS(start + between) + "{", node, "start");
+          let after;
+          if (node.nodes && node.nodes.length) {
+            this.body(node);
+            after = this.raw(node, "after");
+          } else {
+            after = this.raw(node, "after", "emptyBody");
+          }
+          if (after) this.builder(escapeHTMLInCSS(after));
+          this.builder("}", node, "end");
+        }
+        body(node) {
+          let proto = _Stringifier.prototype;
+          let expandable = ["atrule", "block", "body", "rule", "stringify"].every(
+            (method) => this[method] === proto[method]
+          );
+          let stack = [];
+          pushBody(this, stack, node);
+          while (stack.length > 0) {
+            let entry = stack.pop();
+            if (typeof entry === "function") {
+              entry();
+              continue;
+            }
+            let child = entry.node;
+            let before = this.raw(child, "before");
+            if (before) {
+              this.builder(entry.document ? before : escapeHTMLInCSS(before));
+            }
+            if (expandable && child.type === "rule") {
+              pushBlock(this, stack, child, this.rawValue(child, "selector"));
+            } else if (expandable && child.type === "atrule" && child.nodes) {
+              pushBlock(this, stack, child, atruleStart(this, child));
+            } else {
+              this.stringify(child, entry.semicolon);
+            }
+          }
+        }
+        comment(node) {
+          let left = this.raw(node, "left", "commentLeft");
+          let right = this.raw(node, "right", "commentRight");
+          this.builder(escapeHTMLInCSS("/*" + left + node.text + right + "*/"), node);
+        }
+        decl(node, semicolon) {
+          let raws = node.raws;
+          let between = this.raw(node, "between", "colon");
+          let string = node.prop + between + this.rawValue(node, "value");
+          if (node.important) {
+            string += raws.important || " !important";
+          }
+          if (semicolon) string += ";";
+          this.builder(escapeHTMLInCSS(string), node);
+        }
+        document(node) {
+          this.body(node);
+        }
+        raw(node, own, detect) {
+          let value;
+          if (!detect) detect = own;
+          if (own) {
+            value = node.raws[own];
+            if (typeof value !== "undefined") return value;
+          }
+          let parent = node.parent;
+          if (detect === "before") {
+            if (!parent || parent.type === "root" && parent.first === node) {
+              return "";
+            }
+            if (parent && parent.type === "document") {
+              return "";
+            }
+          }
+          if (!parent) return DEFAULT_RAW[detect];
+          let root2 = node.root();
+          let cache = root2.rawCache || (root2.rawCache = {});
+          if (typeof cache[detect] !== "undefined") {
+            return cache[detect];
+          }
+          if (detect === "before" || detect === "after") {
+            return this.beforeAfter(node, detect);
+          } else {
+            let method = "raw" + capitalize(detect);
+            if (this[method]) {
+              value = this[method](root2, node);
+            } else {
+              root2.walk((i) => {
+                value = i.raws[own];
+                if (typeof value !== "undefined") return false;
+              });
+            }
+          }
+          if (typeof value === "undefined") value = DEFAULT_RAW[detect];
+          cache[detect] = value;
+          return value;
+        }
+        rawBeforeClose(root2) {
+          let value;
+          root2.walk((i) => {
+            if (i.nodes && i.nodes.length > 0) {
+              if (typeof i.raws.after !== "undefined") {
+                value = i.raws.after;
+                if (value.includes("\n")) {
+                  value = value.replace(/[^\n]+$/, "");
+                }
+                return false;
+              }
+            }
+          });
+          if (value) value = value.replace(/\S/g, "");
+          return value;
+        }
+        rawBeforeComment(root2, node) {
+          let value;
+          root2.walkComments((i) => {
+            if (typeof i.raws.before !== "undefined") {
+              value = i.raws.before;
+              if (value.includes("\n")) {
+                value = value.replace(/[^\n]+$/, "");
+              }
+              return false;
+            }
+          });
+          if (typeof value === "undefined") {
+            value = this.raw(node, null, "beforeDecl");
+          } else if (value) {
+            value = value.replace(/\S/g, "");
+          }
+          return value;
+        }
+        rawBeforeDecl(root2, node) {
+          let value;
+          root2.walkDecls((i) => {
+            if (typeof i.raws.before !== "undefined") {
+              value = i.raws.before;
+              if (value.includes("\n")) {
+                value = value.replace(/[^\n]+$/, "");
+              }
+              return false;
+            }
+          });
+          if (typeof value === "undefined") {
+            value = this.raw(node, null, "beforeRule");
+          } else if (value) {
+            value = value.replace(/\S/g, "");
+          }
+          return value;
+        }
+        rawBeforeOpen(root2) {
+          let value;
+          root2.walk((i) => {
+            if (i.type !== "decl") {
+              value = i.raws.between;
+              if (typeof value !== "undefined") return false;
+            }
+          });
+          return value;
+        }
+        rawBeforeRule(root2) {
+          let value;
+          root2.walk((i) => {
+            if (i.nodes && (i.parent !== root2 || root2.first !== i)) {
+              if (typeof i.raws.before !== "undefined") {
+                value = i.raws.before;
+                if (value.includes("\n")) {
+                  value = value.replace(/[^\n]+$/, "");
+                }
+                return false;
+              }
+            }
+          });
+          if (value) value = value.replace(/\S/g, "");
+          return value;
+        }
+        rawColon(root2) {
+          let value;
+          root2.walkDecls((i) => {
+            if (typeof i.raws.between !== "undefined") {
+              value = i.raws.between.replace(/[^\s:]/g, "");
+              return false;
+            }
+          });
+          return value;
+        }
+        rawEmptyBody(root2) {
+          let value;
+          root2.walk((i) => {
+            if (i.nodes && i.nodes.length === 0) {
+              value = i.raws.after;
+              if (typeof value !== "undefined") return false;
+            }
+          });
+          return value;
+        }
+        rawIndent(root2) {
+          if (root2.raws.indent) return root2.raws.indent;
+          let value;
+          root2.walk((i) => {
+            let p = i.parent;
+            if (p && p !== root2 && p.parent && p.parent === root2) {
+              if (typeof i.raws.before !== "undefined") {
+                let parts = i.raws.before.split("\n");
+                value = parts[parts.length - 1];
+                value = value.replace(/\S/g, "");
+                return false;
+              }
+            }
+          });
+          return value;
+        }
+        rawSemicolon(root2) {
+          let value;
+          root2.walk((i) => {
+            if (i.nodes && i.nodes.length && i.last.type === "decl") {
+              value = i.raws.semicolon;
+              if (typeof value !== "undefined") return false;
+            }
+          });
+          return value;
+        }
+        rawValue(node, prop) {
+          let value = node[prop];
+          let raw = node.raws[prop];
+          if (raw && raw.value === value) {
+            return raw.raw;
+          }
+          return value;
+        }
+        root(node) {
+          if (node.source && node.source.input.hasBOM) {
+            this.builder("\uFEFF", node, "start");
+          }
+          this.body(node);
+          if (node.raws.after) {
+            let after = node.raws.after;
+            let isDocument = node.parent && node.parent.type === "document";
+            this.builder(isDocument ? after : escapeHTMLInCSS(after));
+          }
+        }
+        rule(node) {
+          this.block(node, this.rawValue(node, "selector"));
+          if (node.raws.ownSemicolon) {
+            this.builder(escapeHTMLInCSS(node.raws.ownSemicolon), node, "end");
+          }
+        }
+        stringify(node, semicolon) {
+          if (!this[node.type]) {
+            throw new Error(
+              "Unknown AST node type " + node.type + ". Maybe you need to change PostCSS stringifier."
+            );
+          }
+          this[node.type](node, semicolon);
+        }
+      };
+      module.exports = Stringifier;
+      Stringifier.default = Stringifier;
+    }
+  });
+
+  // node_modules/postcss/lib/stringify.js
+  var require_stringify = __commonJS({
+    "node_modules/postcss/lib/stringify.js"(exports, module) {
+      "use strict";
+      var Stringifier = require_stringifier();
+      function stringify3(node, builder) {
+        let str = new Stringifier(builder);
+        str.stringify(node);
+      }
+      module.exports = stringify3;
+      stringify3.default = stringify3;
+    }
+  });
+
+  // node_modules/postcss/lib/symbols.js
+  var require_symbols = __commonJS({
+    "node_modules/postcss/lib/symbols.js"(exports, module) {
+      "use strict";
+      module.exports.isClean = /* @__PURE__ */ Symbol("isClean");
+      module.exports.my = /* @__PURE__ */ Symbol("my");
+    }
+  });
+
+  // node_modules/postcss/lib/node.js
+  var require_node = __commonJS({
+    "node_modules/postcss/lib/node.js"(exports, module) {
+      "use strict";
+      var CssSyntaxError2 = require_css_syntax_error();
+      var Stringifier = require_stringifier();
+      var stringify3 = require_stringify();
+      var { isClean, my } = require_symbols();
+      function cloneNode(obj, parent) {
+        let cloned = new obj.constructor();
+        let stack = [[obj, cloned, parent]];
+        while (stack.length > 0) {
+          let [source, target, targetParent] = stack.pop();
+          for (let i in source) {
+            if (!Object.prototype.hasOwnProperty.call(source, i)) {
+              continue;
+            }
+            if (i === "proxyCache") continue;
+            let value = source[i];
+            let type = typeof value;
+            if (i === "parent" && type === "object") {
+              if (targetParent) target[i] = targetParent;
+            } else if (i === "source") {
+              target[i] = value;
+            } else if (Array.isArray(value)) {
+              let children = [];
+              target[i] = children;
+              for (let j of value) {
+                let childClone = new j.constructor();
+                children.push(childClone);
+                stack.push([j, childClone, target]);
+              }
+            } else {
+              if (type === "object" && value !== null) {
+                let valueClone = new value.constructor();
+                stack.push([value, valueClone, void 0]);
+                value = valueClone;
+              }
+              target[i] = value;
+            }
+          }
+        }
+        return cloned;
+      }
+      function sourceOffset(inputCSS, position) {
+        if (position && typeof position.offset !== "undefined") {
+          return position.offset;
+        }
+        let column = 1;
+        let line = 1;
+        let offset = 0;
+        for (let i = 0; i < inputCSS.length; i++) {
+          if (line === position.line && column === position.column) {
+            offset = i;
+            break;
+          }
+          if (inputCSS[i] === "\n") {
+            column = 1;
+            line += 1;
+          } else {
+            column += 1;
+          }
+        }
+        return offset;
+      }
+      var Node3 = class _Node {
+        get proxyOf() {
+          return this;
+        }
+        constructor(defaults2 = {}) {
+          this.raws = {};
+          this[isClean] = false;
+          this[my] = true;
+          for (let name of Object.keys(defaults2)) {
+            if (name === "__proto__") continue;
+            if (name === "nodes") {
+              this.nodes = [];
+              for (let node of defaults2[name]) {
+                if (typeof node.clone === "function" && node.parent) {
+                  this.append(node.clone());
+                } else {
+                  this.append(node);
+                }
+              }
+            } else {
+              this[name] = defaults2[name];
+            }
+          }
+        }
+        addToError(error) {
+          error.postcssNode = this;
+          if (error.stack && this.source && /\n\s{4}at /.test(error.stack)) {
+            let s = this.source;
+            error.stack = error.stack.replace(
+              /\n\s{4}at /,
+              `$&${s.input.from}:${s.start.line}:${s.start.column}$&`
+            );
+          }
+          return error;
+        }
+        after(add) {
+          this.parent.insertAfter(this, add);
+          return this;
+        }
+        assign(overrides = {}) {
+          for (let name in overrides) {
+            this[name] = overrides[name];
+          }
+          return this;
+        }
+        before(add) {
+          this.parent.insertBefore(this, add);
+          return this;
+        }
+        cleanRaws(keepBetween) {
+          delete this.raws.before;
+          delete this.raws.after;
+          if (!keepBetween) delete this.raws.between;
+        }
+        clone(overrides = {}) {
+          let cloned = cloneNode(this);
+          for (let name in overrides) {
+            cloned[name] = overrides[name];
+          }
+          return cloned;
+        }
+        cloneAfter(overrides = {}) {
+          let cloned = this.clone(overrides);
+          this.parent.insertAfter(this, cloned);
+          return cloned;
+        }
+        cloneBefore(overrides = {}) {
+          let cloned = this.clone(overrides);
+          this.parent.insertBefore(this, cloned);
+          return cloned;
+        }
+        error(message, opts = {}) {
+          if (this.source) {
+            let { end, start } = this.rangeBy(opts);
+            return this.source.input.error(
+              message,
+              { column: start.column, line: start.line },
+              { column: end.column, line: end.line },
+              opts
+            );
+          }
+          return new CssSyntaxError2(message);
+        }
+        getProxyProcessor() {
+          return {
+            get(node, prop) {
+              if (prop === "proxyOf") {
+                return node;
+              } else if (prop === "root") {
+                return () => node.root().toProxy();
+              } else {
+                return node[prop];
+              }
+            },
+            set(node, prop, value) {
+              if (node[prop] === value) return true;
+              node[prop] = value;
+              if (prop === "prop" || prop === "value" || prop === "name" || prop === "params" || prop === "important" || /* c8 ignore next */
+              prop === "text") {
+                node.markDirty();
+              }
+              return true;
+            }
+          };
+        }
+        /* c8 ignore next 3 */
+        markClean() {
+          this[isClean] = true;
+        }
+        markDirty() {
+          if (this[isClean]) {
+            this[isClean] = false;
+            let next = this;
+            while (next = next.parent) {
+              next[isClean] = false;
+            }
+          }
+        }
+        next() {
+          if (!this.parent) return void 0;
+          let index = this.parent.index(this);
+          return this.parent.nodes[index + 1];
+        }
+        positionBy(opts = {}) {
+          let inputString = "document" in this.source.input ? this.source.input.document : this.source.input.css;
+          let pos = {
+            column: this.source.start.column,
+            line: this.source.start.line,
+            offset: sourceOffset(inputString, this.source.start)
+          };
+          if (opts.index) {
+            pos = this.positionInside(opts.index);
+          } else if (opts.word) {
+            let stringRepresentation = inputString.slice(
+              sourceOffset(inputString, this.source.start),
+              sourceOffset(inputString, this.source.end)
+            );
+            let index = stringRepresentation.indexOf(opts.word);
+            if (index !== -1) pos = this.positionInside(index);
+          }
+          return pos;
+        }
+        positionInside(index) {
+          let column = this.source.start.column;
+          let line = this.source.start.line;
+          let inputString = "document" in this.source.input ? this.source.input.document : this.source.input.css;
+          let offset = sourceOffset(inputString, this.source.start);
+          let end = offset + index;
+          for (let i = offset; i < end; i++) {
+            if (inputString[i] === "\n") {
+              column = 1;
+              line += 1;
+            } else {
+              column += 1;
+            }
+          }
+          return { column, line, offset: end };
+        }
+        prev() {
+          if (!this.parent) return void 0;
+          let index = this.parent.index(this);
+          return this.parent.nodes[index - 1];
+        }
+        rangeBy(opts = {}) {
+          let inputString = "document" in this.source.input ? this.source.input.document : this.source.input.css;
+          let start = {
+            column: this.source.start.column,
+            line: this.source.start.line,
+            offset: sourceOffset(inputString, this.source.start)
+          };
+          let end = this.source.end ? {
+            column: this.source.end.column + 1,
+            line: this.source.end.line,
+            offset: typeof this.source.end.offset === "number" ? (
+              // `source.end.offset` is exclusive, so we don't need to add 1
+              this.source.end.offset
+            ) : (
+              // Since line/column in this.source.end is inclusive,
+              // the `sourceOffset(... , this.source.end)` returns an inclusive offset.
+              // So, we add 1 to convert it to exclusive.
+              sourceOffset(inputString, this.source.end) + 1
+            )
+          } : {
+            column: start.column + 1,
+            line: start.line,
+            offset: start.offset + 1
+          };
+          if (opts.word) {
+            let stringRepresentation = inputString.slice(
+              sourceOffset(inputString, this.source.start),
+              sourceOffset(inputString, this.source.end)
+            );
+            let index = stringRepresentation.indexOf(opts.word);
+            if (index !== -1) {
+              start = this.positionInside(index);
+              end = this.positionInside(index + opts.word.length);
+            }
+          } else {
+            if (opts.start) {
+              start = {
+                column: opts.start.column,
+                line: opts.start.line,
+                offset: sourceOffset(inputString, opts.start)
+              };
+            } else if (typeof opts.index === "number") {
+              start = this.positionInside(opts.index);
+            }
+            if (opts.end) {
+              end = {
+                column: opts.end.column,
+                line: opts.end.line,
+                offset: sourceOffset(inputString, opts.end)
+              };
+            } else if (typeof opts.endIndex === "number") {
+              end = this.positionInside(opts.endIndex);
+            } else if (typeof opts.index === "number") {
+              end = this.positionInside(opts.index + 1);
+            }
+          }
+          if (end.line < start.line || end.line === start.line && end.column <= start.column) {
+            end = {
+              column: start.column + 1,
+              line: start.line,
+              offset: start.offset + 1
+            };
+          }
+          return { end, start };
+        }
+        raw(prop, defaultType) {
+          let str = new Stringifier();
+          return str.raw(this, prop, defaultType);
+        }
+        remove() {
+          if (this.parent) {
+            this.parent.removeChild(this);
+          }
+          this.parent = void 0;
+          return this;
+        }
+        replaceWith(...nodes) {
+          if (this.parent) {
+            let bookmark = this;
+            let foundSelf = false;
+            for (let node of nodes) {
+              if (node === this) {
+                foundSelf = true;
+              } else if (foundSelf) {
+                this.parent.insertAfter(bookmark, node);
+                bookmark = node;
+              } else {
+                this.parent.insertBefore(bookmark, node);
+              }
+            }
+            if (!foundSelf) {
+              this.remove();
+            }
+          }
+          return this;
+        }
+        root() {
+          let result = this;
+          while (result.parent && result.parent.type !== "document") {
+            result = result.parent;
+          }
+          return result;
+        }
+        toJSON(_, inputs) {
+          let emitInputs = inputs == null;
+          inputs = inputs || /* @__PURE__ */ new Map();
+          let holderOfRoot = [];
+          let queue = [[this, holderOfRoot, 0]];
+          for (let step = 0; step < queue.length; step++) {
+            let [node, holder, key] = queue[step];
+            let fixed2 = {};
+            holder[key] = fixed2;
+            for (let name in node) {
+              if (!Object.prototype.hasOwnProperty.call(node, name)) {
+                continue;
+              }
+              if (name === "parent" || name === "proxyCache") continue;
+              let value = node[name];
+              if (Array.isArray(value)) {
+                let fixedArray = [];
+                fixed2[name] = fixedArray;
+                for (let i = 0; i < value.length; i++) {
+                  let item = value[i];
+                  if (typeof item === "object" && item.toJSON) {
+                    if (item.toJSON === _Node.prototype.toJSON) {
+                      queue.push([item, fixedArray, i]);
+                    } else {
+                      fixedArray[i] = item.toJSON(null, inputs);
+                    }
+                  } else {
+                    fixedArray[i] = item;
+                  }
+                }
+              } else if (typeof value === "object" && value.toJSON) {
+                if (value.toJSON === _Node.prototype.toJSON) {
+                  queue.push([value, fixed2, name]);
+                } else {
+                  fixed2[name] = value.toJSON(null, inputs);
+                }
+              } else if (name === "source") {
+                if (value == null) continue;
+                let inputId = inputs.get(value.input);
+                if (inputId == null) {
+                  inputId = inputs.size;
+                  inputs.set(value.input, inputId);
+                }
+                fixed2[name] = {
+                  end: value.end,
+                  inputId,
+                  start: value.start
+                };
+              } else {
+                fixed2[name] = value;
+              }
+            }
+          }
+          let fixed = holderOfRoot[0];
+          if (emitInputs) {
+            fixed.inputs = [...inputs.keys()].map((input) => input.toJSON());
+          }
+          return fixed;
+        }
+        toProxy() {
+          if (!this.proxyCache) {
+            this.proxyCache = new Proxy(this, this.getProxyProcessor());
+          }
+          return this.proxyCache;
+        }
+        toString(stringifier = stringify3) {
+          if (stringifier.stringify) stringifier = stringifier.stringify;
+          let result = "";
+          stringifier(this, (i) => {
+            result += i;
+          });
+          return result;
+        }
+        warn(result, text, opts = {}) {
+          let data = { node: this };
+          for (let i in opts) data[i] = opts[i];
+          return result.warn(text, data);
+        }
+      };
+      module.exports = Node3;
+      Node3.default = Node3;
+    }
+  });
+
+  // node_modules/postcss/lib/comment.js
+  var require_comment = __commonJS({
+    "node_modules/postcss/lib/comment.js"(exports, module) {
+      "use strict";
+      var Node3 = require_node();
+      var Comment2 = class extends Node3 {
+        constructor(defaults2) {
+          super(defaults2);
+          this.type = "comment";
+        }
+      };
+      module.exports = Comment2;
+      Comment2.default = Comment2;
+    }
+  });
+
+  // node_modules/postcss/lib/declaration.js
+  var require_declaration = __commonJS({
+    "node_modules/postcss/lib/declaration.js"(exports, module) {
+      "use strict";
+      var Node3 = require_node();
+      var Declaration2 = class extends Node3 {
+        get variable() {
+          return this.prop.startsWith("--") || this.prop[0] === "$";
+        }
+        constructor(defaults2) {
+          if (defaults2 && typeof defaults2.value !== "undefined" && typeof defaults2.value !== "string") {
+            defaults2 = { ...defaults2, value: String(defaults2.value) };
+          }
+          super(defaults2);
+          this.type = "decl";
+        }
+      };
+      module.exports = Declaration2;
+      Declaration2.default = Declaration2;
+    }
+  });
+
+  // node_modules/postcss/lib/container.js
+  var require_container = __commonJS({
+    "node_modules/postcss/lib/container.js"(exports, module) {
+      "use strict";
+      var Comment2 = require_comment();
+      var Declaration2 = require_declaration();
+      var Node3 = require_node();
+      var { isClean, my } = require_symbols();
+      var AtRule2;
+      var parse3;
+      var Root2;
+      var Rule2;
+      function cleanSource(nodes) {
+        let stack = nodes.slice();
+        while (stack.length > 0) {
+          let node = stack.pop();
+          delete node.source;
+          if (node.nodes) {
+            node.nodes = node.nodes.slice();
+            for (let i of node.nodes) stack.push(i);
+          }
+        }
+        return nodes.slice();
+      }
+      function markTreeDirty(node) {
+        let stack = [node];
+        while (stack.length > 0) {
+          let next = stack.pop();
+          next[isClean] = false;
+          if (next.proxyOf.nodes) {
+            for (let i of next.proxyOf.nodes) stack.push(i);
+          }
+        }
+      }
+      var Container2 = class _Container extends Node3 {
+        get first() {
+          if (!this.proxyOf.nodes) return void 0;
+          return this.proxyOf.nodes[0];
+        }
+        get last() {
+          if (!this.proxyOf.nodes) return void 0;
+          return this.proxyOf.nodes[this.proxyOf.nodes.length - 1];
+        }
+        append(...children) {
+          for (let child of children) {
+            let nodes = this.normalize(child, this.last);
+            for (let node of nodes) this.proxyOf.nodes.push(node);
+          }
+          this.markDirty();
+          return this;
+        }
+        cleanRaws(keepBetween) {
+          let stack = [this];
+          while (stack.length > 0) {
+            let node = stack.pop();
+            if (node !== this && node.cleanRaws !== _Container.prototype.cleanRaws) {
+              node.cleanRaws(keepBetween);
+              continue;
+            }
+            Node3.prototype.cleanRaws.call(node, keepBetween);
+            if (node.nodes) {
+              for (let child of node.nodes) stack.push(child);
+            }
+          }
+        }
+        each(callback) {
+          if (!this.proxyOf.nodes) return void 0;
+          let iterator = this.getIterator();
+          let index, result;
+          while (this.indexes[iterator] < this.proxyOf.nodes.length) {
+            index = this.indexes[iterator];
+            result = callback(this.proxyOf.nodes[index], index);
+            if (result === false) break;
+            this.indexes[iterator] += 1;
+          }
+          delete this.indexes[iterator];
+          return result;
+        }
+        every(condition) {
+          return this.nodes.every(condition);
+        }
+        getIterator() {
+          if (!this.lastEach) this.lastEach = 0;
+          if (!this.indexes) this.indexes = {};
+          this.lastEach += 1;
+          let iterator = this.lastEach;
+          this.indexes[iterator] = 0;
+          return iterator;
+        }
+        getProxyProcessor() {
+          return {
+            get(node, prop) {
+              if (prop === "proxyOf") {
+                return node;
+              } else if (!node[prop]) {
+                return node[prop];
+              } else if (prop === "each" || typeof prop === "string" && prop.startsWith("walk")) {
+                return (...args) => {
+                  return node[prop](
+                    ...args.map((i) => {
+                      if (typeof i === "function") {
+                        return (child, index) => i(child.toProxy(), index);
+                      } else {
+                        return i;
+                      }
+                    })
+                  );
+                };
+              } else if (prop === "every" || prop === "some") {
+                return (cb) => {
+                  return node[prop](
+                    (child, ...other) => cb(child.toProxy(), ...other)
+                  );
+                };
+              } else if (prop === "root") {
+                return () => node.root().toProxy();
+              } else if (prop === "nodes") {
+                return node.nodes.map((i) => i.toProxy());
+              } else if (prop === "first" || prop === "last") {
+                return node[prop].toProxy();
+              } else {
+                return node[prop];
+              }
+            },
+            set(node, prop, value) {
+              if (node[prop] === value) return true;
+              node[prop] = value;
+              if (prop === "name" || prop === "params" || prop === "selector") {
+                node.markDirty();
+              }
+              return true;
+            }
+          };
+        }
+        index(child) {
+          if (typeof child === "number") return child;
+          if (child.proxyOf) child = child.proxyOf;
+          return this.proxyOf.nodes.indexOf(child);
+        }
+        insertAfter(exist, add) {
+          let existIndex = this.index(exist);
+          let nodes = this.normalize(add, this.proxyOf.nodes[existIndex]).reverse();
+          existIndex = this.index(exist);
+          for (let node of nodes) this.proxyOf.nodes.splice(existIndex + 1, 0, node);
+          let index;
+          for (let id in this.indexes) {
+            index = this.indexes[id];
+            if (existIndex < index) {
+              this.indexes[id] = index + nodes.length;
+            }
+          }
+          this.markDirty();
+          return this;
+        }
+        insertBefore(exist, add) {
+          let existIndex = this.index(exist);
+          let type = existIndex === 0 ? "prepend" : false;
+          let nodes = this.normalize(
+            add,
+            this.proxyOf.nodes[existIndex],
+            type
+          ).reverse();
+          existIndex = this.index(exist);
+          for (let node of nodes) this.proxyOf.nodes.splice(existIndex, 0, node);
+          let index;
+          for (let id in this.indexes) {
+            index = this.indexes[id];
+            if (existIndex <= index) {
+              this.indexes[id] = index + nodes.length;
+            }
+          }
+          this.markDirty();
+          return this;
+        }
+        normalize(nodes, sample) {
+          if (typeof nodes === "string") {
+            nodes = cleanSource(parse3(nodes).nodes);
+          } else if (typeof nodes === "undefined") {
+            nodes = [];
+          } else if (Array.isArray(nodes)) {
+            nodes = nodes.slice(0);
+            for (let i of nodes) {
+              if (i.parent) i.parent.removeChild(i, "ignore");
+            }
+          } else if (nodes.type === "root" && this.type !== "document") {
+            nodes = nodes.nodes.slice(0);
+            for (let i of nodes) {
+              if (i.parent) i.parent.removeChild(i, "ignore");
+            }
+          } else if (nodes.type) {
+            nodes = [nodes];
+          } else if (nodes.prop) {
+            if (typeof nodes.value === "undefined") {
+              throw new Error("Value field is missed in node creation");
+            } else if (typeof nodes.value !== "string") {
+              nodes.value = String(nodes.value);
+            }
+            nodes = [new Declaration2(nodes)];
+          } else if (nodes.selector || nodes.selectors) {
+            nodes = [new Rule2(nodes)];
+          } else if (nodes.name) {
+            nodes = [new AtRule2(nodes)];
+          } else if (nodes.text) {
+            nodes = [new Comment2(nodes)];
+          } else {
+            throw new Error("Unknown node type in node creation");
+          }
+          let processed = nodes.map((i) => {
+            if (!i[my]) _Container.rebuild(i);
+            i = i.proxyOf;
+            if (i.parent) i.parent.removeChild(i);
+            if (i[isClean]) markTreeDirty(i);
+            if (!i.raws) i.raws = {};
+            if (typeof i.raws.before === "undefined") {
+              if (sample && typeof sample.raws.before !== "undefined") {
+                i.raws.before = sample.raws.before.replace(/\S/g, "");
+              }
+            }
+            i.parent = this.proxyOf;
+            return i;
+          });
+          return processed;
+        }
+        prepend(...children) {
+          children = children.reverse();
+          for (let child of children) {
+            let nodes = this.normalize(child, this.first, "prepend").reverse();
+            for (let node of nodes) this.proxyOf.nodes.unshift(node);
+            for (let id in this.indexes) {
+              this.indexes[id] = this.indexes[id] + nodes.length;
+            }
+          }
+          this.markDirty();
+          return this;
+        }
+        push(child) {
+          child.parent = this;
+          this.proxyOf.nodes.push(child);
+          return this;
+        }
+        removeAll() {
+          for (let node of this.proxyOf.nodes) node.parent = void 0;
+          this.proxyOf.nodes = [];
+          this.markDirty();
+          return this;
+        }
+        removeChild(child) {
+          child = this.index(child);
+          this.proxyOf.nodes[child].parent = void 0;
+          this.proxyOf.nodes.splice(child, 1);
+          let index;
+          for (let id in this.indexes) {
+            index = this.indexes[id];
+            if (index >= child) {
+              this.indexes[id] = index - 1;
+            }
+          }
+          this.markDirty();
+          return this;
+        }
+        replaceValues(pattern, opts, callback) {
+          if (!callback) {
+            callback = opts;
+            opts = {};
+          }
+          this.walkDecls((decl2) => {
+            if (opts.props && !opts.props.includes(decl2.prop)) return;
+            if (opts.fast && !decl2.value.includes(opts.fast)) return;
+            decl2.value = decl2.value.replace(pattern, callback);
+          });
+          this.markDirty();
+          return this;
+        }
+        some(condition) {
+          return this.nodes.some(condition);
+        }
+        walk(callback) {
+          if (!this.proxyOf.nodes) return void 0;
+          let stack = [{ iterator: this.getIterator(), node: this.proxyOf }];
+          while (stack.length > 0) {
+            let { iterator, node } = stack[stack.length - 1];
+            let index = node.indexes[iterator];
+            if (index >= node.proxyOf.nodes.length) {
+              delete node.indexes[iterator];
+              stack.pop();
+              let parent = stack[stack.length - 1];
+              if (parent) parent.node.indexes[parent.iterator] += 1;
+              continue;
+            }
+            let child = node.proxyOf.nodes[index];
+            let result;
+            try {
+              result = callback(child, index);
+            } catch (e) {
+              throw child.addToError(e);
+            }
+            if (result === false) {
+              for (let opened of stack) {
+                delete opened.node.indexes[opened.iterator];
+              }
+              return false;
+            }
+            if (child.walk && child.proxyOf.nodes) {
+              stack.push({ iterator: child.getIterator(), node: child });
+            } else {
+              node.indexes[iterator] += 1;
+            }
+          }
+          return void 0;
+        }
+        walkAtRules(name, callback) {
+          if (!callback) {
+            callback = name;
+            return this.walk((child, i) => {
+              if (child.type === "atrule") {
+                return callback(child, i);
+              }
+            });
+          }
+          if (name instanceof RegExp) {
+            return this.walk((child, i) => {
+              if (child.type === "atrule" && name.test(child.name)) {
+                return callback(child, i);
+              }
+            });
+          }
+          return this.walk((child, i) => {
+            if (child.type === "atrule" && child.name === name) {
+              return callback(child, i);
+            }
+          });
+        }
+        walkComments(callback) {
+          return this.walk((child, i) => {
+            if (child.type === "comment") {
+              return callback(child, i);
+            }
+          });
+        }
+        walkDecls(prop, callback) {
+          if (!callback) {
+            callback = prop;
+            return this.walk((child, i) => {
+              if (child.type === "decl") {
+                return callback(child, i);
+              }
+            });
+          }
+          if (prop instanceof RegExp) {
+            return this.walk((child, i) => {
+              if (child.type === "decl" && prop.test(child.prop)) {
+                return callback(child, i);
+              }
+            });
+          }
+          return this.walk((child, i) => {
+            if (child.type === "decl" && child.prop === prop) {
+              return callback(child, i);
+            }
+          });
+        }
+        walkRules(selector, callback) {
+          if (!callback) {
+            callback = selector;
+            return this.walk((child, i) => {
+              if (child.type === "rule") {
+                return callback(child, i);
+              }
+            });
+          }
+          if (selector instanceof RegExp) {
+            return this.walk((child, i) => {
+              if (child.type === "rule" && selector.test(child.selector)) {
+                return callback(child, i);
+              }
+            });
+          }
+          return this.walk((child, i) => {
+            if (child.type === "rule" && child.selector === selector) {
+              return callback(child, i);
+            }
+          });
+        }
+      };
+      Container2.registerParse = (dependant) => {
+        parse3 = dependant;
+      };
+      Container2.registerRule = (dependant) => {
+        Rule2 = dependant;
+      };
+      Container2.registerAtRule = (dependant) => {
+        AtRule2 = dependant;
+      };
+      Container2.registerRoot = (dependant) => {
+        Root2 = dependant;
+      };
+      module.exports = Container2;
+      Container2.default = Container2;
+      Container2.rebuild = (node) => {
+        let stack = [node];
+        while (stack.length > 0) {
+          let next = stack.pop();
+          if (next.type === "atrule") {
+            Object.setPrototypeOf(next, AtRule2.prototype);
+          } else if (next.type === "rule") {
+            Object.setPrototypeOf(next, Rule2.prototype);
+          } else if (next.type === "decl") {
+            Object.setPrototypeOf(next, Declaration2.prototype);
+          } else if (next.type === "comment") {
+            Object.setPrototypeOf(next, Comment2.prototype);
+          } else if (next.type === "root") {
+            Object.setPrototypeOf(next, Root2.prototype);
+          }
+          next[my] = true;
+          if (next.nodes) {
+            for (let child of next.nodes) stack.push(child);
+          }
+        }
+      };
+    }
+  });
+
+  // node_modules/postcss/lib/at-rule.js
+  var require_at_rule = __commonJS({
+    "node_modules/postcss/lib/at-rule.js"(exports, module) {
+      "use strict";
+      var Container2 = require_container();
+      var AtRule2 = class extends Container2 {
+        constructor(defaults2) {
+          super(defaults2);
+          this.type = "atrule";
+        }
+        append(...children) {
+          if (!this.proxyOf.nodes) this.nodes = [];
+          return super.append(...children);
+        }
+        prepend(...children) {
+          if (!this.proxyOf.nodes) this.nodes = [];
+          return super.prepend(...children);
+        }
+      };
+      module.exports = AtRule2;
+      AtRule2.default = AtRule2;
+      Container2.registerAtRule(AtRule2);
+    }
+  });
+
+  // node_modules/postcss/lib/document.js
+  var require_document = __commonJS({
+    "node_modules/postcss/lib/document.js"(exports, module) {
+      "use strict";
+      var Container2 = require_container();
+      var LazyResult;
+      var Processor2;
+      var Document2 = class extends Container2 {
+        constructor(defaults2) {
+          super({ type: "document", ...defaults2 });
+          if (!this.nodes) {
+            this.nodes = [];
+          }
+        }
+        toResult(opts = {}) {
+          let lazy = new LazyResult(new Processor2(), this, opts);
+          return lazy.stringify();
+        }
+      };
+      Document2.registerLazyResult = (dependant) => {
+        LazyResult = dependant;
+      };
+      Document2.registerProcessor = (dependant) => {
+        Processor2 = dependant;
+      };
+      module.exports = Document2;
+      Document2.default = Document2;
+    }
+  });
+
+  // node_modules/nanoid/non-secure/index.cjs
+  var require_non_secure = __commonJS({
+    "node_modules/nanoid/non-secure/index.cjs"(exports, module) {
+      var urlAlphabet = "useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict";
+      var customAlphabet = (alphabet, defaultSize = 21) => {
+        return (size = defaultSize) => {
+          let id = "";
+          let i = size | 0;
+          while (i-- > 0) {
+            id += alphabet[Math.random() * alphabet.length | 0];
+          }
+          return id;
+        };
+      };
+      var nanoid = (size = 21) => {
+        let id = "";
+        let i = size | 0;
+        while (i-- > 0) {
+          id += urlAlphabet[Math.random() * 64 | 0];
+        }
+        return id;
+      };
+      module.exports = { nanoid, customAlphabet };
+    }
+  });
+
+  // (disabled):path
+  var require_path = __commonJS({
+    "(disabled):path"() {
+    }
+  });
+
+  // (disabled):node_modules/source-map-js/source-map.js
+  var require_source_map = __commonJS({
+    "(disabled):node_modules/source-map-js/source-map.js"() {
+    }
+  });
+
+  // (disabled):url
+  var require_url = __commonJS({
+    "(disabled):url"() {
+    }
+  });
+
+  // (disabled):fs
+  var require_fs = __commonJS({
+    "(disabled):fs"() {
+    }
+  });
+
+  // node_modules/postcss/lib/previous-map.js
+  var require_previous_map = __commonJS({
+    "node_modules/postcss/lib/previous-map.js"(exports, module) {
+      "use strict";
+      var { existsSync, readFileSync } = require_fs();
+      var { dirname, isAbsolute, join, relative, sep } = require_path();
+      var { SourceMapConsumer, SourceMapGenerator } = require_source_map();
+      function fromBase64(str) {
+        if (Buffer) {
+          return Buffer.from(str, "base64").toString();
+        } else {
+          return window.atob(str);
+        }
+      }
+      var PreviousMap = class {
+        constructor(css, opts) {
+          if (opts.map === false) return;
+          if (opts.unsafeMap) this.unsafeMap = true;
+          this.loadAnnotation(css);
+          this.inline = this.startWith(this.annotation, "data:");
+          let prev = opts.map ? opts.map.prev : void 0;
+          let text = this.loadMap(opts.from, prev);
+          if (!this.mapFile && opts.from) {
+            this.mapFile = opts.from;
+          }
+          if (this.mapFile) this.root = dirname(this.mapFile);
+          if (text) this.text = text;
+        }
+        consumer() {
+          if (!this.consumerCache) {
+            this.consumerCache = new SourceMapConsumer(this.json || this.text);
+          }
+          return this.consumerCache;
+        }
+        decodeInline(text) {
+          let baseCharsetUri = /^data:application\/json;charset=utf-?8;base64,/;
+          let baseUri = /^data:application\/json;base64,/;
+          let charsetUri = /^data:application\/json;charset=utf-?8,/;
+          let uri = /^data:application\/json,/;
+          let uriMatch = text.match(charsetUri) || text.match(uri);
+          if (uriMatch) {
+            return decodeURIComponent(text.substr(uriMatch[0].length));
+          }
+          let baseUriMatch = text.match(baseCharsetUri) || text.match(baseUri);
+          if (baseUriMatch) {
+            return fromBase64(text.substr(baseUriMatch[0].length));
+          }
+          let encoding = text.slice("data:application/json;".length);
+          encoding = encoding.slice(0, encoding.indexOf(","));
+          throw new Error("Unsupported source map encoding " + encoding);
+        }
+        getAnnotationURL(sourceMapString) {
+          return sourceMapString.replace(/^\/\*\s*# sourceMappingURL=/, "").trim();
+        }
+        isMap(map) {
+          if (typeof map !== "object") return false;
+          return typeof map.mappings === "string" || typeof map._mappings === "string" || Array.isArray(map.sections);
+        }
+        loadAnnotation(css) {
+          let comments = css.match(/\/\*\s*# sourceMappingURL=/g);
+          if (!comments) return;
+          let start = css.lastIndexOf(comments.pop());
+          let end = css.indexOf("*/", start);
+          if (start > -1 && end > -1) {
+            this.annotation = this.getAnnotationURL(css.substring(start, end));
+          }
+        }
+        loadFile(path, cssFile, trusted) {
+          if (!trusted && !this.unsafeMap) {
+            if (!/\.map$/i.test(path)) return void 0;
+            if (!cssFile) return void 0;
+            let rel = relative(dirname(cssFile), path);
+            if (rel === ".." || rel.startsWith(".." + sep) || isAbsolute(rel)) {
+              return void 0;
+            }
+          }
+          this.root = dirname(path);
+          if (existsSync(path)) {
+            this.mapFile = path;
+            return readFileSync(path, "utf-8").toString().trim();
+          }
+        }
+        loadMap(file, prev) {
+          if (prev === false) return false;
+          if (prev) {
+            if (typeof prev === "string") {
+              return prev;
+            } else if (typeof prev === "function") {
+              let prevPath = prev(file);
+              if (prevPath) {
+                let map = this.loadFile(prevPath, file, true);
+                if (!map) {
+                  throw new Error(
+                    "Unable to load previous source map: " + prevPath.toString()
+                  );
+                }
+                return map;
+              }
+            } else if (prev instanceof SourceMapConsumer) {
+              return SourceMapGenerator.fromSourceMap(prev).toString();
+            } else if (prev instanceof SourceMapGenerator) {
+              return prev.toString();
+            } else if (this.isMap(prev)) {
+              return JSON.stringify(prev);
+            } else {
+              throw new Error(
+                "Unsupported previous source map format: " + prev.toString()
+              );
+            }
+          } else if (this.inline) {
+            return this.decodeInline(this.annotation);
+          } else if (this.annotation) {
+            let map = this.annotation;
+            if (file) map = join(dirname(file), map);
+            let unknown = this.loadFile(map, file, false);
+            if (unknown) {
+              try {
+                this.json = JSON.parse(unknown.replace(/^\)]}'[^\n]*\n/, ""));
+              } catch {
+                return void 0;
+              }
+            }
+            return unknown;
+          }
+        }
+        startWith(string, start) {
+          if (!string) return false;
+          return string.substr(0, start.length) === start;
+        }
+        withContent() {
+          return !!(this.consumer().sourcesContent && this.consumer().sourcesContent.length > 0);
+        }
+      };
+      module.exports = PreviousMap;
+      PreviousMap.default = PreviousMap;
+    }
+  });
+
+  // node_modules/postcss/lib/input.js
+  var require_input = __commonJS({
+    "node_modules/postcss/lib/input.js"(exports, module) {
+      "use strict";
+      var { nanoid } = require_non_secure();
+      var { isAbsolute, resolve } = require_path();
+      var { SourceMapConsumer, SourceMapGenerator } = require_source_map();
+      var { fileURLToPath, pathToFileURL } = require_url();
+      var CssSyntaxError2 = require_css_syntax_error();
+      var PreviousMap = require_previous_map();
+      var terminalHighlight = require_terminal_highlight();
+      var lineToIndexCache = /* @__PURE__ */ Symbol("lineToIndexCache");
+      var sourceMapAvailable = Boolean(SourceMapConsumer && SourceMapGenerator);
+      var pathAvailable = Boolean(resolve && isAbsolute);
+      function getLineToIndex(input) {
+        if (input[lineToIndexCache]) return input[lineToIndexCache];
+        let lines = input.css.split("\n");
+        let lineToIndex = new Array(lines.length);
+        let prevIndex = 0;
+        for (let i = 0, l = lines.length; i < l; i++) {
+          lineToIndex[i] = prevIndex;
+          prevIndex += lines[i].length + 1;
+        }
+        input[lineToIndexCache] = lineToIndex;
+        return lineToIndex;
+      }
+      var Input2 = class {
+        get from() {
+          return this.file || this.id;
+        }
+        constructor(css, opts = {}) {
+          if (css === null || typeof css === "undefined" || typeof css === "object" && !css.toString) {
+            throw new Error(`PostCSS received ${css} instead of CSS string`);
+          }
+          this.css = css.toString();
+          if (this.css[0] === "\uFEFF" || this.css[0] === "\uFFFE") {
+            this.hasBOM = true;
+            this.css = this.css.slice(1);
+          } else {
+            this.hasBOM = false;
+          }
+          this.document = this.css;
+          if (opts.document) this.document = opts.document.toString();
+          if (opts.from) {
+            if (!pathAvailable || /^\w+:\/\//.test(opts.from) || isAbsolute(opts.from)) {
+              this.file = opts.from;
+            } else {
+              this.file = resolve(opts.from);
+            }
+          }
+          if (pathAvailable && sourceMapAvailable) {
+            let map = new PreviousMap(this.css, opts);
+            if (map.text) {
+              this.map = map;
+              let file = map.consumer().file;
+              if (!this.file && file) this.file = this.mapResolve(file);
+            }
+          }
+          if (!this.file) {
+            this.id = "<input css " + nanoid(6) + ">";
+          }
+          if (this.map) this.map.file = this.from;
+        }
+        error(message, line, column, opts = {}) {
+          let endColumn, endLine, endOffset, offset, result;
+          if (line && typeof line === "object") {
+            let start = line;
+            let end = column;
+            if (typeof start.offset === "number") {
+              offset = start.offset;
+              let pos = this.fromOffset(offset);
+              line = pos.line;
+              column = pos.col;
+            } else {
+              line = start.line;
+              column = start.column;
+              offset = this.fromLineAndColumn(line, column);
+            }
+            if (typeof end.offset === "number") {
+              endOffset = end.offset;
+              let pos = this.fromOffset(endOffset);
+              endLine = pos.line;
+              endColumn = pos.col;
+            } else {
+              endLine = end.line;
+              endColumn = end.column;
+              endOffset = this.fromLineAndColumn(end.line, end.column);
+            }
+          } else if (!column) {
+            offset = line;
+            let pos = this.fromOffset(offset);
+            line = pos.line;
+            column = pos.col;
+          } else {
+            offset = this.fromLineAndColumn(line, column);
+          }
+          let origin = this.origin(line, column, endLine, endColumn);
+          if (origin) {
+            result = new CssSyntaxError2(
+              message,
+              origin.endLine === void 0 ? origin.line : { column: origin.column, line: origin.line },
+              origin.endLine === void 0 ? origin.column : { column: origin.endColumn, line: origin.endLine },
+              origin.source,
+              origin.file,
+              opts.plugin
+            );
+          } else {
+            result = new CssSyntaxError2(
+              message,
+              endLine === void 0 ? line : { column, line },
+              endLine === void 0 ? column : { column: endColumn, line: endLine },
+              this.css,
+              this.file,
+              opts.plugin
+            );
+          }
+          result.input = {
+            column,
+            endColumn,
+            endLine,
+            endOffset,
+            line,
+            offset,
+            source: this.css
+          };
+          if (this.file) {
+            if (pathToFileURL) {
+              result.input.url = pathToFileURL(this.file).toString();
+            }
+            result.input.file = this.file;
+          }
+          return result;
+        }
+        fromLineAndColumn(line, column) {
+          let lineToIndex = getLineToIndex(this);
+          let index = lineToIndex[line - 1];
+          return index + column - 1;
+        }
+        fromOffset(offset) {
+          let lineToIndex = getLineToIndex(this);
+          let lastLine = lineToIndex[lineToIndex.length - 1];
+          let min = 0;
+          if (offset >= lastLine) {
+            min = lineToIndex.length - 1;
+          } else {
+            let max = lineToIndex.length - 2;
+            let mid;
+            while (min < max) {
+              mid = min + (max - min >> 1);
+              if (offset < lineToIndex[mid]) {
+                max = mid - 1;
+              } else if (offset >= lineToIndex[mid + 1]) {
+                min = mid + 1;
+              } else {
+                min = mid;
+                break;
+              }
+            }
+          }
+          return {
+            col: offset - lineToIndex[min] + 1,
+            line: min + 1
+          };
+        }
+        mapResolve(file) {
+          if (/^\w+:\/\//.test(file)) {
+            return file;
+          }
+          return resolve(this.map.consumer().sourceRoot || this.map.root || ".", file);
+        }
+        origin(line, column, endLine, endColumn) {
+          if (!this.map) return false;
+          let consumer = this.map.consumer();
+          let from = consumer.originalPositionFor({ column: column - 1, line });
+          if (!from.source) return false;
+          let to;
+          if (typeof endLine === "number") {
+            let toPosition = consumer.originalPositionFor({
+              column: endColumn - 1,
+              line: endLine
+            });
+            if (toPosition.source) to = toPosition;
+          }
+          let fromUrl;
+          if (isAbsolute(from.source)) {
+            fromUrl = pathToFileURL(from.source);
+          } else {
+            fromUrl = new URL(
+              from.source,
+              this.map.consumer().sourceRoot || pathToFileURL(this.map.mapFile)
+            );
+          }
+          let result = {
+            column: from.column + 1,
+            endColumn: to && to.column + 1,
+            endLine: to && to.line,
+            line: from.line,
+            url: fromUrl.toString()
+          };
+          if (fromUrl.protocol === "file:") {
+            if (fileURLToPath) {
+              result.file = fileURLToPath(fromUrl);
+            } else {
+              throw new Error(`file: protocol is not available in this PostCSS build`);
+            }
+          }
+          let source = consumer.sourceContentFor(from.source);
+          if (source) result.source = source;
+          return result;
+        }
+        toJSON() {
+          let json = {};
+          for (let name of ["hasBOM", "css", "file", "id"]) {
+            if (this[name] != null) {
+              json[name] = this[name];
+            }
+          }
+          if (this.map) {
+            json.map = { ...this.map };
+            if (json.map.consumerCache) {
+              json.map.consumerCache = void 0;
+            }
+          }
+          return json;
+        }
+      };
+      module.exports = Input2;
+      Input2.default = Input2;
+      if (terminalHighlight && terminalHighlight.registerInput) {
+        terminalHighlight.registerInput(Input2);
+      }
+    }
+  });
+
+  // node_modules/postcss/lib/root.js
+  var require_root = __commonJS({
+    "node_modules/postcss/lib/root.js"(exports, module) {
+      "use strict";
+      var Container2 = require_container();
+      var LazyResult;
+      var Processor2;
+      var Root2 = class extends Container2 {
+        constructor(defaults2) {
+          super(defaults2);
+          this.type = "root";
+          if (!this.nodes) this.nodes = [];
+        }
+        normalize(child, sample, type) {
+          let keepBefore = /* @__PURE__ */ new Set();
+          for (let node of Array.isArray(child) ? child : [child]) {
+            if (node && typeof node === "object" && !node.parent && node.raws && typeof node.raws.before !== "undefined") {
+              keepBefore.add(node.raws);
+            }
+          }
+          let nodes = super.normalize(child);
+          if (sample) {
+            if (type === "prepend") {
+              if (this.nodes.length > 1) {
+                sample.raws.before = this.nodes[1].raws.before;
+              } else {
+                delete sample.raws.before;
+              }
+            } else if (this.first !== sample) {
+              for (let node of nodes) {
+                if (!keepBefore.has(node.raws)) {
+                  node.raws.before = sample.raws.before;
+                }
+              }
+            }
+          }
+          return nodes;
+        }
+        removeChild(child, ignore) {
+          let index = this.index(child);
+          if (!ignore && index === 0 && this.nodes.length > 1) {
+            this.nodes[1].raws.before = this.nodes[index].raws.before;
+          }
+          return super.removeChild(child);
+        }
+        toResult(opts = {}) {
+          let lazy = new LazyResult(new Processor2(), this, opts);
+          return lazy.stringify();
+        }
+      };
+      Root2.registerLazyResult = (dependant) => {
+        LazyResult = dependant;
+      };
+      Root2.registerProcessor = (dependant) => {
+        Processor2 = dependant;
+      };
+      module.exports = Root2;
+      Root2.default = Root2;
+      Container2.registerRoot(Root2);
+    }
+  });
+
+  // node_modules/postcss/lib/list.js
+  var require_list = __commonJS({
+    "node_modules/postcss/lib/list.js"(exports, module) {
+      "use strict";
+      var list2 = {
+        comma(string) {
+          return list2.split(string, [","], true);
+        },
+        space(string) {
+          let spaces = [" ", "\n", "	"];
+          return list2.split(string, spaces);
+        },
+        split(string, separators, last) {
+          if (!string) return [];
+          let array = [];
+          let current = "";
+          let split = false;
+          let func = 0;
+          let inQuote = false;
+          let prevQuote = "";
+          let escape = false;
+          for (let letter of string) {
+            if (escape) {
+              escape = false;
+            } else if (letter === "\\") {
+              escape = true;
+            } else if (inQuote) {
+              if (letter === prevQuote) {
+                inQuote = false;
+              }
+            } else if (letter === '"' || letter === "'") {
+              inQuote = true;
+              prevQuote = letter;
+            } else if (letter === "(") {
+              func += 1;
+            } else if (letter === ")") {
+              if (func > 0) func -= 1;
+            } else if (func === 0) {
+              if (separators.includes(letter)) split = true;
+            }
+            if (split) {
+              if (current !== "") array.push(current.trim());
+              current = "";
+              split = false;
+            } else {
+              current += letter;
+            }
+          }
+          if (last || current !== "") array.push(current.trim());
+          return array;
+        }
+      };
+      module.exports = list2;
+      list2.default = list2;
+    }
+  });
+
+  // node_modules/postcss/lib/rule.js
+  var require_rule = __commonJS({
+    "node_modules/postcss/lib/rule.js"(exports, module) {
+      "use strict";
+      var Container2 = require_container();
+      var list2 = require_list();
+      var Rule2 = class extends Container2 {
+        get selectors() {
+          return list2.comma(this.selector);
+        }
+        set selectors(values) {
+          let match = this.selector ? this.selector.match(/,\s*/) : null;
+          let sep = match ? match[0] : "," + this.raw("between", "beforeOpen");
+          this.selector = values.join(sep);
+        }
+        constructor(defaults2) {
+          super(defaults2);
+          this.type = "rule";
+          if (!this.nodes) this.nodes = [];
+        }
+      };
+      module.exports = Rule2;
+      Rule2.default = Rule2;
+      Container2.registerRule(Rule2);
+    }
+  });
+
+  // node_modules/postcss/lib/fromJSON.js
+  var require_fromJSON = __commonJS({
+    "node_modules/postcss/lib/fromJSON.js"(exports, module) {
+      "use strict";
+      var AtRule2 = require_at_rule();
+      var Comment2 = require_comment();
+      var Declaration2 = require_declaration();
+      var Input2 = require_input();
+      var PreviousMap = require_previous_map();
+      var Root2 = require_root();
+      var Rule2 = require_rule();
+      function hydrateInputs(json, inputs) {
+        if (!json.inputs) return inputs;
+        return json.inputs.map((input) => {
+          let inputHydrated = { ...input, __proto__: Input2.prototype };
+          if (inputHydrated.map) {
+            inputHydrated.map = {
+              ...inputHydrated.map,
+              __proto__: PreviousMap.prototype
+            };
+          }
+          return inputHydrated;
+        });
+      }
+      function constructNode(json, inputs, children) {
+        let defaults2 = { ...json };
+        delete defaults2.inputs;
+        delete defaults2.nodes;
+        if (defaults2.source) {
+          let { inputId, ...source } = defaults2.source;
+          defaults2.source = source;
+          if (inputId != null) {
+            defaults2.source.input = inputs[inputId];
+          }
+        }
+        let node;
+        if (defaults2.type === "root") {
+          node = new Root2(defaults2);
+        } else if (defaults2.type === "decl") {
+          node = new Declaration2(defaults2);
+        } else if (defaults2.type === "rule") {
+          node = new Rule2(defaults2);
+        } else if (defaults2.type === "comment") {
+          node = new Comment2(defaults2);
+        } else if (defaults2.type === "atrule") {
+          node = new AtRule2(defaults2);
+        } else {
+          throw new Error("Unknown node type: " + json.type);
+        }
+        if (children) {
+          node.nodes = children;
+          for (let child of children) child.parent = node;
+        }
+        return node;
+      }
+      function fromJSON2(json, inputs) {
+        if (Array.isArray(json)) return json.map((n) => fromJSON2(n));
+        let result;
+        let stack = [
+          { childIndex: 0, children: [], inputs: hydrateInputs(json, inputs), json }
+        ];
+        while (stack.length > 0) {
+          let frame = stack[stack.length - 1];
+          let jsonNodes = frame.json.nodes;
+          if (jsonNodes && frame.childIndex < jsonNodes.length) {
+            let childJson = jsonNodes[frame.childIndex];
+            frame.childIndex += 1;
+            stack.push({
+              childIndex: 0,
+              children: [],
+              inputs: hydrateInputs(childJson, frame.inputs),
+              json: childJson
+            });
+            continue;
+          }
+          stack.pop();
+          let node = constructNode(
+            frame.json,
+            frame.inputs,
+            jsonNodes ? frame.children : void 0
+          );
+          if (stack.length > 0) {
+            stack[stack.length - 1].children.push(node);
+          } else {
+            result = node;
+          }
+        }
+        return result;
+      }
+      module.exports = fromJSON2;
+      fromJSON2.default = fromJSON2;
+    }
+  });
+
+  // node_modules/postcss/lib/map-generator.js
+  var require_map_generator = __commonJS({
+    "node_modules/postcss/lib/map-generator.js"(exports, module) {
+      "use strict";
+      var { dirname, relative, resolve, sep } = require_path();
+      var { SourceMapConsumer, SourceMapGenerator } = require_source_map();
+      var { pathToFileURL } = require_url();
+      var Input2 = require_input();
+      var sourceMapAvailable = Boolean(SourceMapConsumer && SourceMapGenerator);
+      var pathAvailable = Boolean(dirname && resolve && relative && sep);
+      var MapGenerator = class {
+        constructor(stringify3, root2, opts, cssString) {
+          this.stringify = stringify3;
+          this.mapOpts = opts.map || {};
+          this.root = root2;
+          this.opts = opts;
+          this.css = cssString;
+          this.originalCSS = cssString;
+          this.usesFileUrls = !this.mapOpts.from && this.mapOpts.absolute;
+          this.memoizedFileURLs = /* @__PURE__ */ new Map();
+          this.memoizedPaths = /* @__PURE__ */ new Map();
+          this.memoizedURLs = /* @__PURE__ */ new Map();
+        }
+        addAnnotation() {
+          let content;
+          if (this.isInline()) {
+            content = "data:application/json;base64," + this.toBase64(this.map.toString());
+          } else if (typeof this.mapOpts.annotation === "string") {
+            content = this.mapOpts.annotation;
+          } else if (typeof this.mapOpts.annotation === "function") {
+            content = this.mapOpts.annotation(this.opts.to, this.root);
+          } else {
+            content = this.outputFile() + ".map";
+          }
+          let eol = "\n";
+          if (this.css.includes("\r\n")) eol = "\r\n";
+          this.css += eol + "/*# sourceMappingURL=" + content + " */";
+        }
+        applyPrevMaps() {
+          for (let prev of this.previous()) {
+            let from = this.toUrl(this.path(prev.file));
+            let root2 = prev.root || dirname(prev.file);
+            let map;
+            if (this.mapOpts.sourcesContent === false) {
+              map = new SourceMapConsumer(prev.text);
+              if (map.sourcesContent) {
+                map.sourcesContent = null;
+              }
+            } else {
+              map = prev.consumer();
+            }
+            this.map.applySourceMap(map, from, this.toUrl(this.path(root2)));
+          }
+        }
+        clearAnnotation() {
+          if (this.mapOpts.annotation === false) return;
+          if (this.root) {
+            let node;
+            for (let i = this.root.nodes.length - 1; i >= 0; i--) {
+              node = this.root.nodes[i];
+              if (node.type !== "comment") continue;
+              if (node.text.startsWith("# sourceMappingURL=")) {
+                this.root.removeChild(i);
+              }
+            }
+          } else if (this.css) {
+            let startIndex;
+            while ((startIndex = this.css.lastIndexOf("/*#")) !== -1) {
+              let endIndex = this.css.indexOf("*/", startIndex + 3);
+              if (endIndex === -1) break;
+              while (startIndex > 0 && this.css[startIndex - 1] === "\n") {
+                startIndex--;
+              }
+              this.css = this.css.slice(0, startIndex) + this.css.slice(endIndex + 2);
+            }
+          }
+        }
+        generate() {
+          this.clearAnnotation();
+          if (pathAvailable && sourceMapAvailable && this.isMap()) {
+            return this.generateMap();
+          } else {
+            let result = "";
+            this.stringify(this.root, (i) => {
+              result += i;
+            });
+            return [result];
+          }
+        }
+        generateMap() {
+          if (this.root) {
+            this.generateString();
+          } else if (this.previous().length === 1) {
+            let prev = this.previous()[0].consumer();
+            prev.file = this.outputFile();
+            this.map = SourceMapGenerator.fromSourceMap(prev, {
+              ignoreInvalidMapping: true
+            });
+          } else {
+            this.map = new SourceMapGenerator({
+              file: this.outputFile(),
+              ignoreInvalidMapping: true
+            });
+            this.map.addMapping({
+              generated: { column: 0, line: 1 },
+              original: { column: 0, line: 1 },
+              source: this.opts.from ? this.toUrl(this.path(this.opts.from)) : "<no source>"
+            });
+          }
+          if (this.isSourcesContent()) this.setSourcesContent();
+          if (this.root && this.previous().length > 0) this.applyPrevMaps();
+          if (this.isAnnotation()) this.addAnnotation();
+          if (this.isInline()) {
+            return [this.css];
+          } else {
+            return [this.css, this.map];
+          }
+        }
+        generateString() {
+          this.css = "";
+          this.map = new SourceMapGenerator({
+            file: this.outputFile(),
+            ignoreInvalidMapping: true
+          });
+          let line = 1;
+          let column = 1;
+          let noSource = "<no source>";
+          let mapping = {
+            generated: { column: 0, line: 0 },
+            original: { column: 0, line: 0 },
+            source: ""
+          };
+          let last, lines;
+          this.stringify(this.root, (str, node, type) => {
+            this.css += str;
+            if (node && type !== "end") {
+              mapping.generated.line = line;
+              mapping.generated.column = column - 1;
+              if (node.source && node.source.start) {
+                mapping.source = this.sourcePath(node);
+                mapping.original.line = node.source.start.line;
+                mapping.original.column = node.source.start.column - 1;
+                this.map.addMapping(mapping);
+              } else {
+                mapping.source = noSource;
+                mapping.original.line = 1;
+                mapping.original.column = 0;
+                this.map.addMapping(mapping);
+              }
+            }
+            lines = str.match(/\n/g);
+            if (lines) {
+              line += lines.length;
+              last = str.lastIndexOf("\n");
+              column = str.length - last;
+            } else {
+              column += str.length;
+            }
+            if (node && type !== "start") {
+              let p = node.parent || { raws: {} };
+              let childless = node.type === "decl" || node.type === "atrule" && !node.nodes;
+              if (!childless || node !== p.last || p.raws.semicolon) {
+                if (node.source && node.source.end) {
+                  mapping.source = this.sourcePath(node);
+                  mapping.original.line = node.source.end.line;
+                  mapping.original.column = node.source.end.column - 1;
+                  mapping.generated.line = line;
+                  mapping.generated.column = column - 2;
+                  this.map.addMapping(mapping);
+                } else {
+                  mapping.source = noSource;
+                  mapping.original.line = 1;
+                  mapping.original.column = 0;
+                  mapping.generated.line = line;
+                  mapping.generated.column = column - 1;
+                  this.map.addMapping(mapping);
+                }
+              }
+            }
+          });
+        }
+        isAnnotation() {
+          if (this.isInline()) {
+            return true;
+          }
+          if (typeof this.mapOpts.annotation !== "undefined") {
+            return this.mapOpts.annotation;
+          }
+          if (this.previous().length) {
+            return this.previous().some((i) => i.annotation);
+          }
+          return true;
+        }
+        isInline() {
+          if (typeof this.mapOpts.inline !== "undefined") {
+            return this.mapOpts.inline;
+          }
+          let annotation = this.mapOpts.annotation;
+          if (typeof annotation !== "undefined" && annotation !== true) {
+            return false;
+          }
+          if (this.previous().length) {
+            return this.previous().some((i) => i.inline);
+          }
+          return true;
+        }
+        isMap() {
+          if (typeof this.opts.map !== "undefined") {
+            return !!this.opts.map;
+          }
+          return this.previous().length > 0;
+        }
+        isSourcesContent() {
+          if (typeof this.mapOpts.sourcesContent !== "undefined") {
+            return this.mapOpts.sourcesContent;
+          }
+          if (this.previous().length) {
+            return this.previous().some((i) => i.withContent());
+          }
+          return true;
+        }
+        outputFile() {
+          if (this.opts.to) {
+            return this.path(this.opts.to);
+          } else if (this.opts.from) {
+            return this.path(this.opts.from);
+          } else {
+            return "to.css";
+          }
+        }
+        path(file) {
+          if (this.mapOpts.absolute) return file;
+          if (file.charCodeAt(0) === 60) return file;
+          if (/^\w+:\/\//.test(file)) return file;
+          let cached = this.memoizedPaths.get(file);
+          if (cached) return cached;
+          let from = this.opts.to ? dirname(this.opts.to) : ".";
+          if (typeof this.mapOpts.annotation === "string") {
+            from = dirname(resolve(from, this.mapOpts.annotation));
+          }
+          let path = relative(from, file);
+          this.memoizedPaths.set(file, path);
+          return path;
+        }
+        previous() {
+          if (!this.previousMaps) {
+            this.previousMaps = [];
+            if (this.root) {
+              this.root.walk((node) => {
+                if (node.source && node.source.input.map) {
+                  let map = node.source.input.map;
+                  if (!this.previousMaps.includes(map)) {
+                    this.previousMaps.push(map);
+                  }
+                }
+              });
+            } else {
+              let input = new Input2(this.originalCSS, this.opts);
+              if (input.map) this.previousMaps.push(input.map);
+            }
+          }
+          return this.previousMaps;
+        }
+        setSourcesContent() {
+          let already = {};
+          if (this.root) {
+            this.root.walk((node) => {
+              if (node.source) {
+                let from = node.source.input.from;
+                if (from && !already[from]) {
+                  already[from] = true;
+                  let fromUrl = this.usesFileUrls ? this.toFileUrl(from) : this.toUrl(this.path(from));
+                  this.map.setSourceContent(fromUrl, node.source.input.css);
+                }
+              }
+            });
+          } else if (this.css) {
+            let from = this.opts.from ? this.toUrl(this.path(this.opts.from)) : "<no source>";
+            this.map.setSourceContent(from, this.css);
+          }
+        }
+        sourcePath(node) {
+          if (this.mapOpts.from) {
+            return this.toUrl(this.mapOpts.from);
+          } else if (this.usesFileUrls) {
+            return this.toFileUrl(node.source.input.from);
+          } else {
+            return this.toUrl(this.path(node.source.input.from));
+          }
+        }
+        toBase64(str) {
+          if (Buffer) {
+            return Buffer.from(str).toString("base64");
+          } else {
+            return window.btoa(unescape(encodeURIComponent(str)));
+          }
+        }
+        toFileUrl(path) {
+          let cached = this.memoizedFileURLs.get(path);
+          if (cached) return cached;
+          if (pathToFileURL) {
+            let fileURL = pathToFileURL(path).toString();
+            this.memoizedFileURLs.set(path, fileURL);
+            return fileURL;
+          } else {
+            throw new Error(
+              "`map.absolute` option is not available in this PostCSS build"
+            );
+          }
+        }
+        toUrl(path) {
+          let cached = this.memoizedURLs.get(path);
+          if (cached) return cached;
+          if (sep === "\\") {
+            path = path.replace(/\\/g, "/");
+          }
+          let url = encodeURI(path).replace(/[#?]/g, encodeURIComponent);
+          this.memoizedURLs.set(path, url);
+          return url;
+        }
+      };
+      module.exports = MapGenerator;
+    }
+  });
+
+  // node_modules/postcss/lib/tokenize.js
+  var require_tokenize = __commonJS({
+    "node_modules/postcss/lib/tokenize.js"(exports, module) {
+      "use strict";
+      var SINGLE_QUOTE = "'".charCodeAt(0);
+      var DOUBLE_QUOTE = '"'.charCodeAt(0);
+      var BACKSLASH = "\\".charCodeAt(0);
+      var SLASH = "/".charCodeAt(0);
+      var NEWLINE = "\n".charCodeAt(0);
+      var SPACE = " ".charCodeAt(0);
+      var FEED = "\f".charCodeAt(0);
+      var TAB = "	".charCodeAt(0);
+      var CR = "\r".charCodeAt(0);
+      var OPEN_SQUARE = "[".charCodeAt(0);
+      var CLOSE_SQUARE = "]".charCodeAt(0);
+      var OPEN_PARENTHESES = "(".charCodeAt(0);
+      var CLOSE_PARENTHESES = ")".charCodeAt(0);
+      var OPEN_CURLY = "{".charCodeAt(0);
+      var CLOSE_CURLY = "}".charCodeAt(0);
+      var SEMICOLON = ";".charCodeAt(0);
+      var ASTERISK = "*".charCodeAt(0);
+      var COLON = ":".charCodeAt(0);
+      var AT = "@".charCodeAt(0);
+      var RE_AT_END = /[\t\n\f\r "#'()/;[\\\]{}]/g;
+      var RE_WORD_END = /[\t\n\f\r !"#'():;@[\\\]{}]|\/(?=\*)/g;
+      var RE_BAD_BRACKET = /.[\r\n"'(/\\]/;
+      var RE_HEX_ESCAPE = /[\da-f]/i;
+      module.exports = function tokenizer(input, options = {}) {
+        let css = input.css.valueOf();
+        let ignore = options.ignoreErrors;
+        let code, content, escape, next, quote;
+        let currentToken, escaped, escapePos, n, prev;
+        let length = css.length;
+        let pos = 0;
+        let buffer = [];
+        let returned = [];
+        let lastBadParen = -1;
+        function position() {
+          return pos;
+        }
+        function unclosed(what) {
+          throw input.error("Unclosed " + what, pos);
+        }
+        function endOfFile() {
+          return returned.length === 0 && pos >= length;
+        }
+        function nextToken(opts) {
+          if (returned.length) return returned.pop();
+          if (pos >= length) return;
+          let ignoreUnclosed = opts ? opts.ignoreUnclosed : false;
+          code = css.charCodeAt(pos);
+          switch (code) {
+            case NEWLINE:
+            case SPACE:
+            case TAB:
+            case CR:
+            case FEED: {
+              next = pos;
+              do {
+                next += 1;
+                code = css.charCodeAt(next);
+              } while (code === SPACE || code === NEWLINE || code === TAB || code === CR || code === FEED);
+              currentToken = ["space", css.slice(pos, next)];
+              pos = next - 1;
+              break;
+            }
+            case OPEN_SQUARE:
+            case CLOSE_SQUARE:
+            case OPEN_CURLY:
+            case CLOSE_CURLY:
+            case COLON:
+            case SEMICOLON:
+            case CLOSE_PARENTHESES: {
+              let controlChar = String.fromCharCode(code);
+              currentToken = [controlChar, controlChar, pos];
+              break;
+            }
+            case OPEN_PARENTHESES: {
+              prev = buffer.length ? buffer.pop()[1] : "";
+              n = css.charCodeAt(pos + 1);
+              if (prev === "url" && n !== SINGLE_QUOTE && n !== DOUBLE_QUOTE && n !== SPACE && n !== NEWLINE && n !== TAB && n !== FEED && n !== CR) {
+                next = pos;
+                do {
+                  escaped = false;
+                  next = css.indexOf(")", next + 1);
+                  if (next === -1) {
+                    if (ignore || ignoreUnclosed) {
+                      next = pos;
+                      break;
+                    } else {
+                      unclosed("bracket");
+                    }
+                  }
+                  escapePos = next;
+                  while (css.charCodeAt(escapePos - 1) === BACKSLASH) {
+                    escapePos -= 1;
+                    escaped = !escaped;
+                  }
+                } while (escaped);
+                currentToken = ["brackets", css.slice(pos, next + 1), pos, next];
+                pos = next;
+              } else if (pos <= lastBadParen) {
+                currentToken = ["(", "(", pos];
+              } else {
+                next = css.indexOf(")", pos + 1);
+                content = css.slice(pos, next + 1);
+                if (next === -1 || RE_BAD_BRACKET.test(content)) {
+                  lastBadParen = next === -1 ? length : next;
+                  currentToken = ["(", "(", pos];
+                } else {
+                  currentToken = ["brackets", content, pos, next];
+                  pos = next;
+                }
+              }
+              break;
+            }
+            case SINGLE_QUOTE:
+            case DOUBLE_QUOTE: {
+              quote = code === SINGLE_QUOTE ? "'" : '"';
+              next = pos;
+              do {
+                escaped = false;
+                next = css.indexOf(quote, next + 1);
+                if (next === -1) {
+                  if (ignore || ignoreUnclosed) {
+                    next = pos + 1;
+                    break;
+                  } else {
+                    unclosed("string");
+                  }
+                }
+                escapePos = next;
+                while (css.charCodeAt(escapePos - 1) === BACKSLASH) {
+                  escapePos -= 1;
+                  escaped = !escaped;
+                }
+              } while (escaped);
+              currentToken = ["string", css.slice(pos, next + 1), pos, next];
+              pos = next;
+              break;
+            }
+            case AT: {
+              RE_AT_END.lastIndex = pos + 1;
+              RE_AT_END.test(css);
+              if (RE_AT_END.lastIndex === 0) {
+                next = css.length - 1;
+              } else {
+                next = RE_AT_END.lastIndex - 2;
+              }
+              currentToken = ["at-word", css.slice(pos, next + 1), pos, next];
+              pos = next;
+              break;
+            }
+            case BACKSLASH: {
+              next = pos;
+              escape = true;
+              while (css.charCodeAt(next + 1) === BACKSLASH) {
+                next += 1;
+                escape = !escape;
+              }
+              code = css.charCodeAt(next + 1);
+              if (escape && code !== SLASH && code !== SPACE && code !== NEWLINE && code !== TAB && code !== CR && code !== FEED) {
+                next += 1;
+                if (RE_HEX_ESCAPE.test(css.charAt(next))) {
+                  while (RE_HEX_ESCAPE.test(css.charAt(next + 1))) {
+                    next += 1;
+                  }
+                  if (css.charCodeAt(next + 1) === SPACE) {
+                    next += 1;
+                  }
+                }
+              }
+              currentToken = ["word", css.slice(pos, next + 1), pos, next];
+              pos = next;
+              break;
+            }
+            default: {
+              if (code === SLASH && css.charCodeAt(pos + 1) === ASTERISK) {
+                next = css.indexOf("*/", pos + 2) + 1;
+                if (next === 0) {
+                  if (ignore || ignoreUnclosed) {
+                    next = css.length;
+                  } else {
+                    unclosed("comment");
+                  }
+                }
+                currentToken = ["comment", css.slice(pos, next + 1), pos, next];
+                pos = next;
+              } else {
+                RE_WORD_END.lastIndex = pos + 1;
+                RE_WORD_END.test(css);
+                if (RE_WORD_END.lastIndex === 0) {
+                  next = css.length - 1;
+                } else {
+                  next = RE_WORD_END.lastIndex - 2;
+                }
+                currentToken = ["word", css.slice(pos, next + 1), pos, next];
+                buffer.push(currentToken);
+                pos = next;
+              }
+              break;
+            }
+          }
+          pos++;
+          return currentToken;
+        }
+        function back(token) {
+          returned.push(token);
+        }
+        return {
+          back,
+          endOfFile,
+          nextToken,
+          position
+        };
+      };
+    }
+  });
+
+  // node_modules/postcss/lib/parser.js
+  var require_parser = __commonJS({
+    "node_modules/postcss/lib/parser.js"(exports, module) {
+      "use strict";
+      var AtRule2 = require_at_rule();
+      var Comment2 = require_comment();
+      var Declaration2 = require_declaration();
+      var Root2 = require_root();
+      var Rule2 = require_rule();
+      var tokenizer = require_tokenize();
+      var SAFE_COMMENT_NEIGHBOR = {
+        empty: true,
+        space: true
+      };
+      function findLastWithPosition(tokens) {
+        for (let i = tokens.length - 1; i >= 0; i--) {
+          let token = tokens[i];
+          let pos = token[3] || token[2];
+          if (pos) return pos;
+        }
+      }
+      function tokensToString(tokens, from, to) {
+        let result = "";
+        for (let i = from; i < to; i++) result += tokens[i][1];
+        return result;
+      }
+      var Parser = class {
+        constructor(input) {
+          this.input = input;
+          this.root = new Root2();
+          this.current = this.root;
+          this.spaces = "";
+          this.semicolon = false;
+          this.createTokenizer();
+          this.root.source = { input, start: { column: 1, line: 1, offset: 0 } };
+        }
+        atrule(token) {
+          let node = new AtRule2();
+          node.name = token[1].slice(1);
+          if (node.name === "") {
+            this.unnamedAtrule(node, token);
+          }
+          this.init(node, token[2]);
+          let type;
+          let prev;
+          let shift;
+          let last = false;
+          let open = false;
+          let params = [];
+          let brackets = [];
+          while (!this.tokenizer.endOfFile()) {
+            token = this.tokenizer.nextToken();
+            type = token[0];
+            if (type === "(" || type === "[") {
+              brackets.push(type === "(" ? ")" : "]");
+            } else if (type === "{" && brackets.length > 0) {
+              brackets.push("}");
+            } else if (type === brackets[brackets.length - 1]) {
+              brackets.pop();
+            }
+            if (brackets.length === 0) {
+              if (type === ";") {
+                node.source.end = this.getPosition(token[2]);
+                node.source.end.offset++;
+                this.semicolon = true;
+                break;
+              } else if (type === "{") {
+                open = true;
+                break;
+              } else if (type === "}") {
+                if (params.length > 0) {
+                  shift = params.length - 1;
+                  prev = params[shift];
+                  while (prev && prev[0] === "space") {
+                    prev = params[--shift];
+                  }
+                  if (prev) {
+                    node.source.end = this.getPosition(prev[3] || prev[2]);
+                    node.source.end.offset++;
+                  }
+                }
+                this.end(token);
+                break;
+              } else {
+                params.push(token);
+              }
+            } else {
+              params.push(token);
+            }
+            if (this.tokenizer.endOfFile()) {
+              last = true;
+              break;
+            }
+          }
+          node.raws.between = this.spacesAndCommentsFromEnd(params);
+          if (params.length) {
+            node.raws.afterName = this.spacesAndCommentsFromStart(params);
+            this.raw(node, "params", params);
+            if (last) {
+              token = params[params.length - 1];
+              node.source.end = this.getPosition(token[3] || token[2]);
+              node.source.end.offset++;
+              this.spaces = node.raws.between;
+              node.raws.between = "";
+            }
+          } else {
+            node.raws.afterName = "";
+            node.params = "";
+          }
+          if (open) {
+            node.nodes = [];
+            this.current = node;
+          }
+        }
+        checkMissedSemicolon(tokens) {
+          let colon = this.colon(tokens);
+          if (colon === false) return;
+          let founded = 0;
+          let token;
+          for (let j = colon - 1; j >= 0; j--) {
+            token = tokens[j];
+            if (token[0] !== "space") {
+              founded += 1;
+              if (founded === 2) break;
+            }
+          }
+          throw this.input.error(
+            "Missed semicolon",
+            token[0] === "word" ? token[3] + 1 : token[2]
+          );
+        }
+        colon(tokens) {
+          let brackets = 0;
+          let prev, token, type;
+          for (let [i, element] of tokens.entries()) {
+            token = element;
+            type = token[0];
+            if (type === "(") {
+              brackets += 1;
+            }
+            if (type === ")") {
+              brackets -= 1;
+            }
+            if (brackets === 0 && type === ":") {
+              if (!prev) {
+                this.doubleColon(token);
+              } else if (prev[0] === "word" && prev[1] === "progid") {
+                continue;
+              } else {
+                return i;
+              }
+            }
+            prev = token;
+          }
+          return false;
+        }
+        comment(token) {
+          let node = new Comment2();
+          this.init(node, token[2]);
+          node.source.end = this.getPosition(token[3] || token[2]);
+          node.source.end.offset++;
+          let text = token[1].slice(2, -2);
+          if (!text.trim()) {
+            node.text = "";
+            node.raws.left = text;
+            node.raws.right = "";
+          } else {
+            let match = text.match(/^(\s*)([^]*\S)(\s*)$/);
+            node.text = match[2];
+            node.raws.left = match[1];
+            node.raws.right = match[3];
+          }
+        }
+        createTokenizer() {
+          this.tokenizer = tokenizer(this.input);
+        }
+        decl(tokens, customProperty) {
+          let node = new Declaration2();
+          this.init(node, tokens[0][2]);
+          let last = tokens[tokens.length - 1];
+          if (last[0] === ";") {
+            this.semicolon = true;
+            tokens.pop();
+          }
+          node.source.end = this.getPosition(
+            last[3] || last[2] || findLastWithPosition(tokens)
+          );
+          node.source.end.offset++;
+          let start = 0;
+          while (tokens[start][0] !== "word") {
+            if (start === tokens.length - 1) this.unknownWord([tokens[start]]);
+            start++;
+          }
+          node.raws.before += tokensToString(tokens, 0, start);
+          node.source.start = this.getPosition(tokens[start][2]);
+          let propStart = start;
+          while (start < tokens.length) {
+            let type = tokens[start][0];
+            if (type === ":" || type === "space" || type === "comment") {
+              break;
+            }
+            start++;
+          }
+          node.prop = tokensToString(tokens, propStart, start);
+          let betweenStart = start;
+          let token;
+          while (start < tokens.length) {
+            token = tokens[start];
+            start++;
+            if (token[0] === ":") break;
+            if (token[0] === "word" && /\w/.test(token[1])) {
+              this.unknownWord([token]);
+            }
+          }
+          node.raws.between = tokensToString(tokens, betweenStart, start);
+          if (node.prop[0] === "_" || node.prop[0] === "*") {
+            node.raws.before += node.prop[0];
+            node.prop = node.prop.slice(1);
+          }
+          let firstSpacesStart = start;
+          while (start < tokens.length) {
+            let next = tokens[start][0];
+            if (next !== "space" && next !== "comment") break;
+            start++;
+          }
+          let firstSpaces = tokens.slice(firstSpacesStart, start);
+          tokens = tokens.slice(start);
+          this.precheckMissedSemicolon(tokens);
+          for (let i = tokens.length - 1; i >= 0; i--) {
+            token = tokens[i];
+            if (token[1].toLowerCase() === "!important") {
+              node.important = true;
+              let string = this.stringFrom(tokens, i);
+              string = this.spacesFromEnd(tokens) + string;
+              if (string !== " !important") node.raws.important = string;
+              break;
+            } else if (token[1].toLowerCase() === "important") {
+              let cache = tokens.slice(0);
+              let str = "";
+              for (let j = i; j > 0; j--) {
+                let type = cache[j][0];
+                if (str.trim().startsWith("!") && type !== "space") {
+                  break;
+                }
+                str = cache.pop()[1] + str;
+              }
+              if (str.trim().startsWith("!")) {
+                node.important = true;
+                node.raws.important = str;
+                tokens = cache;
+              }
+            }
+            if (token[0] !== "space" && token[0] !== "comment") {
+              break;
+            }
+          }
+          let hasWord = tokens.some((i) => i[0] !== "space" && i[0] !== "comment");
+          if (hasWord) {
+            node.raws.between += firstSpaces.map((i) => i[1]).join("");
+            firstSpaces = [];
+          }
+          this.raw(node, "value", firstSpaces.concat(tokens), customProperty);
+          if (node.value.includes(":") && !customProperty) {
+            this.checkMissedSemicolon(tokens);
+          }
+        }
+        doubleColon(token) {
+          throw this.input.error(
+            "Double colon",
+            { offset: token[2] },
+            { offset: token[2] + token[1].length }
+          );
+        }
+        emptyRule(token) {
+          let node = new Rule2();
+          this.init(node, token[2]);
+          node.selector = "";
+          node.raws.between = "";
+          this.current = node;
+        }
+        end(token) {
+          if (this.current.nodes && this.current.nodes.length) {
+            this.current.raws.semicolon = this.semicolon;
+          }
+          this.semicolon = false;
+          this.current.raws.after = (this.current.raws.after || "") + this.spaces;
+          this.spaces = "";
+          if (this.current.parent) {
+            this.current.source.end = this.getPosition(token[2]);
+            this.current.source.end.offset++;
+            this.current = this.current.parent;
+          } else {
+            this.unexpectedClose(token);
+          }
+        }
+        endFile() {
+          if (this.current.parent) this.unclosedBlock();
+          if (this.current.nodes && this.current.nodes.length) {
+            this.current.raws.semicolon = this.semicolon;
+          }
+          this.current.raws.after = (this.current.raws.after || "") + this.spaces;
+          this.root.source.end = this.getPosition(this.tokenizer.position());
+        }
+        freeSemicolon(token) {
+          this.spaces += token[1];
+          if (this.current.nodes) {
+            let prev = this.current.nodes[this.current.nodes.length - 1];
+            if (prev && prev.type === "rule" && !prev.raws.ownSemicolon) {
+              prev.raws.ownSemicolon = this.spaces;
+              this.spaces = "";
+              prev.source.end = this.getPosition(token[2]);
+              prev.source.end.offset += prev.raws.ownSemicolon.length;
+            }
+          }
+        }
+        // Helpers
+        getPosition(offset) {
+          let pos = this.input.fromOffset(offset);
+          return {
+            column: pos.col,
+            line: pos.line,
+            offset
+          };
+        }
+        init(node, offset) {
+          this.current.push(node);
+          node.source = {
+            input: this.input,
+            start: this.getPosition(offset)
+          };
+          node.raws.before = this.spaces;
+          this.spaces = "";
+          if (node.type !== "comment") this.semicolon = false;
+        }
+        other(start) {
+          let end = false;
+          let type = null;
+          let colon = false;
+          let bracket = null;
+          let brackets = [];
+          let customProperty = start[1].startsWith("--");
+          let tokens = [];
+          let token = start;
+          while (token) {
+            type = token[0];
+            tokens.push(token);
+            if (type === "(" || type === "[") {
+              if (!bracket) bracket = token;
+              brackets.push(type === "(" ? ")" : "]");
+            } else if (customProperty && colon && type === "{") {
+              if (!bracket) bracket = token;
+              brackets.push("}");
+            } else if (brackets.length === 0) {
+              if (type === ";") {
+                if (colon) {
+                  this.decl(tokens, customProperty);
+                  return;
+                } else {
+                  break;
+                }
+              } else if (type === "{") {
+                this.rule(tokens);
+                return;
+              } else if (type === "}") {
+                this.tokenizer.back(tokens.pop());
+                end = true;
+                break;
+              } else if (type === ":") {
+                colon = true;
+              }
+            } else if (type === brackets[brackets.length - 1]) {
+              brackets.pop();
+              if (brackets.length === 0) bracket = null;
+            }
+            token = this.tokenizer.nextToken();
+          }
+          if (this.tokenizer.endOfFile()) end = true;
+          if (brackets.length > 0) this.unclosedBracket(bracket);
+          if (end && colon) {
+            if (!customProperty) {
+              while (tokens.length) {
+                token = tokens[tokens.length - 1][0];
+                if (token !== "space" && token !== "comment") break;
+                this.tokenizer.back(tokens.pop());
+              }
+            }
+            this.decl(tokens, customProperty);
+          } else {
+            this.unknownWord(tokens);
+          }
+        }
+        parse() {
+          let token;
+          while (!this.tokenizer.endOfFile()) {
+            token = this.tokenizer.nextToken();
+            switch (token[0]) {
+              case "space":
+                this.spaces += token[1];
+                break;
+              case ";":
+                this.freeSemicolon(token);
+                break;
+              case "}":
+                this.end(token);
+                break;
+              case "comment":
+                this.comment(token);
+                break;
+              case "at-word":
+                this.atrule(token);
+                break;
+              case "{":
+                this.emptyRule(token);
+                break;
+              default:
+                this.other(token);
+                break;
+            }
+          }
+          this.endFile();
+        }
+        precheckMissedSemicolon() {
+        }
+        raw(node, prop, tokens, customProperty) {
+          let token, type;
+          let length = tokens.length;
+          let value = "";
+          let clean = true;
+          let next, prev;
+          for (let i = 0; i < length; i += 1) {
+            token = tokens[i];
+            type = token[0];
+            if (type === "space" && i === length - 1 && !customProperty) {
+              clean = false;
+            } else if (type === "comment") {
+              prev = tokens[i - 1] ? tokens[i - 1][0] : "empty";
+              next = tokens[i + 1] ? tokens[i + 1][0] : "empty";
+              if (!SAFE_COMMENT_NEIGHBOR[prev] && !SAFE_COMMENT_NEIGHBOR[next]) {
+                if (value.slice(-1) === ",") {
+                  clean = false;
+                } else {
+                  value += token[1];
+                }
+              } else {
+                clean = false;
+              }
+            } else {
+              value += token[1];
+            }
+          }
+          if (!clean) {
+            let raw = tokens.reduce((all, i) => all + i[1], "");
+            node.raws[prop] = { raw, value };
+          }
+          node[prop] = value;
+        }
+        rule(tokens) {
+          tokens.pop();
+          let node = new Rule2();
+          this.init(node, tokens[0][2]);
+          node.raws.between = this.spacesAndCommentsFromEnd(tokens);
+          this.raw(node, "selector", tokens);
+          this.current = node;
+        }
+        spacesAndCommentsFromEnd(tokens) {
+          let lastTokenType;
+          let spaces = "";
+          while (tokens.length) {
+            lastTokenType = tokens[tokens.length - 1][0];
+            if (lastTokenType !== "space" && lastTokenType !== "comment") break;
+            spaces = tokens.pop()[1] + spaces;
+          }
+          return spaces;
+        }
+        // Errors
+        spacesAndCommentsFromStart(tokens) {
+          let next;
+          let spaces = "";
+          while (tokens.length) {
+            next = tokens[0][0];
+            if (next !== "space" && next !== "comment") break;
+            spaces += tokens.shift()[1];
+          }
+          return spaces;
+        }
+        spacesFromEnd(tokens) {
+          let lastTokenType;
+          let spaces = "";
+          while (tokens.length) {
+            lastTokenType = tokens[tokens.length - 1][0];
+            if (lastTokenType !== "space") break;
+            spaces = tokens.pop()[1] + spaces;
+          }
+          return spaces;
+        }
+        stringFrom(tokens, from) {
+          let result = "";
+          for (let i = from; i < tokens.length; i++) {
+            result += tokens[i][1];
+          }
+          tokens.splice(from, tokens.length - from);
+          return result;
+        }
+        unclosedBlock() {
+          let pos = this.current.source.start;
+          throw this.input.error("Unclosed block", pos.line, pos.column);
+        }
+        unclosedBracket(bracket) {
+          throw this.input.error(
+            "Unclosed bracket",
+            { offset: bracket[2] },
+            { offset: bracket[2] + 1 }
+          );
+        }
+        unexpectedClose(token) {
+          throw this.input.error(
+            "Unexpected }",
+            { offset: token[2] },
+            { offset: token[2] + 1 }
+          );
+        }
+        unknownWord(tokens) {
+          throw this.input.error(
+            "Unknown word " + tokens[0][1],
+            { offset: tokens[0][2] },
+            { offset: tokens[0][2] + tokens[0][1].length }
+          );
+        }
+        unnamedAtrule(node, token) {
+          throw this.input.error(
+            "At-rule without name",
+            { offset: token[2] },
+            { offset: token[2] + token[1].length }
+          );
+        }
+      };
+      module.exports = Parser;
+    }
+  });
+
+  // node_modules/postcss/lib/parse.js
+  var require_parse = __commonJS({
+    "node_modules/postcss/lib/parse.js"(exports, module) {
+      "use strict";
+      var Container2 = require_container();
+      var Input2 = require_input();
+      var Parser = require_parser();
+      function parse3(css, opts) {
+        let input = new Input2(css, opts);
+        let parser = new Parser(input);
+        try {
+          parser.parse();
+        } catch (e) {
+          if (true) {
+            if (e.name === "CssSyntaxError" && opts && opts.from) {
+              if (/\.scss$/i.test(opts.from)) {
+                e.message += "\nYou tried to parse SCSS with the standard CSS parser; try again with the postcss-scss parser";
+              } else if (/\.sass/i.test(opts.from)) {
+                e.message += "\nYou tried to parse Sass with the standard CSS parser; try again with the postcss-sass parser";
+              } else if (/\.less$/i.test(opts.from)) {
+                e.message += "\nYou tried to parse Less with the standard CSS parser; try again with the postcss-less parser";
+              }
+            }
+          }
+          throw e;
+        }
+        return parser.root;
+      }
+      module.exports = parse3;
+      parse3.default = parse3;
+      Container2.registerParse(parse3);
+    }
+  });
+
+  // node_modules/postcss/lib/warning.js
+  var require_warning = __commonJS({
+    "node_modules/postcss/lib/warning.js"(exports, module) {
+      "use strict";
+      var Container2 = require_container();
+      var { my } = require_symbols();
+      var Warning2 = class {
+        constructor(text, opts = {}) {
+          this.type = "warning";
+          this.text = text;
+          if (opts.node && opts.node.source) {
+            if (!opts.node[my]) {
+              Container2.rebuild(opts.node);
+            }
+            let range = opts.node.rangeBy(opts);
+            this.line = range.start.line;
+            this.column = range.start.column;
+            this.endLine = range.end.line;
+            this.endColumn = range.end.column;
+          }
+          for (let opt in opts) this[opt] = opts[opt];
+        }
+        toString() {
+          if (this.node) {
+            return this.node.error(this.text, {
+              index: this.index,
+              plugin: this.plugin,
+              word: this.word
+            }).message;
+          }
+          if (this.plugin) {
+            return this.plugin + ": " + this.text;
+          }
+          return this.text;
+        }
+      };
+      module.exports = Warning2;
+      Warning2.default = Warning2;
+    }
+  });
+
+  // node_modules/postcss/lib/result.js
+  var require_result = __commonJS({
+    "node_modules/postcss/lib/result.js"(exports, module) {
+      "use strict";
+      var Warning2 = require_warning();
+      var Result2 = class {
+        get content() {
+          return this.css;
+        }
+        constructor(processor, root2, opts) {
+          this.processor = processor;
+          this.messages = [];
+          this.root = root2;
+          this.opts = opts;
+          this.css = "";
+          this.map = void 0;
+        }
+        toString() {
+          return this.css;
+        }
+        warn(text, opts = {}) {
+          if (!opts.plugin) {
+            if (this.lastPlugin && this.lastPlugin.postcssPlugin) {
+              opts.plugin = this.lastPlugin.postcssPlugin;
+            }
+          }
+          let warning = new Warning2(text, opts);
+          this.messages.push(warning);
+          return warning;
+        }
+        warnings() {
+          return this.messages.filter((i) => i.type === "warning");
+        }
+      };
+      module.exports = Result2;
+      Result2.default = Result2;
+    }
+  });
+
+  // node_modules/postcss/lib/warn-once.js
+  var require_warn_once = __commonJS({
+    "node_modules/postcss/lib/warn-once.js"(exports, module) {
+      "use strict";
+      var printed = {};
+      module.exports = function warnOnce(message) {
+        if (printed[message]) return;
+        printed[message] = true;
+        if (typeof console !== "undefined" && console.warn) {
+          console.warn(message);
+        }
+      };
+    }
+  });
+
+  // node_modules/postcss/lib/lazy-result.js
+  var require_lazy_result = __commonJS({
+    "node_modules/postcss/lib/lazy-result.js"(exports, module) {
+      "use strict";
+      var Container2 = require_container();
+      var Document2 = require_document();
+      var MapGenerator = require_map_generator();
+      var parse3 = require_parse();
+      var Result2 = require_result();
+      var Root2 = require_root();
+      var stringify3 = require_stringify();
+      var { isClean, my } = require_symbols();
+      var warnOnce = require_warn_once();
+      var TYPE_TO_CLASS_NAME = {
+        atrule: "AtRule",
+        comment: "Comment",
+        decl: "Declaration",
+        document: "Document",
+        root: "Root",
+        rule: "Rule"
+      };
+      var PLUGIN_PROPS = {
+        AtRule: true,
+        AtRuleExit: true,
+        Comment: true,
+        CommentExit: true,
+        Declaration: true,
+        DeclarationExit: true,
+        Document: true,
+        DocumentExit: true,
+        Once: true,
+        OnceExit: true,
+        postcssPlugin: true,
+        prepare: true,
+        Root: true,
+        RootExit: true,
+        Rule: true,
+        RuleExit: true
+      };
+      var NOT_VISITORS = {
+        Once: true,
+        postcssPlugin: true,
+        prepare: true
+      };
+      var CHILDREN = 0;
+      function isPromise(obj) {
+        return typeof obj === "object" && typeof obj.then === "function";
+      }
+      function getEvents(node) {
+        let key = false;
+        let type = TYPE_TO_CLASS_NAME[node.type];
+        if (node.type === "decl") {
+          key = node.prop.toLowerCase();
+        } else if (node.type === "atrule") {
+          key = node.name.toLowerCase();
+        }
+        if (key && node.append) {
+          return [
+            type,
+            type + "-" + key,
+            CHILDREN,
+            type + "Exit",
+            type + "Exit-" + key
+          ];
+        } else if (key) {
+          return [type, type + "-" + key, type + "Exit", type + "Exit-" + key];
+        } else if (node.append) {
+          return [type, CHILDREN, type + "Exit"];
+        } else {
+          return [type, type + "Exit"];
+        }
+      }
+      function toStack(node) {
+        let events;
+        if (node.type === "document") {
+          events = ["Document", CHILDREN, "DocumentExit"];
+        } else if (node.type === "root") {
+          events = ["Root", CHILDREN, "RootExit"];
+        } else {
+          events = getEvents(node);
+        }
+        return {
+          eventIndex: 0,
+          events,
+          iterator: 0,
+          node,
+          visitorIndex: 0,
+          visitors: []
+        };
+      }
+      function cleanMarks(node) {
+        let stack = [node];
+        while (stack.length > 0) {
+          let next = stack.pop();
+          next[isClean] = false;
+          if (next.nodes) {
+            for (let i of next.nodes) stack.push(i);
+          }
+        }
+        return node;
+      }
+      var postcss2 = {};
+      var LazyResult = class _LazyResult {
+        get content() {
+          return this.stringify().content;
+        }
+        get css() {
+          return this.stringify().css;
+        }
+        get map() {
+          return this.stringify().map;
+        }
+        get messages() {
+          return this.sync().messages;
+        }
+        get opts() {
+          return this.result.opts;
+        }
+        get processor() {
+          return this.result.processor;
+        }
+        get root() {
+          return this.sync().root;
+        }
+        get [Symbol.toStringTag]() {
+          return "LazyResult";
+        }
+        constructor(processor, css, opts) {
+          this.stringified = false;
+          this.processed = false;
+          let root2;
+          if (typeof css === "object" && css !== null && (css.type === "root" || css.type === "document")) {
+            root2 = cleanMarks(css);
+          } else if (css instanceof _LazyResult || css instanceof Result2) {
+            root2 = cleanMarks(css.root);
+            if (css.map) {
+              if (typeof opts.map === "undefined") opts.map = {};
+              if (!opts.map.inline) opts.map.inline = false;
+              opts.map.prev = css.map;
+            }
+          } else {
+            let parser = parse3;
+            if (opts.syntax) parser = opts.syntax.parse;
+            if (opts.parser) parser = opts.parser;
+            if (parser.parse) parser = parser.parse;
+            try {
+              root2 = parser(css, opts);
+            } catch (error) {
+              this.processed = true;
+              this.error = error;
+            }
+            if (root2 && !root2[my]) {
+              Container2.rebuild(root2);
+            }
+          }
+          this.result = new Result2(processor, root2, opts);
+          this.helpers = { ...postcss2, postcss: postcss2, result: this.result };
+          this.plugins = this.processor.plugins.map((plugin2) => {
+            if (typeof plugin2 === "object" && plugin2.prepare) {
+              return { ...plugin2, ...plugin2.prepare(this.result) };
+            } else {
+              return plugin2;
+            }
+          });
+        }
+        async() {
+          if (this.error) return Promise.reject(this.error);
+          if (this.processed) return Promise.resolve(this.result);
+          if (!this.processing) {
+            this.processing = this.runAsync();
+          }
+          return this.processing;
+        }
+        catch(onRejected) {
+          return this.async().catch(onRejected);
+        }
+        finally(onFinally) {
+          return this.async().then(onFinally, onFinally);
+        }
+        getAsyncError() {
+          throw new Error("Use process(css).then(cb) to work with async plugins");
+        }
+        handleError(error, node) {
+          let plugin2 = this.result.lastPlugin;
+          try {
+            if (node) node.addToError(error);
+            this.error = error;
+            if (error.name === "CssSyntaxError" && !error.plugin) {
+              error.plugin = plugin2.postcssPlugin;
+              error.setMessage();
+            } else if (plugin2.postcssVersion) {
+              if (true) {
+                let pluginName = plugin2.postcssPlugin;
+                let pluginVer = plugin2.postcssVersion;
+                let runtimeVer = this.result.processor.version;
+                let a = pluginVer.split(".");
+                let b = runtimeVer.split(".");
+                if (a[0] !== b[0] || parseInt(a[1]) > parseInt(b[1])) {
+                  console.error(
+                    "Unknown error from PostCSS plugin. Your current PostCSS version is " + runtimeVer + ", but " + pluginName + " uses " + pluginVer + ". Perhaps this is the source of the error below."
+                  );
+                }
+              }
+            }
+          } catch (err) {
+            if (console && console.error) console.error(err);
+          }
+          return error;
+        }
+        prepareVisitors() {
+          this.listeners = {};
+          let add = (plugin2, type, cb) => {
+            if (!this.listeners[type]) this.listeners[type] = [];
+            this.listeners[type].push([plugin2, cb]);
+          };
+          for (let plugin2 of this.plugins) {
+            if (typeof plugin2 === "object") {
+              for (let event in plugin2) {
+                if (!PLUGIN_PROPS[event] && /^[A-Z]/.test(event)) {
+                  throw new Error(
+                    `Unknown event ${event} in ${plugin2.postcssPlugin}. Try to update PostCSS (${this.processor.version} now).`
+                  );
+                }
+                if (!NOT_VISITORS[event]) {
+                  if (typeof plugin2[event] === "object") {
+                    for (let filter in plugin2[event]) {
+                      if (filter === "*") {
+                        add(plugin2, event, plugin2[event][filter]);
+                      } else {
+                        add(
+                          plugin2,
+                          event + "-" + filter.toLowerCase(),
+                          plugin2[event][filter]
+                        );
+                      }
+                    }
+                  } else if (typeof plugin2[event] === "function") {
+                    add(plugin2, event, plugin2[event]);
+                  }
+                }
+              }
+            }
+          }
+          this.hasListener = Object.keys(this.listeners).length > 0;
+        }
+        async runAsync() {
+          this.plugin = 0;
+          for (let i = 0; i < this.plugins.length; i++) {
+            let plugin2 = this.plugins[i];
+            let promise = this.runOnRoot(plugin2);
+            if (isPromise(promise)) {
+              try {
+                await promise;
+              } catch (error) {
+                throw this.handleError(error);
+              }
+            }
+          }
+          this.prepareVisitors();
+          if (this.hasListener) {
+            let root2 = this.result.root;
+            while (!root2[isClean]) {
+              root2[isClean] = true;
+              let stack = [toStack(root2)];
+              while (stack.length > 0) {
+                let promise = this.visitTick(stack);
+                if (isPromise(promise)) {
+                  try {
+                    await promise;
+                  } catch (e) {
+                    let node = stack[stack.length - 1].node;
+                    throw this.handleError(e, node);
+                  }
+                }
+              }
+            }
+            if (this.listeners.OnceExit) {
+              for (let [plugin2, visitor] of this.listeners.OnceExit) {
+                this.result.lastPlugin = plugin2;
+                try {
+                  if (root2.type === "document") {
+                    let roots = root2.nodes.map(
+                      (subRoot) => visitor(subRoot, this.helpers)
+                    );
+                    await Promise.all(roots);
+                  } else {
+                    await visitor(root2, this.helpers);
+                  }
+                } catch (e) {
+                  throw this.handleError(e);
+                }
+              }
+            }
+          }
+          this.processed = true;
+          return this.stringify();
+        }
+        runOnRoot(plugin2) {
+          this.result.lastPlugin = plugin2;
+          try {
+            if (typeof plugin2 === "object" && plugin2.Once) {
+              if (this.result.root.type === "document") {
+                let roots = this.result.root.nodes.map(
+                  (root2) => plugin2.Once(root2, this.helpers)
+                );
+                if (isPromise(roots[0])) {
+                  return Promise.all(roots);
+                }
+                return roots;
+              }
+              return plugin2.Once(this.result.root, this.helpers);
+            } else if (typeof plugin2 === "function") {
+              return plugin2(this.result.root, this.result);
+            }
+          } catch (error) {
+            throw this.handleError(error);
+          }
+        }
+        stringify() {
+          if (this.error) throw this.error;
+          if (this.stringified) return this.result;
+          this.stringified = true;
+          this.sync();
+          let opts = this.result.opts;
+          let str = stringify3;
+          if (opts.syntax) str = opts.syntax.stringify;
+          if (opts.stringifier) str = opts.stringifier;
+          if (str.stringify) str = str.stringify;
+          let rootSource = this.result.root.source;
+          if (opts.map === void 0 && !(rootSource && rootSource.input && rootSource.input.map)) {
+            let result = "";
+            str(this.result.root, (i) => {
+              result += i;
+            });
+            this.result.css = result;
+            return this.result;
+          }
+          let map = new MapGenerator(str, this.result.root, this.result.opts);
+          let data = map.generate();
+          this.result.css = data[0];
+          this.result.map = data[1];
+          return this.result;
+        }
+        sync() {
+          if (this.error) throw this.error;
+          if (this.processed) return this.result;
+          this.processed = true;
+          if (this.processing) {
+            throw this.getAsyncError();
+          }
+          for (let plugin2 of this.plugins) {
+            let promise = this.runOnRoot(plugin2);
+            if (isPromise(promise)) {
+              throw this.getAsyncError();
+            }
+          }
+          this.prepareVisitors();
+          if (this.hasListener) {
+            let root2 = this.result.root;
+            while (!root2[isClean]) {
+              root2[isClean] = true;
+              this.walkSync(root2);
+            }
+            if (this.listeners.OnceExit) {
+              if (root2.type === "document") {
+                for (let subRoot of root2.nodes) {
+                  this.visitSync(this.listeners.OnceExit, subRoot);
+                }
+              } else {
+                this.visitSync(this.listeners.OnceExit, root2);
+              }
+            }
+          }
+          return this.result;
+        }
+        then(onFulfilled, onRejected) {
+          if (true) {
+            if (!("from" in this.opts)) {
+              warnOnce(
+                "Without `from` option PostCSS could generate wrong source map and will not find Browserslist config. Set it to CSS file path or to `undefined` to prevent this warning."
+              );
+            }
+          }
+          return this.async().then(onFulfilled, onRejected);
+        }
+        toString() {
+          return this.css;
+        }
+        visitSync(visitors, node) {
+          for (let [plugin2, visitor] of visitors) {
+            this.result.lastPlugin = plugin2;
+            let promise;
+            try {
+              promise = visitor(node, this.helpers);
+            } catch (e) {
+              throw this.handleError(e, node.proxyOf);
+            }
+            if (node.type !== "root" && node.type !== "document" && !node.parent) {
+              return true;
+            }
+            if (isPromise(promise)) {
+              throw this.getAsyncError();
+            }
+          }
+        }
+        visitTick(stack) {
+          let visit = stack[stack.length - 1];
+          let { node, visitors } = visit;
+          if (node.type !== "root" && node.type !== "document" && !node.parent) {
+            stack.pop();
+            return;
+          }
+          if (visitors.length > 0 && visit.visitorIndex < visitors.length) {
+            let [plugin2, visitor] = visitors[visit.visitorIndex];
+            visit.visitorIndex += 1;
+            if (visit.visitorIndex === visitors.length) {
+              visit.visitors = [];
+              visit.visitorIndex = 0;
+            }
+            this.result.lastPlugin = plugin2;
+            try {
+              return visitor(node.toProxy(), this.helpers);
+            } catch (e) {
+              throw this.handleError(e, node);
+            }
+          }
+          if (visit.iterator !== 0) {
+            let iterator = visit.iterator;
+            if (visit.descending) {
+              visit.descending = false;
+              node.indexes[iterator] += 1;
+            }
+            let child;
+            while (child = node.nodes[node.indexes[iterator]]) {
+              if (!child[isClean]) {
+                child[isClean] = true;
+                visit.descending = true;
+                stack.push(toStack(child));
+                return;
+              }
+              node.indexes[iterator] += 1;
+            }
+            visit.iterator = 0;
+            delete node.indexes[iterator];
+          }
+          let events = visit.events;
+          while (visit.eventIndex < events.length) {
+            let event = events[visit.eventIndex];
+            visit.eventIndex += 1;
+            if (event === CHILDREN) {
+              if (node.nodes && node.nodes.length) {
+                node[isClean] = true;
+                visit.iterator = node.getIterator();
+              }
+              return;
+            } else if (this.listeners[event]) {
+              visit.visitors = this.listeners[event];
+              return;
+            }
+          }
+          stack.pop();
+        }
+        walkSync(node) {
+          node[isClean] = true;
+          let stack = [{ eventIndex: 0, events: getEvents(node), iterator: 0, node }];
+          while (stack.length > 0) {
+            let visit = stack[stack.length - 1];
+            let visitNode = visit.node;
+            if (visit.iterator !== 0) {
+              let iterator = visit.iterator;
+              if (visit.descending) {
+                visit.descending = false;
+                visitNode.indexes[iterator] += 1;
+              }
+              let child;
+              let descended = false;
+              while (child = visitNode.nodes[visitNode.indexes[iterator]]) {
+                if (!child[isClean]) {
+                  child[isClean] = true;
+                  visit.descending = true;
+                  stack.push({
+                    eventIndex: 0,
+                    events: getEvents(child),
+                    iterator: 0,
+                    node: child
+                  });
+                  descended = true;
+                  break;
+                }
+                visitNode.indexes[iterator] += 1;
+              }
+              if (descended) continue;
+              visit.iterator = 0;
+              delete visitNode.indexes[iterator];
+            }
+            if (visit.eventIndex < visit.events.length) {
+              let event = visit.events[visit.eventIndex];
+              visit.eventIndex += 1;
+              if (event === CHILDREN) {
+                if (visitNode.nodes && visitNode.nodes.length) {
+                  visit.iterator = visitNode.getIterator();
+                }
+              } else {
+                let visitors = this.listeners[event];
+                if (visitors) {
+                  if (this.visitSync(visitors, visitNode.toProxy())) stack.pop();
+                }
+              }
+              continue;
+            }
+            stack.pop();
+          }
+        }
+        warnings() {
+          return this.sync().warnings();
+        }
+      };
+      LazyResult.registerPostcss = (dependant) => {
+        postcss2 = dependant;
+      };
+      module.exports = LazyResult;
+      LazyResult.default = LazyResult;
+      Root2.registerLazyResult(LazyResult);
+      Document2.registerLazyResult(LazyResult);
+    }
+  });
+
+  // node_modules/postcss/lib/no-work-result.js
+  var require_no_work_result = __commonJS({
+    "node_modules/postcss/lib/no-work-result.js"(exports, module) {
+      "use strict";
+      var MapGenerator = require_map_generator();
+      var parse3 = require_parse();
+      var Result2 = require_result();
+      var stringify3 = require_stringify();
+      var warnOnce = require_warn_once();
+      var NoWorkResult = class {
+        get content() {
+          return this.result.css;
+        }
+        get css() {
+          return this.result.css;
+        }
+        get map() {
+          return this.result.map;
+        }
+        get messages() {
+          return [];
+        }
+        get opts() {
+          return this.result.opts;
+        }
+        get processor() {
+          return this.result.processor;
+        }
+        get root() {
+          if (this._root) {
+            return this._root;
+          }
+          let root2;
+          let parser = parse3;
+          try {
+            root2 = parser(this._css, this._opts);
+          } catch (error) {
+            this.error = error;
+          }
+          if (this.error) {
+            throw this.error;
+          } else {
+            this._root = root2;
+            return root2;
+          }
+        }
+        get [Symbol.toStringTag]() {
+          return "NoWorkResult";
+        }
+        constructor(processor, css, opts) {
+          css = css.toString();
+          this.stringified = false;
+          this._processor = processor;
+          this._css = css;
+          this._opts = opts;
+          this._map = void 0;
+          let str = stringify3;
+          this.result = new Result2(this._processor, void 0, this._opts);
+          this.result.css = css;
+          let self = this;
+          Object.defineProperty(this.result, "root", {
+            get() {
+              return self.root;
+            }
+          });
+          let map = new MapGenerator(str, void 0, this._opts, css);
+          if (map.isMap()) {
+            let [generatedCSS, generatedMap] = map.generate();
+            if (generatedCSS) {
+              this.result.css = generatedCSS;
+            }
+            if (generatedMap) {
+              this.result.map = generatedMap;
+            }
+          } else {
+            map.clearAnnotation();
+            this.result.css = map.css;
+          }
+        }
+        async() {
+          if (this.error) return Promise.reject(this.error);
+          return Promise.resolve(this.result);
+        }
+        catch(onRejected) {
+          return this.async().catch(onRejected);
+        }
+        finally(onFinally) {
+          return this.async().then(onFinally, onFinally);
+        }
+        sync() {
+          if (this.error) throw this.error;
+          return this.result;
+        }
+        then(onFulfilled, onRejected) {
+          if (true) {
+            if (!("from" in this._opts)) {
+              warnOnce(
+                "Without `from` option PostCSS could generate wrong source map and will not find Browserslist config. Set it to CSS file path or to `undefined` to prevent this warning."
+              );
+            }
+          }
+          return this.async().then(onFulfilled, onRejected);
+        }
+        toString() {
+          return this._css;
+        }
+        warnings() {
+          return [];
+        }
+      };
+      module.exports = NoWorkResult;
+      NoWorkResult.default = NoWorkResult;
+    }
+  });
+
+  // node_modules/postcss/lib/processor.js
+  var require_processor = __commonJS({
+    "node_modules/postcss/lib/processor.js"(exports, module) {
+      "use strict";
+      var Document2 = require_document();
+      var LazyResult = require_lazy_result();
+      var NoWorkResult = require_no_work_result();
+      var Root2 = require_root();
+      var Processor2 = class {
+        constructor(plugins = []) {
+          this.version = "8.5.25";
+          this.plugins = this.normalize(plugins);
+        }
+        normalize(plugins) {
+          let normalized = [];
+          for (let i of plugins) {
+            if (i.postcss === true) {
+              i = i();
+            } else if (i.postcss) {
+              i = i.postcss;
+            }
+            if (typeof i === "object" && Array.isArray(i.plugins)) {
+              normalized = normalized.concat(i.plugins);
+            } else if (typeof i === "object" && i.postcssPlugin) {
+              normalized.push(i);
+            } else if (typeof i === "function") {
+              normalized.push(i);
+            } else if (typeof i === "object" && (i.parse || i.stringify)) {
+              if (true) {
+                throw new Error(
+                  "PostCSS syntaxes cannot be used as plugins. Instead, please use one of the syntax/parser/stringifier options as outlined in your PostCSS runner documentation."
+                );
+              }
+            } else {
+              throw new Error(i + " is not a PostCSS plugin");
+            }
+          }
+          return normalized;
+        }
+        process(css, opts = {}) {
+          if (!this.plugins.length && !opts.parser && !opts.stringifier && !opts.syntax) {
+            return new NoWorkResult(this, css, opts);
+          } else {
+            return new LazyResult(this, css, opts);
+          }
+        }
+        use(plugin2) {
+          this.plugins = this.plugins.concat(this.normalize([plugin2]));
+          return this;
+        }
+      };
+      module.exports = Processor2;
+      Processor2.default = Processor2;
+      Root2.registerProcessor(Processor2);
+      Document2.registerProcessor(Processor2);
+    }
+  });
+
+  // node_modules/postcss/lib/postcss.js
+  var require_postcss = __commonJS({
+    "node_modules/postcss/lib/postcss.js"(exports, module) {
+      "use strict";
+      var AtRule2 = require_at_rule();
+      var Comment2 = require_comment();
+      var Container2 = require_container();
+      var CssSyntaxError2 = require_css_syntax_error();
+      var Declaration2 = require_declaration();
+      var Document2 = require_document();
+      var fromJSON2 = require_fromJSON();
+      var Input2 = require_input();
+      var LazyResult = require_lazy_result();
+      var list2 = require_list();
+      var Node3 = require_node();
+      var parse3 = require_parse();
+      var Processor2 = require_processor();
+      var Result2 = require_result();
+      var Root2 = require_root();
+      var Rule2 = require_rule();
+      var stringify3 = require_stringify();
+      var Warning2 = require_warning();
+      function postcss2(...plugins) {
+        if (plugins.length === 1 && Array.isArray(plugins[0])) {
+          plugins = plugins[0];
+        }
+        return new Processor2(plugins);
+      }
+      postcss2.plugin = function plugin2(name, initializer) {
+        let warningPrinted = false;
+        function creator(...args) {
+          if (console && console.warn && !warningPrinted) {
+            warningPrinted = true;
+            console.warn(
+              name + ": postcss.plugin was deprecated. Migration guide:\nhttps://evilmartians.com/chronicles/postcss-8-plugin-migration"
+            );
+            if (process.env.LANG && process.env.LANG.startsWith("cn")) {
+              console.warn(
+                name + ": \u91CC\u9762 postcss.plugin \u88AB\u5F03\u7528. \u8FC1\u79FB\u6307\u5357:\nhttps://www.w3ctech.com/topic/2226"
+              );
+            }
+          }
+          let transformer = initializer(...args);
+          transformer.postcssPlugin = name;
+          transformer.postcssVersion = new Processor2().version;
+          return transformer;
+        }
+        let cache;
+        Object.defineProperty(creator, "postcss", {
+          get() {
+            if (!cache) cache = creator();
+            return cache;
+          }
+        });
+        creator.process = function(css, processOpts, pluginOpts) {
+          return postcss2([creator(pluginOpts)]).process(css, processOpts);
+        };
+        return creator;
+      };
+      postcss2.stringify = stringify3;
+      postcss2.parse = parse3;
+      postcss2.fromJSON = fromJSON2;
+      postcss2.list = list2;
+      postcss2.comment = (defaults2) => new Comment2(defaults2);
+      postcss2.atRule = (defaults2) => new AtRule2(defaults2);
+      postcss2.decl = (defaults2) => new Declaration2(defaults2);
+      postcss2.rule = (defaults2) => new Rule2(defaults2);
+      postcss2.root = (defaults2) => new Root2(defaults2);
+      postcss2.document = (defaults2) => new Document2(defaults2);
+      postcss2.CssSyntaxError = CssSyntaxError2;
+      postcss2.Declaration = Declaration2;
+      postcss2.Container = Container2;
+      postcss2.Processor = Processor2;
+      postcss2.Document = Document2;
+      postcss2.Comment = Comment2;
+      postcss2.Warning = Warning2;
+      postcss2.AtRule = AtRule2;
+      postcss2.Result = Result2;
+      postcss2.Input = Input2;
+      postcss2.Rule = Rule2;
+      postcss2.Root = Root2;
+      postcss2.Node = Node3;
+      LazyResult.registerPostcss(postcss2);
+      module.exports = postcss2;
+      postcss2.default = postcss2;
+    }
+  });
+
+  // node_modules/postcss-value-parser/lib/parse.js
+  var require_parse2 = __commonJS({
+    "node_modules/postcss-value-parser/lib/parse.js"(exports, module) {
+      var openParentheses = "(".charCodeAt(0);
+      var closeParentheses = ")".charCodeAt(0);
+      var singleQuote = "'".charCodeAt(0);
+      var doubleQuote = '"'.charCodeAt(0);
+      var backslash = "\\".charCodeAt(0);
+      var slash = "/".charCodeAt(0);
+      var comma = ",".charCodeAt(0);
+      var colon = ":".charCodeAt(0);
+      var star = "*".charCodeAt(0);
+      var uLower = "u".charCodeAt(0);
+      var uUpper = "U".charCodeAt(0);
+      var plus = "+".charCodeAt(0);
+      var isUnicodeRange = /^[a-f0-9?-]+$/i;
+      module.exports = function(input) {
+        var tokens = [];
+        var value = input;
+        var next, quote, prev, token, escape, escapePos, whitespacePos, parenthesesOpenPos;
+        var pos = 0;
+        var code = value.charCodeAt(pos);
+        var max = value.length;
+        var stack = [{ nodes: tokens }];
+        var balanced = 0;
+        var parent;
+        var name = "";
+        var before = "";
+        var after = "";
+        while (pos < max) {
+          if (code <= 32) {
+            next = pos;
+            do {
+              next += 1;
+              code = value.charCodeAt(next);
+            } while (code <= 32);
+            token = value.slice(pos, next);
+            prev = tokens[tokens.length - 1];
+            if (code === closeParentheses && balanced) {
+              after = token;
+            } else if (prev && prev.type === "div") {
+              prev.after = token;
+              prev.sourceEndIndex += token.length;
+            } else if (code === comma || code === colon || code === slash && value.charCodeAt(next + 1) !== star && (!parent || parent && parent.type === "function" && parent.value !== "calc")) {
+              before = token;
+            } else {
+              tokens.push({
+                type: "space",
+                sourceIndex: pos,
+                sourceEndIndex: next,
+                value: token
+              });
+            }
+            pos = next;
+          } else if (code === singleQuote || code === doubleQuote) {
+            next = pos;
+            quote = code === singleQuote ? "'" : '"';
+            token = {
+              type: "string",
+              sourceIndex: pos,
+              quote
+            };
+            do {
+              escape = false;
+              next = value.indexOf(quote, next + 1);
+              if (~next) {
+                escapePos = next;
+                while (value.charCodeAt(escapePos - 1) === backslash) {
+                  escapePos -= 1;
+                  escape = !escape;
+                }
+              } else {
+                value += quote;
+                next = value.length - 1;
+                token.unclosed = true;
+              }
+            } while (escape);
+            token.value = value.slice(pos + 1, next);
+            token.sourceEndIndex = token.unclosed ? next : next + 1;
+            tokens.push(token);
+            pos = next + 1;
+            code = value.charCodeAt(pos);
+          } else if (code === slash && value.charCodeAt(pos + 1) === star) {
+            next = value.indexOf("*/", pos);
+            token = {
+              type: "comment",
+              sourceIndex: pos,
+              sourceEndIndex: next + 2
+            };
+            if (next === -1) {
+              token.unclosed = true;
+              next = value.length;
+              token.sourceEndIndex = next;
+            }
+            token.value = value.slice(pos + 2, next);
+            tokens.push(token);
+            pos = next + 2;
+            code = value.charCodeAt(pos);
+          } else if ((code === slash || code === star) && parent && parent.type === "function" && parent.value === "calc") {
+            token = value[pos];
+            tokens.push({
+              type: "word",
+              sourceIndex: pos - before.length,
+              sourceEndIndex: pos + token.length,
+              value: token
+            });
+            pos += 1;
+            code = value.charCodeAt(pos);
+          } else if (code === slash || code === comma || code === colon) {
+            token = value[pos];
+            tokens.push({
+              type: "div",
+              sourceIndex: pos - before.length,
+              sourceEndIndex: pos + token.length,
+              value: token,
+              before,
+              after: ""
+            });
+            before = "";
+            pos += 1;
+            code = value.charCodeAt(pos);
+          } else if (openParentheses === code) {
+            next = pos;
+            do {
+              next += 1;
+              code = value.charCodeAt(next);
+            } while (code <= 32);
+            parenthesesOpenPos = pos;
+            token = {
+              type: "function",
+              sourceIndex: pos - name.length,
+              value: name,
+              before: value.slice(parenthesesOpenPos + 1, next)
+            };
+            pos = next;
+            if (name === "url" && code !== singleQuote && code !== doubleQuote) {
+              next -= 1;
+              do {
+                escape = false;
+                next = value.indexOf(")", next + 1);
+                if (~next) {
+                  escapePos = next;
+                  while (value.charCodeAt(escapePos - 1) === backslash) {
+                    escapePos -= 1;
+                    escape = !escape;
+                  }
+                } else {
+                  value += ")";
+                  next = value.length - 1;
+                  token.unclosed = true;
+                }
+              } while (escape);
+              whitespacePos = next;
+              do {
+                whitespacePos -= 1;
+                code = value.charCodeAt(whitespacePos);
+              } while (code <= 32);
+              if (parenthesesOpenPos < whitespacePos) {
+                if (pos !== whitespacePos + 1) {
+                  token.nodes = [
+                    {
+                      type: "word",
+                      sourceIndex: pos,
+                      sourceEndIndex: whitespacePos + 1,
+                      value: value.slice(pos, whitespacePos + 1)
+                    }
+                  ];
+                } else {
+                  token.nodes = [];
+                }
+                if (token.unclosed && whitespacePos + 1 !== next) {
+                  token.after = "";
+                  token.nodes.push({
+                    type: "space",
+                    sourceIndex: whitespacePos + 1,
+                    sourceEndIndex: next,
+                    value: value.slice(whitespacePos + 1, next)
+                  });
+                } else {
+                  token.after = value.slice(whitespacePos + 1, next);
+                  token.sourceEndIndex = next;
+                }
+              } else {
+                token.after = "";
+                token.nodes = [];
+              }
+              pos = next + 1;
+              token.sourceEndIndex = token.unclosed ? next : pos;
+              code = value.charCodeAt(pos);
+              tokens.push(token);
+            } else {
+              balanced += 1;
+              token.after = "";
+              token.sourceEndIndex = pos + 1;
+              tokens.push(token);
+              stack.push(token);
+              tokens = token.nodes = [];
+              parent = token;
+            }
+            name = "";
+          } else if (closeParentheses === code && balanced) {
+            pos += 1;
+            code = value.charCodeAt(pos);
+            parent.after = after;
+            parent.sourceEndIndex += after.length;
+            after = "";
+            balanced -= 1;
+            stack[stack.length - 1].sourceEndIndex = pos;
+            stack.pop();
+            parent = stack[balanced];
+            tokens = parent.nodes;
+          } else {
+            next = pos;
+            do {
+              if (code === backslash) {
+                next += 1;
+              }
+              next += 1;
+              code = value.charCodeAt(next);
+            } while (next < max && !(code <= 32 || code === singleQuote || code === doubleQuote || code === comma || code === colon || code === slash || code === openParentheses || code === star && parent && parent.type === "function" && parent.value === "calc" || code === slash && parent.type === "function" && parent.value === "calc" || code === closeParentheses && balanced));
+            token = value.slice(pos, next);
+            if (openParentheses === code) {
+              name = token;
+            } else if ((uLower === token.charCodeAt(0) || uUpper === token.charCodeAt(0)) && plus === token.charCodeAt(1) && isUnicodeRange.test(token.slice(2))) {
+              tokens.push({
+                type: "unicode-range",
+                sourceIndex: pos,
+                sourceEndIndex: next,
+                value: token
+              });
+            } else {
+              tokens.push({
+                type: "word",
+                sourceIndex: pos,
+                sourceEndIndex: next,
+                value: token
+              });
+            }
+            pos = next;
+          }
+        }
+        for (pos = stack.length - 1; pos; pos -= 1) {
+          stack[pos].unclosed = true;
+          stack[pos].sourceEndIndex = value.length;
+        }
+        return stack[0].nodes;
+      };
+    }
+  });
+
+  // node_modules/postcss-value-parser/lib/walk.js
+  var require_walk = __commonJS({
+    "node_modules/postcss-value-parser/lib/walk.js"(exports, module) {
+      module.exports = function walk(nodes, cb, bubble) {
+        var i, max, node, result;
+        for (i = 0, max = nodes.length; i < max; i += 1) {
+          node = nodes[i];
+          if (!bubble) {
+            result = cb(node, i, nodes);
+          }
+          if (result !== false && node.type === "function" && Array.isArray(node.nodes)) {
+            walk(node.nodes, cb, bubble);
+          }
+          if (bubble) {
+            cb(node, i, nodes);
+          }
+        }
+      };
+    }
+  });
+
+  // node_modules/postcss-value-parser/lib/stringify.js
+  var require_stringify2 = __commonJS({
+    "node_modules/postcss-value-parser/lib/stringify.js"(exports, module) {
+      function stringifyNode(node, custom) {
+        var type = node.type;
+        var value = node.value;
+        var buf;
+        var customResult;
+        if (custom && (customResult = custom(node)) !== void 0) {
+          return customResult;
+        } else if (type === "word" || type === "space") {
+          return value;
+        } else if (type === "string") {
+          buf = node.quote || "";
+          return buf + value + (node.unclosed ? "" : buf);
+        } else if (type === "comment") {
+          return "/*" + value + (node.unclosed ? "" : "*/");
+        } else if (type === "div") {
+          return (node.before || "") + value + (node.after || "");
+        } else if (Array.isArray(node.nodes)) {
+          buf = stringify3(node.nodes, custom);
+          if (type !== "function") {
+            return buf;
+          }
+          return value + "(" + (node.before || "") + buf + (node.after || "") + (node.unclosed ? "" : ")");
+        }
+        return value;
+      }
+      function stringify3(nodes, custom) {
+        var result, i;
+        if (Array.isArray(nodes)) {
+          result = "";
+          for (i = nodes.length - 1; ~i; i -= 1) {
+            result = stringifyNode(nodes[i], custom) + result;
+          }
+          return result;
+        }
+        return stringifyNode(nodes, custom);
+      }
+      module.exports = stringify3;
+    }
+  });
+
+  // node_modules/postcss-value-parser/lib/unit.js
+  var require_unit = __commonJS({
+    "node_modules/postcss-value-parser/lib/unit.js"(exports, module) {
+      var minus = "-".charCodeAt(0);
+      var plus = "+".charCodeAt(0);
+      var dot = ".".charCodeAt(0);
+      var exp = "e".charCodeAt(0);
+      var EXP = "E".charCodeAt(0);
+      function likeNumber(value) {
+        var code = value.charCodeAt(0);
+        var nextCode;
+        if (code === plus || code === minus) {
+          nextCode = value.charCodeAt(1);
+          if (nextCode >= 48 && nextCode <= 57) {
+            return true;
+          }
+          var nextNextCode = value.charCodeAt(2);
+          if (nextCode === dot && nextNextCode >= 48 && nextNextCode <= 57) {
+            return true;
+          }
+          return false;
+        }
+        if (code === dot) {
+          nextCode = value.charCodeAt(1);
+          if (nextCode >= 48 && nextCode <= 57) {
+            return true;
+          }
+          return false;
+        }
+        if (code >= 48 && code <= 57) {
+          return true;
+        }
+        return false;
+      }
+      module.exports = function(value) {
+        var pos = 0;
+        var length = value.length;
+        var code;
+        var nextCode;
+        var nextNextCode;
+        if (length === 0 || !likeNumber(value)) {
+          return false;
+        }
+        code = value.charCodeAt(pos);
+        if (code === plus || code === minus) {
+          pos++;
+        }
+        while (pos < length) {
+          code = value.charCodeAt(pos);
+          if (code < 48 || code > 57) {
+            break;
+          }
+          pos += 1;
+        }
+        code = value.charCodeAt(pos);
+        nextCode = value.charCodeAt(pos + 1);
+        if (code === dot && nextCode >= 48 && nextCode <= 57) {
+          pos += 2;
+          while (pos < length) {
+            code = value.charCodeAt(pos);
+            if (code < 48 || code > 57) {
+              break;
+            }
+            pos += 1;
+          }
+        }
+        code = value.charCodeAt(pos);
+        nextCode = value.charCodeAt(pos + 1);
+        nextNextCode = value.charCodeAt(pos + 2);
+        if ((code === exp || code === EXP) && (nextCode >= 48 && nextCode <= 57 || (nextCode === plus || nextCode === minus) && nextNextCode >= 48 && nextNextCode <= 57)) {
+          pos += nextCode === plus || nextCode === minus ? 3 : 2;
+          while (pos < length) {
+            code = value.charCodeAt(pos);
+            if (code < 48 || code > 57) {
+              break;
+            }
+            pos += 1;
+          }
+        }
+        return {
+          number: value.slice(0, pos),
+          unit: value.slice(pos)
+        };
+      };
+    }
+  });
+
+  // node_modules/postcss-value-parser/lib/index.js
+  var require_lib = __commonJS({
+    "node_modules/postcss-value-parser/lib/index.js"(exports, module) {
+      var parse3 = require_parse2();
+      var walk = require_walk();
+      var stringify3 = require_stringify2();
+      function ValueParser(value) {
+        if (this instanceof ValueParser) {
+          this.nodes = parse3(value);
+          return this;
+        }
+        return new ValueParser(value);
+      }
+      ValueParser.prototype.toString = function() {
+        return Array.isArray(this.nodes) ? stringify3(this.nodes) : "";
+      };
+      ValueParser.prototype.walk = function(cb, bubble) {
+        walk(this.nodes, cb, bubble);
+        return this;
+      };
+      ValueParser.unit = require_unit();
+      ValueParser.walk = walk;
+      ValueParser.stringify = stringify3;
+      module.exports = ValueParser;
+    }
+  });
+
+  // node_modules/postcss/lib/postcss.mjs
+  var import_postcss = __toESM(require_postcss(), 1);
+  var stringify = import_postcss.default.stringify;
+  var fromJSON = import_postcss.default.fromJSON;
+  var plugin = import_postcss.default.plugin;
+  var parse = import_postcss.default.parse;
+  var list = import_postcss.default.list;
+  var document = import_postcss.default.document;
+  var comment = import_postcss.default.comment;
+  var atRule = import_postcss.default.atRule;
+  var rule = import_postcss.default.rule;
+  var decl = import_postcss.default.decl;
+  var root = import_postcss.default.root;
+  var CssSyntaxError = import_postcss.default.CssSyntaxError;
+  var Declaration = import_postcss.default.Declaration;
+  var Container = import_postcss.default.Container;
+  var Processor = import_postcss.default.Processor;
+  var Document = import_postcss.default.Document;
+  var Comment = import_postcss.default.Comment;
+  var Warning = import_postcss.default.Warning;
+  var AtRule = import_postcss.default.AtRule;
+  var Result = import_postcss.default.Result;
+  var Input = import_postcss.default.Input;
+  var Rule = import_postcss.default.Rule;
+  var Root = import_postcss.default.Root;
+  var Node2 = import_postcss.default.Node;
+
+  // node_modules/dom-to-svg/lib/index.js
+  var import_postcss_value_parser4 = __toESM(require_lib(), 1);
+
+  // node_modules/dom-to-svg/lib/css.js
+  var isCSSFontFaceRule = (rule2) => rule2.type === CSSRule.FONT_FACE_RULE;
+  var isInline = (styles) => styles.displayOutside === "inline" || styles.display.startsWith("inline-");
+  var isPositioned = (styles) => styles.position !== "static";
+  var isInFlow = (styles) => styles.float !== "none" && styles.position !== "absolute" && styles.position !== "fixed";
+  var isTransparent = (color) => color === "transparent" || color === "rgba(0, 0, 0, 0)";
+  var hasUniformBorder = (styles) => parseFloat(styles.borderTopWidth) !== 0 && styles.borderTopStyle !== "none" && styles.borderTopStyle !== "inset" && styles.borderTopStyle !== "outset" && !isTransparent(styles.borderTopColor) && // Cannot use border property directly as in Firefox those are empty strings.
+  // Need to get the specific border properties from the specific sides.
+  // https://stackoverflow.com/questions/41696063/getcomputedstyle-returns-empty-strings-on-ff-when-instead-crome-returns-a-comp
+  styles.borderTopWidth === styles.borderLeftWidth && styles.borderTopWidth === styles.borderRightWidth && styles.borderTopWidth === styles.borderBottomWidth && styles.borderTopColor === styles.borderLeftColor && styles.borderTopColor === styles.borderRightColor && styles.borderTopColor === styles.borderBottomColor && styles.borderTopStyle === styles.borderLeftStyle && styles.borderTopStyle === styles.borderRightStyle && styles.borderTopStyle === styles.borderBottomStyle;
+  var SIDES = ["top", "bottom", "right", "left"];
+  var isHorizontal = (side) => side === "bottom" || side === "top";
+  var CORNERS = {
+    top: ["left", "right"],
+    bottom: ["left", "right"],
+    left: ["top", "bottom"],
+    right: ["top", "bottom"]
+  };
+  function getBorderRadiiForSide(side, styles, bounds) {
+    var _a, _b, _c, _d;
+    const [horizontalStyle1, verticalStyle1] = styles.getPropertyValue(isHorizontal(side) ? `border-${side}-${CORNERS[side][0]}-radius` : `border-${CORNERS[side][0]}-${side}-radius`).split(" ");
+    const [horizontalStyle2, verticalStyle2] = styles.getPropertyValue(isHorizontal(side) ? `border-${side}-${CORNERS[side][1]}-radius` : `border-${CORNERS[side][1]}-${side}-radius`).split(" ");
+    if (isHorizontal(side)) {
+      return [
+        (_a = parseCSSLength(horizontalStyle1 || "0px", bounds.width)) !== null && _a !== void 0 ? _a : 0,
+        (_b = parseCSSLength(horizontalStyle2 || "0px", bounds.width)) !== null && _b !== void 0 ? _b : 0
+      ];
+    }
+    return [
+      (_c = parseCSSLength(verticalStyle1 || horizontalStyle1 || "0px", bounds.height)) !== null && _c !== void 0 ? _c : 0,
+      (_d = parseCSSLength(verticalStyle2 || horizontalStyle2 || "0px", bounds.height)) !== null && _d !== void 0 ? _d : 0
+    ];
+  }
+  var calculateOverlappingCurvesFactor = (styles, bounds) => Math.min(...SIDES.map((side) => {
+    const length = isHorizontal(side) ? bounds.width : bounds.height;
+    const radiiSum = getBorderRadiiForSide(side, styles, bounds).reduce((sum, radius) => sum + radius, 0);
+    return length / radiiSum;
+  }), 1);
+  var isVisible = (styles) => styles.displayOutside !== "none" && styles.display !== "none" && styles.visibility !== "hidden" && styles.opacity !== "0";
+  function parseCSSLength(length, containerLength) {
+    if (length.endsWith("px")) {
+      return parseFloat(length);
+    }
+    if (length.endsWith("%")) {
+      return parseFloat(length) / 100 * containerLength;
+    }
+    return void 0;
+  }
+  var unescapeStringValue = (value) => value.replace(/\\([\da-f]{1,2})/gi, (substring, codePoint) => String.fromCodePoint(parseInt(codePoint, 16))).replace(/\\(.)/g, "$1");
+  function copyCssStyles(from, to) {
+    for (const property of from) {
+      to.setProperty(property, from.getPropertyValue(property), from.getPropertyPriority(property));
+    }
+  }
+
+  // node_modules/dom-to-svg/lib/dom.js
+  var svgNamespace = "http://www.w3.org/2000/svg";
+  var xlinkNamespace = "http://www.w3.org/1999/xlink";
+  var xhtmlNamespace = "http://www.w3.org/1999/xhtml";
+  var isElement = (node) => node.nodeType === Node.ELEMENT_NODE;
+  var isTextNode = (node) => node.nodeType === Node.TEXT_NODE;
+  var isSVGElement = (element) => element.namespaceURI === svgNamespace;
+  var isSVGSVGElement = (element) => isSVGElement(element) && element.tagName === "svg";
+  var isSVGGraphicsElement = (element) => isSVGElement(element) && "getCTM" in element && "getScreenCTM" in element;
+  var isSVGAnchorElement = (element) => isSVGElement(element) && element.tagName === "a";
+  var isSVGTextContentElement = (element) => isSVGElement(element) && "textLength" in element;
+  var isSVGImageElement = (element) => element.tagName === "image" && isSVGElement(element);
+  var isSVGStyleElement = (element) => element.tagName === "style" && isSVGElement(element);
+  var isHTMLElement = (element) => element.namespaceURI === xhtmlNamespace;
+  var isHTMLAnchorElement = (element) => element.tagName === "A" && isHTMLElement(element);
+  var isHTMLImageElement = (element) => element.tagName === "IMG" && isHTMLElement(element);
+  var isHTMLInputElement = (element) => element.tagName === "INPUT" && isHTMLElement(element);
+  var hasLabels = (element) => "labels" in element;
+
+  // node_modules/dom-to-svg/lib/stacking.js
+  var stackingContextEstablishingProperties = /* @__PURE__ */ new Set([
+    "clipPath",
+    "contain",
+    "filter",
+    "isolation",
+    "mask",
+    "maskBorder",
+    "maskImage",
+    "mixBlendMode",
+    "opacity",
+    "perspective",
+    "position",
+    "transform",
+    "webkitOverflowScrolling",
+    "zIndex"
+  ]);
+  function establishesStackingContext(styles, parentStyles) {
+    return !!((styles.position === "absolute" || styles.position === "relative") && styles.zIndex !== "auto" || styles.position === "fixed" || styles.position === "sticky" || parentStyles && (parentStyles.display === "flex" || parentStyles.display === "grid") && styles.zIndex !== "auto" || parseFloat(styles.opacity) !== 1 || styles.mixBlendMode !== "normal" || styles.transform !== "none" || styles.filter !== "none" || styles.perspective !== "none" || styles.clipPath !== "none" || styles.mask !== "none" || styles.maskImage !== "none" || styles.maskBorder !== "none" || styles.isolation === "isolate" || styles.webkitOverflowScrolling === "touch" || styles.contain === "layout" || styles.contain === "paint" || styles.contain === "strict" || styles.contain === "content" || styles.willChange.split(",").some((property) => stackingContextEstablishingProperties.has(property.trim())));
+  }
+  var STACKING_LAYER_NAMES = [
+    "rootBackgroundAndBorders",
+    "childStackingContextsWithNegativeStackLevels",
+    "inFlowNonInlineNonPositionedDescendants",
+    "nonPositionedFloats",
+    "inFlowInlineLevelNonPositionedDescendants",
+    "childStackingContextsWithStackLevelZeroAndPositionedDescendantsWithStackLevelZero",
+    "childStackingContextsWithPositiveStackLevels"
+  ];
+  function createStackingLayer(parent, layerName) {
+    const layer = parent.ownerDocument.createElementNS(svgNamespace, "g");
+    layer.dataset.stackingLayer = layerName;
+    parent.append(layer);
+    return layer;
+  }
+  function createStackingLayers(container) {
+    container.dataset.stackingContext = "true";
+    return {
+      rootBackgroundAndBorders: createStackingLayer(container, "rootBackgroundAndBorders"),
+      childStackingContextsWithNegativeStackLevels: createStackingLayer(container, "childStackingContextsWithNegativeStackLevels"),
+      inFlowNonInlineNonPositionedDescendants: createStackingLayer(container, "inFlowNonInlineNonPositionedDescendants"),
+      nonPositionedFloats: createStackingLayer(container, "nonPositionedFloats"),
+      inFlowInlineLevelNonPositionedDescendants: createStackingLayer(container, "inFlowInlineLevelNonPositionedDescendants"),
+      childStackingContextsWithStackLevelZeroAndPositionedDescendantsWithStackLevelZero: createStackingLayer(container, "childStackingContextsWithStackLevelZeroAndPositionedDescendantsWithStackLevelZero"),
+      childStackingContextsWithPositiveStackLevels: createStackingLayer(container, "childStackingContextsWithPositiveStackLevels")
+    };
+  }
+  function determineStackingLayer(styles, parentStyles) {
+    const zIndex = styles.zIndex !== "auto" ? parseInt(styles.zIndex, 10) : void 0;
+    if (zIndex !== void 0 && zIndex < 0 && establishesStackingContext(styles, parentStyles)) {
+      return "childStackingContextsWithNegativeStackLevels";
+    }
+    if (isInFlow(styles) && !isInline(styles) && !isPositioned(styles)) {
+      return "inFlowNonInlineNonPositionedDescendants";
+    }
+    if (!isPositioned(styles) && styles.float !== "none") {
+      return "nonPositionedFloats";
+    }
+    if (isInFlow(styles) && isInline(styles) && !isPositioned(styles)) {
+      return "inFlowInlineLevelNonPositionedDescendants";
+    }
+    if (zIndex === 0 && (isPositioned(styles) || establishesStackingContext(styles, parentStyles))) {
+      return "childStackingContextsWithStackLevelZeroAndPositionedDescendantsWithStackLevelZero";
+    }
+    if (zIndex !== void 0 && zIndex > 0 && establishesStackingContext(styles, parentStyles)) {
+      return "childStackingContextsWithPositiveStackLevels";
+    }
+    return void 0;
+  }
+  function sortChildrenByZIndex(parent) {
+    const sorted = [...parent.children].sort((a, b) => {
+      const zIndexA = a.dataset.zIndex;
+      const zIndexB = b.dataset.zIndex;
+      if (!zIndexA || !zIndexB) {
+        return 0;
+      }
+      return parseInt(zIndexA, 10) - parseInt(zIndexB, 10);
+    });
+    for (const child of sorted) {
+      parent.append(child);
+    }
+  }
+  function sortStackingLayerChildren(stackingLayers) {
+    sortChildrenByZIndex(stackingLayers.childStackingContextsWithNegativeStackLevels);
+    sortChildrenByZIndex(stackingLayers.childStackingContextsWithPositiveStackLevels);
+  }
+  function cleanupStackingLayerChildren(stackingLayers) {
+    for (const name of STACKING_LAYER_NAMES) {
+      const layer = stackingLayers[name];
+      if (!layer.hasChildNodes()) {
+        layer.remove();
+      }
+    }
+  }
+
+  // node_modules/dom-to-svg/lib/element.js
+  var import_postcss_value_parser2 = __toESM(require_lib(), 1);
+
+  // node_modules/dom-to-svg/lib/accessibility.js
+  var isStandaloneFooter = (element) => !element.closest('article, aside, main, nav, section, [role="article"], [role="complementary"], [role="main"], [role="navigation"], [role="region"]');
+  function getAccessibilityAttributes(element, { labels, getUniqueId }) {
+    var _a, _b, _c;
+    const attributes = /* @__PURE__ */ new Map();
+    switch (element.tagName) {
+      case "A":
+        attributes.set("role", "link");
+        break;
+      case "ARTICLE":
+        attributes.set("role", "article");
+        break;
+      case "ASIDE":
+        attributes.set("role", "complementary");
+        break;
+      case "BODY":
+        attributes.set("role", "document");
+        break;
+      case "BUTTON":
+      case "SUMMARY":
+        attributes.set("role", "button");
+        break;
+      case "DD":
+        attributes.set("role", "definition");
+        break;
+      case "DETAILS":
+        attributes.set("role", "group");
+        break;
+      case "DFN":
+        attributes.set("role", "term");
+        break;
+      case "DIALOG":
+        attributes.set("role", "dialog");
+        break;
+      case "DT":
+        attributes.set("role", "term");
+        break;
+      case "FIELDSET":
+        attributes.set("role", "group");
+        break;
+      case "FIGURE":
+        attributes.set("role", "figure");
+        break;
+      case "FOOTER":
+        if (isStandaloneFooter(element)) {
+          attributes.set("role", "contentinfo");
+        }
+        break;
+      case "FORM":
+        attributes.set("role", "form");
+        break;
+      case "H1":
+      case "H2":
+      case "H3":
+      case "H4":
+      case "H5":
+      case "H6":
+        attributes.set("role", "heading");
+        attributes.set("aria-level", element.tagName.slice(1));
+        break;
+      case "HEADER":
+        if (isStandaloneFooter(element)) {
+          attributes.set("role", "banner");
+        }
+        break;
+      case "HR":
+        attributes.set("role", "separator");
+        break;
+      case "IMG": {
+        const alt = element.getAttribute("alt");
+        if (alt === null || alt !== "") {
+          attributes.set("role", "img");
+          if (alt) {
+            attributes.set("aria-label", alt);
+          }
+        }
+        break;
+      }
+      case "INPUT":
+        switch (element.type) {
+          case "button":
+          case "image":
+          case "reset":
+          case "submit":
+            attributes.set("role", "button");
+            break;
+          case "number":
+            attributes.set("role", "spinbutton");
+            break;
+          case "range":
+            attributes.set("role", "slider");
+            break;
+          case "checkbox":
+            attributes.set("role", "checkbox");
+            break;
+          case "radio":
+            attributes.set("role", "radio");
+            break;
+          case "email":
+          case "tel":
+            if (!element.hasAttribute("list")) {
+              attributes.set("role", "textbox");
+            }
+            break;
+        }
+        break;
+      case "LI":
+        if (((_a = element.parentElement) === null || _a === void 0 ? void 0 : _a.tagName) === "OL" || ((_b = element.parentElement) === null || _b === void 0 ? void 0 : _b.tagName) === "UL" || ((_c = element.parentElement) === null || _c === void 0 ? void 0 : _c.tagName) === "MENU") {
+          attributes.set("role", "listitem");
+        }
+        break;
+      case "LINK":
+        if (element.href) {
+          attributes.set("role", "link");
+        }
+        break;
+      case "MAIN":
+        attributes.set("role", "main");
+        break;
+      case "MATH":
+        attributes.set("role", "math");
+        break;
+      case "OL":
+      case "UL":
+      case "MENU":
+        attributes.set("role", "list");
+        break;
+      case "NAV":
+        attributes.set("role", "navigation");
+        break;
+      case "OPTION":
+        attributes.set("role", "option");
+        break;
+      case "PROGRESS":
+        attributes.set("role", "progressbar");
+        break;
+      case "SECTION":
+        attributes.set("role", "region");
+        break;
+      case "SELECT":
+        attributes.set("role", !element.hasAttribute("multiple") && element.size <= 1 ? "combobox" : "listbox");
+        break;
+      case "TABLE":
+        attributes.set("role", "table");
+        break;
+      case "THEAD":
+      case "TBODY":
+      case "TFOOT":
+        attributes.set("role", "rowgroup");
+        break;
+      case "TEXTAREA":
+        attributes.set("role", "textbox");
+        break;
+      case "TD":
+        attributes.set("role", "cell");
+        break;
+      case "TH":
+        attributes.set("role", element.closest("thead") ? "columnheader" : "rowheader");
+        break;
+      case "TR":
+        attributes.set("role", "tablerow");
+        break;
+    }
+    if (element.hasAttribute("disabled")) {
+      attributes.set("aria-disabled", "true");
+    }
+    if (element.hasAttribute("placeholder")) {
+      attributes.set("aria-placeholder", element.getAttribute("placeholder") || "");
+    }
+    const tabIndex = element.getAttribute("tabindex");
+    if (tabIndex) {
+      attributes.set("tabindex", tabIndex);
+    }
+    if (isHTMLElement(element) && hasLabels(element) && element.labels) {
+      attributes.set("aria-labelledby", [...element.labels].map((label) => {
+        let labelId = label.id || labels.get(label);
+        if (!labelId) {
+          labelId = getUniqueId("label");
+          labels.set(label, labelId);
+        }
+        return labelId;
+      }).join(" "));
+    }
+    for (const attribute of element.attributes) {
+      if (attribute.name.startsWith("aria-")) {
+        attributes.set(attribute.name, attribute.value);
+      }
+    }
+    const customRole = element.getAttribute("role");
+    if (customRole) {
+      attributes.set("role", customRole);
+    }
+    return attributes;
+  }
+
+  // node_modules/gradient-parser/build/esm.mjs
+  var GradientParser = GradientParser || {};
+  GradientParser.stringify = /* @__PURE__ */ (function() {
+    var visitor = {
+      "visit_linear-gradient": function(node) {
+        return visitor.visit_gradient(node);
+      },
+      "visit_repeating-linear-gradient": function(node) {
+        return visitor.visit_gradient(node);
+      },
+      "visit_radial-gradient": function(node) {
+        return visitor.visit_gradient(node);
+      },
+      "visit_repeating-radial-gradient": function(node) {
+        return visitor.visit_gradient(node);
+      },
+      "visit_conic-gradient": function(node) {
+        return visitor.visit_gradient(node);
+      },
+      "visit_repeating-conic-gradient": function(node) {
+        return visitor.visit_gradient(node);
+      },
+      "visit_gradient": function(node) {
+        var orientation = visitor.visit(node.orientation);
+        if (orientation) {
+          orientation += ", ";
+        }
+        return node.type + "(" + orientation + visitor.visit(node.colorStops) + ")";
+      },
+      "visit_shape": function(node) {
+        var result = node.value, at = visitor.visit(node.at), style = visitor.visit(node.style);
+        if (style) {
+          result += " " + style;
+        }
+        if (at) {
+          result += " at " + at;
+        }
+        return result;
+      },
+      "visit_default-radial": function(node) {
+        var result = "", at = visitor.visit(node.at);
+        if (at) {
+          if (node.hasAtKeyword) {
+            result += "at " + at;
+          } else {
+            result += at;
+          }
+        }
+        return result;
+      },
+      "visit_extent-keyword": function(node) {
+        var result = node.value, at = visitor.visit(node.at);
+        if (at) {
+          result += " at " + at;
+        }
+        return result;
+      },
+      "visit_position-keyword": function(node) {
+        return node.value;
+      },
+      "visit_position": function(node) {
+        return visitor.visit(node.value.x) + " " + visitor.visit(node.value.y);
+      },
+      "visit_%": function(node) {
+        return node.value + "%";
+      },
+      "visit_em": function(node) {
+        return node.value + "em";
+      },
+      "visit_px": function(node) {
+        return node.value + "px";
+      },
+      "visit_rem": function(node) {
+        return node.value + "rem";
+      },
+      "visit_vw": function(node) {
+        return node.value + "vw";
+      },
+      "visit_vh": function(node) {
+        return node.value + "vh";
+      },
+      "visit_vmin": function(node) {
+        return node.value + "vmin";
+      },
+      "visit_vmax": function(node) {
+        return node.value + "vmax";
+      },
+      "visit_ch": function(node) {
+        return node.value + "ch";
+      },
+      "visit_ex": function(node) {
+        return node.value + "ex";
+      },
+      "visit_calc": function(node) {
+        return "calc(" + node.value + ")";
+      },
+      "visit_literal": function(node) {
+        return visitor.visit_color(node.value, node);
+      },
+      "visit_hex": function(node) {
+        return visitor.visit_color("#" + node.value, node);
+      },
+      "visit_rgb": function(node) {
+        return visitor.visit_color("rgb(" + node.value.join(", ") + ")", node);
+      },
+      "visit_rgba": function(node) {
+        return visitor.visit_color("rgba(" + node.value.join(", ") + ")", node);
+      },
+      "visit_hsl": function(node) {
+        return visitor.visit_color("hsl(" + node.value[0] + ", " + node.value[1] + "%, " + node.value[2] + "%)", node);
+      },
+      "visit_hsla": function(node) {
+        return visitor.visit_color("hsla(" + node.value[0] + ", " + node.value[1] + "%, " + node.value[2] + "%, " + node.value[3] + ")", node);
+      },
+      "visit_var": function(node) {
+        return visitor.visit_color("var(" + node.value + ")", node);
+      },
+      "visit_color": function(resultColor, node) {
+        var result = resultColor, length = visitor.visit(node.length);
+        if (length) {
+          result += " " + length;
+        }
+        var length2 = visitor.visit(node.length2);
+        if (length2) {
+          result += " " + length2;
+        }
+        return result;
+      },
+      "visit_angular": function(node) {
+        return node.value + (node.unit || "deg");
+      },
+      "visit_directional": function(node) {
+        return "to " + node.value;
+      },
+      "visit_conic": function(node) {
+        var result = "";
+        if (node.angle) {
+          result += "from " + visitor.visit(node.angle);
+        }
+        if (node.at) {
+          if (result) {
+            result += " ";
+          }
+          result += "at " + visitor.visit(node.at);
+        }
+        return result;
+      },
+      "visit_array": function(elements) {
+        var result = "", size = elements.length;
+        elements.forEach(function(element, i) {
+          result += visitor.visit(element);
+          if (i < size - 1) {
+            result += ", ";
+          }
+        });
+        return result;
+      },
+      "visit_object": function(obj) {
+        if (obj.width && obj.height) {
+          return visitor.visit(obj.width) + " " + visitor.visit(obj.height);
+        }
+        return "";
+      },
+      "visit": function(element) {
+        if (!element) {
+          return "";
+        }
+        var result = "";
+        if (element instanceof Array) {
+          return visitor.visit_array(element);
+        } else if (typeof element === "object" && !element.type) {
+          return visitor.visit_object(element);
+        } else if (element.type) {
+          var nodeVisitor = visitor["visit_" + element.type];
+          if (nodeVisitor) {
+            return nodeVisitor(element);
+          } else {
+            throw Error("Missing visitor visit_" + element.type);
+          }
+        } else {
+          throw Error("Invalid node.");
+        }
+      }
+    };
+    return function(root2) {
+      return visitor.visit(root2);
+    };
+  })();
+  var GradientParser = GradientParser || {};
+  GradientParser.parse = /* @__PURE__ */ (function() {
+    var tokens = {
+      linearGradient: /^(\-(webkit|o|ms|moz)\-)?(linear\-gradient)/i,
+      repeatingLinearGradient: /^(\-(webkit|o|ms|moz)\-)?(repeating\-linear\-gradient)/i,
+      radialGradient: /^(\-(webkit|o|ms|moz)\-)?(radial\-gradient)/i,
+      repeatingRadialGradient: /^(\-(webkit|o|ms|moz)\-)?(repeating\-radial\-gradient)/i,
+      conicGradient: /^(\-(webkit|o|ms|moz)\-)?(conic\-gradient)/i,
+      repeatingConicGradient: /^(\-(webkit|o|ms|moz)\-)?(repeating\-conic\-gradient)/i,
+      sideOrCorner: /^to (left (top|bottom)|right (top|bottom)|top (left|right)|bottom (left|right)|left|right|top|bottom)/i,
+      extentKeywords: /^(closest\-side|closest\-corner|farthest\-side|farthest\-corner|contain|cover)/,
+      positionKeywords: /^(left|center|right|top|bottom)/i,
+      pixelValue: /^(-?(([0-9]*\.[0-9]+)|([0-9]+\.?)))px/,
+      percentageValue: /^(-?(([0-9]*\.[0-9]+)|([0-9]+\.?)))\%/,
+      emValue: /^(-?(([0-9]*\.[0-9]+)|([0-9]+\.?)))em/,
+      remValue: /^(-?(([0-9]*\.[0-9]+)|([0-9]+\.?)))rem/,
+      vwValue: /^(-?(([0-9]*\.[0-9]+)|([0-9]+\.?)))vw/,
+      vhValue: /^(-?(([0-9]*\.[0-9]+)|([0-9]+\.?)))vh/,
+      vminValue: /^(-?(([0-9]*\.[0-9]+)|([0-9]+\.?)))vmin/,
+      vmaxValue: /^(-?(([0-9]*\.[0-9]+)|([0-9]+\.?)))vmax/,
+      chValue: /^(-?(([0-9]*\.[0-9]+)|([0-9]+\.?)))ch/,
+      exValue: /^(-?(([0-9]*\.[0-9]+)|([0-9]+\.?)))ex/,
+      angleValue: /^(-?(([0-9]*\.[0-9]+)|([0-9]+\.?)))deg/,
+      radianValue: /^(-?(([0-9]*\.[0-9]+)|([0-9]+\.?)))rad/,
+      gradianValue: /^(-?(([0-9]*\.[0-9]+)|([0-9]+\.?)))grad/,
+      turnValue: /^(-?(([0-9]*\.[0-9]+)|([0-9]+\.?)))turn/,
+      startCall: /^\(/,
+      endCall: /^\)/,
+      comma: /^,/,
+      slash: /^\//,
+      hexColor: /^\#([0-9a-fA-F]{8}|[0-9a-fA-F]{6}|[0-9a-fA-F]{4}|[0-9a-fA-F]{3})(?![0-9a-fA-F])/,
+      literalColor: /^([a-zA-Z]+)/,
+      rgbColor: /^rgb/i,
+      rgbaColor: /^rgba/i,
+      varColor: /^var/i,
+      calcValue: /^calc/i,
+      variableName: /^(--[a-zA-Z0-9-,\s\#]+)/,
+      number: /^(([0-9]*\.[0-9]+)|([0-9]+\.?))/,
+      hslColor: /^hsl/i,
+      hslaColor: /^hsla/i
+    };
+    var input = "";
+    function error(msg) {
+      var err = new Error(input + ": " + msg);
+      err.source = input;
+      throw err;
+    }
+    function getAST() {
+      var ast = matchListDefinitions();
+      if (input.length > 0) {
+        error("Invalid input not EOF");
+      }
+      return ast;
+    }
+    function matchListDefinitions() {
+      return matchListing(matchDefinition);
+    }
+    function matchDefinition() {
+      return matchGradient(
+        "linear-gradient",
+        tokens.linearGradient,
+        matchLinearOrientation
+      ) || matchGradient(
+        "repeating-linear-gradient",
+        tokens.repeatingLinearGradient,
+        matchLinearOrientation
+      ) || matchGradient(
+        "radial-gradient",
+        tokens.radialGradient,
+        matchListRadialOrientations
+      ) || matchGradient(
+        "repeating-radial-gradient",
+        tokens.repeatingRadialGradient,
+        matchListRadialOrientations
+      ) || matchGradient(
+        "conic-gradient",
+        tokens.conicGradient,
+        matchConicOrientation
+      ) || matchGradient(
+        "repeating-conic-gradient",
+        tokens.repeatingConicGradient,
+        matchConicOrientation
+      );
+    }
+    function matchGradient(gradientType, pattern, orientationMatcher) {
+      return matchCall(pattern, function(captures) {
+        var orientation = orientationMatcher();
+        if (orientation) {
+          if (!scan(tokens.comma)) {
+            error("Missing comma before color stops");
+          }
+        }
+        return {
+          type: gradientType,
+          orientation,
+          colorStops: matchListing(matchColorStop)
+        };
+      });
+    }
+    function matchCall(pattern, callback) {
+      var captures = scan(pattern);
+      if (captures) {
+        if (!scan(tokens.startCall)) {
+          error("Missing (");
+        }
+        var result = callback(captures);
+        if (!scan(tokens.endCall)) {
+          error("Missing )");
+        }
+        return result;
+      }
+    }
+    function matchLinearOrientation() {
+      var sideOrCorner = matchSideOrCorner();
+      if (sideOrCorner) {
+        return sideOrCorner;
+      }
+      var legacyDirection = match("position-keyword", tokens.positionKeywords, 1);
+      if (legacyDirection) {
+        return {
+          type: "directional",
+          value: legacyDirection.value
+        };
+      }
+      return matchAngle();
+    }
+    function matchConicOrientation() {
+      var angle = matchFrom();
+      var atPosition = matchAtPosition();
+      if (angle || atPosition) {
+        return {
+          type: "conic",
+          angle: angle || void 0,
+          at: atPosition || void 0
+        };
+      }
+    }
+    function matchFrom() {
+      if (match("from", /^from/, 0)) {
+        var angle = matchAngle();
+        if (!angle) {
+          error('Missing angle after "from" in conic-gradient');
+        }
+        return angle;
+      }
+    }
+    function matchSideOrCorner() {
+      return match("directional", tokens.sideOrCorner, 1);
+    }
+    function matchAngle() {
+      return matchAngularWithUnit("deg", tokens.angleValue) || matchAngularWithUnit("rad", tokens.radianValue) || matchAngularWithUnit("grad", tokens.gradianValue) || matchAngularWithUnit("turn", tokens.turnValue);
+    }
+    function matchAngularWithUnit(unit, pattern) {
+      var captures = scan(pattern);
+      if (captures) {
+        return {
+          type: "angular",
+          value: captures[1],
+          unit
+        };
+      }
+    }
+    function matchListRadialOrientations() {
+      var radialOrientations, radialOrientation = matchRadialOrientation(), lookaheadCache;
+      if (radialOrientation) {
+        radialOrientations = [];
+        radialOrientations.push(radialOrientation);
+        lookaheadCache = input;
+        if (scan(tokens.comma)) {
+          radialOrientation = matchRadialOrientation();
+          if (radialOrientation) {
+            radialOrientations.push(radialOrientation);
+          } else {
+            input = lookaheadCache;
+          }
+        }
+      }
+      return radialOrientations;
+    }
+    function matchRadialOrientation() {
+      var radialType = matchCircle() || matchEllipse();
+      if (radialType) {
+        radialType.at = matchAtPosition();
+      } else {
+        var extent = matchExtentKeyword();
+        if (extent) {
+          radialType = extent;
+          var positionAt = matchAtPosition();
+          if (positionAt) {
+            radialType.at = positionAt;
+          }
+        } else {
+          var atPosition = matchAtPosition();
+          if (atPosition) {
+            radialType = {
+              type: "default-radial",
+              at: atPosition,
+              hasAtKeyword: true
+            };
+          } else {
+            var defaultPosition = matchPositioning();
+            if (defaultPosition) {
+              radialType = {
+                type: "default-radial",
+                at: defaultPosition
+              };
+            }
+          }
+        }
+      }
+      return radialType;
+    }
+    function matchCircle() {
+      var circle = match("shape", /^(circle)/i, 0);
+      if (circle) {
+        circle.style = matchLength() || matchExtentKeyword();
+      }
+      return circle;
+    }
+    function matchEllipse() {
+      var ellipse = match("shape", /^(ellipse)/i, 0);
+      if (ellipse) {
+        ellipse.style = matchPositioning() || matchDistance() || matchExtentKeyword();
+      }
+      return ellipse;
+    }
+    function matchExtentKeyword() {
+      return match("extent-keyword", tokens.extentKeywords, 1);
+    }
+    function matchAtPosition() {
+      if (match("position", /^at/, 0)) {
+        var positioning = matchPositioning();
+        if (!positioning) {
+          error("Missing positioning value");
+        }
+        return positioning;
+      }
+    }
+    function matchPositioning() {
+      var location = matchCoordinates();
+      if (location.x || location.y) {
+        return {
+          type: "position",
+          value: location
+        };
+      }
+    }
+    function matchCoordinates() {
+      return {
+        x: matchDistance(),
+        y: matchDistance()
+      };
+    }
+    function matchListing(matcher) {
+      var captures = matcher(), result = [];
+      if (captures) {
+        result.push(captures);
+        while (scan(tokens.comma)) {
+          captures = matcher();
+          if (captures) {
+            result.push(captures);
+          } else {
+            error("One extra comma");
+          }
+        }
+      }
+      return result;
+    }
+    function matchColorStop() {
+      var color = matchColor();
+      if (!color) {
+        error("Expected color definition");
+      }
+      color.length = matchDistance();
+      if (color.length) {
+        color.length2 = matchDistance();
+      }
+      return color;
+    }
+    function matchColor() {
+      return matchHexColor() || matchHSLAColor() || matchHSLColor() || matchRGBAColor() || matchRGBColor() || matchVarColor() || matchLiteralColor();
+    }
+    function matchLiteralColor() {
+      return match("literal", tokens.literalColor, 0);
+    }
+    function matchHexColor() {
+      return match("hex", tokens.hexColor, 1);
+    }
+    function matchRGBColor() {
+      return matchCall(tokens.rgbColor, function() {
+        return matchRGBValues("rgb");
+      });
+    }
+    function matchRGBAColor() {
+      return matchCall(tokens.rgbaColor, function() {
+        return matchRGBValues("rgba");
+      });
+    }
+    function matchRGBValues(baseType) {
+      var r = matchNumber();
+      if (scan(tokens.comma)) {
+        var g = matchNumber();
+        scan(tokens.comma);
+        var b = matchNumber();
+        var values = [r, g, b];
+        if (scan(tokens.comma)) {
+          values.push(matchNumber());
+          return { type: "rgba", value: values };
+        }
+        return { type: baseType, value: values };
+      } else {
+        var g = matchNumber();
+        var b = matchNumber();
+        var values = [r, g, b];
+        if (scan(tokens.slash)) {
+          values.push(matchNumber());
+          return { type: "rgba", value: values };
+        }
+        return { type: baseType, value: values };
+      }
+    }
+    function matchVarColor() {
+      return matchCall(tokens.varColor, function() {
+        return {
+          type: "var",
+          value: matchVariableName()
+        };
+      });
+    }
+    function matchHSLColor() {
+      return matchCall(tokens.hslColor, function() {
+        return matchHSLValues("hsl");
+      });
+    }
+    function matchHSLAColor() {
+      return matchCall(tokens.hslaColor, function() {
+        return matchHSLValues("hsla");
+      });
+    }
+    function matchHSLValues(baseType) {
+      var lookahead = scan(tokens.percentageValue);
+      if (lookahead) {
+        error("HSL hue value must be a number in degrees (0-360) or normalized (-360 to 360), not a percentage");
+      }
+      var hue = matchNumber();
+      if (scan(tokens.comma)) {
+        var captures = scan(tokens.percentageValue);
+        var sat = captures ? captures[1] : null;
+        scan(tokens.comma);
+        captures = scan(tokens.percentageValue);
+        var light = captures ? captures[1] : null;
+        if (!sat || !light) {
+          error("Expected percentage value for saturation and lightness in HSL");
+        }
+        if (scan(tokens.comma)) {
+          var alpha = matchNumber();
+          return { type: "hsla", value: [hue, sat, light, alpha] };
+        }
+        return { type: baseType, value: [hue, sat, light] };
+      } else {
+        var captures = scan(tokens.percentageValue);
+        var sat = captures ? captures[1] : null;
+        captures = scan(tokens.percentageValue);
+        var light = captures ? captures[1] : null;
+        if (!sat || !light) {
+          error("Expected percentage value for saturation and lightness in HSL");
+        }
+        if (scan(tokens.slash)) {
+          var alpha = matchNumber();
+          return { type: "hsla", value: [hue, sat, light, alpha] };
+        }
+        return { type: baseType, value: [hue, sat, light] };
+      }
+    }
+    function matchPercentage() {
+      var captures = scan(tokens.percentageValue);
+      return captures ? captures[1] : null;
+    }
+    function matchVariableName() {
+      return scan(tokens.variableName)[1];
+    }
+    function matchNumber() {
+      return scan(tokens.number)[1];
+    }
+    function matchDistance() {
+      return match("%", tokens.percentageValue, 1) || matchPositionKeyword() || matchCalc() || matchLength();
+    }
+    function matchPositionKeyword() {
+      return match("position-keyword", tokens.positionKeywords, 1);
+    }
+    function matchCalc() {
+      return matchCall(tokens.calcValue, function() {
+        var openParenCount = 1;
+        var i = 0;
+        while (openParenCount > 0 && i < input.length) {
+          var char = input.charAt(i);
+          if (char === "(") {
+            openParenCount++;
+          } else if (char === ")") {
+            openParenCount--;
+          }
+          i++;
+        }
+        if (openParenCount > 0) {
+          error("Missing closing parenthesis in calc() expression");
+        }
+        var calcContent = input.substring(0, i - 1);
+        consume(i - 1);
+        return {
+          type: "calc",
+          value: calcContent
+        };
+      });
+    }
+    function matchLength() {
+      return match("px", tokens.pixelValue, 1) || match("em", tokens.emValue, 1) || match("rem", tokens.remValue, 1) || match("vw", tokens.vwValue, 1) || match("vh", tokens.vhValue, 1) || match("vmin", tokens.vminValue, 1) || match("vmax", tokens.vmaxValue, 1) || match("ch", tokens.chValue, 1) || match("ex", tokens.exValue, 1);
+    }
+    function match(type, pattern, captureIndex) {
+      var captures = scan(pattern);
+      if (captures) {
+        return {
+          type,
+          value: captures[captureIndex]
+        };
+      }
+    }
+    function scan(regexp) {
+      var captures, blankCaptures;
+      blankCaptures = /^[\n\r\t\s]+/.exec(input);
+      if (blankCaptures) {
+        consume(blankCaptures[0].length);
+      }
+      captures = regexp.exec(input);
+      if (captures) {
+        consume(captures[0].length);
+      }
+      return captures;
+    }
+    function consume(size) {
+      input = input.substring(size);
+    }
+    return function(code) {
+      input = code.toString().trim();
+      if (input.endsWith(";")) {
+        input = input.slice(0, -1);
+      }
+      return getAST();
+    };
+  })();
+  var parse2 = GradientParser.parse;
+  var stringify2 = GradientParser.stringify;
+  var esm_default = { parse: GradientParser.parse, stringify: GradientParser.stringify };
+
+  // node_modules/dom-to-svg/lib/gradients.js
+  var positionsForOrientation = (orientation) => {
+    const positions = {
+      x1: "0%",
+      x2: "0%",
+      y1: "0%",
+      y2: "0%"
+    };
+    if ((orientation === null || orientation === void 0 ? void 0 : orientation.type) === "angular") {
+      const anglePI = orientation.value * (Math.PI / 180);
+      positions.x1 = `${Math.round(50 + Math.sin(anglePI + Math.PI) * 50)}%`;
+      positions.y1 = `${Math.round(50 + Math.cos(anglePI) * 50)}%`;
+      positions.x2 = `${Math.round(50 + Math.sin(anglePI) * 50)}%`;
+      positions.y2 = `${Math.round(50 + Math.cos(anglePI + Math.PI) * 50)}%`;
+    } else if ((orientation === null || orientation === void 0 ? void 0 : orientation.type) === "directional") {
+      switch (orientation.value) {
+        case "left":
+          positions.x1 = "100%";
+          break;
+        case "top":
+          positions.y1 = "100%";
+          break;
+        case "right":
+          positions.x2 = "100%";
+          break;
+        case "bottom":
+          positions.y2 = "100%";
+          break;
+      }
+    }
+    return positions;
+  };
+  function convertLinearGradient(css, { svgDocument }) {
+    const { orientation, colorStops } = parse2(css)[0];
+    const { x1, x2, y1, y2 } = positionsForOrientation(orientation);
+    const getColorStops = (colorStop, index) => {
+      const offset = `${index / (colorStops.length - 1) * 100}%`;
+      let stopColor = "rgb(0,0,0)";
+      let stopOpacity = 1;
+      switch (colorStop.type) {
+        case "rgb": {
+          const [red, green, blue] = colorStop.value;
+          stopColor = `rgb(${red},${green},${blue})`;
+          break;
+        }
+        case "rgba": {
+          const [red, green, blue, alpha] = colorStop.value;
+          stopColor = `rgb(${red},${green},${blue})`;
+          stopOpacity = alpha;
+          break;
+        }
+        case "hex": {
+          stopColor = `#${colorStop.value}`;
+          break;
+        }
+        case "literal": {
+          stopColor = colorStop.value;
+          break;
+        }
+      }
+      const stop = svgDocument.createElementNS(svgNamespace, "stop");
+      stop.setAttribute("offset", offset);
+      stop.setAttribute("stop-color", stopColor);
+      stop.setAttribute("stop-opacity", stopOpacity.toString());
+      return stop;
+    };
+    const linearGradient = svgDocument.createElementNS(svgNamespace, "linearGradient");
+    linearGradient.setAttribute("x1", x1);
+    linearGradient.setAttribute("y1", y1);
+    linearGradient.setAttribute("x2", x2);
+    linearGradient.setAttribute("y2", y2);
+    linearGradient.append(...colorStops.map(getColorStops));
+    return linearGradient;
+  }
+
+  // node_modules/dom-to-svg/lib/svg.js
+  var import_postcss_value_parser = __toESM(require_lib(), 1);
+
+  // node_modules/dom-to-svg/lib/util.js
+  var createIdGenerator = () => {
+    const nextCounts = /* @__PURE__ */ new Map();
+    return (prefix) => {
+      var _a;
+      const count = (_a = nextCounts.get(prefix)) !== null && _a !== void 0 ? _a : 1;
+      nextCounts.set(prefix, count + 1);
+      return `${prefix}${count}`;
+    };
+  };
+  var doRectanglesIntersect = (a, b) => !(a.bottom < b.top || // A is above B
+  a.top > b.bottom || // A is below B
+  a.right < b.left || // A is left of B
+  // A is right of B
+  a.left > b.right);
+  function diagonale(box) {
+    return Math.sqrt(box.width ** 2 + box.height ** 2);
+  }
+  function withTimeout(timeout, message, func) {
+    return Promise.race([
+      func(),
+      new Promise((resolve, reject) => setTimeout(() => reject(new Error(message)), timeout))
+    ]);
+  }
+  var isTaggedUnionMember = (key, value) => (object) => object[key] === value;
+  function assert(condition, message) {
+    if (!condition) {
+      throw new Error(message);
+    }
+  }
+
+  // node_modules/dom-to-svg/lib/text.js
+  function handleTextNode(textNode, context) {
+    if (!textNode.ownerDocument.defaultView) {
+      throw new Error("Element's ownerDocument has no defaultView");
+    }
+    const window2 = textNode.ownerDocument.defaultView;
+    const parentElement = textNode.parentElement;
+    const styles = window2.getComputedStyle(parentElement);
+    if (!isVisible(styles)) {
+      return;
+    }
+    const selection = window2.getSelection();
+    assert(selection, "Could not obtain selection from window. Selection is needed for detecting whitespace collapsing in text.");
+    const svgTextElement = context.svgDocument.createElementNS(svgNamespace, "text");
+    copyTextStyles(styles, svgTextElement);
+    const tabSize = parseInt(styles.tabSize, 10);
+    svgTextElement.setAttribute("dominant-baseline", "text-after-edge");
+    const lineRange = textNode.ownerDocument.createRange();
+    lineRange.setStart(textNode, 0);
+    lineRange.setEnd(textNode, 0);
+    while (true) {
+      const addTextSpanForLineRange = () => {
+        if (lineRange.collapsed) {
+          return;
+        }
+        const lineRectangle = lineRange.getClientRects()[0];
+        if (!doRectanglesIntersect(lineRectangle, context.options.captureArea)) {
+          return;
+        }
+        const textSpan = context.svgDocument.createElementNS(svgNamespace, "tspan");
+        textSpan.setAttribute("xml:space", "preserve");
+        const previousUserSelect = parentElement.style.userSelect;
+        parentElement.style.userSelect = "all";
+        try {
+          selection.removeAllRanges();
+          selection.addRange(lineRange);
+          textSpan.textContent = selection.toString().replace(/\t/g, " ".repeat(tabSize));
+        } finally {
+          parentElement.style.userSelect = previousUserSelect;
+          selection.removeAllRanges();
+        }
+        textSpan.setAttribute("x", lineRectangle.x.toString());
+        textSpan.setAttribute("y", lineRectangle.bottom.toString());
+        textSpan.setAttribute("textLength", lineRectangle.width.toString());
+        textSpan.setAttribute("lengthAdjust", "spacingAndGlyphs");
+        svgTextElement.append(textSpan);
+      };
+      try {
+        lineRange.setEnd(textNode, lineRange.endOffset + 1);
+      } catch (error) {
+        if (error.code === DOMException.INDEX_SIZE_ERR) {
+          addTextSpanForLineRange();
+          break;
+        }
+        throw error;
+      }
+      const lineRectangles = lineRange.getClientRects();
+      if (!lineRectangles[0]) {
+        return;
+      }
+      if (lineRectangles[1] && lineRectangles[0].top !== lineRectangles[1].top) {
+        lineRange.setEnd(textNode, lineRange.endOffset - 1);
+        addTextSpanForLineRange();
+        lineRange.setStart(textNode, lineRange.endOffset);
+      }
+    }
+    context.currentSvgParent.append(svgTextElement);
+  }
+  var textAttributes = /* @__PURE__ */ new Set([
+    "color",
+    "dominant-baseline",
+    "font-family",
+    "font-size",
+    "font-size-adjust",
+    "font-stretch",
+    "font-style",
+    "font-variant",
+    "font-weight",
+    "direction",
+    "letter-spacing",
+    "text-decoration",
+    "text-anchor",
+    "text-decoration",
+    "text-rendering",
+    "unicode-bidi",
+    "word-spacing",
+    "writing-mode",
+    "user-select"
+  ]);
+  function copyTextStyles(styles, svgElement) {
+    for (const textProperty of textAttributes) {
+      const value = styles.getPropertyValue(textProperty);
+      if (value) {
+        svgElement.setAttribute(textProperty, value);
+      }
+    }
+    svgElement.setAttribute("fill", styles.color);
+  }
+
+  // node_modules/dom-to-svg/lib/svg.js
+  function handleSvgNode(node, context) {
+    if (isElement(node)) {
+      if (!isSVGElement(node)) {
+        return;
+      }
+      handleSvgElement(node, context);
+    } else if (isTextNode(node)) {
+      const clonedTextNode = node.cloneNode(true);
+      context.currentSvgParent.append(clonedTextNode);
+    }
+  }
+  var ignoredElements = /* @__PURE__ */ new Set(["script", "style", "foreignElement"]);
+  var URL_ID_REFERENCE_REGEX = /\burl\(["']?#/;
+  function handleSvgElement(element, context) {
+    var _a, _b, _c, _d;
+    if (ignoredElements.has(element.tagName)) {
+      return;
+    }
+    let elementToAppend;
+    if (isSVGSVGElement(element)) {
+      const contentContainer = context.svgDocument.createElementNS(svgNamespace, "g");
+      elementToAppend = contentContainer;
+      contentContainer.classList.add("svg-content", ...element.classList);
+      contentContainer.dataset.viewBox = (_a = element.getAttribute("viewBox")) !== null && _a !== void 0 ? _a : "";
+      contentContainer.dataset.width = (_b = element.getAttribute("width")) !== null && _b !== void 0 ? _b : "";
+      contentContainer.dataset.height = (_c = element.getAttribute("height")) !== null && _c !== void 0 ? _c : "";
+      for (const child of element.children) {
+        if (!isSVGGraphicsElement(child)) {
+          continue;
+        }
+        let viewBoxTransformMatrix = (
+          // When this function is called on an inline <svg> element in the original DOM, we want
+          // getScreenCTM() to map it to the DOM coordinate system. When this function is called from
+          // inlineResources() the <svg> is already embedded into the output <svg>. In that case the output
+          // SVG already has a viewBox, and the coordinate system of the SVG is not equal to the coordinate
+          // system of the screen, therefor we need to use getCTM() to map it into the output SVG's
+          // coordinate system.
+          child.ownerDocument !== context.svgDocument && // When we inline an SVG, we put a transform on it for the getScreenCTM(). When that SVG also
+          // contains another SVG, the inner SVG should just get transformed relative to the outer SVG, not
+          // relative to the screen, because the transforms will stack in the output SVG.
+          !((_d = element.parentElement) === null || _d === void 0 ? void 0 : _d.closest("svg")) ? child.getScreenCTM() : child.getCTM()
+        );
+        if (!viewBoxTransformMatrix) {
+          break;
+        }
+        if (child.transform.baseVal.numberOfItems > 0) {
+          child.transform.baseVal.consolidate();
+          const existingTransform = child.transform.baseVal.getItem(0).matrix;
+          viewBoxTransformMatrix = viewBoxTransformMatrix.multiply(existingTransform.inverse());
+        }
+        contentContainer.transform.baseVal.appendItem(contentContainer.transform.baseVal.createSVGTransformFromMatrix(viewBoxTransformMatrix));
+        break;
+      }
+    } else {
+      if (isSVGAnchorElement(element) && !context.options.keepLinks) {
+        elementToAppend = context.svgDocument.createElementNS(svgNamespace, "g");
+      } else {
+        elementToAppend = element.cloneNode(false);
+      }
+      for (const attribute of elementToAppend.attributes) {
+        if (attribute.localName.startsWith("on")) {
+          elementToAppend.attributes.removeNamedItemNS(attribute.namespaceURI, attribute.localName);
+        } else if (attribute.localName === "href" && attribute.value.startsWith("javascript:")) {
+          elementToAppend.attributes.removeNamedItemNS(attribute.namespaceURI, attribute.localName);
+        }
+      }
+      const window2 = element.ownerDocument.defaultView;
+      assert(window2, "Element's ownerDocument has no defaultView");
+      const svgViewportElement = element.ownerSVGElement;
+      assert(svgViewportElement, "Expected element to have ownerSVGElement");
+      const styles = window2.getComputedStyle(element);
+      if (isSVGGraphicsElement(element)) {
+        copyGraphicalPresentationAttributes(styles, elementToAppend, svgViewportElement.viewBox.animVal);
+        if (isSVGTextContentElement(element)) {
+          copyTextStyles(styles, elementToAppend);
+        }
+      }
+      for (const attribute of elementToAppend.attributes) {
+        if (attribute.localName === "href") {
+          if (attribute.value.startsWith("#")) {
+            attribute.value = attribute.value.replace("#", `#${context.idPrefix}`);
+          }
+        } else if (URL_ID_REFERENCE_REGEX.test(attribute.value)) {
+          attribute.value = rewriteUrlIdReferences(attribute.value, context);
+        }
+      }
+      for (const property of elementToAppend.style) {
+        const value = elementToAppend.style.getPropertyValue(property);
+        if (URL_ID_REFERENCE_REGEX.test(value)) {
+          elementToAppend.style.setProperty(property, rewriteUrlIdReferences(value, context), elementToAppend.style.getPropertyPriority(property));
+        }
+      }
+    }
+    if (elementToAppend.id) {
+      elementToAppend.id = context.idPrefix + elementToAppend.id;
+    }
+    context.currentSvgParent.append(elementToAppend);
+    for (const child of element.childNodes) {
+      handleSvgNode(child, { ...context, currentSvgParent: elementToAppend });
+    }
+  }
+  var graphicalPresentationAttributes = [
+    "alignment-baseline",
+    "baseline-shift",
+    // 'clip',
+    "clip-path",
+    "clip-rule",
+    "color",
+    "color-interpolation",
+    "color-interpolation-filters",
+    // 'color-profile',
+    "color-rendering",
+    // 'cursor',
+    "direction",
+    // 'display',
+    // 'enable-background',
+    "fill",
+    "fill-opacity",
+    "fill-rule",
+    "filter",
+    "flood-color",
+    "flood-opacity",
+    "image-rendering",
+    "lighting-color",
+    "marker-end",
+    "marker-mid",
+    "marker-start",
+    "mask",
+    "opacity",
+    // 'overflow',
+    "pointer-events",
+    "shape-rendering",
+    // 'solid-color',
+    // 'solid-opacity',
+    "stop-color",
+    "stop-opacity",
+    "stroke",
+    "stroke-dasharray",
+    "stroke-dashoffset",
+    "stroke-linecap",
+    "stroke-linejoin",
+    "stroke-miterlimit",
+    "stroke-opacity",
+    "stroke-width",
+    "transform",
+    "vector-effect",
+    "visibility"
+  ];
+  var defaults = {
+    "alignment-baseline": "auto",
+    "baseline-shift": "0px",
+    "clip-path": "none",
+    "clip-rule": "nonzero",
+    "color-interpolation-filters": "linearrgb",
+    "color-interpolation": "srgb",
+    "color-rendering": "auto",
+    "fill-opacity": "1",
+    "fill-rule": "nonzero",
+    "flood-color": "rgb(0, 0, 0)",
+    "flood-opacity": "1",
+    "image-rendering": "auto",
+    "lighting-color": "rgb(255, 255, 255)",
+    "marker-end": "none",
+    "marker-mid": "none",
+    "marker-start": "none",
+    "pointer-events": "auto",
+    "shape-rendering": "auto",
+    "stop-color": "rgb(0, 0, 0)",
+    "stop-opacity": "1",
+    "stroke-dasharray": "none",
+    "stroke-dashoffset": "0px",
+    "stroke-linecap": "butt",
+    "stroke-linejoin": "miter",
+    "stroke-miterlimit": "4",
+    "stroke-opacity": "1",
+    "stroke-width": "1px",
+    "vector-effect": "none",
+    color: "",
+    direction: "ltr",
+    fill: "",
+    filter: "none",
+    mask: "none",
+    opacity: "1",
+    stroke: "",
+    transform: "none",
+    visibility: "visible"
+  };
+  function rewriteUrlIdReferences(value, { idPrefix }) {
+    const parsedValue = (0, import_postcss_value_parser.default)(value);
+    parsedValue.walk((node) => {
+      if (node.type !== "function" || node.value !== "url") {
+        return;
+      }
+      const urlArgument = node.nodes[0];
+      if (!urlArgument) {
+        return;
+      }
+      urlArgument.value = urlArgument.value.replace("#", `#${idPrefix}`);
+    });
+    return import_postcss_value_parser.default.stringify(parsedValue.nodes);
+  }
+  function copyGraphicalPresentationAttributes(styles, target, viewBox) {
+    var _a;
+    for (const attribute of graphicalPresentationAttributes) {
+      let value = styles.getPropertyValue(attribute);
+      if (value && value !== defaults[attribute]) {
+        if (value.endsWith("%")) {
+          value = (_a = parseCSSLength(value, diagonale(viewBox))) !== null && _a !== void 0 ? _a : 0;
+        }
+        target.setAttribute(attribute, value.toString());
+      }
+    }
+  }
+
+  // node_modules/dom-to-svg/lib/element.js
+  function handleElement(element, context) {
+    var _a, _b, _c, _d, _e, _f, _g;
+    const cleanupFunctions = [];
+    try {
+      const window2 = element.ownerDocument.defaultView;
+      if (!window2) {
+        throw new Error("Element's ownerDocument has no defaultView");
+      }
+      const bounds = element.getBoundingClientRect();
+      const rectanglesIntersect = doRectanglesIntersect(bounds, context.options.captureArea);
+      const styles = window2.getComputedStyle(element);
+      const parentStyles = element.parentElement && window2.getComputedStyle(element.parentElement);
+      const svgContainer = isHTMLAnchorElement(element) && context.options.keepLinks ? createSvgAnchor(element, context) : context.svgDocument.createElementNS(svgNamespace, "g");
+      svgContainer.dataset.tag = element.tagName.toLowerCase();
+      const id = element.id || context.getUniqueId(element.classList[0] || element.tagName.toLowerCase());
+      svgContainer.id = id;
+      const className = element.getAttribute("class");
+      if (className) {
+        svgContainer.setAttribute("class", className);
+      }
+      if (isHTMLElement(element) && element.title) {
+        const svgTitle = context.svgDocument.createElementNS(svgNamespace, "title");
+        svgTitle.textContent = element.title;
+        svgContainer.prepend(svgTitle);
+      }
+      const stackingLayerName = determineStackingLayer(styles, parentStyles);
+      const stackingLayer = stackingLayerName ? context.stackingLayers[stackingLayerName] : context.parentStackingLayer;
+      if (stackingLayer) {
+        context.currentSvgParent.setAttribute("aria-owns", [context.currentSvgParent.getAttribute("aria-owns"), svgContainer.id].filter(Boolean).join(" "));
+      }
+      const elementToAppendTo = context.parentStackingLayer === stackingLayer ? context.currentSvgParent : stackingLayer;
+      svgContainer.dataset.zIndex = styles.zIndex;
+      elementToAppendTo.append(svgContainer);
+      let childContext;
+      let backgroundContainer;
+      let ownStackingLayers;
+      if (establishesStackingContext(styles, parentStyles)) {
+        ownStackingLayers = createStackingLayers(svgContainer);
+        backgroundContainer = ownStackingLayers.rootBackgroundAndBorders;
+        childContext = {
+          ...context,
+          currentSvgParent: svgContainer,
+          stackingLayers: ownStackingLayers,
+          parentStackingLayer: stackingLayer
+        };
+      } else {
+        backgroundContainer = svgContainer;
+        childContext = {
+          ...context,
+          currentSvgParent: svgContainer,
+          parentStackingLayer: stackingLayer
+        };
+      }
+      if (styles.opacity !== "1") {
+        svgContainer.setAttribute("opacity", styles.opacity);
+      }
+      for (const [name, value] of getAccessibilityAttributes(element, context)) {
+        svgContainer.setAttribute(name, value);
+      }
+      if (isHTMLElement(element) && !element.dataset.pseudoElement) {
+        const handlePseudoElement = (pseudoSelector, position) => {
+          const pseudoElementStyles = window2.getComputedStyle(element, pseudoSelector);
+          const content = (0, import_postcss_value_parser2.default)(pseudoElementStyles.content).nodes.find(isTaggedUnionMember("type", "string"));
+          if (!content) {
+            return;
+          }
+          const span = element.ownerDocument.createElement("span");
+          span.dataset.pseudoElement = pseudoSelector;
+          copyCssStyles(pseudoElementStyles, span.style);
+          span.textContent = unescapeStringValue(content.value);
+          element.dataset.pseudoElementOwner = id;
+          cleanupFunctions.push(() => element.removeAttribute("data-pseudo-element-owner"));
+          const style = element.ownerDocument.createElement("style");
+          style.textContent = `[data-pseudo-element-owner="${id}"]${pseudoSelector} { display: none !important; }`;
+          element.before(style);
+          cleanupFunctions.push(() => style.remove());
+          element[position](span);
+          cleanupFunctions.push(() => span.remove());
+        };
+        handlePseudoElement("::before", "prepend");
+        handlePseudoElement("::after", "append");
+      }
+      if (rectanglesIntersect) {
+        addBackgroundAndBorders(styles, bounds, backgroundContainer, window2, context);
+      }
+      if (styles.overflow !== "visible") {
+        const mask = context.svgDocument.createElementNS(svgNamespace, "mask");
+        mask.id = context.getUniqueId("mask-for-" + id);
+        const visibleRectangle = createBox(bounds, context);
+        visibleRectangle.setAttribute("fill", "#ffffff");
+        mask.append(visibleRectangle);
+        svgContainer.append(mask);
+        svgContainer.setAttribute("mask", `url(#${mask.id})`);
+        childContext = {
+          ...childContext,
+          ancestorMasks: [{ mask, forElement: element }, ...childContext.ancestorMasks]
+        };
+      }
+      if (isHTMLElement(element) && (styles.position === "absolute" || styles.position === "fixed") && context.ancestorMasks.length > 0 && element.offsetParent) {
+        for (const { mask, forElement } of context.ancestorMasks) {
+          if (element.offsetParent.contains(forElement) || element.offsetParent === forElement) {
+            const visibleRectangle = createBox(bounds, context);
+            visibleRectangle.setAttribute("fill", "#ffffff");
+            mask.append(visibleRectangle);
+          } else {
+            break;
+          }
+        }
+      }
+      if (rectanglesIntersect && isHTMLImageElement(element) && // Make sure the element has a src/srcset attribute (the relative URL). `element.src` is absolute and always defined.
+      (element.getAttribute("src") || element.getAttribute("srcset"))) {
+        const svgImage = context.svgDocument.createElementNS(svgNamespace, "image");
+        svgImage.id = `${id}-image`;
+        svgImage.setAttribute("xlink:href", element.currentSrc || element.src);
+        const paddingLeft = (_a = parseCSSLength(styles.paddingLeft, bounds.width)) !== null && _a !== void 0 ? _a : 0;
+        const paddingRight = (_b = parseCSSLength(styles.paddingRight, bounds.width)) !== null && _b !== void 0 ? _b : 0;
+        const paddingTop = (_c = parseCSSLength(styles.paddingTop, bounds.height)) !== null && _c !== void 0 ? _c : 0;
+        const paddingBottom = (_d = parseCSSLength(styles.paddingBottom, bounds.height)) !== null && _d !== void 0 ? _d : 0;
+        svgImage.setAttribute("x", (bounds.x + paddingLeft).toString());
+        svgImage.setAttribute("y", (bounds.y + paddingTop).toString());
+        svgImage.setAttribute("width", (bounds.width - paddingLeft - paddingRight).toString());
+        svgImage.setAttribute("height", (bounds.height - paddingTop - paddingBottom).toString());
+        if (element.alt) {
+          svgImage.setAttribute("aria-label", element.alt);
+        }
+        svgContainer.append(svgImage);
+      } else if (rectanglesIntersect && isHTMLInputElement(element) && bounds.width > 0 && bounds.height > 0) {
+        if (element.value) {
+          const svgTextElement = context.svgDocument.createElementNS(svgNamespace, "text");
+          copyTextStyles(styles, svgTextElement);
+          svgTextElement.setAttribute("dominant-baseline", "central");
+          svgTextElement.setAttribute("xml:space", "preserve");
+          svgTextElement.setAttribute("x", (bounds.x + ((_e = parseCSSLength(styles.paddingLeft, bounds.width)) !== null && _e !== void 0 ? _e : 0)).toString());
+          const top = bounds.top + ((_f = parseCSSLength(styles.paddingTop, bounds.height)) !== null && _f !== void 0 ? _f : 0);
+          const bottom = bounds.bottom + ((_g = parseCSSLength(styles.paddingBottom, bounds.height)) !== null && _g !== void 0 ? _g : 0);
+          const middle = (top + bottom) / 2;
+          svgTextElement.setAttribute("y", middle.toString());
+          svgTextElement.textContent = element.value;
+          childContext.stackingLayers.inFlowInlineLevelNonPositionedDescendants.append(svgTextElement);
+        }
+      } else if (rectanglesIntersect && isSVGSVGElement(element) && isVisible(styles)) {
+        handleSvgNode(element, { ...childContext, idPrefix: `${id}-` });
+      } else {
+        for (const child of element.childNodes) {
+          walkNode(child, childContext);
+        }
+        if (ownStackingLayers) {
+          sortStackingLayerChildren(ownStackingLayers);
+          cleanupStackingLayerChildren(ownStackingLayers);
+        }
+      }
+    } finally {
+      for (const cleanup of cleanupFunctions) {
+        cleanup();
+      }
+    }
+  }
+  function addBackgroundAndBorders(styles, bounds, backgroundAndBordersContainer, window2, context) {
+    var _a, _b, _c, _d;
+    if (isVisible(styles)) {
+      if (bounds.width > 0 && bounds.height > 0 && (!isTransparent(styles.backgroundColor) || hasUniformBorder(styles) || styles.backgroundImage !== "none")) {
+        const box = createBackgroundAndBorderBox(bounds, styles, context);
+        backgroundAndBordersContainer.append(box);
+        if (styles.backgroundImage !== "none") {
+          const backgrounds = (0, import_postcss_value_parser2.default)(styles.backgroundImage).nodes.filter(isTaggedUnionMember("type", "function")).reverse();
+          const xBackgroundPositions = styles.backgroundPositionX.split(/\s*,\s*/g);
+          const yBackgroundPositions = styles.backgroundPositionY.split(/\s*,\s*/g);
+          const backgroundRepeats = styles.backgroundRepeat.split(/\s*,\s*/g);
+          for (const [index, backgroundNode] of backgrounds.entries()) {
+            const backgroundPositionX = (_a = parseCSSLength(xBackgroundPositions[index], bounds.width)) !== null && _a !== void 0 ? _a : 0;
+            const backgroundPositionY = (_b = parseCSSLength(yBackgroundPositions[index], bounds.height)) !== null && _b !== void 0 ? _b : 0;
+            const backgroundRepeat = backgroundRepeats[index];
+            if (backgroundNode.value === "url" && backgroundNode.nodes[0]) {
+              const urlArgument = backgroundNode.nodes[0];
+              const image = context.svgDocument.createElementNS(svgNamespace, "image");
+              image.id = context.getUniqueId("background-image");
+              const [cssWidth = "auto", cssHeight = "auto"] = styles.backgroundSize.split(" ");
+              const backgroundWidth = (_c = parseCSSLength(cssWidth, bounds.width)) !== null && _c !== void 0 ? _c : bounds.width;
+              const backgroundHeight = (_d = parseCSSLength(cssHeight, bounds.height)) !== null && _d !== void 0 ? _d : bounds.height;
+              image.setAttribute("width", backgroundWidth.toString());
+              image.setAttribute("height", backgroundHeight.toString());
+              if (cssWidth !== "auto" && cssHeight !== "auto") {
+                image.setAttribute("preserveAspectRatio", "none");
+              } else if (styles.backgroundSize === "contain") {
+                image.setAttribute("preserveAspectRatio", "xMidYMid meet");
+              } else if (styles.backgroundSize === "cover") {
+                image.setAttribute("preserveAspectRatio", "xMidYMid slice");
+              }
+              const url = new URL(unescapeStringValue(urlArgument.value), window2.location.href);
+              image.setAttribute("xlink:href", url.href);
+              if (backgroundRepeat === "no-repeat" || backgroundPositionX === 0 && backgroundPositionY === 0 && backgroundWidth === bounds.width && backgroundHeight === bounds.height) {
+                image.setAttribute("x", bounds.x.toString());
+                image.setAttribute("y", bounds.y.toString());
+                backgroundAndBordersContainer.append(image);
+              } else {
+                image.setAttribute("x", "0");
+                image.setAttribute("y", "0");
+                const pattern = context.svgDocument.createElementNS(svgNamespace, "pattern");
+                pattern.setAttribute("patternUnits", "userSpaceOnUse");
+                pattern.setAttribute("patternContentUnits", "userSpaceOnUse");
+                pattern.setAttribute("x", (bounds.x + backgroundPositionX).toString());
+                pattern.setAttribute("y", (bounds.y + backgroundPositionY).toString());
+                pattern.setAttribute("width", (backgroundRepeat === "repeat" || backgroundRepeat === "repeat-x" ? backgroundWidth : (
+                  // If background shouldn't repeat on this axis, make the tile as big as the element so the repetition is cut off.
+                  backgroundWidth + bounds.x + backgroundPositionX
+                )).toString());
+                pattern.setAttribute("height", (backgroundRepeat === "repeat" || backgroundRepeat === "repeat-y" ? backgroundHeight : (
+                  // If background shouldn't repeat on this axis, make the tile as big as the element so the repetition is cut off.
+                  backgroundHeight + bounds.y + backgroundPositionY
+                )).toString());
+                pattern.id = context.getUniqueId("pattern");
+                pattern.append(image);
+                box.before(pattern);
+                box.setAttribute("fill", `url(#${pattern.id})`);
+              }
+            } else if (/^(-webkit-)?linear-gradient$/.test(backgroundNode.value)) {
+              const linearGradientCss = import_postcss_value_parser2.default.stringify(backgroundNode);
+              const svgLinearGradient = convertLinearGradient(linearGradientCss, context);
+              if (backgroundPositionX !== 0 || backgroundPositionY !== 0) {
+                svgLinearGradient.setAttribute("gradientTransform", `translate(${backgroundPositionX}, ${backgroundPositionY})`);
+              }
+              svgLinearGradient.id = context.getUniqueId("linear-gradient");
+              box.before(svgLinearGradient);
+              box.setAttribute("fill", `url(#${svgLinearGradient.id})`);
+            }
+          }
+        }
+      }
+      if (!hasUniformBorder(styles)) {
+        for (const borderLine of createBorders(styles, bounds, context)) {
+          backgroundAndBordersContainer.append(borderLine);
+        }
+      }
+    }
+  }
+  function createBox(bounds, context) {
+    const box = context.svgDocument.createElementNS(svgNamespace, "rect");
+    box.setAttribute("width", bounds.width.toString());
+    box.setAttribute("height", bounds.height.toString());
+    box.setAttribute("x", bounds.x.toString());
+    box.setAttribute("y", bounds.y.toString());
+    return box;
+  }
+  function createBackgroundAndBorderBox(bounds, styles, context) {
+    const background = createBox(bounds, context);
+    if (styles.backgroundColor) {
+      background.setAttribute("fill", styles.backgroundColor);
+    }
+    if (hasUniformBorder(styles)) {
+      background.setAttribute("stroke", styles.borderTopColor);
+      background.setAttribute("stroke-width", styles.borderTopWidth);
+      if (styles.borderTopStyle === "dashed") {
+        background.setAttribute("stroke-dasharray", "1");
+      }
+    }
+    const overlappingCurvesFactor = calculateOverlappingCurvesFactor(styles, bounds);
+    const radiusX = getBorderRadiiForSide("top", styles, bounds)[0] * overlappingCurvesFactor;
+    const radiusY = getBorderRadiiForSide("left", styles, bounds)[0] * overlappingCurvesFactor;
+    if (radiusX !== 0) {
+      background.setAttribute("rx", radiusX.toString());
+    }
+    if (radiusY !== 0) {
+      background.setAttribute("ry", radiusY.toString());
+    }
+    return background;
+  }
+  function* createBorders(styles, bounds, context) {
+    for (const side of ["top", "bottom", "right", "left"]) {
+      if (hasBorder(styles, side)) {
+        yield createBorder(styles, bounds, side, context);
+      }
+    }
+  }
+  function hasBorder(styles, side) {
+    return !!styles.getPropertyValue(`border-${side}-color`) && !isTransparent(styles.getPropertyValue(`border-${side}-color`)) && styles.getPropertyValue(`border-${side}-width`) !== "0px";
+  }
+  function createBorder(styles, bounds, side, context) {
+    const border = context.svgDocument.createElementNS(svgNamespace, "line");
+    border.setAttribute("stroke-linecap", "square");
+    const color = styles.getPropertyValue(`border-${side}-color`);
+    border.setAttribute("stroke", color);
+    border.setAttribute("stroke-width", styles.getPropertyValue(`border-${side}-width`));
+    const borderStyle = styles.getPropertyValue(`border-${side}-style`);
+    if (borderStyle === "inset" && (side === "top" || side === "left") || borderStyle === "outset" && (side === "right" || side === "bottom")) {
+      const match = color.match(/rgba?\((\d+), (\d+), (\d+)(?:, ([\d.]+))?\)/);
+      if (!match) {
+        throw new Error(`Unexpected color: ${color}`);
+      }
+      const components = match.slice(1, 4).map((value) => parseInt(value, 10) * 0.3);
+      if (match[4]) {
+        components.push(parseFloat(match[4]));
+      }
+      border.setAttribute("stroke", `rgba(${components.join(", ")})`);
+    }
+    if (side === "top") {
+      border.setAttribute("x1", bounds.left.toString());
+      border.setAttribute("x2", bounds.right.toString());
+      border.setAttribute("y1", bounds.top.toString());
+      border.setAttribute("y2", bounds.top.toString());
+    } else if (side === "left") {
+      border.setAttribute("x1", bounds.left.toString());
+      border.setAttribute("x2", bounds.left.toString());
+      border.setAttribute("y1", bounds.top.toString());
+      border.setAttribute("y2", bounds.bottom.toString());
+    } else if (side === "right") {
+      border.setAttribute("x1", bounds.right.toString());
+      border.setAttribute("x2", bounds.right.toString());
+      border.setAttribute("y1", bounds.top.toString());
+      border.setAttribute("y2", bounds.bottom.toString());
+    } else if (side === "bottom") {
+      border.setAttribute("x1", bounds.left.toString());
+      border.setAttribute("x2", bounds.right.toString());
+      border.setAttribute("y1", bounds.bottom.toString());
+      border.setAttribute("y2", bounds.bottom.toString());
+    }
+    return border;
+  }
+  function createSvgAnchor(element, context) {
+    const svgAnchor = context.svgDocument.createElementNS(svgNamespace, "a");
+    if (element.href && !element.href.startsWith("javascript:")) {
+      svgAnchor.setAttribute("href", element.href);
+    }
+    if (element.rel) {
+      svgAnchor.setAttribute("rel", element.rel);
+    }
+    if (element.target) {
+      svgAnchor.setAttribute("target", element.target);
+    }
+    if (element.download) {
+      svgAnchor.setAttribute("download", element.download);
+    }
+    return svgAnchor;
+  }
+
+  // node_modules/dom-to-svg/lib/traversal.js
+  function walkNode(node, context) {
+    if (isElement(node)) {
+      handleElement(node, context);
+    } else if (isTextNode(node)) {
+      handleTextNode(node, context);
+    }
+  }
+
+  // node_modules/dom-to-svg/lib/inline.js
+  var import_postcss_value_parser3 = __toESM(require_lib(), 1);
+  async function inlineResources(element) {
+    await Promise.all([
+      ...[...element.children].map(inlineResources),
+      (async () => {
+        var _a;
+        if (isSVGImageElement(element)) {
+          const blob = await withTimeout(1e4, `Timeout fetching ${element.href.baseVal}`, () => fetchResource(element.href.baseVal));
+          if (blob.type === "image/svg+xml") {
+            assert(element.ownerDocument, "Expected <image> element to have ownerDocument");
+            const embeddedSvgDocument = new DOMParser().parseFromString(await blob.text(), "image/svg+xml");
+            const svgRoot = embeddedSvgDocument.documentElement;
+            svgRoot.setAttribute("x", element.getAttribute("x"));
+            svgRoot.setAttribute("y", element.getAttribute("y"));
+            svgRoot.setAttribute("width", element.getAttribute("width"));
+            svgRoot.setAttribute("height", element.getAttribute("height"));
+            svgRoot.remove();
+            element.replaceWith(svgRoot);
+            try {
+              const svgDocument = element.ownerDocument;
+              const mount = svgDocument.createElementNS(svgNamespace, "g");
+              assert(element.id, "<image> element must have ID");
+              handleSvgNode(svgRoot, {
+                currentSvgParent: mount,
+                svgDocument,
+                idPrefix: `${element.id}-`,
+                options: {
+                  // SVGs embedded through <img> are never interactive.
+                  keepLinks: false,
+                  captureArea: svgRoot.viewBox.baseVal
+                }
+              });
+              mount.dataset.tag = "img";
+              mount.setAttribute("role", "img");
+              svgRoot.replaceWith(mount);
+            } finally {
+              svgRoot.remove();
+            }
+          } else {
+            const dataUrl = await blobToDataURL(blob);
+            element.dataset.src = element.href.baseVal;
+            element.setAttribute("xlink:href", dataUrl.href);
+          }
+        } else if (isSVGStyleElement(element)) {
+          try {
+            const promises = [];
+            const parsedSheet = parse((_a = element.textContent) !== null && _a !== void 0 ? _a : "");
+            parsedSheet.walkAtRules("font-face", (fontFaceRule) => {
+              fontFaceRule.walkDecls("src", (sourceDeclaration) => {
+                const parsedSourceValue = (0, import_postcss_value_parser3.default)(sourceDeclaration.value);
+                parsedSourceValue.walk((node) => {
+                  if (node.type === "function" && node.value === "url" && node.nodes[0]) {
+                    const urlArgumentNode = node.nodes[0];
+                    if (urlArgumentNode.type === "string" || urlArgumentNode.type === "word") {
+                      promises.push(inlineCssFontUrlArgumentNode(urlArgumentNode));
+                    }
+                  }
+                });
+                sourceDeclaration.value = import_postcss_value_parser3.default.stringify(parsedSourceValue.nodes);
+              });
+            });
+            await Promise.all(promises);
+            element.textContent = parsedSheet.toString();
+          } catch (error) {
+            console.error("Error inlining stylesheet", element.sheet, error);
+          }
+        }
+      })().catch((error) => {
+        console.error("Error inlining resource for element", element, error);
+      })
+    ]);
+  }
+  async function inlineCssFontUrlArgumentNode(urlArgumentNode) {
+    try {
+      const url = new URL(unescapeStringValue(urlArgumentNode.value));
+      const blob = await withTimeout(1e4, `Timeout fetching ${url.href}`, () => fetchResource(url.href));
+      if (!blob.type.startsWith("font/") && !blob.type.startsWith("application/font-") && !blob.type.startsWith("application/x-font-") && !blob.type.startsWith("image/svg+xml") && !blob.type.startsWith("application/vnd.ms-fontobject")) {
+        throw new Error(`Invalid response MIME type inlining font at ${url.href}: Expected font MIME type, got ${blob.type}`);
+      }
+      const dataUrl = await blobToDataURL(blob);
+      urlArgumentNode.value = dataUrl.href;
+    } catch (error) {
+      console.error(`Error inlining ${urlArgumentNode.value}`, error);
+    }
+  }
+  async function fetchResource(url) {
+    assert(url, "No URL passed");
+    const headers = new Headers();
+    const response = await fetch(url, { headers });
+    if (!response.ok) {
+      throw new Error(response.statusText);
+    }
+    const blob = await response.blob();
+    return blob;
+  }
+  async function blobToDataURL(blob) {
+    const reader = new FileReader();
+    await new Promise((resolve, reject) => {
+      reader.addEventListener("error", () => reject(new Error("Error loading resource with FileLoader")));
+      reader.addEventListener("load", () => resolve());
+      reader.readAsDataURL(blob);
+    });
+    return new URL(reader.result);
+  }
+
+  // node_modules/dom-to-svg/lib/index.js
+  function documentToSVG(document2, options) {
+    return elementToSVG(document2.documentElement, options);
+  }
+  function elementToSVG(element, options) {
+    var _a, _b, _c, _d;
+    const svgDocument = element.ownerDocument.implementation.createDocument(svgNamespace, "svg", null);
+    const svgElement = svgDocument.documentElement;
+    svgElement.setAttribute("xmlns", svgNamespace);
+    svgElement.setAttribute("xmlns:xlink", xlinkNamespace);
+    svgElement.append(svgDocument.createComment(
+      // "--" is invalid in comments, percent-encode.
+      ` Generated by dom-to-svg from ${element.ownerDocument.location.href.replace(/--/g, "%2D%2D")} `
+    ));
+    const styleElement = svgDocument.createElementNS(svgNamespace, "style");
+    for (const styleSheet of element.ownerDocument.styleSheets) {
+      try {
+        for (const rule2 of (_a = styleSheet.rules) !== null && _a !== void 0 ? _a : []) {
+          if (!isCSSFontFaceRule(rule2)) {
+            continue;
+          }
+          const styleSheetHref = (_b = rule2.parentStyleSheet) === null || _b === void 0 ? void 0 : _b.href;
+          if (styleSheetHref) {
+            const parsedSourceValue = (0, import_postcss_value_parser4.default)(rule2.style.getPropertyValue("src"));
+            parsedSourceValue.walk((node) => {
+              if (node.type === "function" && node.value === "url" && node.nodes[0]) {
+                const urlArgumentNode = node.nodes[0];
+                if (urlArgumentNode.type === "string" || urlArgumentNode.type === "word") {
+                  urlArgumentNode.value = new URL(unescapeStringValue(urlArgumentNode.value), styleSheetHref).href;
+                }
+              }
+            });
+            const updatedFontFaceRule = parse(rule2.cssText);
+            updatedFontFaceRule.walkDecls("src", (declaration) => {
+              declaration.value = import_postcss_value_parser4.default.stringify(parsedSourceValue.nodes);
+            });
+            styleElement.append(updatedFontFaceRule.toString() + "\n");
+          }
+        }
+      } catch (error) {
+        console.error("Error resolving @font-face src URLs for styleSheet, skipping", styleSheet, error);
+      }
+    }
+    svgElement.append(styleElement);
+    walkNode(element, {
+      svgDocument,
+      currentSvgParent: svgElement,
+      stackingLayers: createStackingLayers(svgElement),
+      parentStackingLayer: svgElement,
+      getUniqueId: createIdGenerator(),
+      labels: /* @__PURE__ */ new Map(),
+      ancestorMasks: [],
+      options: {
+        captureArea: (_c = options === null || options === void 0 ? void 0 : options.captureArea) !== null && _c !== void 0 ? _c : element.getBoundingClientRect(),
+        keepLinks: (options === null || options === void 0 ? void 0 : options.keepLinks) !== false
+      }
+    });
+    const bounds = (_d = options === null || options === void 0 ? void 0 : options.captureArea) !== null && _d !== void 0 ? _d : element.getBoundingClientRect();
+    svgElement.setAttribute("width", bounds.width.toString());
+    svgElement.setAttribute("height", bounds.height.toString());
+    svgElement.setAttribute("viewBox", `${bounds.x} ${bounds.y} ${bounds.width} ${bounds.height}`);
+    return svgDocument;
+  }
+
+  // ppt-output/.bundle_entry.js
+  window.__domToSvg = { documentToSVG, elementToSVG, inlineResources };
+})();

BIN
ppt-output/images/slide_01_0.png


BIN
ppt-output/images/slide_03_0.png


BIN
ppt-output/images/slide_04_0.png


BIN
ppt-output/images/slide_05_0.png


BIN
ppt-output/images/slide_06_2.png


BIN
ppt-output/images/slide_08_0.png


BIN
ppt-output/images/slide_10_1.png


BIN
ppt-output/images/slide_11_0.png


BIN
ppt-output/images/slide_13_2.png


BIN
ppt-output/images/slide_14_2.png


BIN
ppt-output/images/slide_15_0.png


BIN
ppt-output/images/slide_16_0.png


BIN
ppt-output/images/slide_17_1.png


BIN
ppt-output/images/slide_18_1.png


BIN
ppt-output/images/slide_19_0.png


BIN
ppt-output/images/slide_20_2.png


BIN
ppt-output/images/slide_21_0.png


BIN
ppt-output/images/slide_22_1.png


BIN
ppt-output/images/slide_23_0.png


BIN
ppt-output/images/slide_24_1.png


BIN
ppt-output/images/slide_25_0.png


BIN
ppt-output/images/slide_26_1_robot.png


BIN
ppt-output/images/slide_26_2_glasses.png


BIN
ppt-output/images/slide_26_3_wristband.png


BIN
ppt-output/images/slide_26_4_earbuds.png


BIN
ppt-output/images/slide_26_5_cook.png


BIN
ppt-output/images/slide_28_2.png


+ 360 - 0
ppt-output/outline.json

@@ -0,0 +1,360 @@
+{
+  "ppt_outline": {
+    "cover": {
+      "title": "给全家的一站式幸福提案",
+      "sub_title": "AI 驱动的家庭主动健康管理平台——守护家庭\"身·智·富·行·心\"五维成长",
+      "presenter": "北京安盈创卓科技有限公司",
+      "date": "2026",
+      "company": "浠艾福(Care Family)"
+    },
+    "table_of_contents": {
+      "title": "目录",
+      "content": [
+        "项目概述",
+        "背景政策",
+        "市场容量",
+        "需求与设计",
+        "技术壁垒",
+        "应用成效"
+      ]
+    },
+    "parts": [
+      {
+        "part_title": "第一部分:项目概述",
+        "part_goal": "让评委快速理解项目是什么:为解决什么痛点而生、产品长什么样、已建成什么规模、由谁来做",
+        "pages": [
+          {
+            "title": "项目缘起与创新主张",
+            "goal": "家庭健康服务割裂的痛点 + \"AI 健康教练 + 人机协同\"的核心主张",
+            "content": [
+              "结构性困境:健康检查在医院、心理辅导在机构、素质教育在培训机构,数据与服务完全割裂,家长需对接 5-8 个服务商",
+              "恶性循环:信息碎片化—干预滞后化—效果不可知",
+              "核心主张:AI 不该只是聊天机器人,而是专属 AI 健康教练 + 健康管家/营养师/医生后台团队的人机协同体系",
+              "范式转变:被动就医 → 主动健康;千人一面 → 千人千面",
+              "闭环路径:检测/测评 → AI 分析 → 个性化方案 → 行为执行 → 复测迭代"
+            ],
+            "data_needs": [
+              "5-8 个服务商",
+              "恶性循环三环节"
+            ]
+          },
+          {
+            "title": "产品形态:四端协同",
+            "goal": "展示平台连接家长、孩子、规划师、运营四方的完整产品矩阵",
+            "content": [
+              "家长端(微信小程序):家庭管理、任务下发、测评预约、套餐购买、五维能量看板、AI 健康教练",
+              "孩子端(微信小程序):任务执行、小游戏训练、心愿兑换、成长报告、健康打卡",
+              "规划师端(小程序+Web):套餐发布、测评结果录入、批量任务下发、家庭健康管理",
+              "管理端(Vue Web):审核、配置、数据统计、内容与知识库管理、专业服务支持",
+              "人机协同:AI 7×24 智能应答 + 后台专业服务团队人工兜底"
+            ],
+            "data_needs": [
+              "四端能力清单"
+            ]
+          },
+          {
+            "title": "系统规模与 AI 服务",
+            "goal": "用已上线数据证明工程成熟度",
+            "content": [
+              "48 个后端控制器、47 个数据库实体、15 大功能模块",
+              "50+ 小程序页面、29 个 Web 管理端页面",
+              "技术栈:Java 8 + Spring Boot 2.7.18 + MyBatis-Plus / Python 智能体服务 / uni-app Vue 2 / Vue 2 + Element UI / MySQL 8.0",
+              "AI 服务:自研智能体(健康咨询/营养建议/报告解读/任务生成/内容推荐)+ 家庭知识库问答(RAG)"
+            ],
+            "data_needs": [
+              "48/47/15",
+              "50+/29"
+            ]
+          },
+          {
+            "title": "公司实力与核心团队",
+            "goal": "用公司资质与专家团队建立信任背书",
+            "content": [
+              "北京安盈创卓科技有限公司:2013 年成立的国家级高新技术企业,中石化 A 级供应商",
+              "服务 500+ 家央企/政府/金融/军工客户(中国石化、国家电网、中航工业等)",
+              "60 余项软件著作权、ISO9001 认证;10 年自研\"图腾\"云原生平台",
+              "9 人核心团队:工程骨干 + 心理学/医疗/教育专家(魏高峡、施建农、张挽时等学术带头人)"
+            ],
+            "data_needs": [
+              "500+ 客户",
+              "60+ 著作权",
+              "9 人团队"
+            ]
+          }
+        ]
+      },
+      {
+        "part_title": "第二部分:背景政策",
+        "part_goal": "论证\"健康中国 + 人工智能+\"双轮驱动下,AI 主动健康项目正处最佳窗口期",
+        "pages": [
+          {
+            "title": "国家战略与专项政策",
+            "goal": "政策端明确鼓励 AI 赋能健康/心理服务",
+            "content": [
+              "《健康中国2030》确立\"健康优先、预防为主\"总方针——\"主动健康\"的最高纲领",
+              "国务院《关于深入实施\"人工智能+\"行动的意见》(国发〔2025〕11号)——\"人工智能+\"上升为国家行动",
+              "卫健委等四部门《人工智能+医疗卫生实施意见》:以学生为重点提供心理问题智能筛查、预警、干预",
+              "教育部《十条措施》明确\"利用人工智能赋能学生心理健康,支持探索'AI心理助手'\"",
+              "卫健委等13部门《儿童青少年\"五健\"促进行动计划》:探索数智技术,分级分类精细化管理"
+            ],
+            "data_needs": [
+              "政策文件名称与年份"
+            ]
+          },
+          {
+            "title": "社会痛点与技术窗口期",
+            "goal": "需求端问题高发、供给短缺;技术端大模型智能体已就绪——三者交汇即窗口期",
+            "content": [
+              "初中生抑郁检出率 24.6%,高中生 29.1%;63% 家庭求助无门转向市场化服务",
+              "家庭治疗资质专业人员不足 8000 人,缺口超 20 万人",
+              "78% 家长认为孩子缺乏自主学习动力;非传统家庭结构占比超 43%",
+              "技术成熟:多模态大模型 + RAG 知识库 + 智能体(Agent)使\"读得懂报告、给得出方案\"具备工程可行性",
+              "结论:政策鼓励 + 需求高发 + 技术就绪 = AI 精准干预最佳窗口期"
+            ],
+            "data_needs": [
+              "24.6%/29.1%",
+              "8000 人/20 万缺口",
+              "78%",
+              "43%"
+            ]
+          }
+        ]
+      },
+      {
+        "part_title": "第三部分:市场容量",
+        "part_goal": "论证万亿赛道中高增长的细分市场与已验证的商业模型",
+        "pages": [
+          {
+            "title": "市场空间:TAM / SAM / SOM",
+            "goal": "万亿赛道中的高增长细分,渗透率极低、空间巨大",
+            "content": [
+              "TAM:中国家庭教育消费市场 4.5 万亿元(2025,艾瑞咨询)",
+              "SAM:儿童健康管理市场 5000 亿元+;家庭教育服务市场 217 亿元→2030 年 500 亿元(CAGR 19.3%)",
+              "细分:泛心理健康服务 104.1 亿元(CAGR 27%);心理健康教育 286.3 亿元→810 亿元(CAGR 23.1%)",
+              "SOM:家庭教育服务平台渗透率不足 3%,97% 待开拓"
+            ],
+            "data_needs": [
+              "4.5 万亿",
+              "5000 亿+",
+              "CAGR 19.3%/27%/23.1%",
+              "3% 渗透率"
+            ]
+          },
+          {
+            "title": "用户基数与竞争格局",
+            "goal": "1.2 亿户目标家庭 + 高度分散的供给 = 一体化平台的机会",
+            "content": [
+              "目标用户:3-18 岁儿童家庭约 1.2 亿户;小学及幼儿园在校生约 1.4 亿人次",
+              "需求升级:78.6% 城市家庭要求定制化方案;90 后父母偏好数据驱动、即时反馈",
+              "付费能力:家长年均教育支出 1.2-3 万元/孩子;心理辅导年度服务包均价 1.2 万元,复购率 78.3%",
+              "竞争格局:纯工具无服务闭环、纯内容无数据闭环、纯机构缺健康维度——尚无\"健康+教育+心理\"一体化规模平台",
+              "卡位:AI 辅助干预(37.2% 机构已部署 AI 情绪识别)+ 个性化定制 = 新壁垒"
+            ],
+            "data_needs": [
+              "1.2 亿户",
+              "78.6%",
+              "复购率 78.3%",
+              "37.2%"
+            ]
+          },
+          {
+            "title": "商业模式与单位经济",
+            "goal": "三边市场平台经济,四类收入,LTV/CAC 远超行业健康线",
+            "content": [
+              "测评服务费:订单抽佣 15-25%,毛利率 60-70%",
+              "规划师佣金:套餐抽佣 10-20%,毛利率 80-90%",
+              "商品交易费:商城抽佣 5-15%,毛利率 85-95%",
+              "会员订阅:家庭会员增值服务(含 AI 健康教练),毛利率 70-80%",
+              "单位经济:CAC 15-25 元(微信生态社交推荐),LTV 2000-5000 元,LTV/CAC 达 80-330 倍(行业健康线 3 倍)"
+            ],
+            "data_needs": [
+              "四类收入毛利率",
+              "LTV/CAC 80-330 倍"
+            ]
+          }
+        ]
+      },
+      {
+        "part_title": "第四部分:需求与设计",
+        "part_goal": "展示以\"五维能量\"方法论重构家庭健康管理:有理论源头、有产品落点、有业务闭环",
+        "pages": [
+          {
+            "title": "痛点洞察与方法论演进",
+            "goal": "从 WHO 三维到浠艾福五维:有国际共识起点、有国家战略源头、有本土化创新",
+            "content": [
+              "三大痛点:信息碎片化(无全景图)、干预滞后化(效果不可量化)、执行断层化(知道做不到)",
+              "WHO 三维健康观(1948):身体·心理·社会适应——国际共识起点",
+              "健康中国四维健康观:身体·心理·认知·社会性——国家战略核心理念",
+              "浠艾福五维:身·智·富·行·心 = 四维 × 五行智慧与家庭行为实践",
+              "五维闭环:测得到、评得准、做得到"
+            ],
+            "data_needs": [
+              "WHO 1948",
+              "四维→五维演进"
+            ]
+          },
+          {
+            "title": "五维全景幸福矩阵",
+            "goal": "五维×五行×功能的完整产品方法论体系",
+            "content": [
+              "身(土·主动健康):肠道菌群检测、健康报告 AI 解析、饮食推荐",
+              "心(火·大爱相随):DAN 测评、情绪打卡、家庭关系图、亲子沟通",
+              "智(金·赋能未来):认知测评、舒尔特/数独小游戏、阅读任务",
+              "行(木·人际和谐):任务系统、积分激励、健康打卡、家庭挑战",
+              "富(水·利他创富):财富打卡、创客生态、心愿兑换",
+              "双轨机制:能量沙盘(5 维×3-8 数据源=25+ 数据源,每日计算家庭健康指数 0-100%)+ 能量账本(任务→能量→心愿兑换,日上限 200 防刷分)"
+            ],
+            "data_needs": [
+              "五维五行对应",
+              "25+ 数据源",
+              "日上限 200"
+            ]
+          },
+          {
+            "title": "业务闭环:检测到复测",
+            "goal": "五大环节构成数据飞轮,精准干预闭环可解释可追踪",
+            "content": [
+              "① 检测/测评:居家采样、测评预约",
+              "② AI 分析:报告解析、指标解读、风险标注",
+              "③ 个性化方案:基于个体画像的饮食/训练/亲子方案",
+              "④ 行为执行:任务下发、能量积分、即时反馈、智能预警",
+              "⑤ 复测迭代:周期复测、效果对比、方案调优",
+              "回到 ① —— 数据飞轮持续积累"
+            ],
+            "data_needs": [
+              "五环节闭环图"
+            ]
+          }
+        ]
+      },
+      {
+        "part_title": "第五部分:技术壁垒",
+        "part_goal": "论证\"为什么别人要追 2-3 年\":方法论壁垒 + 数据壁垒 + 网络效应 + AI 能力栈",
+        "pages": [
+          {
+            "title": "五维相生溯源算法",
+            "goal": "核心算法壁垒:每一笔能量可追溯、可解释、可验证",
+            "content": [
+              "自研五维评价体系:融合五行相生与多源加权,每维 3-8 个异构数据源,energy_source_config 动态配置",
+              "全链路溯源:EnergyLog 记录来源类型/业务单/维度占比,从家庭健康指数下钻到单次行为",
+              "五行相生动态调节:身生智→智生富→富生行→行生心→心生身,相克约束,\"一人一比例、一日一状态\"",
+              "规则引擎可配置:EnergyRuleEngine 零代码配置,防刷分日上限 200 能量",
+              "行为-健康指标归因:回答\"过去 30 天哪些行为贡献了菌群多样性改善\""
+            ],
+            "data_needs": [
+              "3-8 数据源/维度",
+              "日上限 200",
+              "25+ 数据源"
+            ]
+          },
+          {
+            "title": "AI 能力栈(核心)",
+            "goal": "自研智能体服务体系,从\"没有记忆的机器人\"到\"比家长更懂家庭的健康顾问\"",
+            "content": [
+              "家庭 360° 上下文引擎:对话前自动组装家庭全景数据(成员/任务完成率/健康报告/五维分数/历史记忆)",
+              "AI 能力矩阵(已实现):智能对话、知识库问答(RAG)、报告智能解析、AI 生成任务、AI 内容推荐、AI 每日反馈",
+              "报告解析:135 页菌群报告 → 3 页行动方案",
+              "AI 健康教练智能体:意图分类器→路由→知识检索→大模型推理→工具调用→记忆保存,全链路可观测",
+              "人机协同:AI 智能应答 + 健康管家/营养师/医生后台兜底"
+            ],
+            "data_needs": [
+              "135 页→3 页",
+              "六类 AI 能力"
+            ]
+          },
+          {
+            "title": "数据护城河·六阶模型·工程安全",
+            "goal": "多重复合壁垒:长周期数据不可复制 + 行为科学机制 + 生产级工程",
+            "content": [
+              "数据壁垒:47 实体、25+ 数据源、10+ 能量来源;6-18 岁成长数据长周期积累不可烧钱复制",
+              "网络效应:更多家长→更多规划师→更优质服务→更多家庭(需求端-供给端双向飞轮,已上线服务商入驻/等级/佣金体系)",
+              "主动健康六阶模型:有效启动→持续坚持→获得感→身份重塑→意见领袖化→社会扩散",
+              "工程安全:JWT 认证+角色权限、敏感字段加密、统一 Result 规范、数据库迁移自动化、四级地址、全量数据迁移验证"
+            ],
+            "data_needs": [
+              "47/25+/10+",
+              "六阶模型"
+            ]
+          }
+        ]
+      },
+      {
+        "part_title": "第六部分:应用成效",
+        "part_goal": "用真实案例、权威工程背书、可验证系统建设证明项目已从概念走向生产",
+        "pages": [
+          {
+            "title": "真实案例:精准营养闭环",
+            "goal": "135 页专业报告 → 3 页行动方案,全链路在家完成",
+            "content": [
+              "用户 530010234(匿名):2026 年 1 月完成肠道菌群健康风险评估(募极生物,135 页报告)",
+              "AI 分析:健康总分 73(均值 75,干预空间大);菌群健康 53 偏低;检出菌种 313 种(均值约 800)",
+              "AI 自动标注三大缺乏指标:维生素 B12(14 分)→疲劳/贫血风险;苏氨酸(14 分)→肠道屏障受损;丙氨酸(9 分)→能量代谢异常",
+              "平台成效:压缩为 3 页行动方案——拟杆菌型肠型四步干预(纤维 25-30g/天→发酵蛋白→控风险食物→定期复测),任务系统+能量积分推动执行",
+              "替代\"跑医院、找专家、花数千元\"的传统路径"
+            ],
+            "data_needs": [
+              "73/53/313 种",
+              "B12 14 分",
+              "135→3 页"
+            ]
+          },
+          {
+            "title": "权威背书与系统建设成效",
+            "goal": "\"国家队\"级学术底座 + 15 大模块生产运行 + 商业化验证",
+            "content": [
+              "双培强基工程:人民日报《人民周刊》+ 中科院心理研究所联合发起,四维测评体系(DAN 测评等)已引入平台,学术带头人魏高峡、施建农为工程核心专家",
+              "15 大模块生产运行:认证/家庭/任务/心愿/积分/测评/成长档案/商城/服务商/AI 健康教练/健康报告/饮食推荐/文章/关系/联系人",
+              "AI 三端落地:健康教练小程序上线,覆盖家庭上下文/报告解读/营养咨询/任务生成/内容推荐",
+              "工程验证:Maven 编译+启动验证+分层测试体系齐备,完成全量数据迁移",
+              "商业化验证:三边市场四类交易上线、LTV/CAC 80-330 倍、公众号案例内容体系验证低成本获客"
+            ],
+            "data_needs": [
+              "15 大模块",
+              "DAN 测评",
+              "LTV/CAC"
+            ]
+          },
+          {
+            "title": "未来展望:AI 健康教练走进生活场",
+            "goal": "具身智能 × 智能穿戴,让 AI 健康教练从对话框走进生活场",
+            "content": [
+              "具身智能家庭服务:接入家庭服务机器人、智能健康终端,机器人完成健康提醒、陪伴训练、体征采集等物理动作,实现感知—决策—执行完整闭环",
+              "智能穿戴随身感知:AR 眼镜(实时健康叠加、舌诊面色 AI 望诊、情绪识别)、智能手环(心率/睡眠连续监测、异常分级预警)、智能耳机(语音健康教练、冥想引导)",
+              "技术演进路径:单点 AI → 家庭数字孪生 → 具身智能×穿戴 → 开放生态(家庭健康服务超级入口 2029)"
+            ],
+            "data_needs": [
+              "感知—决策—执行闭环",
+              "2029 超级入口"
+            ]
+          },
+          {
+            "title": "行业应用场景拓展:从家庭走向千行百业",
+            "goal": "核心能力一次建设、多行业复制,从家庭健康走向全民主动健康",
+            "content": [
+              "学校/教育机构:学生心理问题高发(初中抑郁 24.6%、高中 29.1%),AI 心理助手 + 一生一策心理档案 + 分级预警 + 家校协同干预,政策明确支持",
+              "企业/职场健康:员工 EAP 与健康管理,员工健康测评 + AI 健康教练 + 干预任务闭环 + 管理层数据看板,母公司 500+ 政企客户渠道打通",
+              "军队/国防:官兵心理健康与韧性训练,心理测评建档 + 韧性训练任务 + 智能预警,军工服务经验形成信任背书",
+              "公安/政法:一线民警心理服务,情绪识别 + AI 分级预警 + 心理服务闭环 + 随访跟踪,涉密合规交付能力已验证",
+              "共性底座:五维测评引擎 × AI 健康教练 × 行为干预闭环 × 数据看板——一次建设,多行业复用"
+            ],
+            "data_needs": [
+              "24.6%/29.1%",
+              "500+ 政企客户",
+              "四大行业场景"
+            ]
+          }
+        ]
+      }
+    ],
+    "end_page": {
+      "title": "让健康从被动等待走向主动掌握",
+      "content": [
+        "核心回顾:五维能量方法论 × AI 健康教练 + 人机协同 × 五维相生溯源算法 × 生产级系统(48 控制器/50+ 页面/15 模块)",
+        "未来 12 个月:Q1 AI 深化 → Q2 服务商网络 → Q3 商业化闭环 → Q4 生态构建",
+        "三年愿景:2027 年 10 万+ 家庭 → 2028 年五维健康行业标准 → 2029 年开放生态超级入口",
+        "行动号召:给全家的一站式幸福提案——让每一个家庭拥有\"看得懂报告、跟得上方案、做得到行动\"的专属 AI 健康教练",
+        "北京安盈创卓科技有限公司 · 浠艾福(Care Family)"
+      ]
+    }
+  }
+}

+ 3491 - 0
ppt-output/planning.json

@@ -0,0 +1,3491 @@
+{
+ "ppt_planning": {
+  "deck_title": "浠艾福(Care Family)AI 家庭主动健康管理平台 · 项目介绍",
+  "pages": [
+   {
+    "slide_number": 1,
+    "page_type": "cover",
+    "title": "给全家的一站式幸福提案",
+    "page_goal": "让评委第一眼理解:这是一家以 AI 驱动家庭主动健康管理的平台,守护家庭五维成长",
+    "narrative_role": "cover",
+    "visual_weight": 8,
+    "density_label": "low",
+    "density_reason": "封面页:品牌主张 + 视觉冲击,信息克制",
+    "density_contract": {
+     "deck_bias": "balanced",
+     "page_lower_bound": "low",
+     "page_upper_bound": "mid_low",
+     "max_cards": 2,
+     "max_charts": 1,
+     "min_body_font_px": 24,
+     "max_lines_per_card": 3,
+     "image_policy": "flexible",
+     "decoration_budget": "generous",
+     "overflow_strategy": "rebalance_layout"
+    },
+    "director_command": {
+     "focus": "card_hierarchy",
+     "rule": "ensure_anchor_visual_dominance",
+     "note": "给全家的一站式幸福提案"
+    },
+    "decoration_hints": {
+     "style": "tech_blue_warm_orange",
+     "elements": [
+      "hero_image",
+      "brand_mark",
+      "five_dim_badges",
+      "footer"
+     ]
+    },
+    "resources": {
+     "layout_refs": [],
+     "block_refs": [],
+     "chart_refs": [],
+     "principle_refs": []
+    },
+    "cards": [
+     {
+      "card_id": "c1",
+      "role": "anchor",
+      "card_type": "text",
+      "card_style": "elevated",
+      "argument_role": "claim",
+      "headline": "AI 驱动的家庭主动健康管理平台",
+      "body": [
+       "以 AI 实现精准干预、主动健康,守护中国家庭的\"身 · 心 · 智 · 行 · 富\"五维成长",
+       "让每一个家庭都拥有\"看得懂报告、跟得上方案、做得到行动\"的专属 AI 健康教练"
+      ],
+      "content_budget": {
+       "body_max_lines": 3
+      },
+      "image": {
+       "needed": true,
+       "usage": "hero-background",
+       "placement": "full-bleed",
+       "content_description": "温暖家庭场景:父母与孩子一起在阳光下查看健康报告,科技蓝与暖橙色调",
+       "source_hint": "封面主视觉"
+      },
+      "data_points": [
+       {
+        "value": "身·心·智·行·富",
+        "label": "五维成长体系",
+        "interpretation": "方法论底座"
+       },
+       {
+        "value": "7×24",
+        "label": "AI 健康教练",
+        "interpretation": "专属智能体服务"
+       }
+      ]
+     }
+    ],
+    "workflow_metadata": {
+     "workflow_version": "2026.04.09-v4.1",
+     "planning_schema_version": "4.1",
+     "planning_packet_version": "4.1",
+     "planning_continuity_version": "4.1"
+    }
+   },
+   {
+    "slide_number": 2,
+    "page_type": "toc",
+    "title": "目录",
+    "page_goal": "六大部分清晰导航,让评委预知内容结构",
+    "narrative_role": "toc",
+    "visual_weight": 6,
+    "density_label": "low",
+    "density_reason": "目录页:清晰导航,每章有编号与主题",
+    "density_contract": {
+     "deck_bias": "balanced",
+     "page_lower_bound": "low",
+     "page_upper_bound": "mid_low",
+     "max_cards": 2,
+     "max_charts": 1,
+     "min_body_font_px": 24,
+     "max_lines_per_card": 3,
+     "image_policy": "flexible",
+     "decoration_budget": "generous",
+     "overflow_strategy": "rebalance_layout"
+    },
+    "director_command": {
+     "focus": "card_hierarchy",
+     "rule": "ensure_anchor_visual_dominance",
+     "note": "目录"
+    },
+    "decoration_hints": {
+     "style": "tech_blue_warm_orange",
+     "elements": [
+      "title_underline",
+      "grid_dots",
+      "footer"
+     ],
+     "brand": "五维色系点缀"
+    },
+    "resources": {
+     "layout_refs": [],
+     "block_refs": [],
+     "chart_refs": [],
+     "principle_refs": []
+    },
+    "cards": [
+     {
+      "card_id": "c1",
+      "role": "anchor",
+      "card_type": "list",
+      "card_style": "elevated",
+      "argument_role": "framework",
+      "headline": "六大部分",
+      "body": [
+       "Part 01 项目概述:缘起 · 产品 · 规模 · 团队",
+       "Part 02 背景政策:国家战略 · 社会痛点",
+       "Part 03 市场容量:TAM/SAM/SOM · 商业模式",
+       "Part 04 需求与设计:五维方法论 · 业务闭环",
+       "Part 05 技术壁垒:溯源算法 · AI 能力栈",
+       "Part 06 应用成效:真实案例 · 权威背书"
+      ],
+      "content_budget": {
+       "body_max_lines": 3
+      },
+      "image": {
+       "needed": false,
+       "usage": null,
+       "placement": null,
+       "content_description": null,
+       "source_hint": null
+      }
+     }
+    ],
+    "workflow_metadata": {
+     "workflow_version": "2026.04.09-v4.1",
+     "planning_schema_version": "4.1",
+     "planning_packet_version": "4.1",
+     "planning_continuity_version": "4.1"
+    },
+    "layout_hint": "toc-route"
+   },
+   {
+    "slide_number": 3,
+    "page_type": "section",
+    "title": "Part 01 · 项目概述",
+    "page_goal": "让评委理解项目为解决什么而生、产品形态与建成规模",
+    "narrative_role": "section",
+    "visual_weight": 7,
+    "density_label": "mid_low",
+    "density_reason": "章节封面:引入项目缘起与整体图景",
+    "density_contract": {
+     "deck_bias": "balanced",
+     "page_lower_bound": "low",
+     "page_upper_bound": "high",
+     "max_cards": 3,
+     "max_charts": 1,
+     "min_body_font_px": 20,
+     "max_lines_per_card": 4,
+     "image_policy": "flexible",
+     "decoration_budget": "medium",
+     "overflow_strategy": "rebalance_layout"
+    },
+    "director_command": {
+     "focus": "card_hierarchy",
+     "rule": "ensure_anchor_visual_dominance",
+     "note": "Part 01 · 项目概述"
+    },
+    "decoration_hints": {
+     "style": "tech_blue_warm_orange",
+     "elements": [
+      "title_underline",
+      "grid_dots",
+      "footer"
+     ],
+     "brand": "五维色系点缀"
+    },
+    "resources": {
+     "layout_refs": [],
+     "block_refs": [],
+     "chart_refs": [],
+     "principle_refs": []
+    },
+    "cards": [
+     {
+      "card_id": "c1",
+      "role": "anchor",
+      "card_type": "text",
+      "card_style": "elevated",
+      "argument_role": "claim",
+      "headline": "从割裂到闭环:一个 AI 健康教练连接全家",
+      "body": [
+       "健康检查在医院、心理辅导在机构、素质教育在培训机构——数据与服务完全割裂",
+       "浠艾福以五维能量体系为底座、AI 智能体为引擎,构建连接家长、孩子、规划师与专业服务团队的家庭健康平台"
+      ],
+      "content_budget": {
+       "body_max_lines": 4,
+       "strict": true
+      },
+      "image": {
+       "needed": true,
+       "usage": "hero-background",
+       "placement": "right-half",
+       "content_description": "家庭与数字健康服务的连接意象,暖橙主调",
+       "source_hint": "章节封面配图"
+      }
+     }
+    ],
+    "workflow_metadata": {
+     "workflow_version": "2026.04.09-v4.1",
+     "planning_schema_version": "4.1",
+     "planning_packet_version": "4.1",
+     "planning_continuity_version": "4.1"
+    },
+    "layout_hint": "free-section"
+   },
+   {
+    "slide_number": 4,
+    "page_type": "content",
+    "title": "项目缘起与创新主张",
+    "page_goal": "家庭健康服务割裂的结构性困境 + \"AI 健康教练 + 人机协同\"的核心主张",
+    "narrative_role": "evidence",
+    "visual_weight": 7,
+    "density_label": "medium",
+    "density_reason": "核心痛点与主张:需突出结构性困境与范式转变对比",
+    "density_contract": {
+     "deck_bias": "balanced",
+     "page_lower_bound": "mid_low",
+     "page_upper_bound": "high",
+     "max_cards": 4,
+     "max_charts": 2,
+     "min_body_font_px": 18,
+     "max_lines_per_card": 5,
+     "image_policy": "support_only",
+     "decoration_budget": "medium",
+     "overflow_strategy": "tighten_budget"
+    },
+    "director_command": {
+     "focus": "card_hierarchy",
+     "rule": "ensure_anchor_visual_dominance",
+     "note": "项目缘起与创新主张"
+    },
+    "decoration_hints": {
+     "style": "tech_blue_warm_orange",
+     "elements": [
+      "title_underline",
+      "process_flow",
+      "footer"
+     ],
+     "brand": "accent 橙"
+    },
+    "resources": {
+     "layout_refs": [],
+     "block_refs": [],
+     "chart_refs": [],
+     "principle_refs": []
+    },
+    "cards": [
+     {
+      "card_id": "c1",
+      "role": "anchor",
+      "card_type": "text",
+      "card_style": "accent",
+      "argument_role": "claim",
+      "headline": "结构性困境:数据与服务完全割裂",
+      "body": [
+       "健康检查在医院、心理辅导在机构、素质教育在培训机构,一个家长需对接 5-8 个服务商才能拼凑\"这个孩子到底怎么样\"的模糊答案",
+       "内驱力不足、管理疲惫、服务商难找、效果无法量化——形成\"信息碎片化 → 干预滞后化 → 效果不可知\"的恶性循环"
+      ],
+      "content_budget": {
+       "body_max_lines": 4,
+       "strict": true
+      },
+      "image": {
+       "needed": true,
+       "usage": "inline-illustration",
+       "placement": "inline",
+       "content_description": "碎片化数据拼图的意象",
+       "source_hint": "痛点配图"
+      },
+      "data_points": [
+       {
+        "value": "5-8",
+        "label": "需对接服务商数",
+        "interpretation": "一个家长为拼凑孩子全景需对接的服务商数量"
+       }
+      ]
+     },
+     {
+      "card_id": "c2",
+      "role": "support",
+      "card_type": "diagram",
+      "card_style": "elevated",
+      "argument_role": "claim",
+      "headline": "核心创新主张",
+      "body": [
+       "AI 不该只是一个聊天机器人,而是家庭健康的\"专属 AI 健康教练 + 后台专业服务团队(健康管家/营养师/医生)\"人机协同体系",
+       "可量化评估 → 可解释分析 → 可执行干预 → 可追踪反馈的精准干预闭环"
+      ],
+      "content_budget": {
+       "body_max_lines": 4,
+       "strict": true
+      },
+      "image": {
+       "needed": false,
+       "usage": null,
+       "placement": null,
+       "content_description": null,
+       "source_hint": null
+      }
+     },
+     {
+      "card_id": "c3",
+      "role": "support",
+      "card_type": "process",
+      "card_style": "outline",
+      "argument_role": "method",
+      "headline": "闭环路径",
+      "body": [
+       "检测/测评",
+       "AI 分析",
+       "个性化方案",
+       "行为执行",
+       "复测迭代"
+      ],
+      "content_budget": {
+       "body_max_lines": 4,
+       "strict": true
+      },
+      "image": {
+       "needed": false,
+       "usage": null,
+       "placement": null,
+       "content_description": null,
+       "source_hint": null
+      },
+      "data_points": [
+       {
+        "value": "5",
+        "label": "闭环环节",
+        "interpretation": "检测→AI分析→方案→执行→复测的完整闭环"
+       }
+      ]
+     },
+     {
+      "card_id": "c4",
+      "role": "context",
+      "card_type": "list",
+      "card_style": "glass",
+      "argument_role": "contrast",
+      "headline": "范式转变",
+      "body": [
+       "从\"被动就医\"到\"主动健康\"",
+       "从\"千人一面\"到\"千人千面\"",
+       "AI 的精准能力注入家庭健康管理每一个环节"
+      ],
+      "content_budget": {
+       "body_max_lines": 4,
+       "strict": true
+      },
+      "image": {
+       "needed": false,
+       "usage": null,
+       "placement": null,
+       "content_description": null,
+       "source_hint": null
+      }
+     }
+    ],
+    "workflow_metadata": {
+     "workflow_version": "2026.04.09-v4.1",
+     "planning_schema_version": "4.1",
+     "planning_packet_version": "4.1",
+     "planning_continuity_version": "4.1"
+    },
+    "layout_hint": "hero-top",
+    "source_guidance": {
+     "material_ref": "docs/AI创新项目大赛-项目介绍.md §1.1-1.2",
+     "key_facts": [
+      "5-8 个服务商",
+      "恶性循环三环节",
+      "四个可"
+     ]
+    }
+   },
+   {
+    "slide_number": 5,
+    "page_type": "content",
+    "title": "产品形态:四端协同",
+    "page_goal": "平台连接家长、孩子、规划师、运营四方的完整产品矩阵与 AI 人机协同服务",
+    "narrative_role": "evidence",
+    "visual_weight": 8,
+    "density_label": "high",
+    "density_reason": "四端产品矩阵:以四象限展示,突出人机协同体系",
+    "density_contract": {
+     "deck_bias": "balanced",
+     "page_lower_bound": "medium",
+     "page_upper_bound": "high",
+     "max_cards": 6,
+     "max_charts": 2,
+     "min_body_font_px": 16,
+     "max_lines_per_card": 4,
+     "image_policy": "support_only",
+     "decoration_budget": "low",
+     "overflow_strategy": "table_or_microchart"
+    },
+    "director_command": {
+     "focus": "card_hierarchy",
+     "rule": "ensure_anchor_visual_dominance",
+     "note": "产品形态:四端协同"
+    },
+    "decoration_hints": {
+     "style": "tech_blue_warm_orange",
+     "elements": [
+      "title_underline",
+      "quadrant_grid",
+      "footer"
+     ],
+     "brand": "四端四色"
+    },
+    "resources": {
+     "layout_refs": [],
+     "block_refs": [],
+     "chart_refs": [],
+     "principle_refs": []
+    },
+    "cards": [
+     {
+      "card_id": "c1",
+      "role": "anchor",
+      "card_type": "list",
+      "card_style": "elevated",
+      "argument_role": "framework",
+      "headline": "家长端 · 微信小程序",
+      "body": [
+       "付费决策者:家庭管理、任务下发、测评预约、套餐购买",
+       "五维能量看板、AI 健康教练"
+      ],
+      "content_budget": {
+       "body_max_lines": 4,
+       "strict": true
+      },
+      "image": {
+       "needed": true,
+       "usage": "inline-illustration",
+       "placement": "inline",
+       "content_description": "家长在手机上管理家庭健康任务",
+       "source_hint": "家长端配图"
+      }
+     },
+     {
+      "card_id": "c2",
+      "role": "support",
+      "card_type": "list",
+      "card_style": "filled",
+      "argument_role": "framework",
+      "headline": "孩子端 · 微信小程序",
+      "body": [
+       "行为执行者:任务执行、小游戏训练、心愿兑换",
+       "成长报告、健康打卡"
+      ],
+      "content_budget": {
+       "body_max_lines": 4,
+       "strict": true
+      },
+      "image": {
+       "needed": false,
+       "usage": null,
+       "placement": null,
+       "content_description": null,
+       "source_hint": null
+      }
+     },
+     {
+      "card_id": "c3",
+      "role": "support",
+      "card_type": "list",
+      "card_style": "filled",
+      "argument_role": "framework",
+      "headline": "规划师端 · 小程序+Web",
+      "body": [
+       "成长规划师/营养师/活动方:套餐发布、测评结果录入",
+       "批量任务下发、家庭健康管理"
+      ],
+      "content_budget": {
+       "body_max_lines": 4,
+       "strict": true
+      },
+      "image": {
+       "needed": false,
+       "usage": null,
+       "placement": null,
+       "content_description": null,
+       "source_hint": null
+      }
+     },
+     {
+      "card_id": "c4",
+      "role": "support",
+      "card_type": "list",
+      "card_style": "filled",
+      "argument_role": "framework",
+      "headline": "管理端 · Vue Web",
+      "body": [
+       "平台运营+后台服务人员(健康管家/营养师/医生):审核、配置、数据统计",
+       "内容与知识库管理、专业服务支持"
+      ],
+      "content_budget": {
+       "body_max_lines": 4,
+       "strict": true
+      },
+      "image": {
+       "needed": false,
+       "usage": null,
+       "placement": null,
+       "content_description": null,
+       "source_hint": null
+      }
+     },
+     {
+      "card_id": "c5",
+      "role": "context",
+      "card_type": "quote",
+      "card_style": "glass",
+      "argument_role": "synthesis",
+      "headline": "人机协同服务体系",
+      "body": [
+       "AI 健康教练 7×24 智能应答 + 健康管家/营养师/医生后台兜底",
+       "\"AI 智能应答 + 人工专业兜底\""
+      ],
+      "content_budget": {
+       "body_max_lines": 4,
+       "strict": true
+      },
+      "image": {
+       "needed": false,
+       "usage": null,
+       "placement": null,
+       "content_description": null,
+       "source_hint": null
+      }
+     }
+    ],
+    "workflow_metadata": {
+     "workflow_version": "2026.04.09-v4.1",
+     "planning_schema_version": "4.1",
+     "planning_packet_version": "4.1",
+     "planning_continuity_version": "4.1"
+    },
+    "layout_hint": "mixed-grid",
+    "source_guidance": {
+     "material_ref": "docs/AI创新项目大赛-项目介绍.md §1.3",
+     "key_facts": [
+      "四端形态与服务对象",
+      "后台服务体系"
+     ]
+    }
+   },
+   {
+    "slide_number": 6,
+    "page_type": "content",
+    "title": "系统规模与 AI 服务",
+    "page_goal": "用已上线数据证明工程成熟度与技术底座",
+    "narrative_role": "evidence",
+    "visual_weight": 8,
+    "density_label": "medium",
+    "density_reason": "规模数字是核心证据:大数字 KPI 展示,技术栈为辅助",
+    "density_contract": {
+     "deck_bias": "balanced",
+     "page_lower_bound": "mid_low",
+     "page_upper_bound": "high",
+     "max_cards": 4,
+     "max_charts": 2,
+     "min_body_font_px": 18,
+     "max_lines_per_card": 5,
+     "image_policy": "support_only",
+     "decoration_budget": "medium",
+     "overflow_strategy": "tighten_budget"
+    },
+    "director_command": {
+     "focus": "card_hierarchy",
+     "rule": "ensure_anchor_visual_dominance",
+     "note": "系统规模与 AI 服务"
+    },
+    "decoration_hints": {
+     "style": "tech_blue_warm_orange",
+     "elements": [
+      "title_underline",
+      "kpi_row",
+      "footer"
+     ],
+     "brand": "accent 蓝"
+    },
+    "resources": {
+     "layout_refs": [],
+     "block_refs": [],
+     "chart_refs": [],
+     "principle_refs": []
+    },
+    "cards": [
+     {
+      "card_id": "c1",
+      "role": "anchor",
+      "card_type": "data",
+      "card_style": "accent",
+      "argument_role": "evidence",
+      "headline": "系统规模(已上线)",
+      "body": [
+       "48 个后端控制器、47 个数据库实体、15 大功能模块生产运行"
+      ],
+      "content_budget": {
+       "body_max_lines": 4,
+       "strict": true
+      },
+      "image": {
+       "needed": false,
+       "usage": null,
+       "placement": null,
+       "content_description": null,
+       "source_hint": null
+      },
+      "data_points": [
+       {
+        "value": "48",
+        "label": "后端控制器",
+        "interpretation": "覆盖 15 大功能模块"
+       },
+       {
+        "value": "47",
+        "label": "数据库实体",
+        "interpretation": "复杂数据模型支撑五维体系"
+       },
+       {
+        "value": "50+",
+        "label": "小程序页面",
+        "interpretation": "家长/孩子/规划师三端"
+       },
+       {
+        "value": "29",
+        "label": "Web 管理页",
+        "interpretation": "运营与服务端"
+       }
+      ],
+      "chart": {
+       "chart_type": "kpi",
+       "note": "四组 KPI 数字"
+      }
+     },
+     {
+      "card_id": "c2",
+      "role": "support",
+      "card_type": "list",
+      "card_style": "elevated",
+      "argument_role": "framework",
+      "headline": "技术栈",
+      "body": [
+       "Java 8 + Spring Boot 2.7.18 + MyBatis-Plus",
+       "Python 智能体服务(AI 健康教练)",
+       "uni-app Vue 2 小程序 / Vue 2 + Element UI",
+       "MySQL 8.0"
+      ],
+      "content_budget": {
+       "body_max_lines": 4,
+       "strict": true
+      },
+      "image": {
+       "needed": false,
+       "usage": null,
+       "placement": null,
+       "content_description": null,
+       "source_hint": null
+      }
+     },
+     {
+      "card_id": "c3",
+      "role": "support",
+      "card_type": "list",
+      "card_style": "filled",
+      "argument_role": "framework",
+      "headline": "AI 服务(自研)",
+      "body": [
+       "智能体服务体系:健康咨询、营养建议、报告解读、任务生成、内容推荐",
+       "家庭知识库问答(RAG):向量知识库 + 菌属/指标/食物/营养素知识库"
+      ],
+      "content_budget": {
+       "body_max_lines": 4,
+       "strict": true
+      },
+      "image": {
+       "needed": true,
+       "usage": "inline-illustration",
+       "placement": "inline",
+       "content_description": "AI 智能体服务架构意象:机器人+知识库",
+       "source_hint": "AI服务配图"
+      }
+     }
+    ],
+    "workflow_metadata": {
+     "workflow_version": "2026.04.09-v4.1",
+     "planning_schema_version": "4.1",
+     "planning_packet_version": "4.1",
+     "planning_continuity_version": "4.1"
+    },
+    "layout_hint": "primary-secondary",
+    "source_guidance": {
+     "material_ref": "docs/AI创新项目大赛-项目介绍.md §1.4",
+     "key_facts": [
+      "48/47/15",
+      "50+/29",
+      "技术栈清单"
+     ]
+    }
+   },
+   {
+    "slide_number": 7,
+    "page_type": "content",
+    "title": "公司实力与核心团队",
+    "page_goal": "国家级高新技术企业的工程实力 + 心理学/医疗/教育专家团队背书",
+    "narrative_role": "evidence",
+    "visual_weight": 8,
+    "density_label": "medium",
+    "density_reason": "公司实力与团队:数据卡为主,团队突出专家背景",
+    "density_contract": {
+     "deck_bias": "balanced",
+     "page_lower_bound": "mid_low",
+     "page_upper_bound": "high",
+     "max_cards": 4,
+     "max_charts": 2,
+     "min_body_font_px": 18,
+     "max_lines_per_card": 5,
+     "image_policy": "support_only",
+     "decoration_budget": "medium",
+     "overflow_strategy": "tighten_budget"
+    },
+    "director_command": {
+     "focus": "card_hierarchy",
+     "rule": "ensure_anchor_visual_dominance",
+     "note": "公司实力与核心团队"
+    },
+    "decoration_hints": {
+     "style": "tech_blue_warm_orange",
+     "elements": [
+      "title_underline",
+      "kpi_row",
+      "footer"
+     ],
+     "brand": "accent 金"
+    },
+    "resources": {
+     "layout_refs": [],
+     "block_refs": [],
+     "chart_refs": [],
+     "principle_refs": []
+    },
+    "cards": [
+     {
+      "card_id": "c1",
+      "role": "anchor",
+      "card_type": "data",
+      "card_style": "accent",
+      "argument_role": "evidence",
+      "headline": "公司实力",
+      "body": [
+       "北京安盈创卓科技有限公司(2013 年成立,中关村软件核心区)国家级高新技术企业、中石化 A 级供应商"
+      ],
+      "content_budget": {
+       "body_max_lines": 4,
+       "strict": true
+      },
+      "image": {
+       "needed": false,
+       "usage": null,
+       "placement": null,
+       "content_description": null,
+       "source_hint": null
+      },
+      "data_points": [
+       {
+        "value": "500+",
+        "label": "央企/政府/金融/军工客户",
+        "interpretation": "中国石化、国家电网、中航工业等"
+       },
+       {
+        "value": "60+",
+        "label": "软件著作权",
+        "interpretation": "ISO9001 认证,自主知识产权"
+       },
+       {
+        "value": "10 年",
+        "label": "自研\"图腾\"云原生平台",
+        "interpretation": "DDD 模型,业务与技术分离"
+       }
+      ],
+      "chart": {
+       "chart_type": "kpi",
+       "note": "公司关键实力指标"
+      }
+     },
+     {
+      "card_id": "c2",
+      "role": "support",
+      "card_type": "list",
+      "card_style": "elevated",
+      "argument_role": "evidence",
+      "headline": "业务覆盖",
+      "body": [
+       "政府:云南省办公厅、交通运输部等",
+       "石化:中国石化、长城电商等",
+       "电力:国网北京信通、北京电力及 60 余分支",
+       "军工/金融:中航工业、航天科工、银保监云南局"
+      ],
+      "content_budget": {
+       "body_max_lines": 4,
+       "strict": true
+      },
+      "image": {
+       "needed": false,
+       "usage": null,
+       "placement": null,
+       "content_description": null,
+       "source_hint": null
+      }
+     },
+     {
+      "card_id": "c3",
+      "role": "support",
+      "card_type": "people",
+      "card_style": "filled",
+      "argument_role": "evidence",
+      "headline": "9 人核心团队",
+      "body": [
+       "廖晓光·项目负责人 | 魏高峡·学术带头人 | 施建农·学术带头人",
+       "张挽时·学术带头人 | 林小朋·技术专家 | 韩庆宇·销售专家",
+       "曹丽君·心理学专家 | 闫德波·医疗专家 | 王卫东·教育专家"
+      ],
+      "content_budget": {
+       "body_max_lines": 4,
+       "strict": true
+      },
+      "image": {
+       "needed": false,
+       "usage": null,
+       "placement": null,
+       "content_description": null,
+       "source_hint": null
+      },
+      "data_points": [
+       {
+        "value": "9",
+        "label": "核心团队成员",
+        "interpretation": "工程骨干 + 心理学/医疗/教育专家"
+       }
+      ]
+     }
+    ],
+    "workflow_metadata": {
+     "workflow_version": "2026.04.09-v4.1",
+     "planning_schema_version": "4.1",
+     "planning_packet_version": "4.1",
+     "planning_continuity_version": "4.1"
+    },
+    "layout_hint": "mixed-grid",
+    "source_guidance": {
+     "material_ref": "docs/AI创新项目大赛-项目介绍.md §1.5",
+     "key_facts": [
+      "500+ 客户",
+      "60+ 著作权",
+      "9 人团队",
+      "双培强基学术背书"
+     ]
+    }
+   },
+   {
+    "slide_number": 8,
+    "page_type": "section",
+    "title": "Part 02 · 背景政策",
+    "page_goal": "论证\"健康中国 + 人工智能+\"双轮驱动下 AI 主动健康正处最佳窗口期",
+    "narrative_role": "section",
+    "visual_weight": 7,
+    "density_label": "mid_low",
+    "density_reason": "章节封面:窗口期结论先行",
+    "density_contract": {
+     "deck_bias": "balanced",
+     "page_lower_bound": "low",
+     "page_upper_bound": "high",
+     "max_cards": 3,
+     "max_charts": 1,
+     "min_body_font_px": 20,
+     "max_lines_per_card": 4,
+     "image_policy": "flexible",
+     "decoration_budget": "medium",
+     "overflow_strategy": "rebalance_layout"
+    },
+    "director_command": {
+     "focus": "card_hierarchy",
+     "rule": "ensure_anchor_visual_dominance",
+     "note": "Part 02 · 背景政策"
+    },
+    "decoration_hints": {
+     "style": "tech_blue_warm_orange",
+     "elements": [
+      "title_underline",
+      "grid_dots",
+      "footer"
+     ],
+     "brand": "五维色系点缀"
+    },
+    "resources": {
+     "layout_refs": [],
+     "block_refs": [],
+     "chart_refs": [],
+     "principle_refs": []
+    },
+    "cards": [
+     {
+      "card_id": "c1",
+      "role": "anchor",
+      "card_type": "text",
+      "card_style": "elevated",
+      "argument_role": "claim",
+      "headline": "国家战略 · 时代需求 · 技术就绪",
+      "body": [
+       "政策端\"鼓励 AI 赋能健康/心理服务\"、需求端\"问题高发且供给短缺\"、技术端\"大模型智能体已就绪\"——三者交汇,正是 AI 精准干预、主动健康类项目的最佳窗口期"
+      ],
+      "content_budget": {
+       "body_max_lines": 4,
+       "strict": true
+      },
+      "image": {
+       "needed": true,
+       "usage": "hero-background",
+       "placement": "right-half",
+       "content_description": "国家政策与科技趋势交汇意象,蓝色调",
+       "source_hint": "章节封面配图"
+      }
+     }
+    ],
+    "workflow_metadata": {
+     "workflow_version": "2026.04.09-v4.1",
+     "planning_schema_version": "4.1",
+     "planning_packet_version": "4.1",
+     "planning_continuity_version": "4.1"
+    },
+    "layout_hint": "free-section"
+   },
+   {
+    "slide_number": 9,
+    "page_type": "content",
+    "title": "国家战略与专项政策",
+    "page_goal": "从健康中国到\"人工智能+\",政策已为 AI 心理/健康干预产品打开准入大门",
+    "narrative_role": "evidence",
+    "visual_weight": 8,
+    "density_label": "medium",
+    "density_reason": "政策是硬证据:时间线展示政策演进,专项政策聚焦儿童青少年",
+    "density_contract": {
+     "deck_bias": "balanced",
+     "page_lower_bound": "mid_low",
+     "page_upper_bound": "high",
+     "max_cards": 4,
+     "max_charts": 2,
+     "min_body_font_px": 18,
+     "max_lines_per_card": 5,
+     "image_policy": "support_only",
+     "decoration_budget": "medium",
+     "overflow_strategy": "tighten_budget"
+    },
+    "director_command": {
+     "focus": "card_hierarchy",
+     "rule": "ensure_anchor_visual_dominance",
+     "note": "国家战略与专项政策"
+    },
+    "decoration_hints": {
+     "style": "tech_blue_warm_orange",
+     "elements": [
+      "title_underline",
+      "timeline",
+      "footer"
+     ],
+     "brand": "accent 蓝"
+    },
+    "resources": {
+     "layout_refs": [],
+     "block_refs": [],
+     "chart_refs": [],
+     "principle_refs": []
+    },
+    "cards": [
+     {
+      "card_id": "c1",
+      "role": "anchor",
+      "card_type": "timeline",
+      "card_style": "accent",
+      "argument_role": "evidence",
+      "headline": "政策脉络:从健康中国到 AI+",
+      "body": [
+       "2016《健康中国2030规划纲要》:\"健康优先、预防为主\"——主动健康最高纲领",
+       "2019《健康中国行动》:3 个儿童青少年专项行动",
+       "2022《家庭教育促进法》:家庭教育从\"家事\"上升为\"国事\"",
+       "2025.08 国发〔2025〕11号:\"人工智能+\"上升为国家行动",
+       "2025.10 国卫办规划发〔2025〕30号:以学生为重点,心理问题智能筛查、预警、干预,2027 年形成垂直大模型和智能体应用"
+      ],
+      "content_budget": {
+       "body_max_lines": 4,
+       "strict": true
+      },
+      "image": {
+       "needed": false,
+       "usage": null,
+       "placement": null,
+       "content_description": null,
+       "source_hint": null
+      },
+      "chart": {
+       "chart_type": "timeline",
+       "note": "5 个政策节点"
+      }
+     },
+     {
+      "card_id": "c2",
+      "role": "support",
+      "card_type": "list",
+      "card_style": "elevated",
+      "argument_role": "evidence",
+      "headline": "儿童青少年健康专项",
+      "body": [
+       "教育部等17部门《专项行动计划》:\"一生一策\"心理健康档案,四位一体体系",
+       "教基厅〔2025〕2号《十条措施》:明确\"利用人工智能赋能学生心理健康,支持探索'AI心理助手'\"",
+       "卫健委等13部门《\"五健\"促进行动计划》:防筛诊治康一体化,数智技术分级分类精细化管理"
+      ],
+      "content_budget": {
+       "body_max_lines": 4,
+       "strict": true
+      },
+      "image": {
+       "needed": false,
+       "usage": null,
+       "placement": null,
+       "content_description": null,
+       "source_hint": null
+      },
+      "data_points": [
+       {
+        "value": "2027",
+        "label": "垂直大模型应用目标",
+        "interpretation": "国卫办规划发〔2025〕30号"
+       }
+      ]
+     }
+    ],
+    "workflow_metadata": {
+     "workflow_version": "2026.04.09-v4.1",
+     "planning_schema_version": "4.1",
+     "planning_packet_version": "4.1",
+     "planning_continuity_version": "4.1"
+    },
+    "layout_hint": "asymmetric",
+    "source_guidance": {
+     "material_ref": "docs/AI创新项目大赛-项目介绍.md §2.1-2.2",
+     "key_facts": [
+      "5 份政策文件",
+      "AI 心理助手明确入政策"
+     ]
+    }
+   },
+   {
+    "slide_number": 10,
+    "page_type": "content",
+    "title": "社会痛点与技术窗口期",
+    "page_goal": "需求端问题高发、供给短缺,技术端智能体已就绪——窗口期成立",
+    "narrative_role": "evidence",
+    "visual_weight": 8,
+    "density_label": "medium",
+    "density_reason": "痛点数据是核心冲击点:数据卡突出,技术就绪作支撑",
+    "density_contract": {
+     "deck_bias": "balanced",
+     "page_lower_bound": "mid_low",
+     "page_upper_bound": "high",
+     "max_cards": 4,
+     "max_charts": 2,
+     "min_body_font_px": 18,
+     "max_lines_per_card": 5,
+     "image_policy": "support_only",
+     "decoration_budget": "medium",
+     "overflow_strategy": "tighten_budget"
+    },
+    "director_command": {
+     "focus": "card_hierarchy",
+     "rule": "ensure_anchor_visual_dominance",
+     "note": "社会痛点与技术窗口期"
+    },
+    "decoration_hints": {
+     "style": "tech_blue_warm_orange",
+     "elements": [
+      "title_underline",
+      "metric_row",
+      "footer"
+     ],
+     "brand": "accent 红橙"
+    },
+    "resources": {
+     "layout_refs": [],
+     "block_refs": [],
+     "chart_refs": [],
+     "principle_refs": []
+    },
+    "cards": [
+     {
+      "card_id": "c1",
+      "role": "anchor",
+      "card_type": "data",
+      "card_style": "accent",
+      "argument_role": "evidence",
+      "headline": "问题高发 · 供给严重不足",
+      "body": [
+       "教育部 2025 监测:初中生抑郁检出率 24.6%,高中生 29.1%;63% 家庭求助无门转向市场化服务",
+       "78% 家长认为孩子缺乏自主学习动力;隔代抚养/单亲等非传统家庭占比超 43%"
+      ],
+      "content_budget": {
+       "body_max_lines": 4,
+       "strict": true
+      },
+      "image": {
+       "needed": false,
+       "usage": null,
+       "placement": null,
+       "content_description": null,
+       "source_hint": null
+      },
+      "data_points": [
+       {
+        "value": "24.6%",
+        "label": "初中生抑郁检出率",
+        "interpretation": "教育部 2025 监测数据"
+       },
+       {
+        "value": "29.1%",
+        "label": "高中生抑郁检出率",
+        "interpretation": "问题高发,触目惊心"
+       },
+       {
+        "value": "20 万+",
+        "label": "专业人才缺口",
+        "interpretation": "持证家庭治疗师不足 8000 人"
+       },
+       {
+        "value": "78%",
+        "label": "家长认为孩子缺乏自驱力",
+        "interpretation": "中国教育科学研究院"
+       }
+      ],
+      "chart": {
+       "chart_type": "metric_row",
+       "note": "痛点四组关键数据"
+      }
+     },
+     {
+      "card_id": "c2",
+      "role": "support",
+      "card_type": "list",
+      "card_style": "elevated",
+      "argument_role": "claim",
+      "headline": "行业困境:数据有,洞察无",
+      "body": [
+       "一份 135 页的肠道菌群检测报告、数百项指标的测评报告,家长根本看不懂",
+       "\"数据有,洞察无;报告有,方案无\"是行业普遍困境"
+      ],
+      "content_budget": {
+       "body_max_lines": 4,
+       "strict": true
+      },
+      "image": {
+       "needed": true,
+       "usage": "inline-illustration",
+       "placement": "inline",
+       "content_description": "看不懂的厚厚专业报告,家长困惑",
+       "source_hint": "行业痛点配图"
+      }
+     },
+     {
+      "card_id": "c3",
+      "role": "support",
+      "card_type": "list",
+      "card_style": "filled",
+      "argument_role": "method",
+      "headline": "技术就绪:大模型让精准干预成为可能",
+      "body": [
+       "多模态大模型 + 数字疗法成为学界与产业共识方向",
+       "大模型 + RAG + 智能体(Agent)成熟:读得懂报告、说得清原理、给得出方案",
+       "神经科学驱动的\"主动健康六阶模型\"提供行为科学标尺"
+      ],
+      "content_budget": {
+       "body_max_lines": 4,
+       "strict": true
+      },
+      "image": {
+       "needed": false,
+       "usage": null,
+       "placement": null,
+       "content_description": null,
+       "source_hint": null
+      }
+     }
+    ],
+    "workflow_metadata": {
+     "workflow_version": "2026.04.09-v4.1",
+     "planning_schema_version": "4.1",
+     "planning_packet_version": "4.1",
+     "planning_continuity_version": "4.1"
+    },
+    "layout_hint": "primary-secondary",
+    "source_guidance": {
+     "material_ref": "docs/AI创新项目大赛-项目介绍.md §2.3-2.4",
+     "key_facts": [
+      "24.6%/29.1%",
+      "8000 人 vs 20 万缺口",
+      "78%/43%",
+      "135 页报告"
+     ]
+    }
+   },
+   {
+    "slide_number": 11,
+    "page_type": "section",
+    "title": "Part 03 · 市场容量",
+    "page_goal": "万亿赛道中的高增长细分:市场空间、用户结构、商业模式三连证",
+    "narrative_role": "section",
+    "visual_weight": 7,
+    "density_label": "mid_low",
+    "density_reason": "章节封面:市场规模结论先行",
+    "density_contract": {
+     "deck_bias": "balanced",
+     "page_lower_bound": "low",
+     "page_upper_bound": "high",
+     "max_cards": 3,
+     "max_charts": 1,
+     "min_body_font_px": 20,
+     "max_lines_per_card": 4,
+     "image_policy": "flexible",
+     "decoration_budget": "medium",
+     "overflow_strategy": "rebalance_layout"
+    },
+    "director_command": {
+     "focus": "card_hierarchy",
+     "rule": "ensure_anchor_visual_dominance",
+     "note": "Part 03 · 市场容量"
+    },
+    "decoration_hints": {
+     "style": "tech_blue_warm_orange",
+     "elements": [
+      "title_underline",
+      "grid_dots",
+      "footer"
+     ],
+     "brand": "五维色系点缀"
+    },
+    "resources": {
+     "layout_refs": [],
+     "block_refs": [],
+     "chart_refs": [],
+     "principle_refs": []
+    },
+    "cards": [
+     {
+      "card_id": "c1",
+      "role": "anchor",
+      "card_type": "text",
+      "card_style": "elevated",
+      "argument_role": "claim",
+      "headline": "4.5 万亿赛道 · 渗透率不足 3% · 97% 待开拓",
+      "body": [
+       "中国家庭教育消费市场 4.5 万亿元,家庭教育服务平台渗透率不足 3%——高增长细分 + 极低渗透率 = 巨大的平台化机会"
+      ],
+      "content_budget": {
+       "body_max_lines": 4,
+       "strict": true
+      },
+      "image": {
+       "needed": true,
+       "usage": "hero-background",
+       "placement": "right-half",
+       "content_description": "万亿市场规模意象:上升曲线与广阔空间,蓝橙色调",
+       "source_hint": "章节封面配图"
+      }
+     }
+    ],
+    "workflow_metadata": {
+     "workflow_version": "2026.04.09-v4.1",
+     "planning_schema_version": "4.1",
+     "planning_packet_version": "4.1",
+     "planning_continuity_version": "4.1"
+    },
+    "layout_hint": "free-section"
+   },
+   {
+    "slide_number": 12,
+    "page_type": "content",
+    "title": "市场空间:TAM / SAM / SOM",
+    "page_goal": "从 4.5 万亿家庭消费到 5000 亿儿童健康管理,逐级聚焦到 97% 待开拓的平台市场",
+    "narrative_role": "evidence",
+    "visual_weight": 8,
+    "density_label": "medium",
+    "density_reason": "TAM/SAM/SOM 三层漏斗:大数字逐级聚焦,增速对比",
+    "density_contract": {
+     "deck_bias": "balanced",
+     "page_lower_bound": "mid_low",
+     "page_upper_bound": "high",
+     "max_cards": 4,
+     "max_charts": 2,
+     "min_body_font_px": 18,
+     "max_lines_per_card": 5,
+     "image_policy": "support_only",
+     "decoration_budget": "medium",
+     "overflow_strategy": "tighten_budget"
+    },
+    "director_command": {
+     "focus": "card_hierarchy",
+     "rule": "ensure_anchor_visual_dominance",
+     "note": "市场空间:TAM / SAM / SOM"
+    },
+    "decoration_hints": {
+     "style": "tech_blue_warm_orange",
+     "elements": [
+      "title_underline",
+      "funnel_layers",
+      "footer"
+     ],
+     "brand": "accent 蓝→橙渐变层级"
+    },
+    "resources": {
+     "layout_refs": [],
+     "block_refs": [],
+     "chart_refs": [],
+     "principle_refs": []
+    },
+    "cards": [
+     {
+      "card_id": "c1",
+      "role": "anchor",
+      "card_type": "data",
+      "card_style": "accent",
+      "argument_role": "evidence",
+      "headline": "TAM · 中国家庭教育消费市场",
+      "body": [
+       "2025 年规模 4.5 万亿元(艾瑞咨询)"
+      ],
+      "content_budget": {
+       "body_max_lines": 4,
+       "strict": true
+      },
+      "image": {
+       "needed": false,
+       "usage": null,
+       "placement": null,
+       "content_description": null,
+       "source_hint": null
+      },
+      "data_points": [
+       {
+        "value": "4.5 万亿",
+        "label": "TAM 家庭教育消费市场",
+        "interpretation": "2025 年,艾瑞咨询"
+       }
+      ],
+      "chart": {
+       "chart_type": "kpi",
+       "note": "TAM 大数字"
+      }
+     },
+     {
+      "card_id": "c2",
+      "role": "support",
+      "card_type": "data",
+      "card_style": "elevated",
+      "argument_role": "evidence",
+      "headline": "SAM · 细分健康管理市场",
+      "body": [
+       "儿童健康管理(体检/营养/心理/运动)5000 亿元+",
+       "家庭教育服务 217 亿→500 亿(CAGR 19.3%,弗若斯特沙利文)",
+       "心理健康教育 286.3 亿→810 亿(CAGR 23.1%,ZVZO)",
+       "泛心理健康服务 104.1 亿(CAGR 27%,沙利文/中邮)"
+      ],
+      "content_budget": {
+       "body_max_lines": 4,
+       "strict": true
+      },
+      "image": {
+       "needed": false,
+       "usage": null,
+       "placement": null,
+       "content_description": null,
+       "source_hint": null
+      },
+      "data_points": [
+       {
+        "value": "19.3%",
+        "label": "家庭教育服务 CAGR",
+        "interpretation": "217 亿→500 亿(2030)"
+       },
+       {
+        "value": "27%",
+        "label": "泛心理服务 CAGR",
+        "interpretation": "2020-2025 复合增速"
+       }
+      ],
+      "chart": {
+       "chart_type": "comparison_bar",
+       "note": "细分市场增速对比"
+      }
+     },
+     {
+      "card_id": "c3",
+      "role": "support",
+      "card_type": "data",
+      "card_style": "filled",
+      "argument_role": "evidence",
+      "headline": "SOM · 平台渗透率",
+      "body": [
+       "家庭教育服务平台渗透率不足 3%,97% 待开拓"
+      ],
+      "content_budget": {
+       "body_max_lines": 4,
+       "strict": true
+      },
+      "image": {
+       "needed": false,
+       "usage": null,
+       "placement": null,
+       "content_description": null,
+       "source_hint": null
+      },
+      "data_points": [
+       {
+        "value": "< 3%",
+        "label": "平台渗透率",
+        "interpretation": "97% 市场待开拓"
+       },
+       {
+        "value": "97%",
+        "label": "待开拓空间",
+        "interpretation": "行业测算"
+       }
+      ]
+     },
+     {
+      "card_id": "c4",
+      "role": "context",
+      "card_type": "quote",
+      "card_style": "glass",
+      "argument_role": "synthesis",
+      "headline": "市场结论",
+      "body": [
+       "万亿赛道中的高增长细分,供给高度分散,一体化平台机会窗口打开"
+      ],
+      "content_budget": {
+       "body_max_lines": 4,
+       "strict": true
+      },
+      "image": {
+       "needed": false,
+       "usage": null,
+       "placement": null,
+       "content_description": null,
+       "source_hint": null
+      }
+     }
+    ],
+    "workflow_metadata": {
+     "workflow_version": "2026.04.09-v4.1",
+     "planning_schema_version": "4.1",
+     "planning_packet_version": "4.1",
+     "planning_continuity_version": "4.1"
+    },
+    "layout_hint": "hero-top",
+    "source_guidance": {
+     "material_ref": "docs/AI创新项目大赛-项目介绍.md §3.1",
+     "key_facts": [
+      "4.5 万亿",
+      "5000 亿+",
+      "CAGR 19.3%/27%/23.1%",
+      "渗透率 <3%"
+     ]
+    }
+   },
+   {
+    "slide_number": 13,
+    "page_type": "content",
+    "title": "用户基数与竞争格局",
+    "page_goal": "1.2 亿户目标家庭 + 高度分散供给 = 一体化平台的卡位机会",
+    "narrative_role": "evidence",
+    "visual_weight": 8,
+    "density_label": "medium",
+    "density_reason": "用户数据+竞争格局:数据卡为核心,空白卡位为结论",
+    "density_contract": {
+     "deck_bias": "balanced",
+     "page_lower_bound": "mid_low",
+     "page_upper_bound": "high",
+     "max_cards": 4,
+     "max_charts": 2,
+     "min_body_font_px": 18,
+     "max_lines_per_card": 5,
+     "image_policy": "support_only",
+     "decoration_budget": "medium",
+     "overflow_strategy": "tighten_budget"
+    },
+    "director_command": {
+     "focus": "card_hierarchy",
+     "rule": "ensure_anchor_visual_dominance",
+     "note": "用户基数与竞争格局"
+    },
+    "decoration_hints": {
+     "style": "tech_blue_warm_orange",
+     "elements": [
+      "title_underline",
+      "metric_row",
+      "footer"
+     ],
+     "brand": "accent 绿"
+    },
+    "resources": {
+     "layout_refs": [],
+     "block_refs": [],
+     "chart_refs": [],
+     "principle_refs": []
+    },
+    "cards": [
+     {
+      "card_id": "c1",
+      "role": "anchor",
+      "card_type": "data",
+      "card_style": "accent",
+      "argument_role": "evidence",
+      "headline": "目标用户与付费能力",
+      "body": [
+       "3-18 岁儿童家庭约 1.2 亿户;2024 年小学及幼儿园在校生约 1.4 亿人次"
+      ],
+      "content_budget": {
+       "body_max_lines": 4,
+       "strict": true
+      },
+      "image": {
+       "needed": false,
+       "usage": null,
+       "placement": null,
+       "content_description": null,
+       "source_hint": null
+      },
+      "data_points": [
+       {
+        "value": "1.2 亿",
+        "label": "目标家庭户数",
+        "interpretation": "3-18 岁儿童家庭"
+       },
+       {
+        "value": "78.6%",
+        "label": "城市家庭要求定制化",
+        "interpretation": "教育目标转向综合素养"
+       },
+       {
+        "value": "78.3%",
+        "label": "心理辅导复购率",
+        "interpretation": "服务包均价 1.2 万/年"
+       },
+       {
+        "value": "1.2-3 万",
+        "label": "家长年均教育支出/孩子",
+        "interpretation": "90 后父母偏好数据驱动"
+       }
+      ],
+      "chart": {
+       "chart_type": "metric_row",
+       "note": "用户结构关键数据"
+      }
+     },
+     {
+      "card_id": "c2",
+      "role": "support",
+      "card_type": "list",
+      "card_style": "elevated",
+      "argument_role": "contrast",
+      "headline": "竞争格局:高度分散,无一体化平台",
+      "body": [
+       "纯工具类 App:无服务闭环",
+       "纯内容平台:无数据闭环",
+       "纯教育机构:缺健康维度",
+       "尚无\"健康+教育+心理\"一体化的成规模平台"
+      ],
+      "content_budget": {
+       "body_max_lines": 4,
+       "strict": true
+      },
+      "image": {
+       "needed": false,
+       "usage": null,
+       "placement": null,
+       "content_description": null,
+       "source_hint": null
+      }
+     },
+     {
+      "card_id": "c3",
+      "role": "support",
+      "card_type": "list",
+      "card_style": "filled",
+      "argument_role": "claim",
+      "headline": "新壁垒:AI 辅助干预 + 个性化定制",
+      "body": [
+       "行业从\"卖课/卖工具\"转向\"交付结果\"",
+       "37.2% 机构已部署 AI 情绪识别",
+       "个性化定制正成为新壁垒——正是浠艾福的差异化卡位"
+      ],
+      "content_budget": {
+       "body_max_lines": 4,
+       "strict": true
+      },
+      "image": {
+       "needed": true,
+       "usage": "inline-illustration",
+       "placement": "inline",
+       "content_description": "竞争格局中的差异化卡位意象",
+       "source_hint": "竞争配图"
+      },
+      "data_points": [
+       {
+        "value": "37.2%",
+        "label": "机构已部署 AI 情绪识别",
+        "interpretation": "AI 辅助干预成新壁垒"
+       }
+      ]
+     }
+    ],
+    "workflow_metadata": {
+     "workflow_version": "2026.04.09-v4.1",
+     "planning_schema_version": "4.1",
+     "planning_packet_version": "4.1",
+     "planning_continuity_version": "4.1"
+    },
+    "layout_hint": "mixed-grid",
+    "source_guidance": {
+     "material_ref": "docs/AI创新项目大赛-项目介绍.md §3.2-3.3",
+     "key_facts": [
+      "1.2 亿户",
+      "78.6%",
+      "78.3% 复购",
+      "37.2% AI 部署"
+     ]
+    }
+   },
+   {
+    "slide_number": 14,
+    "page_type": "content",
+    "title": "商业模式与单位经济",
+    "page_goal": "三边市场平台经济:四类收入 + LTV/CAC 80-330 倍,商业可行性验证",
+    "narrative_role": "evidence",
+    "visual_weight": 8,
+    "density_label": "medium",
+    "density_reason": "商业模型:收入结构+单位经济双数据卡,突出 LTV/CAC 高倍率",
+    "density_contract": {
+     "deck_bias": "balanced",
+     "page_lower_bound": "mid_low",
+     "page_upper_bound": "high",
+     "max_cards": 4,
+     "max_charts": 2,
+     "min_body_font_px": 18,
+     "max_lines_per_card": 5,
+     "image_policy": "support_only",
+     "decoration_budget": "medium",
+     "overflow_strategy": "tighten_budget"
+    },
+    "director_command": {
+     "focus": "card_hierarchy",
+     "rule": "ensure_anchor_visual_dominance",
+     "note": "商业模式与单位经济"
+    },
+    "decoration_hints": {
+     "style": "tech_blue_warm_orange",
+     "elements": [
+      "title_underline",
+      "bar_chart",
+      "footer"
+     ],
+     "brand": "accent 金"
+    },
+    "resources": {
+     "layout_refs": [],
+     "block_refs": [],
+     "chart_refs": [],
+     "principle_refs": []
+    },
+    "cards": [
+     {
+      "card_id": "c1",
+      "role": "anchor",
+      "card_type": "data",
+      "card_style": "accent",
+      "argument_role": "evidence",
+      "headline": "四类收入 · 三边市场",
+      "body": [
+       "测评服务费:订单抽佣 15-25%,毛利率 60-70%",
+       "规划师佣金:套餐抽佣 10-20%,毛利率 80-90%",
+       "商品交易费:商城抽佣 5-15%,毛利率 85-95%",
+       "会员订阅:含 AI 健康教练,毛利率 70-80%"
+      ],
+      "content_budget": {
+       "body_max_lines": 4,
+       "strict": true
+      },
+      "image": {
+       "needed": false,
+       "usage": null,
+       "placement": null,
+       "content_description": null,
+       "source_hint": null
+      },
+      "data_points": [
+       {
+        "value": "80-90%",
+        "label": "规划师佣金毛利率",
+        "interpretation": "四类收入中最高"
+       },
+       {
+        "value": "15-25%",
+        "label": "测评订单抽佣",
+        "interpretation": "平台抽佣区间"
+       }
+      ],
+      "chart": {
+       "chart_type": "stacked_bar",
+       "note": "四类收入与毛利率"
+      }
+     },
+     {
+      "card_id": "c2",
+      "role": "support",
+      "card_type": "data",
+      "card_style": "elevated",
+      "argument_role": "evidence",
+      "headline": "单位经济模型",
+      "body": [
+       "微信生态社交推荐获客:单家庭 CAC 15-25 元",
+       "单家庭年均价值 LTV 2000-5000 元"
+      ],
+      "content_budget": {
+       "body_max_lines": 4,
+       "strict": true
+      },
+      "image": {
+       "needed": false,
+       "usage": null,
+       "placement": null,
+       "content_description": null,
+       "source_hint": null
+      },
+      "data_points": [
+       {
+        "value": "80-330 倍",
+        "label": "LTV/CAC",
+        "interpretation": "行业健康线为 3 倍,具备大规模投放可行性"
+       },
+       {
+        "value": "15-25 元",
+        "label": "单家庭获客成本 CAC",
+        "interpretation": "微信生态社交推荐"
+       },
+       {
+        "value": "2000-5000 元",
+        "label": "单家庭年均价值 LTV",
+        "interpretation": "年化价值"
+       }
+      ],
+      "chart": {
+       "chart_type": "kpi",
+       "note": "LTV/CAC 核心指标"
+      }
+     },
+     {
+      "card_id": "c3",
+      "role": "support",
+      "card_type": "diagram",
+      "card_style": "glass",
+      "argument_role": "framework",
+      "headline": "三边市场平台经济",
+      "body": [
+       "家长(需求端)— 消费 — 规划师/活动方/供应商(供给端)",
+       "平台运营:审核 · 配置 · 数据支撑全链路"
+      ],
+      "content_budget": {
+       "body_max_lines": 4,
+       "strict": true
+      },
+      "image": {
+       "needed": true,
+       "usage": "inline-illustration",
+       "placement": "inline",
+       "content_description": "三边市场平台连接意象",
+       "source_hint": "商业模式配图"
+      }
+     }
+    ],
+    "workflow_metadata": {
+     "workflow_version": "2026.04.09-v4.1",
+     "planning_schema_version": "4.1",
+     "planning_packet_version": "4.1",
+     "planning_continuity_version": "4.1"
+    },
+    "layout_hint": "l-shape",
+    "source_guidance": {
+     "material_ref": "docs/AI创新项目大赛-项目介绍.md §3.4",
+     "key_facts": [
+      "四类收入毛利率",
+      "LTV/CAC 80-330 倍",
+      "CAC 15-25 元"
+     ]
+    }
+   },
+   {
+    "slide_number": 15,
+    "page_type": "section",
+    "title": "Part 04 · 需求与设计",
+    "page_goal": "以\"五维能量\"方法论重构家庭健康管理:有理论源头、有产品落点、有业务闭环",
+    "narrative_role": "section",
+    "visual_weight": 7,
+    "density_label": "mid_low",
+    "density_reason": "章节封面:五维方法论定位",
+    "density_contract": {
+     "deck_bias": "balanced",
+     "page_lower_bound": "low",
+     "page_upper_bound": "high",
+     "max_cards": 3,
+     "max_charts": 1,
+     "min_body_font_px": 20,
+     "max_lines_per_card": 4,
+     "image_policy": "flexible",
+     "decoration_budget": "medium",
+     "overflow_strategy": "rebalance_layout"
+    },
+    "director_command": {
+     "focus": "card_hierarchy",
+     "rule": "ensure_anchor_visual_dominance",
+     "note": "Part 04 · 需求与设计"
+    },
+    "decoration_hints": {
+     "style": "tech_blue_warm_orange",
+     "elements": [
+      "title_underline",
+      "grid_dots",
+      "footer"
+     ],
+     "brand": "五维色系点缀"
+    },
+    "resources": {
+     "layout_refs": [],
+     "block_refs": [],
+     "chart_refs": [],
+     "principle_refs": []
+    },
+    "cards": [
+     {
+      "card_id": "c1",
+      "role": "anchor",
+      "card_type": "text",
+      "card_style": "elevated",
+      "argument_role": "claim",
+      "headline": "五维能量体系:测得到 · 评得准 · 做得到",
+      "body": [
+       "基于 WHO 三维健康观与\"健康中国\"四维健康观演进,结合中国五行智慧,项目自研\"身·心·智·行·富\"五维能量体系,将理论测评与日常行为激励打通"
+      ],
+      "content_budget": {
+       "body_max_lines": 4,
+       "strict": true
+      },
+      "image": {
+       "needed": true,
+       "usage": "hero-background",
+       "placement": "right-half",
+       "content_description": "五行五维能量体系意象:五行相生的环形能量,暖橙主调",
+       "source_hint": "章节封面配图"
+      }
+     }
+    ],
+    "workflow_metadata": {
+     "workflow_version": "2026.04.09-v4.1",
+     "planning_schema_version": "4.1",
+     "planning_packet_version": "4.1",
+     "planning_continuity_version": "4.1"
+    },
+    "layout_hint": "free-section"
+   },
+   {
+    "slide_number": 16,
+    "page_type": "content",
+    "title": "痛点洞察与方法论演进",
+    "page_goal": "三大痛点 + WHO 三维到浠艾福五维的传承脉络",
+    "narrative_role": "evidence",
+    "visual_weight": 8,
+    "density_label": "medium",
+    "density_reason": "痛点+方法论演进:process 展示传承脉络,痛点数据突出",
+    "density_contract": {
+     "deck_bias": "balanced",
+     "page_lower_bound": "mid_low",
+     "page_upper_bound": "high",
+     "max_cards": 4,
+     "max_charts": 2,
+     "min_body_font_px": 18,
+     "max_lines_per_card": 5,
+     "image_policy": "support_only",
+     "decoration_budget": "medium",
+     "overflow_strategy": "tighten_budget"
+    },
+    "director_command": {
+     "focus": "card_hierarchy",
+     "rule": "ensure_anchor_visual_dominance",
+     "note": "痛点洞察与方法论演进"
+    },
+    "decoration_hints": {
+     "style": "tech_blue_warm_orange",
+     "elements": [
+      "title_underline",
+      "process_flow",
+      "footer"
+     ],
+     "brand": "accent 橙"
+    },
+    "resources": {
+     "layout_refs": [],
+     "block_refs": [],
+     "chart_refs": [],
+     "principle_refs": []
+    },
+    "cards": [
+     {
+      "card_id": "c1",
+      "role": "anchor",
+      "card_type": "list",
+      "card_style": "accent",
+      "argument_role": "claim",
+      "headline": "三大核心痛点",
+      "body": [
+       "信息碎片化:健康、心理、教育数据散落各处,无全景图",
+       "干预滞后化:只能\"事后补救\",效果缺乏数据评估",
+       "执行断层化:拿到报告和方案,缺少推动执行机制——\"知道做不到\""
+      ],
+      "content_budget": {
+       "body_max_lines": 4,
+       "strict": true
+      },
+      "image": {
+       "needed": true,
+       "usage": "inline-illustration",
+       "placement": "inline",
+       "content_description": "家庭健康数据碎片化痛点意象",
+       "source_hint": "痛点配图"
+      }
+     },
+     {
+      "card_id": "c2",
+      "role": "support",
+      "card_type": "process",
+      "card_style": "elevated",
+      "argument_role": "framework",
+      "headline": "方法论传承脉络",
+      "body": [
+       "WHO 三维健康观(1948):身体·心理·社会适应",
+       "健康中国四维健康观:身体·心理·认知·社会性",
+       "浠艾福五维能量体系:身·心·智·行·富"
+      ],
+      "content_budget": {
+       "body_max_lines": 4,
+       "strict": true
+      },
+      "image": {
+       "needed": false,
+       "usage": null,
+       "placement": null,
+       "content_description": null,
+       "source_hint": null
+      },
+      "data_points": [
+       {
+        "value": "1948",
+        "label": "WHO 三维健康观",
+        "interpretation": "国际共识起点"
+       },
+       {
+        "value": "3 维→4 维→5 维",
+        "label": "演进路径",
+        "interpretation": "四维×五行智慧与家庭行为实践"
+       }
+      ]
+     },
+     {
+      "card_id": "c3",
+      "role": "support",
+      "card_type": "quote",
+      "card_style": "filled",
+      "argument_role": "synthesis",
+      "headline": "一句话总结",
+      "body": [
+       "浠艾福五维 = 健康中国四维健康观 × 五行智慧与家庭行为实践——既有国家战略理论源头,又有本土化创新"
+      ],
+      "content_budget": {
+       "body_max_lines": 4,
+       "strict": true
+      },
+      "image": {
+       "needed": false,
+       "usage": null,
+       "placement": null,
+       "content_description": null,
+       "source_hint": null
+      }
+     }
+    ],
+    "workflow_metadata": {
+     "workflow_version": "2026.04.09-v4.1",
+     "planning_schema_version": "4.1",
+     "planning_packet_version": "4.1",
+     "planning_continuity_version": "4.1"
+    },
+    "layout_hint": "asymmetric",
+    "source_guidance": {
+     "material_ref": "docs/AI创新项目大赛-项目介绍.md §4.1-4.2",
+     "key_facts": [
+      "三大痛点",
+      "WHO 1948",
+      "四维→五维"
+     ]
+    }
+   },
+   {
+    "slide_number": 17,
+    "page_type": "content",
+    "title": "五维全景幸福矩阵",
+    "page_goal": "五维×五行×功能:产品方法论与双轨激励机制的完整落地",
+    "narrative_role": "evidence",
+    "visual_weight": 8,
+    "density_label": "medium",
+    "density_reason": "五维矩阵是核心:五行色系区分五个维度,双轨机制并排",
+    "density_contract": {
+     "deck_bias": "balanced",
+     "page_lower_bound": "mid_low",
+     "page_upper_bound": "high",
+     "max_cards": 4,
+     "max_charts": 2,
+     "min_body_font_px": 18,
+     "max_lines_per_card": 5,
+     "image_policy": "support_only",
+     "decoration_budget": "medium",
+     "overflow_strategy": "tighten_budget"
+    },
+    "director_command": {
+     "focus": "card_hierarchy",
+     "rule": "ensure_anchor_visual_dominance",
+     "note": "五维全景幸福矩阵"
+    },
+    "decoration_hints": {
+     "style": "tech_blue_warm_orange",
+     "elements": [
+      "title_underline",
+      "five_dim_badges",
+      "footer"
+     ],
+     "brand": "五维五色(身橙心粉智蓝行绿富金)"
+    },
+    "resources": {
+     "layout_refs": [],
+     "block_refs": [],
+     "chart_refs": [],
+     "principle_refs": []
+    },
+    "cards": [
+     {
+      "card_id": "c1",
+      "role": "anchor",
+      "card_type": "list",
+      "card_style": "accent",
+      "argument_role": "framework",
+      "headline": "五维 · 五行 · 功能",
+      "body": [
+       "身(土·主动健康):肠道菌群检测、健康报告 AI 解析、饮食推荐",
+       "心(火·大爱相随):DAN 测评、情绪打卡、家庭关系图、亲子沟通",
+       "智(金·赋能未来):认知测评、舒尔特/数独小游戏、阅读任务",
+       "行(木·人际和谐):任务系统、积分激励、健康打卡、家庭挑战",
+       "富(水·利他创富):财富打卡、创客生态、心愿兑换"
+      ],
+      "content_budget": {
+       "body_max_lines": 4,
+       "strict": true
+      },
+      "image": {
+       "needed": false,
+       "usage": null,
+       "placement": null,
+       "content_description": null,
+       "source_hint": null
+      },
+      "data_points": [
+       {
+        "value": "5 维",
+        "label": "五行对应",
+        "interpretation": "土火金木水"
+       },
+       {
+        "value": "25+",
+        "label": "评分数据源",
+        "interpretation": "5 维×3-8 数据源/维"
+       }
+      ]
+     },
+     {
+      "card_id": "c2",
+      "role": "support",
+      "card_type": "diagram",
+      "card_style": "elevated",
+      "argument_role": "method",
+      "headline": "能量沙盘 · 评分系统",
+      "body": [
+       "基于任务/游戏/测评/阅读/打卡行为数据,每日自动计算家庭五维健康指数(0-100%)",
+       "维度映射比例动态可配置"
+      ],
+      "content_budget": {
+       "body_max_lines": 4,
+       "strict": true
+      },
+      "image": {
+       "needed": true,
+       "usage": "inline-illustration",
+       "placement": "inline",
+       "content_description": "五维能量沙盘仪表盘意象",
+       "source_hint": "能量沙盘配图"
+      },
+      "data_points": [
+       {
+        "value": "0-100%",
+        "label": "家庭五维健康指数",
+        "interpretation": "每日自动计算"
+       }
+      ]
+     },
+     {
+      "card_id": "c3",
+      "role": "support",
+      "card_type": "diagram",
+      "card_style": "filled",
+      "argument_role": "method",
+      "headline": "能量账本 · 计量系统",
+      "body": [
+       "孩子完成任务获得能量积分、兑换心愿:\"努力→成长→奖励\"闭环",
+       "全局日上限 200 能量防刷分"
+      ],
+      "content_budget": {
+       "body_max_lines": 4,
+       "strict": true
+      },
+      "image": {
+       "needed": false,
+       "usage": null,
+       "placement": null,
+       "content_description": null,
+       "source_hint": null
+      },
+      "data_points": [
+       {
+        "value": "200",
+        "label": "能量日上限",
+        "interpretation": "防刷分机制"
+       }
+      ]
+     }
+    ],
+    "workflow_metadata": {
+     "workflow_version": "2026.04.09-v4.1",
+     "planning_schema_version": "4.1",
+     "planning_packet_version": "4.1",
+     "planning_continuity_version": "4.1"
+    },
+    "layout_hint": "mixed-grid",
+    "source_guidance": {
+     "material_ref": "docs/AI创新项目大赛-项目介绍.md §4.3",
+     "key_facts": [
+      "五维五行功能",
+      "25+ 数据源",
+      "日上限 200"
+     ]
+    }
+   },
+   {
+    "slide_number": 18,
+    "page_type": "content",
+    "title": "业务闭环:检测到复测",
+    "page_goal": "五大环节构成数据飞轮,精准干预闭环可解释、可追踪、可迭代",
+    "narrative_role": "evidence",
+    "visual_weight": 8,
+    "density_label": "medium",
+    "density_reason": "业务闭环:process 环形展示五环节,突出数据飞轮",
+    "density_contract": {
+     "deck_bias": "balanced",
+     "page_lower_bound": "mid_low",
+     "page_upper_bound": "high",
+     "max_cards": 4,
+     "max_charts": 2,
+     "min_body_font_px": 18,
+     "max_lines_per_card": 5,
+     "image_policy": "support_only",
+     "decoration_budget": "medium",
+     "overflow_strategy": "tighten_budget"
+    },
+    "director_command": {
+     "focus": "card_hierarchy",
+     "rule": "ensure_anchor_visual_dominance",
+     "note": "业务闭环:检测到复测"
+    },
+    "decoration_hints": {
+     "style": "tech_blue_warm_orange",
+     "elements": [
+      "title_underline",
+      "process_flow",
+      "footer"
+     ],
+     "brand": "accent 蓝"
+    },
+    "resources": {
+     "layout_refs": [],
+     "block_refs": [],
+     "chart_refs": [],
+     "principle_refs": []
+    },
+    "cards": [
+     {
+      "card_id": "c1",
+      "role": "anchor",
+      "card_type": "process",
+      "card_style": "accent",
+      "argument_role": "method",
+      "headline": "五环飞轮",
+      "body": [
+       "① 检测/测评:居家采样、测评预约",
+       "② AI 分析:报告解析、指标解读、风险标注",
+       "③ 个性化方案:基于个体画像的饮食/训练/亲子方案",
+       "④ 行为执行:任务下发、能量积分、即时反馈、智能预警",
+       "⑤ 复测迭代:周期复测、效果对比、方案调优",
+       "回到 ① —— 数据飞轮持续积累"
+      ],
+      "content_budget": {
+       "body_max_lines": 4,
+       "strict": true
+      },
+      "image": {
+       "needed": false,
+       "usage": null,
+       "placement": null,
+       "content_description": null,
+       "source_hint": null
+      },
+      "data_points": [
+       {
+        "value": "5 环",
+        "label": "闭环环节",
+        "interpretation": "检测→分析→方案→执行→复测"
+       },
+       {
+        "value": "∞",
+        "label": "数据飞轮",
+        "interpretation": "复测迭代持续积累家庭数据资产"
+       }
+      ],
+      "chart": {
+       "chart_type": "timeline",
+       "note": "五环节闭环"
+      }
+     },
+     {
+      "card_id": "c2",
+      "role": "support",
+      "card_type": "quote",
+      "card_style": "elevated",
+      "argument_role": "claim",
+      "headline": "精准干预闭环",
+      "body": [
+       "可量化评估 → 可解释分析 → 可执行干预 → 可追踪反馈",
+       "把 AI 的精准能力注入家庭健康管理的每一个环节"
+      ],
+      "content_budget": {
+       "body_max_lines": 4,
+       "strict": true
+      },
+      "image": {
+       "needed": true,
+       "usage": "inline-illustration",
+       "placement": "inline",
+       "content_description": "家庭健康数据飞轮循环意象",
+       "source_hint": "闭环配图"
+      }
+     }
+    ],
+    "workflow_metadata": {
+     "workflow_version": "2026.04.09-v4.1",
+     "planning_schema_version": "4.1",
+     "planning_packet_version": "4.1",
+     "planning_continuity_version": "4.1"
+    },
+    "layout_hint": "primary-secondary",
+    "source_guidance": {
+     "material_ref": "docs/AI创新项目大赛-项目介绍.md §4.4",
+     "key_facts": [
+      "五环节闭环",
+      "数据飞轮持续积累"
+     ]
+    }
+   },
+   {
+    "slide_number": 19,
+    "page_type": "section",
+    "title": "Part 05 · 技术壁垒",
+    "page_goal": "为什么别人要追 2-3 年:方法论壁垒 + 数据壁垒 + 网络效应 + AI 能力栈",
+    "narrative_role": "section",
+    "visual_weight": 7,
+    "density_label": "mid_low",
+    "density_reason": "章节封面:四重壁垒总览",
+    "density_contract": {
+     "deck_bias": "balanced",
+     "page_lower_bound": "low",
+     "page_upper_bound": "high",
+     "max_cards": 3,
+     "max_charts": 1,
+     "min_body_font_px": 20,
+     "max_lines_per_card": 4,
+     "image_policy": "flexible",
+     "decoration_budget": "medium",
+     "overflow_strategy": "rebalance_layout"
+    },
+    "director_command": {
+     "focus": "card_hierarchy",
+     "rule": "ensure_anchor_visual_dominance",
+     "note": "Part 05 · 技术壁垒"
+    },
+    "decoration_hints": {
+     "style": "tech_blue_warm_orange",
+     "elements": [
+      "title_underline",
+      "grid_dots",
+      "footer"
+     ],
+     "brand": "五维色系点缀"
+    },
+    "resources": {
+     "layout_refs": [],
+     "block_refs": [],
+     "chart_refs": [],
+     "principle_refs": []
+    },
+    "cards": [
+     {
+      "card_id": "c1",
+      "role": "anchor",
+      "card_type": "text",
+      "card_style": "elevated",
+      "argument_role": "claim",
+      "headline": "四重壁垒:算法 · 数据 · 网络效应 · AI 能力栈",
+      "body": [
+       "自研五维相生溯源算法、长周期行为数据护城河、三方网络效应、自研 AI 健康教练智能体——构成竞争对手难以复制的复合技术纵深"
+      ],
+      "content_budget": {
+       "body_max_lines": 4,
+       "strict": true
+      },
+      "image": {
+       "needed": true,
+       "usage": "hero-background",
+       "placement": "right-half",
+       "content_description": "技术与数据壁垒意象:盾牌与数据流,科技蓝主调",
+       "source_hint": "章节封面配图"
+      }
+     }
+    ],
+    "workflow_metadata": {
+     "workflow_version": "2026.04.09-v4.1",
+     "planning_schema_version": "4.1",
+     "planning_packet_version": "4.1",
+     "planning_continuity_version": "4.1"
+    },
+    "layout_hint": "free-section"
+   },
+   {
+    "slide_number": 20,
+    "page_type": "content",
+    "title": "五维相生溯源算法",
+    "page_goal": "核心算法壁垒:每一笔能量的来源、去向、归因都可追溯、可解释、可验证",
+    "narrative_role": "evidence",
+    "visual_weight": 8,
+    "density_label": "medium",
+    "density_reason": "核心算法壁垒:溯源能力+动态调节双支撑,突出可解释性",
+    "density_contract": {
+     "deck_bias": "balanced",
+     "page_lower_bound": "mid_low",
+     "page_upper_bound": "high",
+     "max_cards": 4,
+     "max_charts": 2,
+     "min_body_font_px": 18,
+     "max_lines_per_card": 5,
+     "image_policy": "support_only",
+     "decoration_budget": "medium",
+     "overflow_strategy": "tighten_budget"
+    },
+    "director_command": {
+     "focus": "card_hierarchy",
+     "rule": "ensure_anchor_visual_dominance",
+     "note": "五维相生溯源算法"
+    },
+    "decoration_hints": {
+     "style": "tech_blue_warm_orange",
+     "elements": [
+      "title_underline",
+      "ring_chart",
+      "footer"
+     ],
+     "brand": "accent 金"
+    },
+    "resources": {
+     "layout_refs": [],
+     "block_refs": [],
+     "chart_refs": [],
+     "principle_refs": []
+    },
+    "cards": [
+     {
+      "card_id": "c1",
+      "role": "anchor",
+      "card_type": "data",
+      "card_style": "accent",
+      "argument_role": "claim",
+      "headline": "自研五维评价体系",
+      "body": [
+       "融合五行相生理论与多源加权计算,每个维度由 3-8 个异构数据源支撑",
+       "energy_source_config 动态配置维度映射比例——差异化核心,具备\"五维健康行业标准\"演进潜力"
+      ],
+      "content_budget": {
+       "body_max_lines": 4,
+       "strict": true
+      },
+      "image": {
+       "needed": false,
+       "usage": null,
+       "placement": null,
+       "content_description": null,
+       "source_hint": null
+      },
+      "data_points": [
+       {
+        "value": "3-8",
+        "label": "异构数据源/维度",
+        "interpretation": "任务/活动/课程/商品/咨询等"
+       },
+       {
+        "value": "25+",
+        "label": "总评分数据源",
+        "interpretation": "5 维×3-8 源/维"
+       }
+      ],
+      "chart": {
+       "chart_type": "ring",
+       "note": "五维权重动态配置"
+      }
+     },
+     {
+      "card_id": "c2",
+      "role": "support",
+      "card_type": "list",
+      "card_style": "elevated",
+      "argument_role": "method",
+      "headline": "全链路溯源",
+      "body": [
+       "EnergyLog 完整记录来源类型/来源业务单/归属维度与占比权重",
+       "从\"家庭五维健康指数\"逐级下钻到\"某一维度的某一次具体行为\"",
+       "\"涨有据、落可查\""
+      ],
+      "content_budget": {
+       "body_max_lines": 4,
+       "strict": true
+      },
+      "image": {
+       "needed": false,
+       "usage": null,
+       "placement": null,
+       "content_description": null,
+       "source_hint": null
+      }
+     },
+     {
+      "card_id": "c3",
+      "role": "support",
+      "card_type": "list",
+      "card_style": "filled",
+      "argument_role": "method",
+      "headline": "五行相生动态调节",
+      "body": [
+       "身生智→智生富→富生行→行生心→心生身:相生增益",
+       "相克约束:动态平衡,\"一人一比例、一日一状态\"",
+       "EnergyRuleEngine 零代码配置:维度占比、行为映射、防刷分上限",
+       "行为—健康指标归因:回答\"过去 30 天哪些行为贡献了菌群多样性改善\""
+      ],
+      "content_budget": {
+       "body_max_lines": 4,
+       "strict": true
+      },
+      "image": {
+       "needed": true,
+       "usage": "inline-illustration",
+       "placement": "inline",
+       "content_description": "五行相生相克动态平衡意象",
+       "source_hint": "算法配图"
+      }
+     }
+    ],
+    "workflow_metadata": {
+     "workflow_version": "2026.04.09-v4.1",
+     "planning_schema_version": "4.1",
+     "planning_packet_version": "4.1",
+     "planning_continuity_version": "4.1"
+    },
+    "layout_hint": "mixed-grid",
+    "source_guidance": {
+     "material_ref": "docs/AI创新项目大赛-项目介绍.md §5.1",
+     "key_facts": [
+      "3-8 数据源/维",
+      "五行相生链",
+      "日上限 200",
+      "行为归因"
+     ]
+    }
+   },
+   {
+    "slide_number": 21,
+    "page_type": "content",
+    "title": "AI 能力栈(核心)",
+    "page_goal": "从\"没有记忆的机器人\"到\"比家长更了解这个家庭数据的健康顾问\"",
+    "narrative_role": "evidence",
+    "visual_weight": 9,
+    "density_label": "medium",
+    "density_reason": "AI 能力栈是全篇核心:上下文引擎+能力矩阵+智能体架构三层展示",
+    "density_contract": {
+     "deck_bias": "balanced",
+     "page_lower_bound": "mid_low",
+     "page_upper_bound": "high",
+     "max_cards": 4,
+     "max_charts": 2,
+     "min_body_font_px": 18,
+     "max_lines_per_card": 5,
+     "image_policy": "support_only",
+     "decoration_budget": "medium",
+     "overflow_strategy": "tighten_budget"
+    },
+    "director_command": {
+     "focus": "card_hierarchy",
+     "rule": "ensure_anchor_visual_dominance",
+     "note": "AI 能力栈(核心)"
+    },
+    "decoration_hints": {
+     "style": "tech_blue_warm_orange",
+     "elements": [
+      "title_underline",
+      "process_flow",
+      "footer"
+     ],
+     "brand": "accent 蓝"
+    },
+    "resources": {
+     "layout_refs": [],
+     "block_refs": [],
+     "chart_refs": [],
+     "principle_refs": []
+    },
+    "cards": [
+     {
+      "card_id": "c1",
+      "role": "anchor",
+      "card_type": "diagram",
+      "card_style": "accent",
+      "argument_role": "claim",
+      "headline": "家庭 360° 上下文引擎",
+      "body": [
+       "对话前自动组装家庭全景数据:成员信息、孩子列表、任务完成率、健康报告、五维能量分数、认知测评结果、已购商品标签、历史会话记忆",
+       "AI 不是\"没有记忆的机器人\",而是\"比家长更了解这个家庭数据的健康顾问\"——纯聊天产品无法企及的数据厚度"
+      ],
+      "content_budget": {
+       "body_max_lines": 4,
+       "strict": true
+      },
+      "image": {
+       "needed": true,
+       "usage": "inline-illustration",
+       "placement": "inline",
+       "content_description": "AI 健康顾问与家庭数据连接意象",
+       "source_hint": "AI上下文配图"
+      },
+      "data_points": [
+       {
+        "value": "360°",
+        "label": "家庭上下文",
+        "interpretation": "8 类家庭全景数据自动组装"
+       }
+      ]
+     },
+     {
+      "card_id": "c2",
+      "role": "support",
+      "card_type": "list",
+      "card_style": "elevated",
+      "argument_role": "framework",
+      "headline": "AI 能力矩阵(已实现)",
+      "body": [
+       "智能对话:智能体对话+家庭上下文注入+人格化助手",
+       "知识库问答(RAG):向量知识库+菌属/指标/食物/营养素定时同步",
+       "报告智能解析:PDF→结构化指标→AI 标注风险(135 页→3 页)",
+       "AI 生成任务:[TASK] 协议标记→自动创建成长任务",
+       "AI 内容推荐:标签匹配+维度对齐+已购降权",
+       "AI 每日反馈:定时生成个性化鼓励语"
+      ],
+      "content_budget": {
+       "body_max_lines": 4,
+       "strict": true
+      },
+      "image": {
+       "needed": false,
+       "usage": null,
+       "placement": null,
+       "content_description": null,
+       "source_hint": null
+      },
+      "data_points": [
+       {
+        "value": "6 类",
+        "label": "AI 能力",
+        "interpretation": "对话/RAG/解析/任务/推荐/反馈"
+       }
+      ]
+     },
+     {
+      "card_id": "c3",
+      "role": "support",
+      "card_type": "process",
+      "card_style": "filled",
+      "argument_role": "method",
+      "headline": "AI 健康教练智能体服务(自研·生产)",
+      "body": [
+       "意图分类器",
+       "场景路由(咨询/营养/解读/任务/推荐)",
+       "知识检索→大模型推理→工具调用→记忆保存",
+       "全链路追踪+指标监控+慢请求告警"
+      ],
+      "content_budget": {
+       "body_max_lines": 4,
+       "strict": true
+      },
+      "image": {
+       "needed": false,
+       "usage": null,
+       "placement": null,
+       "content_description": null,
+       "source_hint": null
+      },
+      "data_points": [
+       {
+        "value": "7×24",
+        "label": "智能应答",
+        "interpretation": "与后台专业服务团队人机协同"
+       }
+      ]
+     }
+    ],
+    "workflow_metadata": {
+     "workflow_version": "2026.04.09-v4.1",
+     "planning_schema_version": "4.1",
+     "planning_packet_version": "4.1",
+     "planning_continuity_version": "4.1"
+    },
+    "layout_hint": "l-shape",
+    "source_guidance": {
+     "material_ref": "docs/AI创新项目大赛-项目介绍.md §5.4",
+     "key_facts": [
+      "360°上下文",
+      "6 类 AI 能力",
+      "135→3 页",
+      "智能体编排"
+     ]
+    }
+   },
+   {
+    "slide_number": 22,
+    "page_type": "content",
+    "title": "数据护城河 · 六阶模型 · 工程安全",
+    "page_goal": "长周期数据不可复制 + 行为科学机制 + 生产级工程 = 多重复合壁垒",
+    "narrative_role": "evidence",
+    "visual_weight": 8,
+    "density_label": "medium",
+    "density_reason": "多重壁垒并列:数据+网络效应+行为科学+工程安全四卡",
+    "density_contract": {
+     "deck_bias": "balanced",
+     "page_lower_bound": "mid_low",
+     "page_upper_bound": "high",
+     "max_cards": 4,
+     "max_charts": 2,
+     "min_body_font_px": 18,
+     "max_lines_per_card": 5,
+     "image_policy": "support_only",
+     "decoration_budget": "medium",
+     "overflow_strategy": "tighten_budget"
+    },
+    "director_command": {
+     "focus": "card_hierarchy",
+     "rule": "ensure_anchor_visual_dominance",
+     "note": "数据护城河 · 六阶模型 · 工程安全"
+    },
+    "decoration_hints": {
+     "style": "tech_blue_warm_orange",
+     "elements": [
+      "title_underline",
+      "process_flow",
+      "footer"
+     ],
+     "brand": "accent 绿"
+    },
+    "resources": {
+     "layout_refs": [],
+     "block_refs": [],
+     "chart_refs": [],
+     "principle_refs": []
+    },
+    "cards": [
+     {
+      "card_id": "c1",
+      "role": "anchor",
+      "card_type": "data",
+      "card_style": "accent",
+      "argument_role": "evidence",
+      "headline": "行为数据护城河",
+      "body": [
+       "47 个数据库实体、25+ 评分数据源、10+ 种能量来源构成复杂数据模型",
+       "任务记录=努力曲线、五维能量=健康趋势、测评=成长档案、游戏=认知基线",
+       "一个孩子 6-18 岁的成长数据不是烧钱能快速复制的"
+      ],
+      "content_budget": {
+       "body_max_lines": 4,
+       "strict": true
+      },
+      "image": {
+       "needed": false,
+       "usage": null,
+       "placement": null,
+       "content_description": null,
+       "source_hint": null
+      },
+      "data_points": [
+       {
+        "value": "47",
+        "label": "数据库实体",
+        "interpretation": "复杂数据模型"
+       },
+       {
+        "value": "6-18 岁",
+        "label": "成长数据周期",
+        "interpretation": "长周期积累构成迁移壁垒"
+       }
+      ],
+      "chart": {
+       "chart_type": "kpi",
+       "note": "数据资产规模"
+      }
+     },
+     {
+      "card_id": "c2",
+      "role": "support",
+      "card_type": "list",
+      "card_style": "elevated",
+      "argument_role": "framework",
+      "headline": "三方网络效应(平台壁垒)",
+      "body": [
+       "更多家长→更多成长规划师→更优质服务→更多家庭(正向飞轮)",
+       "已上线:服务商入驻体系(规划师/活动方/供应商)、服务商等级、团队管理、佣金分成"
+      ],
+      "content_budget": {
+       "body_max_lines": 4,
+       "strict": true
+      },
+      "image": {
+       "needed": true,
+       "usage": "inline-illustration",
+       "placement": "inline",
+       "content_description": "三方网络飞轮意象",
+       "source_hint": "网络效应配图"
+      }
+     },
+     {
+      "card_id": "c3",
+      "role": "support",
+      "card_type": "process",
+      "card_style": "filled",
+      "argument_role": "framework",
+      "headline": "主动健康六阶模型(行为科学)",
+      "body": [
+       "有效启动(微行动<1s)",
+       "持续坚持(即时多巴胺)",
+       "获得感形成(成就显性化)",
+       "身份重塑(健康等级/称号)",
+       "意见领袖化(家庭互动)",
+       "社会扩散(健康圈/分享)"
+      ],
+      "content_budget": {
+       "body_max_lines": 4,
+       "strict": true
+      },
+      "image": {
+       "needed": false,
+       "usage": null,
+       "placement": null,
+       "content_description": null,
+       "source_hint": null
+      },
+      "data_points": [
+       {
+        "value": "6 阶",
+        "label": "行为干预阶梯",
+        "interpretation": "健康智能预警 6 条规则引擎+健康积分等级"
+       }
+      ]
+     },
+     {
+      "card_id": "c4",
+      "role": "support",
+      "card_type": "list",
+      "card_style": "glass",
+      "argument_role": "evidence",
+      "headline": "工程化与安全合规",
+      "body": [
+       "JWT 认证+角色权限(parent/child/teacher/admin),敏感字段加密",
+       "统一 Result 响应、统一 @PostMapping 规范、数据库迁移自动化",
+       "四级地址匹配(省市区街道+区域回退);完成遗留系统全量数据迁移"
+      ],
+      "content_budget": {
+       "body_max_lines": 4,
+       "strict": true
+      },
+      "image": {
+       "needed": false,
+       "usage": null,
+       "placement": null,
+       "content_description": null,
+       "source_hint": null
+      }
+     }
+    ],
+    "workflow_metadata": {
+     "workflow_version": "2026.04.09-v4.1",
+     "planning_schema_version": "4.1",
+     "planning_packet_version": "4.1",
+     "planning_continuity_version": "4.1"
+    },
+    "layout_hint": "asymmetric",
+    "source_guidance": {
+     "material_ref": "docs/AI创新项目大赛-项目介绍.md §5.2-5.3,5.4(5)",
+     "key_facts": [
+      "47/25+/10+",
+      "六阶模型",
+      "JWT+权限",
+      "全量迁移"
+     ]
+    }
+   },
+   {
+    "slide_number": 23,
+    "page_type": "section",
+    "title": "Part 06 · 应用成效",
+    "page_goal": "真实案例 · 权威背书 · 系统建设:项目已从概念走向生产",
+    "narrative_role": "section",
+    "visual_weight": 7,
+    "density_label": "mid_low",
+    "density_reason": "章节封面:应用成效总览",
+    "density_contract": {
+     "deck_bias": "balanced",
+     "page_lower_bound": "low",
+     "page_upper_bound": "high",
+     "max_cards": 3,
+     "max_charts": 1,
+     "min_body_font_px": 20,
+     "max_lines_per_card": 4,
+     "image_policy": "flexible",
+     "decoration_budget": "medium",
+     "overflow_strategy": "rebalance_layout"
+    },
+    "director_command": {
+     "focus": "card_hierarchy",
+     "rule": "ensure_anchor_visual_dominance",
+     "note": "Part 06 · 应用成效"
+    },
+    "decoration_hints": {
+     "style": "tech_blue_warm_orange",
+     "elements": [
+      "title_underline",
+      "grid_dots",
+      "footer"
+     ],
+     "brand": "五维色系点缀"
+    },
+    "resources": {
+     "layout_refs": [],
+     "block_refs": [],
+     "chart_refs": [],
+     "principle_refs": []
+    },
+    "cards": [
+     {
+      "card_id": "c1",
+      "role": "anchor",
+      "card_type": "text",
+      "card_style": "elevated",
+      "argument_role": "claim",
+      "headline": "从真实案例到生产级系统",
+      "body": [
+       "135 页专业报告压缩为 3 页行动方案的真实闭环、双培强基工程\"国家队\"级学术底座、15 大模块生产运行——用事实说话"
+      ],
+      "content_budget": {
+       "body_max_lines": 4,
+       "strict": true
+      },
+      "image": {
+       "needed": true,
+       "usage": "hero-background",
+       "placement": "right-half",
+       "content_description": "真实成果与成长数据意象:成果证书与上升曲线,暖橙蓝调",
+       "source_hint": "章节封面配图"
+      }
+     }
+    ],
+    "workflow_metadata": {
+     "workflow_version": "2026.04.09-v4.1",
+     "planning_schema_version": "4.1",
+     "planning_packet_version": "4.1",
+     "planning_continuity_version": "4.1"
+    },
+    "layout_hint": "free-section"
+   },
+   {
+    "slide_number": 24,
+    "page_type": "content",
+    "title": "真实案例:精准营养闭环",
+    "page_goal": "135 页专业报告 → 3 页行动方案,全链路在家完成",
+    "narrative_role": "evidence",
+    "visual_weight": 9,
+    "density_label": "medium",
+    "density_reason": "真实案例是说服力核心:数据+风险标注+干预闭环三卡",
+    "density_contract": {
+     "deck_bias": "balanced",
+     "page_lower_bound": "mid_low",
+     "page_upper_bound": "high",
+     "max_cards": 4,
+     "max_charts": 2,
+     "min_body_font_px": 18,
+     "max_lines_per_card": 5,
+     "image_policy": "support_only",
+     "decoration_budget": "medium",
+     "overflow_strategy": "tighten_budget"
+    },
+    "director_command": {
+     "focus": "card_hierarchy",
+     "rule": "ensure_anchor_visual_dominance",
+     "note": "真实案例:精准营养闭环"
+    },
+    "decoration_hints": {
+     "style": "tech_blue_warm_orange",
+     "elements": [
+      "title_underline",
+      "metric_row",
+      "process_flow",
+      "footer"
+     ],
+     "brand": "accent 橙"
+    },
+    "resources": {
+     "layout_refs": [],
+     "block_refs": [],
+     "chart_refs": [],
+     "principle_refs": []
+    },
+    "cards": [
+     {
+      "card_id": "c1",
+      "role": "anchor",
+      "card_type": "data",
+      "card_style": "accent",
+      "argument_role": "evidence",
+      "headline": "案例快照(匿名)",
+      "body": [
+       "用户 530010234,2026 年 1 月完成肠道菌群健康风险评估(募极生物,135 页报告)"
+      ],
+      "content_budget": {
+       "body_max_lines": 4,
+       "strict": true
+      },
+      "image": {
+       "needed": false,
+       "usage": null,
+       "placement": null,
+       "content_description": null,
+       "source_hint": null
+      },
+      "data_points": [
+       {
+        "value": "73",
+        "label": "健康总分",
+        "interpretation": "人群均值 75,干预空间大"
+       },
+       {
+        "value": "53",
+        "label": "菌群健康",
+        "interpretation": "偏低"
+       },
+       {
+        "value": "313 种",
+        "label": "检出菌种",
+        "interpretation": "均值约 800 种,微生态稳定性不足"
+       }
+      ],
+      "chart": {
+       "chart_type": "metric_row",
+       "note": "AI 分析关键指标"
+      }
+     },
+     {
+      "card_id": "c2",
+      "role": "support",
+      "card_type": "list",
+      "card_style": "elevated",
+      "argument_role": "evidence",
+      "headline": "AI 自动标注三大缺乏指标",
+      "body": [
+       "维生素 B12(14 分,正常>50)→ 疲劳/贫血风险",
+       "苏氨酸(14 分)→ 肠道屏障功能受损",
+       "丙氨酸(9 分)→ 能量代谢异常"
+      ],
+      "content_budget": {
+       "body_max_lines": 4,
+       "strict": true
+      },
+      "image": {
+       "needed": true,
+       "usage": "inline-illustration",
+       "placement": "inline",
+       "content_description": "AI 从厚报告提炼关键指标意象",
+       "source_hint": "AI解析配图"
+      },
+      "data_points": [
+       {
+        "value": "3 项",
+        "label": "AI 自动标注风险",
+        "interpretation": "从 135 页报告中精准提炼"
+       }
+      ]
+     },
+     {
+      "card_id": "c3",
+      "role": "support",
+      "card_type": "process",
+      "card_style": "filled",
+      "argument_role": "method",
+      "headline": "平台成效:四步干预闭环",
+      "body": [
+       "增加膳食纤维 25-30g/天",
+       "优选发酵蛋白",
+       "控制风险食物",
+       "定期复测"
+      ],
+      "content_budget": {
+       "body_max_lines": 4,
+       "strict": true
+      },
+      "image": {
+       "needed": false,
+       "usage": null,
+       "placement": null,
+       "content_description": null,
+       "source_hint": null
+      },
+      "data_points": [
+       {
+        "value": "135→3 页",
+        "label": "报告压缩",
+        "interpretation": "AI 解析让报告\"看得懂\""
+       },
+       {
+        "value": "4 步",
+        "label": "拟杆菌型肠型干预策略",
+        "interpretation": "任务系统+能量积分推动执行"
+       }
+      ],
+      "chart": {
+       "chart_type": "progress_bar",
+       "note": "闭环推进"
+      }
+     },
+     {
+      "card_id": "c4",
+      "role": "context",
+      "card_type": "quote",
+      "card_style": "glass",
+      "argument_role": "synthesis",
+      "headline": "价值主张兑现",
+      "body": [
+       "\"检测→解读→方案→执行→跟踪\"全链路在家完成,替代\"跑医院、找专家、花数千元\"的传统路径"
+      ],
+      "content_budget": {
+       "body_max_lines": 4,
+       "strict": true
+      },
+      "image": {
+       "needed": false,
+       "usage": null,
+       "placement": null,
+       "content_description": null,
+       "source_hint": null
+      }
+     }
+    ],
+    "workflow_metadata": {
+     "workflow_version": "2026.04.09-v4.1",
+     "planning_schema_version": "4.1",
+     "planning_packet_version": "4.1",
+     "planning_continuity_version": "4.1"
+    },
+    "layout_hint": "mixed-grid",
+    "source_guidance": {
+     "material_ref": "docs/AI创新项目大赛-项目介绍.md §6.1",
+     "key_facts": [
+      "73/53/313 种",
+      "B12 14 分",
+      "135→3 页",
+      "四步干预"
+     ]
+    }
+   },
+   {
+    "slide_number": 25,
+    "page_type": "content",
+    "title": "权威背书与系统建设成效",
+    "page_goal": "\"国家队\"级学术底座 + 15 大模块生产运行 + 商业化验证",
+    "narrative_role": "evidence",
+    "visual_weight": 8,
+    "density_label": "medium",
+    "density_reason": "权威背书+系统建设+商业化:三卡并列证明可信度",
+    "density_contract": {
+     "deck_bias": "balanced",
+     "page_lower_bound": "mid_low",
+     "page_upper_bound": "high",
+     "max_cards": 4,
+     "max_charts": 2,
+     "min_body_font_px": 18,
+     "max_lines_per_card": 5,
+     "image_policy": "support_only",
+     "decoration_budget": "medium",
+     "overflow_strategy": "tighten_budget"
+    },
+    "director_command": {
+     "focus": "card_hierarchy",
+     "rule": "ensure_anchor_visual_dominance",
+     "note": "权威背书与系统建设成效"
+    },
+    "decoration_hints": {
+     "style": "tech_blue_warm_orange",
+     "elements": [
+      "title_underline",
+      "kpi_row",
+      "footer"
+     ],
+     "brand": "accent 蓝"
+    },
+    "resources": {
+     "layout_refs": [],
+     "block_refs": [],
+     "chart_refs": [],
+     "principle_refs": []
+    },
+    "cards": [
+     {
+      "card_id": "c1",
+      "role": "anchor",
+      "card_type": "list",
+      "card_style": "accent",
+      "argument_role": "evidence",
+      "headline": "双培强基工程 · 权威背书",
+      "body": [
+       "人民日报《人民周刊》+ 中国科学院心理研究所联合发起,\"培育心智、培养志向\"",
+       "四维测评体系(DAN 测评等)已引入平台:权威测评→解读→干预→执行→复测数字化闭环",
+       "学术带头人魏高峡、施建农为工程学术体系核心专家",
+       "依托工程学术委员会、素质发展研究院提供持续背书"
+      ],
+      "content_budget": {
+       "body_max_lines": 4,
+       "strict": true
+      },
+      "image": {
+       "needed": true,
+       "usage": "inline-illustration",
+       "placement": "inline",
+       "content_description": "权威学术机构背书意象",
+       "source_hint": "权威背书配图"
+      },
+      "data_points": [
+       {
+        "value": "中科院",
+        "label": "心理研究所联合发起",
+        "interpretation": "\"国家队\"级学术底座"
+       }
+      ]
+     },
+     {
+      "card_id": "c2",
+      "role": "support",
+      "card_type": "list",
+      "card_style": "elevated",
+      "argument_role": "evidence",
+      "headline": "系统建设成效(可验证)",
+      "body": [
+       "15 大模块生产运行:认证/家庭/任务/心愿/积分/测评/成长档案/商城/服务商/AI 健康教练/健康报告/饮食推荐/文章/关系/联系人",
+       "AI 三端落地:健康教练小程序上线,覆盖上下文/解读/营养/任务生成/推荐",
+       "人机协同:AI 7×24 应答+健康管家/营养师/医生兜底",
+       "工程验证:Maven 编译+启动验证+分层测试(单元/集成/E2E),完成全量数据迁移"
+      ],
+      "content_budget": {
+       "body_max_lines": 4,
+       "strict": true
+      },
+      "image": {
+       "needed": false,
+       "usage": null,
+       "placement": null,
+       "content_description": null,
+       "source_hint": null
+      },
+      "data_points": [
+       {
+        "value": "15",
+        "label": "生产运行模块",
+        "interpretation": "功能闭环全部跑通"
+       }
+      ]
+     },
+     {
+      "card_id": "c3",
+      "role": "support",
+      "card_type": "data",
+      "card_style": "filled",
+      "argument_role": "evidence",
+      "headline": "商业化验证",
+      "body": [
+       "三边市场四类交易已设计并部分上线",
+       "公众号案例研究内容体系验证\"专业内容+案例转化\"低成本获客"
+      ],
+      "content_budget": {
+       "body_max_lines": 4,
+       "strict": true
+      },
+      "image": {
+       "needed": false,
+       "usage": null,
+       "placement": null,
+       "content_description": null,
+       "source_hint": null
+      },
+      "data_points": [
+       {
+        "value": "80-330 倍",
+        "label": "LTV/CAC",
+        "interpretation": "单位经济模型健康"
+       },
+       {
+        "value": "54 岁",
+        "label": "家长转变案例",
+        "interpretation": "从陪读焦虑到主动健康践行者"
+       }
+      ],
+      "chart": {
+       "chart_type": "kpi",
+       "note": "商业化关键指标"
+      }
+     }
+    ],
+    "workflow_metadata": {
+     "workflow_version": "2026.04.09-v4.1",
+     "planning_schema_version": "4.1",
+     "planning_packet_version": "4.1",
+     "planning_continuity_version": "4.1"
+    },
+    "layout_hint": "primary-secondary",
+    "source_guidance": {
+     "material_ref": "docs/AI创新项目大赛-项目介绍.md §6.2-6.5",
+     "key_facts": [
+      "双培强基工程",
+      "15 大模块",
+      "DAN 测评",
+      "LTV/CAC"
+     ]
+    }
+   },
+   {
+    "slide_number": 26,
+    "page_type": "end",
+    "title": "让健康从被动等待走向主动掌握",
+    "page_goal": "总结回顾 + 路线图 + 三年愿景 + 行动号召",
+    "narrative_role": "close",
+    "visual_weight": 8,
+    "density_label": "mid_low",
+    "density_reason": "结束页:核心回顾+路线图+愿景+CTA,首尾呼应",
+    "density_contract": {
+     "deck_bias": "balanced",
+     "page_lower_bound": "low",
+     "page_upper_bound": "high",
+     "max_cards": 3,
+     "max_charts": 1,
+     "min_body_font_px": 20,
+     "max_lines_per_card": 4,
+     "image_policy": "flexible",
+     "decoration_budget": "medium",
+     "overflow_strategy": "rebalance_layout"
+    },
+    "director_command": {
+     "focus": "card_hierarchy",
+     "rule": "ensure_anchor_visual_dominance",
+     "note": "让健康从被动等待走向主动掌握"
+    },
+    "decoration_hints": {
+     "style": "tech_blue_warm_orange",
+     "elements": [
+      "hero_image",
+      "timeline",
+      "footer"
+     ],
+     "brand": "首尾呼应暖橙"
+    },
+    "resources": {
+     "layout_refs": [],
+     "block_refs": [],
+     "chart_refs": [],
+     "principle_refs": []
+    },
+    "cards": [
+     {
+      "card_id": "c1",
+      "role": "anchor",
+      "card_type": "data",
+      "card_style": "accent",
+      "argument_role": "claim",
+      "headline": "核心回顾",
+      "body": [
+       "五维能量方法论 × AI 健康教练 + 人机协同 × 五维相生溯源算法",
+       "生产级系统:48 控制器 / 50+ 页面 / 15 大模块 / 真实数据资产"
+      ],
+      "content_budget": {
+       "body_max_lines": 4,
+       "strict": true
+      },
+      "image": {
+       "needed": false,
+       "usage": null,
+       "placement": null,
+       "content_description": null,
+       "source_hint": null
+      },
+      "data_points": [
+       {
+        "value": "身·心·智·行·富",
+        "label": "五维能量体系",
+        "interpretation": "方法论底座"
+       },
+       {
+        "value": "80-330 倍",
+        "label": "LTV/CAC",
+        "interpretation": "商业可行性验证"
+       },
+       {
+        "value": "10 万+",
+        "label": "2027 年覆盖家庭目标",
+        "interpretation": "三年愿景第一步"
+       }
+      ],
+      "chart": {
+       "chart_type": "kpi",
+       "note": "关键成果指标"
+      }
+     },
+     {
+      "card_id": "c2",
+      "role": "support",
+      "card_type": "timeline",
+      "card_style": "elevated",
+      "argument_role": "framework",
+      "headline": "未来 12 个月路线图与三年愿景",
+      "body": [
+       "Q1 增强:AI 报告自动解读全覆盖、AI 个性化任务生成、健康预警上线",
+       "Q2 增长:规划师认证线上化、活动方工具包、健康圈",
+       "Q3 变现:会员订阅体系、推广佣金系统",
+       "Q4 扩张:社区互动、Open API 第三方接入",
+       "2027 覆盖 10 万+ 家庭 → 2028 五维健康行业标准 → 2029 开放生态超级入口"
+      ],
+      "content_budget": {
+       "body_max_lines": 4,
+       "strict": true
+      },
+      "image": {
+       "needed": false,
+       "usage": null,
+       "placement": null,
+       "content_description": null,
+       "source_hint": null
+      },
+      "data_points": [
+       {
+        "value": "4 阶段",
+        "label": "季度路线图",
+        "interpretation": "增强→增长→变现→扩张"
+       },
+       {
+        "value": "2027-2029",
+        "label": "三年阶梯",
+        "interpretation": "平台→标准→生态"
+       }
+      ]
+     },
+     {
+      "card_id": "c3",
+      "role": "context",
+      "card_type": "quote",
+      "card_style": "glass",
+      "argument_role": "synthesis",
+      "headline": "给全家的一站式幸福提案",
+      "body": [
+       "让每一个家庭都拥有\"看得懂报告、跟得上方案、做得到行动\"的专属 AI 健康教练",
+       "北京安盈创卓科技有限公司 · 浠艾福(Care Family)"
+      ],
+      "content_budget": {
+       "body_max_lines": 4,
+       "strict": true
+      },
+      "image": {
+       "needed": true,
+       "usage": "hero-background",
+       "placement": "full-bleed",
+       "content_description": "温暖家庭剪影与上升光路,呼应封面,暖橙蓝调",
+       "source_hint": "结束页背景"
+      }
+     }
+    ],
+    "workflow_metadata": {
+     "workflow_version": "2026.04.09-v4.1",
+     "planning_schema_version": "4.1",
+     "planning_packet_version": "4.1",
+     "planning_continuity_version": "4.1"
+    },
+    "layout_hint": "free-end",
+    "source_guidance": {
+     "material_ref": "docs/AI创新项目大赛-项目介绍.md §7-8",
+     "key_facts": [
+      "12 个月路线图",
+      "三年愿景",
+      "行动号召"
+     ]
+    }
+   }
+  ]
+ }
+}

+ 84 - 0
ppt-output/planning/planning1.json

@@ -0,0 +1,84 @@
+{
+  "slide_number": 1,
+  "page_type": "cover",
+  "title": "给全家的一站式幸福提案",
+  "page_goal": "让评委第一眼理解:这是一家以 AI 驱动家庭主动健康管理的平台,守护家庭五维成长",
+  "narrative_role": "cover",
+  "visual_weight": 8,
+  "density_label": "low",
+  "density_reason": "封面页:品牌主张 + 视觉冲击,信息克制",
+  "density_contract": {
+    "deck_bias": "balanced",
+    "page_lower_bound": "low",
+    "page_upper_bound": "mid_low",
+    "max_cards": 2,
+    "max_charts": 1,
+    "min_body_font_px": 24,
+    "max_lines_per_card": 3,
+    "image_policy": "flexible",
+    "decoration_budget": "generous",
+    "overflow_strategy": "rebalance_layout"
+  },
+  "director_command": {
+    "focus": "card_hierarchy",
+    "rule": "ensure_anchor_visual_dominance",
+    "note": "给全家的一站式幸福提案"
+  },
+  "decoration_hints": {
+    "style": "tech_blue_warm_orange",
+    "elements": [
+      "hero_image",
+      "brand_mark",
+      "five_dim_badges",
+      "footer"
+    ]
+  },
+  "resources": {
+    "layout_refs": [],
+    "block_refs": [],
+    "chart_refs": [],
+    "principle_refs": []
+  },
+  "cards": [
+    {
+      "card_id": "c1",
+      "role": "anchor",
+      "card_type": "text",
+      "card_style": "elevated",
+      "argument_role": "claim",
+      "headline": "AI 驱动的家庭主动健康管理平台",
+      "body": [
+        "以 AI 实现精准干预、主动健康,守护中国家庭的\"身 · 心 · 智 · 行 · 富\"五维成长",
+        "让每一个家庭都拥有\"看得懂报告、跟得上方案、做得到行动\"的专属 AI 健康教练"
+      ],
+      "content_budget": {
+        "body_max_lines": 3
+      },
+      "image": {
+        "needed": true,
+        "usage": "hero-background",
+        "placement": "full-bleed",
+        "content_description": "温暖家庭场景:父母与孩子一起在阳光下查看健康报告,科技蓝与暖橙色调",
+        "source_hint": "封面主视觉"
+      },
+      "data_points": [
+        {
+          "value": "身·心·智·行·富",
+          "label": "五维成长体系",
+          "interpretation": "方法论底座"
+        },
+        {
+          "value": "7×24",
+          "label": "AI 健康教练",
+          "interpretation": "专属智能体服务"
+        }
+      ]
+    }
+  ],
+  "workflow_metadata": {
+    "workflow_version": "2026.04.09-v4.1",
+    "planning_schema_version": "4.1",
+    "planning_packet_version": "4.1",
+    "planning_continuity_version": "4.1"
+  }
+}

+ 156 - 0
ppt-output/planning/planning10.json

@@ -0,0 +1,156 @@
+{
+  "slide_number": 10,
+  "page_type": "content",
+  "title": "社会痛点与技术窗口期",
+  "page_goal": "需求端问题高发、供给短缺,技术端智能体已就绪——窗口期成立",
+  "narrative_role": "evidence",
+  "visual_weight": 8,
+  "density_label": "medium",
+  "density_reason": "痛点数据是核心冲击点:数据卡突出,技术就绪作支撑",
+  "density_contract": {
+    "deck_bias": "balanced",
+    "page_lower_bound": "mid_low",
+    "page_upper_bound": "high",
+    "max_cards": 4,
+    "max_charts": 2,
+    "min_body_font_px": 18,
+    "max_lines_per_card": 5,
+    "image_policy": "support_only",
+    "decoration_budget": "medium",
+    "overflow_strategy": "tighten_budget"
+  },
+  "director_command": {
+    "focus": "card_hierarchy",
+    "rule": "ensure_anchor_visual_dominance",
+    "note": "社会痛点与技术窗口期"
+  },
+  "decoration_hints": {
+    "style": "tech_blue_warm_orange",
+    "elements": [
+      "title_underline",
+      "metric_row",
+      "footer"
+    ],
+    "brand": "accent 红橙"
+  },
+  "resources": {
+    "layout_refs": [],
+    "block_refs": [],
+    "chart_refs": [],
+    "principle_refs": []
+  },
+  "cards": [
+    {
+      "card_id": "c1",
+      "role": "anchor",
+      "card_type": "data",
+      "card_style": "accent",
+      "argument_role": "evidence",
+      "headline": "问题高发 · 供给严重不足",
+      "body": [
+        "教育部 2025 监测:初中生抑郁检出率 24.6%,高中生 29.1%;63% 家庭求助无门转向市场化服务",
+        "78% 家长认为孩子缺乏自主学习动力;隔代抚养/单亲等非传统家庭占比超 43%"
+      ],
+      "content_budget": {
+        "body_max_lines": 4,
+        "strict": true
+      },
+      "image": {
+        "needed": false,
+        "usage": null,
+        "placement": null,
+        "content_description": null,
+        "source_hint": null
+      },
+      "data_points": [
+        {
+          "value": "24.6%",
+          "label": "初中生抑郁检出率",
+          "interpretation": "教育部 2025 监测数据"
+        },
+        {
+          "value": "29.1%",
+          "label": "高中生抑郁检出率",
+          "interpretation": "问题高发,触目惊心"
+        },
+        {
+          "value": "20 万+",
+          "label": "专业人才缺口",
+          "interpretation": "持证家庭治疗师不足 8000 人"
+        },
+        {
+          "value": "78%",
+          "label": "家长认为孩子缺乏自驱力",
+          "interpretation": "中国教育科学研究院"
+        }
+      ],
+      "chart": {
+        "chart_type": "metric_row",
+        "note": "痛点四组关键数据"
+      }
+    },
+    {
+      "card_id": "c2",
+      "role": "support",
+      "card_type": "list",
+      "card_style": "elevated",
+      "argument_role": "claim",
+      "headline": "行业困境:数据有,洞察无",
+      "body": [
+        "一份 135 页的肠道菌群检测报告、数百项指标的测评报告,家长根本看不懂",
+        "\"数据有,洞察无;报告有,方案无\"是行业普遍困境"
+      ],
+      "content_budget": {
+        "body_max_lines": 4,
+        "strict": true
+      },
+      "image": {
+        "needed": true,
+        "usage": "inline-illustration",
+        "placement": "inline",
+        "content_description": "看不懂的厚厚专业报告,家长困惑",
+        "source_hint": "行业痛点配图"
+      }
+    },
+    {
+      "card_id": "c3",
+      "role": "support",
+      "card_type": "list",
+      "card_style": "filled",
+      "argument_role": "method",
+      "headline": "技术就绪:大模型让精准干预成为可能",
+      "body": [
+        "多模态大模型 + 数字疗法成为学界与产业共识方向",
+        "大模型 + RAG + 智能体(Agent)成熟:读得懂报告、说得清原理、给得出方案",
+        "神经科学驱动的\"主动健康六阶模型\"提供行为科学标尺"
+      ],
+      "content_budget": {
+        "body_max_lines": 4,
+        "strict": true
+      },
+      "image": {
+        "needed": false,
+        "usage": null,
+        "placement": null,
+        "content_description": null,
+        "source_hint": null
+      }
+    }
+  ],
+  "workflow_metadata": {
+    "workflow_version": "2026.04.09-v4.1",
+    "planning_schema_version": "4.1",
+    "planning_packet_version": "4.1",
+    "planning_continuity_version": "4.1"
+  },
+  "layout_hint": "primary-secondary",
+  "source_guidance": {
+    "material_ref": "docs/AI创新项目大赛-项目介绍.md §2.3-2.4",
+    "key_facts": [
+      "24.6%/29.1%",
+      "8000 人 vs 20 万缺口",
+      "78%/43%",
+      "135 页报告"
+    ]
+  }
+}

+ 73 - 0
ppt-output/planning/planning11.json

@@ -0,0 +1,73 @@
+{
+  "slide_number": 11,
+  "page_type": "section",
+  "title": "Part 03 · 市场容量",
+  "page_goal": "万亿赛道中的高增长细分:市场空间、用户结构、商业模式三连证",
+  "narrative_role": "section",
+  "visual_weight": 7,
+  "density_label": "mid_low",
+  "density_reason": "章节封面:市场规模结论先行",
+  "density_contract": {
+    "deck_bias": "balanced",
+    "page_lower_bound": "low",
+    "page_upper_bound": "high",
+    "max_cards": 3,
+    "max_charts": 1,
+    "min_body_font_px": 20,
+    "max_lines_per_card": 4,
+    "image_policy": "flexible",
+    "decoration_budget": "medium",
+    "overflow_strategy": "rebalance_layout"
+  },
+  "director_command": {
+    "focus": "card_hierarchy",
+    "rule": "ensure_anchor_visual_dominance",
+    "note": "Part 03 · 市场容量"
+  },
+  "decoration_hints": {
+    "style": "tech_blue_warm_orange",
+    "elements": [
+      "title_underline",
+      "grid_dots",
+      "footer"
+    ],
+    "brand": "五维色系点缀"
+  },
+  "resources": {
+    "layout_refs": [],
+    "block_refs": [],
+    "chart_refs": [],
+    "principle_refs": []
+  },
+  "cards": [
+    {
+      "card_id": "c1",
+      "role": "anchor",
+      "card_type": "text",
+      "card_style": "elevated",
+      "argument_role": "claim",
+      "headline": "4.5 万亿赛道 · 渗透率不足 3% · 97% 待开拓",
+      "body": [
+        "中国家庭教育消费市场 4.5 万亿元,家庭教育服务平台渗透率不足 3%——高增长细分 + 极低渗透率 = 巨大的平台化机会"
+      ],
+      "content_budget": {
+        "body_max_lines": 4,
+        "strict": true
+      },
+      "image": {
+        "needed": true,
+        "usage": "hero-background",
+        "placement": "right-half",
+        "content_description": "万亿市场规模意象:上升曲线与广阔空间,蓝橙色调",
+        "source_hint": "章节封面配图"
+      }
+    }
+  ],
+  "workflow_metadata": {
+    "workflow_version": "2026.04.09-v4.1",
+    "planning_schema_version": "4.1",
+    "planning_packet_version": "4.1",
+    "planning_continuity_version": "4.1"
+  },
+  "layout_hint": "free-section"
+}

+ 190 - 0
ppt-output/planning/planning12.json

@@ -0,0 +1,190 @@
+{
+  "slide_number": 12,
+  "page_type": "content",
+  "title": "市场空间:TAM / SAM / SOM",
+  "page_goal": "从 4.5 万亿家庭消费到 5000 亿儿童健康管理,逐级聚焦到 97% 待开拓的平台市场",
+  "narrative_role": "evidence",
+  "visual_weight": 8,
+  "density_label": "medium",
+  "density_reason": "TAM/SAM/SOM 三层漏斗:大数字逐级聚焦,增速对比",
+  "density_contract": {
+    "deck_bias": "balanced",
+    "page_lower_bound": "mid_low",
+    "page_upper_bound": "high",
+    "max_cards": 4,
+    "max_charts": 2,
+    "min_body_font_px": 18,
+    "max_lines_per_card": 5,
+    "image_policy": "support_only",
+    "decoration_budget": "medium",
+    "overflow_strategy": "tighten_budget"
+  },
+  "director_command": {
+    "focus": "card_hierarchy",
+    "rule": "ensure_anchor_visual_dominance",
+    "note": "市场空间:TAM / SAM / SOM"
+  },
+  "decoration_hints": {
+    "style": "tech_blue_warm_orange",
+    "elements": [
+      "title_underline",
+      "funnel_layers",
+      "footer"
+    ],
+    "brand": "accent 蓝→橙渐变层级"
+  },
+  "resources": {
+    "layout_refs": [],
+    "block_refs": [],
+    "chart_refs": [],
+    "principle_refs": []
+  },
+  "cards": [
+    {
+      "card_id": "c1",
+      "role": "anchor",
+      "card_type": "data",
+      "card_style": "accent",
+      "argument_role": "evidence",
+      "headline": "TAM · 中国家庭教育消费市场",
+      "body": [
+        "2025 年规模 4.5 万亿元(艾瑞咨询)"
+      ],
+      "content_budget": {
+        "body_max_lines": 4,
+        "strict": true
+      },
+      "image": {
+        "needed": false,
+        "usage": null,
+        "placement": null,
+        "content_description": null,
+        "source_hint": null
+      },
+      "data_points": [
+        {
+          "value": "4.5 万亿",
+          "label": "TAM 家庭教育消费市场",
+          "interpretation": "2025 年,艾瑞咨询"
+        }
+      ],
+      "chart": {
+        "chart_type": "kpi",
+        "note": "TAM 大数字"
+      }
+    },
+    {
+      "card_id": "c2",
+      "role": "support",
+      "card_type": "data",
+      "card_style": "elevated",
+      "argument_role": "evidence",
+      "headline": "SAM · 细分健康管理市场",
+      "body": [
+        "儿童健康管理(体检/营养/心理/运动)5000 亿元+",
+        "家庭教育服务 217 亿→500 亿(CAGR 19.3%,弗若斯特沙利文)",
+        "心理健康教育 286.3 亿→810 亿(CAGR 23.1%,ZVZO)",
+        "泛心理健康服务 104.1 亿(CAGR 27%,沙利文/中邮)"
+      ],
+      "content_budget": {
+        "body_max_lines": 4,
+        "strict": true
+      },
+      "image": {
+        "needed": false,
+        "usage": null,
+        "placement": null,
+        "content_description": null,
+        "source_hint": null
+      },
+      "data_points": [
+        {
+          "value": "19.3%",
+          "label": "家庭教育服务 CAGR",
+          "interpretation": "217 亿→500 亿(2030)"
+        },
+        {
+          "value": "27%",
+          "label": "泛心理服务 CAGR",
+          "interpretation": "2020-2025 复合增速"
+        }
+      ],
+      "chart": {
+        "chart_type": "comparison_bar",
+        "note": "细分市场增速对比"
+      }
+    },
+    {
+      "card_id": "c3",
+      "role": "support",
+      "card_type": "data",
+      "card_style": "filled",
+      "argument_role": "evidence",
+      "headline": "SOM · 平台渗透率",
+      "body": [
+        "家庭教育服务平台渗透率不足 3%,97% 待开拓"
+      ],
+      "content_budget": {
+        "body_max_lines": 4,
+        "strict": true
+      },
+      "image": {
+        "needed": false,
+        "usage": null,
+        "placement": null,
+        "content_description": null,
+        "source_hint": null
+      },
+      "data_points": [
+        {
+          "value": "< 3%",
+          "label": "平台渗透率",
+          "interpretation": "97% 市场待开拓"
+        },
+        {
+          "value": "97%",
+          "label": "待开拓空间",
+          "interpretation": "行业测算"
+        }
+      ]
+    },
+    {
+      "card_id": "c4",
+      "role": "context",
+      "card_type": "quote",
+      "card_style": "glass",
+      "argument_role": "synthesis",
+      "headline": "市场结论",
+      "body": [
+        "万亿赛道中的高增长细分,供给高度分散,一体化平台机会窗口打开"
+      ],
+      "content_budget": {
+        "body_max_lines": 4,
+        "strict": true
+      },
+      "image": {
+        "needed": false,
+        "usage": null,
+        "placement": null,
+        "content_description": null,
+        "source_hint": null
+      }
+    }
+  ],
+  "workflow_metadata": {
+    "workflow_version": "2026.04.09-v4.1",
+    "planning_schema_version": "4.1",
+    "planning_packet_version": "4.1",
+    "planning_continuity_version": "4.1"
+  },
+  "layout_hint": "hero-top",
+  "source_guidance": {
+    "material_ref": "docs/AI创新项目大赛-项目介绍.md §3.1",
+    "key_facts": [
+      "4.5 万亿",
+      "5000 亿+",
+      "CAGR 19.3%/27%/23.1%",
+      "渗透率 <3%"
+    ]
+  }
+}

+ 164 - 0
ppt-output/planning/planning13.json

@@ -0,0 +1,164 @@
+{
+  "slide_number": 13,
+  "page_type": "content",
+  "title": "用户基数与竞争格局",
+  "page_goal": "1.2 亿户目标家庭 + 高度分散供给 = 一体化平台的卡位机会",
+  "narrative_role": "evidence",
+  "visual_weight": 8,
+  "density_label": "medium",
+  "density_reason": "用户数据+竞争格局:数据卡为核心,空白卡位为结论",
+  "density_contract": {
+    "deck_bias": "balanced",
+    "page_lower_bound": "mid_low",
+    "page_upper_bound": "high",
+    "max_cards": 4,
+    "max_charts": 2,
+    "min_body_font_px": 18,
+    "max_lines_per_card": 5,
+    "image_policy": "support_only",
+    "decoration_budget": "medium",
+    "overflow_strategy": "tighten_budget"
+  },
+  "director_command": {
+    "focus": "card_hierarchy",
+    "rule": "ensure_anchor_visual_dominance",
+    "note": "用户基数与竞争格局"
+  },
+  "decoration_hints": {
+    "style": "tech_blue_warm_orange",
+    "elements": [
+      "title_underline",
+      "metric_row",
+      "footer"
+    ],
+    "brand": "accent 绿"
+  },
+  "resources": {
+    "layout_refs": [],
+    "block_refs": [],
+    "chart_refs": [],
+    "principle_refs": []
+  },
+  "cards": [
+    {
+      "card_id": "c1",
+      "role": "anchor",
+      "card_type": "data",
+      "card_style": "accent",
+      "argument_role": "evidence",
+      "headline": "目标用户与付费能力",
+      "body": [
+        "3-18 岁儿童家庭约 1.2 亿户;2024 年小学及幼儿园在校生约 1.4 亿人次"
+      ],
+      "content_budget": {
+        "body_max_lines": 4,
+        "strict": true
+      },
+      "image": {
+        "needed": false,
+        "usage": null,
+        "placement": null,
+        "content_description": null,
+        "source_hint": null
+      },
+      "data_points": [
+        {
+          "value": "1.2 亿",
+          "label": "目标家庭户数",
+          "interpretation": "3-18 岁儿童家庭"
+        },
+        {
+          "value": "78.6%",
+          "label": "城市家庭要求定制化",
+          "interpretation": "教育目标转向综合素养"
+        },
+        {
+          "value": "78.3%",
+          "label": "心理辅导复购率",
+          "interpretation": "服务包均价 1.2 万/年"
+        },
+        {
+          "value": "1.2-3 万",
+          "label": "家长年均教育支出/孩子",
+          "interpretation": "90 后父母偏好数据驱动"
+        }
+      ],
+      "chart": {
+        "chart_type": "metric_row",
+        "note": "用户结构关键数据"
+      }
+    },
+    {
+      "card_id": "c2",
+      "role": "support",
+      "card_type": "list",
+      "card_style": "elevated",
+      "argument_role": "contrast",
+      "headline": "竞争格局:高度分散,无一体化平台",
+      "body": [
+        "纯工具类 App:无服务闭环",
+        "纯内容平台:无数据闭环",
+        "纯教育机构:缺健康维度",
+        "尚无\"健康+教育+心理\"一体化的成规模平台"
+      ],
+      "content_budget": {
+        "body_max_lines": 4,
+        "strict": true
+      },
+      "image": {
+        "needed": false,
+        "usage": null,
+        "placement": null,
+        "content_description": null,
+        "source_hint": null
+      }
+    },
+    {
+      "card_id": "c3",
+      "role": "support",
+      "card_type": "list",
+      "card_style": "filled",
+      "argument_role": "claim",
+      "headline": "新壁垒:AI 辅助干预 + 个性化定制",
+      "body": [
+        "行业从\"卖课/卖工具\"转向\"交付结果\"",
+        "37.2% 机构已部署 AI 情绪识别",
+        "个性化定制正成为新壁垒——正是浠艾福的差异化卡位"
+      ],
+      "content_budget": {
+        "body_max_lines": 4,
+        "strict": true
+      },
+      "image": {
+        "needed": true,
+        "usage": "inline-illustration",
+        "placement": "inline",
+        "content_description": "竞争格局中的差异化卡位意象",
+        "source_hint": "竞争配图"
+      },
+      "data_points": [
+        {
+          "value": "37.2%",
+          "label": "机构已部署 AI 情绪识别",
+          "interpretation": "AI 辅助干预成新壁垒"
+        }
+      ]
+    }
+  ],
+  "workflow_metadata": {
+    "workflow_version": "2026.04.09-v4.1",
+    "planning_schema_version": "4.1",
+    "planning_packet_version": "4.1",
+    "planning_continuity_version": "4.1"
+  },
+  "layout_hint": "mixed-grid",
+  "source_guidance": {
+    "material_ref": "docs/AI创新项目大赛-项目介绍.md §3.2-3.3",
+    "key_facts": [
+      "1.2 亿户",
+      "78.6%",
+      "78.3% 复购",
+      "37.2% AI 部署"
+    ]
+  }
+}

+ 167 - 0
ppt-output/planning/planning14.json

@@ -0,0 +1,167 @@
+{
+  "slide_number": 14,
+  "page_type": "content",
+  "title": "商业模式与单位经济",
+  "page_goal": "三边市场平台经济:四类收入 + LTV/CAC 80-330 倍,商业可行性验证",
+  "narrative_role": "evidence",
+  "visual_weight": 8,
+  "density_label": "medium",
+  "density_reason": "商业模型:收入结构+单位经济双数据卡,突出 LTV/CAC 高倍率",
+  "density_contract": {
+    "deck_bias": "balanced",
+    "page_lower_bound": "mid_low",
+    "page_upper_bound": "high",
+    "max_cards": 4,
+    "max_charts": 2,
+    "min_body_font_px": 18,
+    "max_lines_per_card": 5,
+    "image_policy": "support_only",
+    "decoration_budget": "medium",
+    "overflow_strategy": "tighten_budget"
+  },
+  "director_command": {
+    "focus": "card_hierarchy",
+    "rule": "ensure_anchor_visual_dominance",
+    "note": "商业模式与单位经济"
+  },
+  "decoration_hints": {
+    "style": "tech_blue_warm_orange",
+    "elements": [
+      "title_underline",
+      "bar_chart",
+      "footer"
+    ],
+    "brand": "accent 金"
+  },
+  "resources": {
+    "layout_refs": [],
+    "block_refs": [],
+    "chart_refs": [],
+    "principle_refs": []
+  },
+  "cards": [
+    {
+      "card_id": "c1",
+      "role": "anchor",
+      "card_type": "data",
+      "card_style": "accent",
+      "argument_role": "evidence",
+      "headline": "四类收入 · 三边市场",
+      "body": [
+        "测评服务费:订单抽佣 15-25%,毛利率 60-70%",
+        "规划师佣金:套餐抽佣 10-20%,毛利率 80-90%",
+        "商品交易费:商城抽佣 5-15%,毛利率 85-95%",
+        "会员订阅:含 AI 健康教练,毛利率 70-80%"
+      ],
+      "content_budget": {
+        "body_max_lines": 4,
+        "strict": true
+      },
+      "image": {
+        "needed": false,
+        "usage": null,
+        "placement": null,
+        "content_description": null,
+        "source_hint": null
+      },
+      "data_points": [
+        {
+          "value": "80-90%",
+          "label": "规划师佣金毛利率",
+          "interpretation": "四类收入中最高"
+        },
+        {
+          "value": "15-25%",
+          "label": "测评订单抽佣",
+          "interpretation": "平台抽佣区间"
+        }
+      ],
+      "chart": {
+        "chart_type": "stacked_bar",
+        "note": "四类收入与毛利率"
+      }
+    },
+    {
+      "card_id": "c2",
+      "role": "support",
+      "card_type": "data",
+      "card_style": "elevated",
+      "argument_role": "evidence",
+      "headline": "单位经济模型",
+      "body": [
+        "微信生态社交推荐获客:单家庭 CAC 15-25 元",
+        "单家庭年均价值 LTV 2000-5000 元"
+      ],
+      "content_budget": {
+        "body_max_lines": 4,
+        "strict": true
+      },
+      "image": {
+        "needed": false,
+        "usage": null,
+        "placement": null,
+        "content_description": null,
+        "source_hint": null
+      },
+      "data_points": [
+        {
+          "value": "80-330 倍",
+          "label": "LTV/CAC",
+          "interpretation": "行业健康线为 3 倍,具备大规模投放可行性"
+        },
+        {
+          "value": "15-25 元",
+          "label": "单家庭获客成本 CAC",
+          "interpretation": "微信生态社交推荐"
+        },
+        {
+          "value": "2000-5000 元",
+          "label": "单家庭年均价值 LTV",
+          "interpretation": "年化价值"
+        }
+      ],
+      "chart": {
+        "chart_type": "kpi",
+        "note": "LTV/CAC 核心指标"
+      }
+    },
+    {
+      "card_id": "c3",
+      "role": "support",
+      "card_type": "diagram",
+      "card_style": "glass",
+      "argument_role": "framework",
+      "headline": "三边市场平台经济",
+      "body": [
+        "家长(需求端)— 消费 — 规划师/活动方/供应商(供给端)",
+        "平台运营:审核 · 配置 · 数据支撑全链路"
+      ],
+      "content_budget": {
+        "body_max_lines": 4,
+        "strict": true
+      },
+      "image": {
+        "needed": true,
+        "usage": "inline-illustration",
+        "placement": "inline",
+        "content_description": "三边市场平台连接意象",
+        "source_hint": "商业模式配图"
+      }
+    }
+  ],
+  "workflow_metadata": {
+    "workflow_version": "2026.04.09-v4.1",
+    "planning_schema_version": "4.1",
+    "planning_packet_version": "4.1",
+    "planning_continuity_version": "4.1"
+  },
+  "layout_hint": "l-shape",
+  "source_guidance": {
+    "material_ref": "docs/AI创新项目大赛-项目介绍.md §3.4",
+    "key_facts": [
+      "四类收入毛利率",
+      "LTV/CAC 80-330 倍",
+      "CAC 15-25 元"
+    ]
+  }
+}

+ 73 - 0
ppt-output/planning/planning15.json

@@ -0,0 +1,73 @@
+{
+  "slide_number": 15,
+  "page_type": "section",
+  "title": "Part 04 · 需求与设计",
+  "page_goal": "以\"五维能量\"方法论重构家庭健康管理:有理论源头、有产品落点、有业务闭环",
+  "narrative_role": "section",
+  "visual_weight": 7,
+  "density_label": "mid_low",
+  "density_reason": "章节封面:五维方法论定位",
+  "density_contract": {
+    "deck_bias": "balanced",
+    "page_lower_bound": "low",
+    "page_upper_bound": "high",
+    "max_cards": 3,
+    "max_charts": 1,
+    "min_body_font_px": 20,
+    "max_lines_per_card": 4,
+    "image_policy": "flexible",
+    "decoration_budget": "medium",
+    "overflow_strategy": "rebalance_layout"
+  },
+  "director_command": {
+    "focus": "card_hierarchy",
+    "rule": "ensure_anchor_visual_dominance",
+    "note": "Part 04 · 需求与设计"
+  },
+  "decoration_hints": {
+    "style": "tech_blue_warm_orange",
+    "elements": [
+      "title_underline",
+      "grid_dots",
+      "footer"
+    ],
+    "brand": "五维色系点缀"
+  },
+  "resources": {
+    "layout_refs": [],
+    "block_refs": [],
+    "chart_refs": [],
+    "principle_refs": []
+  },
+  "cards": [
+    {
+      "card_id": "c1",
+      "role": "anchor",
+      "card_type": "text",
+      "card_style": "elevated",
+      "argument_role": "claim",
+      "headline": "五维能量体系:测得到 · 评得准 · 做得到",
+      "body": [
+        "基于 WHO 三维健康观与\"健康中国\"四维健康观演进,结合中国五行智慧,项目自研\"身·心·智·行·富\"五维能量体系,将理论测评与日常行为激励打通"
+      ],
+      "content_budget": {
+        "body_max_lines": 4,
+        "strict": true
+      },
+      "image": {
+        "needed": true,
+        "usage": "hero-background",
+        "placement": "right-half",
+        "content_description": "五行五维能量体系意象:五行相生的环形能量,暖橙主调",
+        "source_hint": "章节封面配图"
+      }
+    }
+  ],
+  "workflow_metadata": {
+    "workflow_version": "2026.04.09-v4.1",
+    "planning_schema_version": "4.1",
+    "planning_packet_version": "4.1",
+    "planning_continuity_version": "4.1"
+  },
+  "layout_hint": "free-section"
+}

+ 141 - 0
ppt-output/planning/planning16.json

@@ -0,0 +1,141 @@
+{
+  "slide_number": 16,
+  "page_type": "content",
+  "title": "痛点洞察与方法论演进",
+  "page_goal": "三大痛点 + WHO 三维到浠艾福五维的传承脉络",
+  "narrative_role": "evidence",
+  "visual_weight": 8,
+  "density_label": "medium",
+  "density_reason": "痛点+方法论演进:process 展示传承脉络,痛点数据突出",
+  "density_contract": {
+    "deck_bias": "balanced",
+    "page_lower_bound": "mid_low",
+    "page_upper_bound": "high",
+    "max_cards": 4,
+    "max_charts": 2,
+    "min_body_font_px": 18,
+    "max_lines_per_card": 5,
+    "image_policy": "support_only",
+    "decoration_budget": "medium",
+    "overflow_strategy": "tighten_budget"
+  },
+  "director_command": {
+    "focus": "card_hierarchy",
+    "rule": "ensure_anchor_visual_dominance",
+    "note": "痛点洞察与方法论演进"
+  },
+  "decoration_hints": {
+    "style": "tech_blue_warm_orange",
+    "elements": [
+      "title_underline",
+      "process_flow",
+      "footer"
+    ],
+    "brand": "accent 橙"
+  },
+  "resources": {
+    "layout_refs": [],
+    "block_refs": [],
+    "chart_refs": [],
+    "principle_refs": []
+  },
+  "cards": [
+    {
+      "card_id": "c1",
+      "role": "anchor",
+      "card_type": "list",
+      "card_style": "accent",
+      "argument_role": "claim",
+      "headline": "三大核心痛点",
+      "body": [
+        "信息碎片化:健康、心理、教育数据散落各处,无全景图",
+        "干预滞后化:只能\"事后补救\",效果缺乏数据评估",
+        "执行断层化:拿到报告和方案,缺少推动执行机制——\"知道做不到\""
+      ],
+      "content_budget": {
+        "body_max_lines": 4,
+        "strict": true
+      },
+      "image": {
+        "needed": true,
+        "usage": "inline-illustration",
+        "placement": "inline",
+        "content_description": "家庭健康数据碎片化痛点意象",
+        "source_hint": "痛点配图"
+      }
+    },
+    {
+      "card_id": "c2",
+      "role": "support",
+      "card_type": "process",
+      "card_style": "elevated",
+      "argument_role": "framework",
+      "headline": "方法论传承脉络",
+      "body": [
+        "WHO 三维健康观(1948):身体·心理·社会适应",
+        "健康中国四维健康观:身体·心理·认知·社会性",
+        "浠艾福五维能量体系:身·心·智·行·富"
+      ],
+      "content_budget": {
+        "body_max_lines": 4,
+        "strict": true
+      },
+      "image": {
+        "needed": false,
+        "usage": null,
+        "placement": null,
+        "content_description": null,
+        "source_hint": null
+      },
+      "data_points": [
+        {
+          "value": "1948",
+          "label": "WHO 三维健康观",
+          "interpretation": "国际共识起点"
+        },
+        {
+          "value": "3 维→4 维→5 维",
+          "label": "演进路径",
+          "interpretation": "四维×五行智慧与家庭行为实践"
+        }
+      ]
+    },
+    {
+      "card_id": "c3",
+      "role": "support",
+      "card_type": "quote",
+      "card_style": "filled",
+      "argument_role": "synthesis",
+      "headline": "一句话总结",
+      "body": [
+        "浠艾福五维 = 健康中国四维健康观 × 五行智慧与家庭行为实践——既有国家战略理论源头,又有本土化创新"
+      ],
+      "content_budget": {
+        "body_max_lines": 4,
+        "strict": true
+      },
+      "image": {
+        "needed": false,
+        "usage": null,
+        "placement": null,
+        "content_description": null,
+        "source_hint": null
+      }
+    }
+  ],
+  "workflow_metadata": {
+    "workflow_version": "2026.04.09-v4.1",
+    "planning_schema_version": "4.1",
+    "planning_packet_version": "4.1",
+    "planning_continuity_version": "4.1"
+  },
+  "layout_hint": "asymmetric",
+  "source_guidance": {
+    "material_ref": "docs/AI创新项目大赛-项目介绍.md §4.1-4.2",
+    "key_facts": [
+      "三大痛点",
+      "WHO 1948",
+      "四维→五维"
+    ]
+  }
+}

+ 157 - 0
ppt-output/planning/planning17.json

@@ -0,0 +1,157 @@
+{
+  "slide_number": 17,
+  "page_type": "content",
+  "title": "五维全景幸福矩阵",
+  "page_goal": "五维×五行×功能:产品方法论与双轨激励机制的完整落地",
+  "narrative_role": "evidence",
+  "visual_weight": 8,
+  "density_label": "medium",
+  "density_reason": "五维矩阵是核心:五行色系区分五个维度,双轨机制并排",
+  "density_contract": {
+    "deck_bias": "balanced",
+    "page_lower_bound": "mid_low",
+    "page_upper_bound": "high",
+    "max_cards": 4,
+    "max_charts": 2,
+    "min_body_font_px": 18,
+    "max_lines_per_card": 5,
+    "image_policy": "support_only",
+    "decoration_budget": "medium",
+    "overflow_strategy": "tighten_budget"
+  },
+  "director_command": {
+    "focus": "card_hierarchy",
+    "rule": "ensure_anchor_visual_dominance",
+    "note": "五维全景幸福矩阵"
+  },
+  "decoration_hints": {
+    "style": "tech_blue_warm_orange",
+    "elements": [
+      "title_underline",
+      "five_dim_badges",
+      "footer"
+    ],
+    "brand": "五维五色(身橙心粉智蓝行绿富金)"
+  },
+  "resources": {
+    "layout_refs": [],
+    "block_refs": [],
+    "chart_refs": [],
+    "principle_refs": []
+  },
+  "cards": [
+    {
+      "card_id": "c1",
+      "role": "anchor",
+      "card_type": "list",
+      "card_style": "accent",
+      "argument_role": "framework",
+      "headline": "五维 · 五行 · 功能",
+      "body": [
+        "身(土·主动健康):肠道菌群检测、健康报告 AI 解析、饮食推荐",
+        "心(火·大爱相随):DAN 测评、情绪打卡、家庭关系图、亲子沟通",
+        "智(金·赋能未来):认知测评、舒尔特/数独小游戏、阅读任务",
+        "行(木·人际和谐):任务系统、积分激励、健康打卡、家庭挑战",
+        "富(水·利他创富):财富打卡、创客生态、心愿兑换"
+      ],
+      "content_budget": {
+        "body_max_lines": 4,
+        "strict": true
+      },
+      "image": {
+        "needed": false,
+        "usage": null,
+        "placement": null,
+        "content_description": null,
+        "source_hint": null
+      },
+      "data_points": [
+        {
+          "value": "5 维",
+          "label": "五行对应",
+          "interpretation": "土火金木水"
+        },
+        {
+          "value": "25+",
+          "label": "评分数据源",
+          "interpretation": "5 维×3-8 数据源/维"
+        }
+      ]
+    },
+    {
+      "card_id": "c2",
+      "role": "support",
+      "card_type": "diagram",
+      "card_style": "elevated",
+      "argument_role": "method",
+      "headline": "能量沙盘 · 评分系统",
+      "body": [
+        "基于任务/游戏/测评/阅读/打卡行为数据,每日自动计算家庭五维健康指数(0-100%)",
+        "维度映射比例动态可配置"
+      ],
+      "content_budget": {
+        "body_max_lines": 4,
+        "strict": true
+      },
+      "image": {
+        "needed": true,
+        "usage": "inline-illustration",
+        "placement": "inline",
+        "content_description": "五维能量沙盘仪表盘意象",
+        "source_hint": "能量沙盘配图"
+      },
+      "data_points": [
+        {
+          "value": "0-100%",
+          "label": "家庭五维健康指数",
+          "interpretation": "每日自动计算"
+        }
+      ]
+    },
+    {
+      "card_id": "c3",
+      "role": "support",
+      "card_type": "diagram",
+      "card_style": "filled",
+      "argument_role": "method",
+      "headline": "能量账本 · 计量系统",
+      "body": [
+        "孩子完成任务获得能量积分、兑换心愿:\"努力→成长→奖励\"闭环",
+        "全局日上限 200 能量防刷分"
+      ],
+      "content_budget": {
+        "body_max_lines": 4,
+        "strict": true
+      },
+      "image": {
+        "needed": false,
+        "usage": null,
+        "placement": null,
+        "content_description": null,
+        "source_hint": null
+      },
+      "data_points": [
+        {
+          "value": "200",
+          "label": "能量日上限",
+          "interpretation": "防刷分机制"
+        }
+      ]
+    }
+  ],
+  "workflow_metadata": {
+    "workflow_version": "2026.04.09-v4.1",
+    "planning_schema_version": "4.1",
+    "planning_packet_version": "4.1",
+    "planning_continuity_version": "4.1"
+  },
+  "layout_hint": "mixed-grid",
+  "source_guidance": {
+    "material_ref": "docs/AI创新项目大赛-项目介绍.md §4.3",
+    "key_facts": [
+      "五维五行功能",
+      "25+ 数据源",
+      "日上限 200"
+    ]
+  }
+}

+ 124 - 0
ppt-output/planning/planning18.json

@@ -0,0 +1,124 @@
+{
+  "slide_number": 18,
+  "page_type": "content",
+  "title": "业务闭环:检测到复测",
+  "page_goal": "五大环节构成数据飞轮,精准干预闭环可解释、可追踪、可迭代",
+  "narrative_role": "evidence",
+  "visual_weight": 8,
+  "density_label": "medium",
+  "density_reason": "业务闭环:process 环形展示五环节,突出数据飞轮",
+  "density_contract": {
+    "deck_bias": "balanced",
+    "page_lower_bound": "mid_low",
+    "page_upper_bound": "high",
+    "max_cards": 4,
+    "max_charts": 2,
+    "min_body_font_px": 18,
+    "max_lines_per_card": 5,
+    "image_policy": "support_only",
+    "decoration_budget": "medium",
+    "overflow_strategy": "tighten_budget"
+  },
+  "director_command": {
+    "focus": "card_hierarchy",
+    "rule": "ensure_anchor_visual_dominance",
+    "note": "业务闭环:检测到复测"
+  },
+  "decoration_hints": {
+    "style": "tech_blue_warm_orange",
+    "elements": [
+      "title_underline",
+      "process_flow",
+      "footer"
+    ],
+    "brand": "accent 蓝"
+  },
+  "resources": {
+    "layout_refs": [],
+    "block_refs": [],
+    "chart_refs": [],
+    "principle_refs": []
+  },
+  "cards": [
+    {
+      "card_id": "c1",
+      "role": "anchor",
+      "card_type": "process",
+      "card_style": "accent",
+      "argument_role": "method",
+      "headline": "五环飞轮",
+      "body": [
+        "① 检测/测评:居家采样、测评预约",
+        "② AI 分析:报告解析、指标解读、风险标注",
+        "③ 个性化方案:基于个体画像的饮食/训练/亲子方案",
+        "④ 行为执行:任务下发、能量积分、即时反馈、智能预警",
+        "⑤ 复测迭代:周期复测、效果对比、方案调优",
+        "回到 ① —— 数据飞轮持续积累"
+      ],
+      "content_budget": {
+        "body_max_lines": 4,
+        "strict": true
+      },
+      "image": {
+        "needed": false,
+        "usage": null,
+        "placement": null,
+        "content_description": null,
+        "source_hint": null
+      },
+      "data_points": [
+        {
+          "value": "5 环",
+          "label": "闭环环节",
+          "interpretation": "检测→分析→方案→执行→复测"
+        },
+        {
+          "value": "∞",
+          "label": "数据飞轮",
+          "interpretation": "复测迭代持续积累家庭数据资产"
+        }
+      ],
+      "chart": {
+        "chart_type": "timeline",
+        "note": "五环节闭环"
+      }
+    },
+    {
+      "card_id": "c2",
+      "role": "support",
+      "card_type": "quote",
+      "card_style": "elevated",
+      "argument_role": "claim",
+      "headline": "精准干预闭环",
+      "body": [
+        "可量化评估 → 可解释分析 → 可执行干预 → 可追踪反馈",
+        "把 AI 的精准能力注入家庭健康管理的每一个环节"
+      ],
+      "content_budget": {
+        "body_max_lines": 4,
+        "strict": true
+      },
+      "image": {
+        "needed": true,
+        "usage": "inline-illustration",
+        "placement": "inline",
+        "content_description": "家庭健康数据飞轮循环意象",
+        "source_hint": "闭环配图"
+      }
+    }
+  ],
+  "workflow_metadata": {
+    "workflow_version": "2026.04.09-v4.1",
+    "planning_schema_version": "4.1",
+    "planning_packet_version": "4.1",
+    "planning_continuity_version": "4.1"
+  },
+  "layout_hint": "primary-secondary",
+  "source_guidance": {
+    "material_ref": "docs/AI创新项目大赛-项目介绍.md §4.4",
+    "key_facts": [
+      "五环节闭环",
+      "数据飞轮持续积累"
+    ]
+  }
+}

+ 73 - 0
ppt-output/planning/planning19.json

@@ -0,0 +1,73 @@
+{
+  "slide_number": 19,
+  "page_type": "section",
+  "title": "Part 05 · 技术壁垒",
+  "page_goal": "为什么别人要追 2-3 年:方法论壁垒 + 数据壁垒 + 网络效应 + AI 能力栈",
+  "narrative_role": "section",
+  "visual_weight": 7,
+  "density_label": "mid_low",
+  "density_reason": "章节封面:四重壁垒总览",
+  "density_contract": {
+    "deck_bias": "balanced",
+    "page_lower_bound": "low",
+    "page_upper_bound": "high",
+    "max_cards": 3,
+    "max_charts": 1,
+    "min_body_font_px": 20,
+    "max_lines_per_card": 4,
+    "image_policy": "flexible",
+    "decoration_budget": "medium",
+    "overflow_strategy": "rebalance_layout"
+  },
+  "director_command": {
+    "focus": "card_hierarchy",
+    "rule": "ensure_anchor_visual_dominance",
+    "note": "Part 05 · 技术壁垒"
+  },
+  "decoration_hints": {
+    "style": "tech_blue_warm_orange",
+    "elements": [
+      "title_underline",
+      "grid_dots",
+      "footer"
+    ],
+    "brand": "五维色系点缀"
+  },
+  "resources": {
+    "layout_refs": [],
+    "block_refs": [],
+    "chart_refs": [],
+    "principle_refs": []
+  },
+  "cards": [
+    {
+      "card_id": "c1",
+      "role": "anchor",
+      "card_type": "text",
+      "card_style": "elevated",
+      "argument_role": "claim",
+      "headline": "四重壁垒:算法 · 数据 · 网络效应 · AI 能力栈",
+      "body": [
+        "自研五维相生溯源算法、长周期行为数据护城河、三方网络效应、自研 AI 健康教练智能体——构成竞争对手难以复制的复合技术纵深"
+      ],
+      "content_budget": {
+        "body_max_lines": 4,
+        "strict": true
+      },
+      "image": {
+        "needed": true,
+        "usage": "hero-background",
+        "placement": "right-half",
+        "content_description": "技术与数据壁垒意象:盾牌与数据流,科技蓝主调",
+        "source_hint": "章节封面配图"
+      }
+    }
+  ],
+  "workflow_metadata": {
+    "workflow_version": "2026.04.09-v4.1",
+    "planning_schema_version": "4.1",
+    "planning_packet_version": "4.1",
+    "planning_continuity_version": "4.1"
+  },
+  "layout_hint": "free-section"
+}

+ 77 - 0
ppt-output/planning/planning2.json

@@ -0,0 +1,77 @@
+{
+  "slide_number": 2,
+  "page_type": "toc",
+  "title": "目录",
+  "page_goal": "六大部分清晰导航,让评委预知内容结构",
+  "narrative_role": "toc",
+  "visual_weight": 6,
+  "density_label": "low",
+  "density_reason": "目录页:清晰导航,每章有编号与主题",
+  "density_contract": {
+    "deck_bias": "balanced",
+    "page_lower_bound": "low",
+    "page_upper_bound": "mid_low",
+    "max_cards": 2,
+    "max_charts": 1,
+    "min_body_font_px": 24,
+    "max_lines_per_card": 3,
+    "image_policy": "flexible",
+    "decoration_budget": "generous",
+    "overflow_strategy": "rebalance_layout"
+  },
+  "director_command": {
+    "focus": "card_hierarchy",
+    "rule": "ensure_anchor_visual_dominance",
+    "note": "目录"
+  },
+  "decoration_hints": {
+    "style": "tech_blue_warm_orange",
+    "elements": [
+      "title_underline",
+      "grid_dots",
+      "footer"
+    ],
+    "brand": "五维色系点缀"
+  },
+  "resources": {
+    "layout_refs": [],
+    "block_refs": [],
+    "chart_refs": [],
+    "principle_refs": []
+  },
+  "cards": [
+    {
+      "card_id": "c1",
+      "role": "anchor",
+      "card_type": "list",
+      "card_style": "elevated",
+      "argument_role": "framework",
+      "headline": "六大部分",
+      "body": [
+        "Part 01 项目概述:缘起 · 产品 · 规模 · 团队",
+        "Part 02 背景政策:国家战略 · 社会痛点",
+        "Part 03 市场容量:TAM/SAM/SOM · 商业模式",
+        "Part 04 需求与设计:五维方法论 · 业务闭环",
+        "Part 05 技术壁垒:溯源算法 · AI 能力栈",
+        "Part 06 应用成效:真实案例 · 权威背书"
+      ],
+      "content_budget": {
+        "body_max_lines": 3
+      },
+      "image": {
+        "needed": false,
+        "usage": null,
+        "placement": null,
+        "content_description": null,
+        "source_hint": null
+      }
+    }
+  ],
+  "workflow_metadata": {
+    "workflow_version": "2026.04.09-v4.1",
+    "planning_schema_version": "4.1",
+    "planning_packet_version": "4.1",
+    "planning_continuity_version": "4.1"
+  },
+  "layout_hint": "toc-route"
+}

+ 148 - 0
ppt-output/planning/planning20.json

@@ -0,0 +1,148 @@
+{
+  "slide_number": 20,
+  "page_type": "content",
+  "title": "五维相生溯源算法",
+  "page_goal": "核心算法壁垒:每一笔能量的来源、去向、归因都可追溯、可解释、可验证",
+  "narrative_role": "evidence",
+  "visual_weight": 8,
+  "density_label": "medium",
+  "density_reason": "核心算法壁垒:溯源能力+动态调节双支撑,突出可解释性",
+  "density_contract": {
+    "deck_bias": "balanced",
+    "page_lower_bound": "mid_low",
+    "page_upper_bound": "high",
+    "max_cards": 4,
+    "max_charts": 2,
+    "min_body_font_px": 18,
+    "max_lines_per_card": 5,
+    "image_policy": "support_only",
+    "decoration_budget": "medium",
+    "overflow_strategy": "tighten_budget"
+  },
+  "director_command": {
+    "focus": "card_hierarchy",
+    "rule": "ensure_anchor_visual_dominance",
+    "note": "五维相生溯源算法"
+  },
+  "decoration_hints": {
+    "style": "tech_blue_warm_orange",
+    "elements": [
+      "title_underline",
+      "ring_chart",
+      "footer"
+    ],
+    "brand": "accent 金"
+  },
+  "resources": {
+    "layout_refs": [],
+    "block_refs": [],
+    "chart_refs": [],
+    "principle_refs": []
+  },
+  "cards": [
+    {
+      "card_id": "c1",
+      "role": "anchor",
+      "card_type": "data",
+      "card_style": "accent",
+      "argument_role": "claim",
+      "headline": "自研五维评价体系",
+      "body": [
+        "融合五行相生理论与多源加权计算,每个维度由 3-8 个异构数据源支撑",
+        "energy_source_config 动态配置维度映射比例——差异化核心,具备\"五维健康行业标准\"演进潜力"
+      ],
+      "content_budget": {
+        "body_max_lines": 4,
+        "strict": true
+      },
+      "image": {
+        "needed": false,
+        "usage": null,
+        "placement": null,
+        "content_description": null,
+        "source_hint": null
+      },
+      "data_points": [
+        {
+          "value": "3-8",
+          "label": "异构数据源/维度",
+          "interpretation": "任务/活动/课程/商品/咨询等"
+        },
+        {
+          "value": "25+",
+          "label": "总评分数据源",
+          "interpretation": "5 维×3-8 源/维"
+        }
+      ],
+      "chart": {
+        "chart_type": "ring",
+        "note": "五维权重动态配置"
+      }
+    },
+    {
+      "card_id": "c2",
+      "role": "support",
+      "card_type": "list",
+      "card_style": "elevated",
+      "argument_role": "method",
+      "headline": "全链路溯源",
+      "body": [
+        "EnergyLog 完整记录来源类型/来源业务单/归属维度与占比权重",
+        "从\"家庭五维健康指数\"逐级下钻到\"某一维度的某一次具体行为\"",
+        "\"涨有据、落可查\""
+      ],
+      "content_budget": {
+        "body_max_lines": 4,
+        "strict": true
+      },
+      "image": {
+        "needed": false,
+        "usage": null,
+        "placement": null,
+        "content_description": null,
+        "source_hint": null
+      }
+    },
+    {
+      "card_id": "c3",
+      "role": "support",
+      "card_type": "list",
+      "card_style": "filled",
+      "argument_role": "method",
+      "headline": "五行相生动态调节",
+      "body": [
+        "身生智→智生富→富生行→行生心→心生身:相生增益",
+        "相克约束:动态平衡,\"一人一比例、一日一状态\"",
+        "EnergyRuleEngine 零代码配置:维度占比、行为映射、防刷分上限",
+        "行为—健康指标归因:回答\"过去 30 天哪些行为贡献了菌群多样性改善\""
+      ],
+      "content_budget": {
+        "body_max_lines": 4,
+        "strict": true
+      },
+      "image": {
+        "needed": true,
+        "usage": "inline-illustration",
+        "placement": "inline",
+        "content_description": "五行相生相克动态平衡意象",
+        "source_hint": "算法配图"
+      }
+    }
+  ],
+  "workflow_metadata": {
+    "workflow_version": "2026.04.09-v4.1",
+    "planning_schema_version": "4.1",
+    "planning_packet_version": "4.1",
+    "planning_continuity_version": "4.1"
+  },
+  "layout_hint": "mixed-grid",
+  "source_guidance": {
+    "material_ref": "docs/AI创新项目大赛-项目介绍.md §5.1",
+    "key_facts": [
+      "3-8 数据源/维",
+      "五行相生链",
+      "日上限 200",
+      "行为归因"
+    ]
+  }
+}

+ 156 - 0
ppt-output/planning/planning21.json

@@ -0,0 +1,156 @@
+{
+  "slide_number": 21,
+  "page_type": "content",
+  "title": "AI 能力栈(核心)",
+  "page_goal": "从\"没有记忆的机器人\"到\"比家长更了解这个家庭数据的健康顾问\"",
+  "narrative_role": "evidence",
+  "visual_weight": 9,
+  "density_label": "medium",
+  "density_reason": "AI 能力栈是全篇核心:上下文引擎+能力矩阵+智能体架构三层展示",
+  "density_contract": {
+    "deck_bias": "balanced",
+    "page_lower_bound": "mid_low",
+    "page_upper_bound": "high",
+    "max_cards": 4,
+    "max_charts": 2,
+    "min_body_font_px": 18,
+    "max_lines_per_card": 5,
+    "image_policy": "support_only",
+    "decoration_budget": "medium",
+    "overflow_strategy": "tighten_budget"
+  },
+  "director_command": {
+    "focus": "card_hierarchy",
+    "rule": "ensure_anchor_visual_dominance",
+    "note": "AI 能力栈(核心)"
+  },
+  "decoration_hints": {
+    "style": "tech_blue_warm_orange",
+    "elements": [
+      "title_underline",
+      "process_flow",
+      "footer"
+    ],
+    "brand": "accent 蓝"
+  },
+  "resources": {
+    "layout_refs": [],
+    "block_refs": [],
+    "chart_refs": [],
+    "principle_refs": []
+  },
+  "cards": [
+    {
+      "card_id": "c1",
+      "role": "anchor",
+      "card_type": "diagram",
+      "card_style": "accent",
+      "argument_role": "claim",
+      "headline": "家庭 360° 上下文引擎",
+      "body": [
+        "对话前自动组装家庭全景数据:成员信息、孩子列表、任务完成率、健康报告、五维能量分数、认知测评结果、已购商品标签、历史会话记忆",
+        "AI 不是\"没有记忆的机器人\",而是\"比家长更了解这个家庭数据的健康顾问\"——纯聊天产品无法企及的数据厚度"
+      ],
+      "content_budget": {
+        "body_max_lines": 4,
+        "strict": true
+      },
+      "image": {
+        "needed": true,
+        "usage": "inline-illustration",
+        "placement": "inline",
+        "content_description": "AI 健康顾问与家庭数据连接意象",
+        "source_hint": "AI上下文配图"
+      },
+      "data_points": [
+        {
+          "value": "360°",
+          "label": "家庭上下文",
+          "interpretation": "8 类家庭全景数据自动组装"
+        }
+      ]
+    },
+    {
+      "card_id": "c2",
+      "role": "support",
+      "card_type": "list",
+      "card_style": "elevated",
+      "argument_role": "framework",
+      "headline": "AI 能力矩阵(已实现)",
+      "body": [
+        "智能对话:智能体对话+家庭上下文注入+人格化助手",
+        "知识库问答(RAG):向量知识库+菌属/指标/食物/营养素定时同步",
+        "报告智能解析:PDF→结构化指标→AI 标注风险(135 页→3 页)",
+        "AI 生成任务:[TASK] 协议标记→自动创建成长任务",
+        "AI 内容推荐:标签匹配+维度对齐+已购降权",
+        "AI 每日反馈:定时生成个性化鼓励语"
+      ],
+      "content_budget": {
+        "body_max_lines": 4,
+        "strict": true
+      },
+      "image": {
+        "needed": false,
+        "usage": null,
+        "placement": null,
+        "content_description": null,
+        "source_hint": null
+      },
+      "data_points": [
+        {
+          "value": "6 类",
+          "label": "AI 能力",
+          "interpretation": "对话/RAG/解析/任务/推荐/反馈"
+        }
+      ]
+    },
+    {
+      "card_id": "c3",
+      "role": "support",
+      "card_type": "process",
+      "card_style": "filled",
+      "argument_role": "method",
+      "headline": "AI 健康教练智能体服务(自研·生产)",
+      "body": [
+        "意图分类器",
+        "场景路由(咨询/营养/解读/任务/推荐)",
+        "知识检索→大模型推理→工具调用→记忆保存",
+        "全链路追踪+指标监控+慢请求告警"
+      ],
+      "content_budget": {
+        "body_max_lines": 4,
+        "strict": true
+      },
+      "image": {
+        "needed": false,
+        "usage": null,
+        "placement": null,
+        "content_description": null,
+        "source_hint": null
+      },
+      "data_points": [
+        {
+          "value": "7×24",
+          "label": "智能应答",
+          "interpretation": "与后台专业服务团队人机协同"
+        }
+      ]
+    }
+  ],
+  "workflow_metadata": {
+    "workflow_version": "2026.04.09-v4.1",
+    "planning_schema_version": "4.1",
+    "planning_packet_version": "4.1",
+    "planning_continuity_version": "4.1"
+  },
+  "layout_hint": "l-shape",
+  "source_guidance": {
+    "material_ref": "docs/AI创新项目大赛-项目介绍.md §5.4",
+    "key_facts": [
+      "360°上下文",
+      "6 类 AI 能力",
+      "135→3 页",
+      "智能体编排"
+    ]
+  }
+}

+ 181 - 0
ppt-output/planning/planning22.json

@@ -0,0 +1,181 @@
+{
+  "slide_number": 22,
+  "page_type": "content",
+  "title": "数据护城河 · 六阶模型 · 工程安全",
+  "page_goal": "长周期数据不可复制 + 行为科学机制 + 生产级工程 = 多重复合壁垒",
+  "narrative_role": "evidence",
+  "visual_weight": 8,
+  "density_label": "medium",
+  "density_reason": "多重壁垒并列:数据+网络效应+行为科学+工程安全四卡",
+  "density_contract": {
+    "deck_bias": "balanced",
+    "page_lower_bound": "mid_low",
+    "page_upper_bound": "high",
+    "max_cards": 4,
+    "max_charts": 2,
+    "min_body_font_px": 18,
+    "max_lines_per_card": 5,
+    "image_policy": "support_only",
+    "decoration_budget": "medium",
+    "overflow_strategy": "tighten_budget"
+  },
+  "director_command": {
+    "focus": "card_hierarchy",
+    "rule": "ensure_anchor_visual_dominance",
+    "note": "数据护城河 · 六阶模型 · 工程安全"
+  },
+  "decoration_hints": {
+    "style": "tech_blue_warm_orange",
+    "elements": [
+      "title_underline",
+      "process_flow",
+      "footer"
+    ],
+    "brand": "accent 绿"
+  },
+  "resources": {
+    "layout_refs": [],
+    "block_refs": [],
+    "chart_refs": [],
+    "principle_refs": []
+  },
+  "cards": [
+    {
+      "card_id": "c1",
+      "role": "anchor",
+      "card_type": "data",
+      "card_style": "accent",
+      "argument_role": "evidence",
+      "headline": "行为数据护城河",
+      "body": [
+        "47 个数据库实体、25+ 评分数据源、10+ 种能量来源构成复杂数据模型",
+        "任务记录=努力曲线、五维能量=健康趋势、测评=成长档案、游戏=认知基线",
+        "一个孩子 6-18 岁的成长数据不是烧钱能快速复制的"
+      ],
+      "content_budget": {
+        "body_max_lines": 4,
+        "strict": true
+      },
+      "image": {
+        "needed": false,
+        "usage": null,
+        "placement": null,
+        "content_description": null,
+        "source_hint": null
+      },
+      "data_points": [
+        {
+          "value": "47",
+          "label": "数据库实体",
+          "interpretation": "复杂数据模型"
+        },
+        {
+          "value": "6-18 岁",
+          "label": "成长数据周期",
+          "interpretation": "长周期积累构成迁移壁垒"
+        }
+      ],
+      "chart": {
+        "chart_type": "kpi",
+        "note": "数据资产规模"
+      }
+    },
+    {
+      "card_id": "c2",
+      "role": "support",
+      "card_type": "list",
+      "card_style": "elevated",
+      "argument_role": "framework",
+      "headline": "三方网络效应(平台壁垒)",
+      "body": [
+        "更多家长→更多成长规划师→更优质服务→更多家庭(正向飞轮)",
+        "已上线:服务商入驻体系(规划师/活动方/供应商)、服务商等级、团队管理、佣金分成"
+      ],
+      "content_budget": {
+        "body_max_lines": 4,
+        "strict": true
+      },
+      "image": {
+        "needed": true,
+        "usage": "inline-illustration",
+        "placement": "inline",
+        "content_description": "三方网络飞轮意象",
+        "source_hint": "网络效应配图"
+      }
+    },
+    {
+      "card_id": "c3",
+      "role": "support",
+      "card_type": "process",
+      "card_style": "filled",
+      "argument_role": "framework",
+      "headline": "主动健康六阶模型(行为科学)",
+      "body": [
+        "有效启动(微行动<1s)",
+        "持续坚持(即时多巴胺)",
+        "获得感形成(成就显性化)",
+        "身份重塑(健康等级/称号)",
+        "意见领袖化(家庭互动)",
+        "社会扩散(健康圈/分享)"
+      ],
+      "content_budget": {
+        "body_max_lines": 4,
+        "strict": true
+      },
+      "image": {
+        "needed": false,
+        "usage": null,
+        "placement": null,
+        "content_description": null,
+        "source_hint": null
+      },
+      "data_points": [
+        {
+          "value": "6 阶",
+          "label": "行为干预阶梯",
+          "interpretation": "健康智能预警 6 条规则引擎+健康积分等级"
+        }
+      ]
+    },
+    {
+      "card_id": "c4",
+      "role": "support",
+      "card_type": "list",
+      "card_style": "glass",
+      "argument_role": "evidence",
+      "headline": "工程化与安全合规",
+      "body": [
+        "JWT 认证+角色权限(parent/child/teacher/admin),敏感字段加密",
+        "统一 Result 响应、统一 @PostMapping 规范、数据库迁移自动化",
+        "四级地址匹配(省市区街道+区域回退);完成遗留系统全量数据迁移"
+      ],
+      "content_budget": {
+        "body_max_lines": 4,
+        "strict": true
+      },
+      "image": {
+        "needed": false,
+        "usage": null,
+        "placement": null,
+        "content_description": null,
+        "source_hint": null
+      }
+    }
+  ],
+  "workflow_metadata": {
+    "workflow_version": "2026.04.09-v4.1",
+    "planning_schema_version": "4.1",
+    "planning_packet_version": "4.1",
+    "planning_continuity_version": "4.1"
+  },
+  "layout_hint": "asymmetric",
+  "source_guidance": {
+    "material_ref": "docs/AI创新项目大赛-项目介绍.md §5.2-5.3,5.4(5)",
+    "key_facts": [
+      "47/25+/10+",
+      "六阶模型",
+      "JWT+权限",
+      "全量迁移"
+    ]
+  }
+}

+ 73 - 0
ppt-output/planning/planning23.json

@@ -0,0 +1,73 @@
+{
+  "slide_number": 23,
+  "page_type": "section",
+  "title": "Part 06 · 应用成效",
+  "page_goal": "真实案例 · 权威背书 · 系统建设:项目已从概念走向生产",
+  "narrative_role": "section",
+  "visual_weight": 7,
+  "density_label": "mid_low",
+  "density_reason": "章节封面:应用成效总览",
+  "density_contract": {
+    "deck_bias": "balanced",
+    "page_lower_bound": "low",
+    "page_upper_bound": "high",
+    "max_cards": 3,
+    "max_charts": 1,
+    "min_body_font_px": 20,
+    "max_lines_per_card": 4,
+    "image_policy": "flexible",
+    "decoration_budget": "medium",
+    "overflow_strategy": "rebalance_layout"
+  },
+  "director_command": {
+    "focus": "card_hierarchy",
+    "rule": "ensure_anchor_visual_dominance",
+    "note": "Part 06 · 应用成效"
+  },
+  "decoration_hints": {
+    "style": "tech_blue_warm_orange",
+    "elements": [
+      "title_underline",
+      "grid_dots",
+      "footer"
+    ],
+    "brand": "五维色系点缀"
+  },
+  "resources": {
+    "layout_refs": [],
+    "block_refs": [],
+    "chart_refs": [],
+    "principle_refs": []
+  },
+  "cards": [
+    {
+      "card_id": "c1",
+      "role": "anchor",
+      "card_type": "text",
+      "card_style": "elevated",
+      "argument_role": "claim",
+      "headline": "从真实案例到生产级系统",
+      "body": [
+        "135 页专业报告压缩为 3 页行动方案的真实闭环、双培强基工程\"国家队\"级学术底座、15 大模块生产运行——用事实说话"
+      ],
+      "content_budget": {
+        "body_max_lines": 4,
+        "strict": true
+      },
+      "image": {
+        "needed": true,
+        "usage": "hero-background",
+        "placement": "right-half",
+        "content_description": "真实成果与成长数据意象:成果证书与上升曲线,暖橙蓝调",
+        "source_hint": "章节封面配图"
+      }
+    }
+  ],
+  "workflow_metadata": {
+    "workflow_version": "2026.04.09-v4.1",
+    "planning_schema_version": "4.1",
+    "planning_packet_version": "4.1",
+    "planning_continuity_version": "4.1"
+  },
+  "layout_hint": "free-section"
+}

+ 198 - 0
ppt-output/planning/planning24.json

@@ -0,0 +1,198 @@
+{
+  "slide_number": 24,
+  "page_type": "content",
+  "title": "真实案例:精准营养闭环",
+  "page_goal": "135 页专业报告 → 3 页行动方案,全链路在家完成",
+  "narrative_role": "evidence",
+  "visual_weight": 9,
+  "density_label": "medium",
+  "density_reason": "真实案例是说服力核心:数据+风险标注+干预闭环三卡",
+  "density_contract": {
+    "deck_bias": "balanced",
+    "page_lower_bound": "mid_low",
+    "page_upper_bound": "high",
+    "max_cards": 4,
+    "max_charts": 2,
+    "min_body_font_px": 18,
+    "max_lines_per_card": 5,
+    "image_policy": "support_only",
+    "decoration_budget": "medium",
+    "overflow_strategy": "tighten_budget"
+  },
+  "director_command": {
+    "focus": "card_hierarchy",
+    "rule": "ensure_anchor_visual_dominance",
+    "note": "真实案例:精准营养闭环"
+  },
+  "decoration_hints": {
+    "style": "tech_blue_warm_orange",
+    "elements": [
+      "title_underline",
+      "metric_row",
+      "process_flow",
+      "footer"
+    ],
+    "brand": "accent 橙"
+  },
+  "resources": {
+    "layout_refs": [],
+    "block_refs": [],
+    "chart_refs": [],
+    "principle_refs": []
+  },
+  "cards": [
+    {
+      "card_id": "c1",
+      "role": "anchor",
+      "card_type": "data",
+      "card_style": "accent",
+      "argument_role": "evidence",
+      "headline": "案例快照(匿名)",
+      "body": [
+        "用户 530010234,2026 年 1 月完成肠道菌群健康风险评估(募极生物,135 页报告)"
+      ],
+      "content_budget": {
+        "body_max_lines": 4,
+        "strict": true
+      },
+      "image": {
+        "needed": false,
+        "usage": null,
+        "placement": null,
+        "content_description": null,
+        "source_hint": null
+      },
+      "data_points": [
+        {
+          "value": "73",
+          "label": "健康总分",
+          "interpretation": "人群均值 75,干预空间大"
+        },
+        {
+          "value": "53",
+          "label": "菌群健康",
+          "interpretation": "偏低"
+        },
+        {
+          "value": "313 种",
+          "label": "检出菌种",
+          "interpretation": "均值约 800 种,微生态稳定性不足"
+        }
+      ],
+      "chart": {
+        "chart_type": "metric_row",
+        "note": "AI 分析关键指标"
+      }
+    },
+    {
+      "card_id": "c2",
+      "role": "support",
+      "card_type": "list",
+      "card_style": "elevated",
+      "argument_role": "evidence",
+      "headline": "AI 自动标注三大缺乏指标",
+      "body": [
+        "维生素 B12(14 分,正常>50)→ 疲劳/贫血风险",
+        "苏氨酸(14 分)→ 肠道屏障功能受损",
+        "丙氨酸(9 分)→ 能量代谢异常"
+      ],
+      "content_budget": {
+        "body_max_lines": 4,
+        "strict": true
+      },
+      "image": {
+        "needed": true,
+        "usage": "inline-illustration",
+        "placement": "inline",
+        "content_description": "AI 从厚报告提炼关键指标意象",
+        "source_hint": "AI解析配图"
+      },
+      "data_points": [
+        {
+          "value": "3 项",
+          "label": "AI 自动标注风险",
+          "interpretation": "从 135 页报告中精准提炼"
+        }
+      ]
+    },
+    {
+      "card_id": "c3",
+      "role": "support",
+      "card_type": "process",
+      "card_style": "filled",
+      "argument_role": "method",
+      "headline": "平台成效:四步干预闭环",
+      "body": [
+        "增加膳食纤维 25-30g/天",
+        "优选发酵蛋白",
+        "控制风险食物",
+        "定期复测"
+      ],
+      "content_budget": {
+        "body_max_lines": 4,
+        "strict": true
+      },
+      "image": {
+        "needed": false,
+        "usage": null,
+        "placement": null,
+        "content_description": null,
+        "source_hint": null
+      },
+      "data_points": [
+        {
+          "value": "135→3 页",
+          "label": "报告压缩",
+          "interpretation": "AI 解析让报告\"看得懂\""
+        },
+        {
+          "value": "4 步",
+          "label": "拟杆菌型肠型干预策略",
+          "interpretation": "任务系统+能量积分推动执行"
+        }
+      ],
+      "chart": {
+        "chart_type": "progress_bar",
+        "note": "闭环推进"
+      }
+    },
+    {
+      "card_id": "c4",
+      "role": "context",
+      "card_type": "quote",
+      "card_style": "glass",
+      "argument_role": "synthesis",
+      "headline": "价值主张兑现",
+      "body": [
+        "\"检测→解读→方案→执行→跟踪\"全链路在家完成,替代\"跑医院、找专家、花数千元\"的传统路径"
+      ],
+      "content_budget": {
+        "body_max_lines": 4,
+        "strict": true
+      },
+      "image": {
+        "needed": false,
+        "usage": null,
+        "placement": null,
+        "content_description": null,
+        "source_hint": null
+      }
+    }
+  ],
+  "workflow_metadata": {
+    "workflow_version": "2026.04.09-v4.1",
+    "planning_schema_version": "4.1",
+    "planning_packet_version": "4.1",
+    "planning_continuity_version": "4.1"
+  },
+  "layout_hint": "mixed-grid",
+  "source_guidance": {
+    "material_ref": "docs/AI创新项目大赛-项目介绍.md §6.1",
+    "key_facts": [
+      "73/53/313 种",
+      "B12 14 分",
+      "135→3 页",
+      "四步干预"
+    ]
+  }
+}

+ 163 - 0
ppt-output/planning/planning25.json

@@ -0,0 +1,163 @@
+{
+  "slide_number": 25,
+  "page_type": "content",
+  "title": "权威背书与系统建设成效",
+  "page_goal": "\"国家队\"级学术底座 + 15 大模块生产运行 + 商业化验证",
+  "narrative_role": "evidence",
+  "visual_weight": 8,
+  "density_label": "medium",
+  "density_reason": "权威背书+系统建设+商业化:三卡并列证明可信度",
+  "density_contract": {
+    "deck_bias": "balanced",
+    "page_lower_bound": "mid_low",
+    "page_upper_bound": "high",
+    "max_cards": 4,
+    "max_charts": 2,
+    "min_body_font_px": 18,
+    "max_lines_per_card": 5,
+    "image_policy": "support_only",
+    "decoration_budget": "medium",
+    "overflow_strategy": "tighten_budget"
+  },
+  "director_command": {
+    "focus": "card_hierarchy",
+    "rule": "ensure_anchor_visual_dominance",
+    "note": "权威背书与系统建设成效"
+  },
+  "decoration_hints": {
+    "style": "tech_blue_warm_orange",
+    "elements": [
+      "title_underline",
+      "kpi_row",
+      "footer"
+    ],
+    "brand": "accent 蓝"
+  },
+  "resources": {
+    "layout_refs": [],
+    "block_refs": [],
+    "chart_refs": [],
+    "principle_refs": []
+  },
+  "cards": [
+    {
+      "card_id": "c1",
+      "role": "anchor",
+      "card_type": "list",
+      "card_style": "accent",
+      "argument_role": "evidence",
+      "headline": "双培强基工程 · 权威背书",
+      "body": [
+        "人民日报《人民周刊》+ 中国科学院心理研究所联合发起,\"培育心智、培养志向\"",
+        "四维测评体系(DAN 测评等)已引入平台:权威测评→解读→干预→执行→复测数字化闭环",
+        "学术带头人魏高峡、施建农为工程学术体系核心专家",
+        "依托工程学术委员会、素质发展研究院提供持续背书"
+      ],
+      "content_budget": {
+        "body_max_lines": 4,
+        "strict": true
+      },
+      "image": {
+        "needed": true,
+        "usage": "inline-illustration",
+        "placement": "inline",
+        "content_description": "权威学术机构背书意象",
+        "source_hint": "权威背书配图"
+      },
+      "data_points": [
+        {
+          "value": "中科院",
+          "label": "心理研究所联合发起",
+          "interpretation": "\"国家队\"级学术底座"
+        }
+      ]
+    },
+    {
+      "card_id": "c2",
+      "role": "support",
+      "card_type": "list",
+      "card_style": "elevated",
+      "argument_role": "evidence",
+      "headline": "系统建设成效(可验证)",
+      "body": [
+        "15 大模块生产运行:认证/家庭/任务/心愿/积分/测评/成长档案/商城/服务商/AI 健康教练/健康报告/饮食推荐/文章/关系/联系人",
+        "AI 三端落地:健康教练小程序上线,覆盖上下文/解读/营养/任务生成/推荐",
+        "人机协同:AI 7×24 应答+健康管家/营养师/医生兜底",
+        "工程验证:Maven 编译+启动验证+分层测试(单元/集成/E2E),完成全量数据迁移"
+      ],
+      "content_budget": {
+        "body_max_lines": 4,
+        "strict": true
+      },
+      "image": {
+        "needed": false,
+        "usage": null,
+        "placement": null,
+        "content_description": null,
+        "source_hint": null
+      },
+      "data_points": [
+        {
+          "value": "15",
+          "label": "生产运行模块",
+          "interpretation": "功能闭环全部跑通"
+        }
+      ]
+    },
+    {
+      "card_id": "c3",
+      "role": "support",
+      "card_type": "data",
+      "card_style": "filled",
+      "argument_role": "evidence",
+      "headline": "商业化验证",
+      "body": [
+        "三边市场四类交易已设计并部分上线",
+        "公众号案例研究内容体系验证\"专业内容+案例转化\"低成本获客"
+      ],
+      "content_budget": {
+        "body_max_lines": 4,
+        "strict": true
+      },
+      "image": {
+        "needed": false,
+        "usage": null,
+        "placement": null,
+        "content_description": null,
+        "source_hint": null
+      },
+      "data_points": [
+        {
+          "value": "80-330 倍",
+          "label": "LTV/CAC",
+          "interpretation": "单位经济模型健康"
+        },
+        {
+          "value": "54 岁",
+          "label": "家长转变案例",
+          "interpretation": "从陪读焦虑到主动健康践行者"
+        }
+      ],
+      "chart": {
+        "chart_type": "kpi",
+        "note": "商业化关键指标"
+      }
+    }
+  ],
+  "workflow_metadata": {
+    "workflow_version": "2026.04.09-v4.1",
+    "planning_schema_version": "4.1",
+    "planning_packet_version": "4.1",
+    "planning_continuity_version": "4.1"
+  },
+  "layout_hint": "primary-secondary",
+  "source_guidance": {
+    "material_ref": "docs/AI创新项目大赛-项目介绍.md §6.2-6.5",
+    "key_facts": [
+      "双培强基工程",
+      "15 大模块",
+      "DAN 测评",
+      "LTV/CAC"
+    ]
+  }
+}

+ 164 - 0
ppt-output/planning/planning26.json

@@ -0,0 +1,164 @@
+{
+  "slide_number": 26,
+  "page_type": "end",
+  "title": "让健康从被动等待走向主动掌握",
+  "page_goal": "总结回顾 + 路线图 + 三年愿景 + 行动号召",
+  "narrative_role": "close",
+  "visual_weight": 8,
+  "density_label": "mid_low",
+  "density_reason": "结束页:核心回顾+路线图+愿景+CTA,首尾呼应",
+  "density_contract": {
+    "deck_bias": "balanced",
+    "page_lower_bound": "low",
+    "page_upper_bound": "high",
+    "max_cards": 3,
+    "max_charts": 1,
+    "min_body_font_px": 20,
+    "max_lines_per_card": 4,
+    "image_policy": "flexible",
+    "decoration_budget": "medium",
+    "overflow_strategy": "rebalance_layout"
+  },
+  "director_command": {
+    "focus": "card_hierarchy",
+    "rule": "ensure_anchor_visual_dominance",
+    "note": "让健康从被动等待走向主动掌握"
+  },
+  "decoration_hints": {
+    "style": "tech_blue_warm_orange",
+    "elements": [
+      "hero_image",
+      "timeline",
+      "footer"
+    ],
+    "brand": "首尾呼应暖橙"
+  },
+  "resources": {
+    "layout_refs": [],
+    "block_refs": [],
+    "chart_refs": [],
+    "principle_refs": []
+  },
+  "cards": [
+    {
+      "card_id": "c1",
+      "role": "anchor",
+      "card_type": "data",
+      "card_style": "accent",
+      "argument_role": "claim",
+      "headline": "核心回顾",
+      "body": [
+        "五维能量方法论 × AI 健康教练 + 人机协同 × 五维相生溯源算法",
+        "生产级系统:48 控制器 / 50+ 页面 / 15 大模块 / 真实数据资产"
+      ],
+      "content_budget": {
+        "body_max_lines": 4,
+        "strict": true
+      },
+      "image": {
+        "needed": false,
+        "usage": null,
+        "placement": null,
+        "content_description": null,
+        "source_hint": null
+      },
+      "data_points": [
+        {
+          "value": "身·心·智·行·富",
+          "label": "五维能量体系",
+          "interpretation": "方法论底座"
+        },
+        {
+          "value": "80-330 倍",
+          "label": "LTV/CAC",
+          "interpretation": "商业可行性验证"
+        },
+        {
+          "value": "10 万+",
+          "label": "2027 年覆盖家庭目标",
+          "interpretation": "三年愿景第一步"
+        }
+      ],
+      "chart": {
+        "chart_type": "kpi",
+        "note": "关键成果指标"
+      }
+    },
+    {
+      "card_id": "c2",
+      "role": "support",
+      "card_type": "timeline",
+      "card_style": "elevated",
+      "argument_role": "framework",
+      "headline": "未来 12 个月路线图与三年愿景",
+      "body": [
+        "Q1 增强:AI 报告自动解读全覆盖、AI 个性化任务生成、健康预警上线",
+        "Q2 增长:规划师认证线上化、活动方工具包、健康圈",
+        "Q3 变现:会员订阅体系、推广佣金系统",
+        "Q4 扩张:社区互动、Open API 第三方接入",
+        "2027 覆盖 10 万+ 家庭 → 2028 五维健康行业标准 → 2029 开放生态超级入口"
+      ],
+      "content_budget": {
+        "body_max_lines": 4,
+        "strict": true
+      },
+      "image": {
+        "needed": false,
+        "usage": null,
+        "placement": null,
+        "content_description": null,
+        "source_hint": null
+      },
+      "data_points": [
+        {
+          "value": "4 阶段",
+          "label": "季度路线图",
+          "interpretation": "增强→增长→变现→扩张"
+        },
+        {
+          "value": "2027-2029",
+          "label": "三年阶梯",
+          "interpretation": "平台→标准→生态"
+        }
+      ]
+    },
+    {
+      "card_id": "c3",
+      "role": "context",
+      "card_type": "quote",
+      "card_style": "glass",
+      "argument_role": "synthesis",
+      "headline": "给全家的一站式幸福提案",
+      "body": [
+        "让每一个家庭都拥有\"看得懂报告、跟得上方案、做得到行动\"的专属 AI 健康教练",
+        "北京安盈创卓科技有限公司 · 浠艾福(Care Family)"
+      ],
+      "content_budget": {
+        "body_max_lines": 4,
+        "strict": true
+      },
+      "image": {
+        "needed": true,
+        "usage": "hero-background",
+        "placement": "full-bleed",
+        "content_description": "温暖家庭剪影与上升光路,呼应封面,暖橙蓝调",
+        "source_hint": "结束页背景"
+      }
+    }
+  ],
+  "workflow_metadata": {
+    "workflow_version": "2026.04.09-v4.1",
+    "planning_schema_version": "4.1",
+    "planning_packet_version": "4.1",
+    "planning_continuity_version": "4.1"
+  },
+  "layout_hint": "free-end",
+  "source_guidance": {
+    "material_ref": "docs/AI创新项目大赛-项目介绍.md §7-8",
+    "key_facts": [
+      "12 个月路线图",
+      "三年愿景",
+      "行动号召"
+    ]
+  }
+}

+ 74 - 0
ppt-output/planning/planning3.json

@@ -0,0 +1,74 @@
+{
+  "slide_number": 3,
+  "page_type": "section",
+  "title": "Part 01 · 项目概述",
+  "page_goal": "让评委理解项目为解决什么而生、产品形态与建成规模",
+  "narrative_role": "section",
+  "visual_weight": 7,
+  "density_label": "mid_low",
+  "density_reason": "章节封面:引入项目缘起与整体图景",
+  "density_contract": {
+    "deck_bias": "balanced",
+    "page_lower_bound": "low",
+    "page_upper_bound": "high",
+    "max_cards": 3,
+    "max_charts": 1,
+    "min_body_font_px": 20,
+    "max_lines_per_card": 4,
+    "image_policy": "flexible",
+    "decoration_budget": "medium",
+    "overflow_strategy": "rebalance_layout"
+  },
+  "director_command": {
+    "focus": "card_hierarchy",
+    "rule": "ensure_anchor_visual_dominance",
+    "note": "Part 01 · 项目概述"
+  },
+  "decoration_hints": {
+    "style": "tech_blue_warm_orange",
+    "elements": [
+      "title_underline",
+      "grid_dots",
+      "footer"
+    ],
+    "brand": "五维色系点缀"
+  },
+  "resources": {
+    "layout_refs": [],
+    "block_refs": [],
+    "chart_refs": [],
+    "principle_refs": []
+  },
+  "cards": [
+    {
+      "card_id": "c1",
+      "role": "anchor",
+      "card_type": "text",
+      "card_style": "elevated",
+      "argument_role": "claim",
+      "headline": "从割裂到闭环:一个 AI 健康教练连接全家",
+      "body": [
+        "健康检查在医院、心理辅导在机构、素质教育在培训机构——数据与服务完全割裂",
+        "浠艾福以五维能量体系为底座、AI 智能体为引擎,构建连接家长、孩子、规划师与专业服务团队的家庭健康平台"
+      ],
+      "content_budget": {
+        "body_max_lines": 4,
+        "strict": true
+      },
+      "image": {
+        "needed": true,
+        "usage": "hero-background",
+        "placement": "right-half",
+        "content_description": "家庭与数字健康服务的连接意象,暖橙主调",
+        "source_hint": "章节封面配图"
+      }
+    }
+  ],
+  "workflow_metadata": {
+    "workflow_version": "2026.04.09-v4.1",
+    "planning_schema_version": "4.1",
+    "planning_packet_version": "4.1",
+    "planning_continuity_version": "4.1"
+  },
+  "layout_hint": "free-section"
+}

+ 169 - 0
ppt-output/planning/planning4.json

@@ -0,0 +1,169 @@
+{
+  "slide_number": 4,
+  "page_type": "content",
+  "title": "项目缘起与创新主张",
+  "page_goal": "家庭健康服务割裂的结构性困境 + \"AI 健康教练 + 人机协同\"的核心主张",
+  "narrative_role": "evidence",
+  "visual_weight": 7,
+  "density_label": "medium",
+  "density_reason": "核心痛点与主张:需突出结构性困境与范式转变对比",
+  "density_contract": {
+    "deck_bias": "balanced",
+    "page_lower_bound": "mid_low",
+    "page_upper_bound": "high",
+    "max_cards": 4,
+    "max_charts": 2,
+    "min_body_font_px": 18,
+    "max_lines_per_card": 5,
+    "image_policy": "support_only",
+    "decoration_budget": "medium",
+    "overflow_strategy": "tighten_budget"
+  },
+  "director_command": {
+    "focus": "card_hierarchy",
+    "rule": "ensure_anchor_visual_dominance",
+    "note": "项目缘起与创新主张"
+  },
+  "decoration_hints": {
+    "style": "tech_blue_warm_orange",
+    "elements": [
+      "title_underline",
+      "process_flow",
+      "footer"
+    ],
+    "brand": "accent 橙"
+  },
+  "resources": {
+    "layout_refs": [],
+    "block_refs": [],
+    "chart_refs": [],
+    "principle_refs": []
+  },
+  "cards": [
+    {
+      "card_id": "c1",
+      "role": "anchor",
+      "card_type": "text",
+      "card_style": "accent",
+      "argument_role": "claim",
+      "headline": "结构性困境:数据与服务完全割裂",
+      "body": [
+        "健康检查在医院、心理辅导在机构、素质教育在培训机构,一个家长需对接 5-8 个服务商才能拼凑\"这个孩子到底怎么样\"的模糊答案",
+        "内驱力不足、管理疲惫、服务商难找、效果无法量化——形成\"信息碎片化 → 干预滞后化 → 效果不可知\"的恶性循环"
+      ],
+      "content_budget": {
+        "body_max_lines": 4,
+        "strict": true
+      },
+      "image": {
+        "needed": true,
+        "usage": "inline-illustration",
+        "placement": "inline",
+        "content_description": "碎片化数据拼图的意象",
+        "source_hint": "痛点配图"
+      },
+      "data_points": [
+        {
+          "value": "5-8",
+          "label": "需对接服务商数",
+          "interpretation": "一个家长为拼凑孩子全景需对接的服务商数量"
+        }
+      ]
+    },
+    {
+      "card_id": "c2",
+      "role": "support",
+      "card_type": "diagram",
+      "card_style": "elevated",
+      "argument_role": "claim",
+      "headline": "核心创新主张",
+      "body": [
+        "AI 不该只是一个聊天机器人,而是家庭健康的\"专属 AI 健康教练 + 后台专业服务团队(健康管家/营养师/医生)\"人机协同体系",
+        "可量化评估 → 可解释分析 → 可执行干预 → 可追踪反馈的精准干预闭环"
+      ],
+      "content_budget": {
+        "body_max_lines": 4,
+        "strict": true
+      },
+      "image": {
+        "needed": false,
+        "usage": null,
+        "placement": null,
+        "content_description": null,
+        "source_hint": null
+      }
+    },
+    {
+      "card_id": "c3",
+      "role": "support",
+      "card_type": "process",
+      "card_style": "outline",
+      "argument_role": "method",
+      "headline": "闭环路径",
+      "body": [
+        "检测/测评",
+        "AI 分析",
+        "个性化方案",
+        "行为执行",
+        "复测迭代"
+      ],
+      "content_budget": {
+        "body_max_lines": 4,
+        "strict": true
+      },
+      "image": {
+        "needed": false,
+        "usage": null,
+        "placement": null,
+        "content_description": null,
+        "source_hint": null
+      },
+      "data_points": [
+        {
+          "value": "5",
+          "label": "闭环环节",
+          "interpretation": "检测→AI分析→方案→执行→复测的完整闭环"
+        }
+      ]
+    },
+    {
+      "card_id": "c4",
+      "role": "context",
+      "card_type": "list",
+      "card_style": "glass",
+      "argument_role": "contrast",
+      "headline": "范式转变",
+      "body": [
+        "从\"被动就医\"到\"主动健康\"",
+        "从\"千人一面\"到\"千人千面\"",
+        "AI 的精准能力注入家庭健康管理每一个环节"
+      ],
+      "content_budget": {
+        "body_max_lines": 4,
+        "strict": true
+      },
+      "image": {
+        "needed": false,
+        "usage": null,
+        "placement": null,
+        "content_description": null,
+        "source_hint": null
+      }
+    }
+  ],
+  "workflow_metadata": {
+    "workflow_version": "2026.04.09-v4.1",
+    "planning_schema_version": "4.1",
+    "planning_packet_version": "4.1",
+    "planning_continuity_version": "4.1"
+  },
+  "layout_hint": "hero-top",
+  "source_guidance": {
+    "material_ref": "docs/AI创新项目大赛-项目介绍.md §1.1-1.2",
+    "key_facts": [
+      "5-8 个服务商",
+      "恶性循环三环节",
+      "四个可"
+    ]
+  }
+}

+ 173 - 0
ppt-output/planning/planning5.json

@@ -0,0 +1,173 @@
+{
+  "slide_number": 5,
+  "page_type": "content",
+  "title": "产品形态:四端协同",
+  "page_goal": "平台连接家长、孩子、规划师、运营四方的完整产品矩阵与 AI 人机协同服务",
+  "narrative_role": "evidence",
+  "visual_weight": 8,
+  "density_label": "high",
+  "density_reason": "四端产品矩阵:以四象限展示,突出人机协同体系",
+  "density_contract": {
+    "deck_bias": "balanced",
+    "page_lower_bound": "medium",
+    "page_upper_bound": "high",
+    "max_cards": 6,
+    "max_charts": 2,
+    "min_body_font_px": 16,
+    "max_lines_per_card": 4,
+    "image_policy": "support_only",
+    "decoration_budget": "low",
+    "overflow_strategy": "table_or_microchart"
+  },
+  "director_command": {
+    "focus": "card_hierarchy",
+    "rule": "ensure_anchor_visual_dominance",
+    "note": "产品形态:四端协同"
+  },
+  "decoration_hints": {
+    "style": "tech_blue_warm_orange",
+    "elements": [
+      "title_underline",
+      "quadrant_grid",
+      "footer"
+    ],
+    "brand": "四端四色"
+  },
+  "resources": {
+    "layout_refs": [],
+    "block_refs": [],
+    "chart_refs": [],
+    "principle_refs": []
+  },
+  "cards": [
+    {
+      "card_id": "c1",
+      "role": "anchor",
+      "card_type": "list",
+      "card_style": "elevated",
+      "argument_role": "framework",
+      "headline": "家长端 · 微信小程序",
+      "body": [
+        "付费决策者:家庭管理、任务下发、测评预约、套餐购买",
+        "五维能量看板、AI 健康教练"
+      ],
+      "content_budget": {
+        "body_max_lines": 4,
+        "strict": true
+      },
+      "image": {
+        "needed": true,
+        "usage": "inline-illustration",
+        "placement": "inline",
+        "content_description": "家长在手机上管理家庭健康任务",
+        "source_hint": "家长端配图"
+      }
+    },
+    {
+      "card_id": "c2",
+      "role": "support",
+      "card_type": "list",
+      "card_style": "filled",
+      "argument_role": "framework",
+      "headline": "孩子端 · 微信小程序",
+      "body": [
+        "行为执行者:任务执行、小游戏训练、心愿兑换",
+        "成长报告、健康打卡"
+      ],
+      "content_budget": {
+        "body_max_lines": 4,
+        "strict": true
+      },
+      "image": {
+        "needed": false,
+        "usage": null,
+        "placement": null,
+        "content_description": null,
+        "source_hint": null
+      }
+    },
+    {
+      "card_id": "c3",
+      "role": "support",
+      "card_type": "list",
+      "card_style": "filled",
+      "argument_role": "framework",
+      "headline": "规划师端 · 小程序+Web",
+      "body": [
+        "成长规划师/营养师/活动方:套餐发布、测评结果录入",
+        "批量任务下发、家庭健康管理"
+      ],
+      "content_budget": {
+        "body_max_lines": 4,
+        "strict": true
+      },
+      "image": {
+        "needed": false,
+        "usage": null,
+        "placement": null,
+        "content_description": null,
+        "source_hint": null
+      }
+    },
+    {
+      "card_id": "c4",
+      "role": "support",
+      "card_type": "list",
+      "card_style": "filled",
+      "argument_role": "framework",
+      "headline": "管理端 · Vue Web",
+      "body": [
+        "平台运营+后台服务人员(健康管家/营养师/医生):审核、配置、数据统计",
+        "内容与知识库管理、专业服务支持"
+      ],
+      "content_budget": {
+        "body_max_lines": 4,
+        "strict": true
+      },
+      "image": {
+        "needed": false,
+        "usage": null,
+        "placement": null,
+        "content_description": null,
+        "source_hint": null
+      }
+    },
+    {
+      "card_id": "c5",
+      "role": "context",
+      "card_type": "quote",
+      "card_style": "glass",
+      "argument_role": "synthesis",
+      "headline": "人机协同服务体系",
+      "body": [
+        "AI 健康教练 7×24 智能应答 + 健康管家/营养师/医生后台兜底",
+        "\"AI 智能应答 + 人工专业兜底\""
+      ],
+      "content_budget": {
+        "body_max_lines": 4,
+        "strict": true
+      },
+      "image": {
+        "needed": false,
+        "usage": null,
+        "placement": null,
+        "content_description": null,
+        "source_hint": null
+      }
+    }
+  ],
+  "workflow_metadata": {
+    "workflow_version": "2026.04.09-v4.1",
+    "planning_schema_version": "4.1",
+    "planning_packet_version": "4.1",
+    "planning_continuity_version": "4.1"
+  },
+  "layout_hint": "mixed-grid",
+  "source_guidance": {
+    "material_ref": "docs/AI创新项目大赛-项目介绍.md §1.3",
+    "key_facts": [
+      "四端形态与服务对象",
+      "后台服务体系"
+    ]
+  }
+}

+ 155 - 0
ppt-output/planning/planning6.json

@@ -0,0 +1,155 @@
+{
+  "slide_number": 6,
+  "page_type": "content",
+  "title": "系统规模与 AI 服务",
+  "page_goal": "用已上线数据证明工程成熟度与技术底座",
+  "narrative_role": "evidence",
+  "visual_weight": 8,
+  "density_label": "medium",
+  "density_reason": "规模数字是核心证据:大数字 KPI 展示,技术栈为辅助",
+  "density_contract": {
+    "deck_bias": "balanced",
+    "page_lower_bound": "mid_low",
+    "page_upper_bound": "high",
+    "max_cards": 4,
+    "max_charts": 2,
+    "min_body_font_px": 18,
+    "max_lines_per_card": 5,
+    "image_policy": "support_only",
+    "decoration_budget": "medium",
+    "overflow_strategy": "tighten_budget"
+  },
+  "director_command": {
+    "focus": "card_hierarchy",
+    "rule": "ensure_anchor_visual_dominance",
+    "note": "系统规模与 AI 服务"
+  },
+  "decoration_hints": {
+    "style": "tech_blue_warm_orange",
+    "elements": [
+      "title_underline",
+      "kpi_row",
+      "footer"
+    ],
+    "brand": "accent 蓝"
+  },
+  "resources": {
+    "layout_refs": [],
+    "block_refs": [],
+    "chart_refs": [],
+    "principle_refs": []
+  },
+  "cards": [
+    {
+      "card_id": "c1",
+      "role": "anchor",
+      "card_type": "data",
+      "card_style": "accent",
+      "argument_role": "evidence",
+      "headline": "系统规模(已上线)",
+      "body": [
+        "48 个后端控制器、47 个数据库实体、15 大功能模块生产运行"
+      ],
+      "content_budget": {
+        "body_max_lines": 4,
+        "strict": true
+      },
+      "image": {
+        "needed": false,
+        "usage": null,
+        "placement": null,
+        "content_description": null,
+        "source_hint": null
+      },
+      "data_points": [
+        {
+          "value": "48",
+          "label": "后端控制器",
+          "interpretation": "覆盖 15 大功能模块"
+        },
+        {
+          "value": "47",
+          "label": "数据库实体",
+          "interpretation": "复杂数据模型支撑五维体系"
+        },
+        {
+          "value": "50+",
+          "label": "小程序页面",
+          "interpretation": "家长/孩子/规划师三端"
+        },
+        {
+          "value": "29",
+          "label": "Web 管理页",
+          "interpretation": "运营与服务端"
+        }
+      ],
+      "chart": {
+        "chart_type": "kpi",
+        "note": "四组 KPI 数字"
+      }
+    },
+    {
+      "card_id": "c2",
+      "role": "support",
+      "card_type": "list",
+      "card_style": "elevated",
+      "argument_role": "framework",
+      "headline": "技术栈",
+      "body": [
+        "Java 8 + Spring Boot 2.7.18 + MyBatis-Plus",
+        "Python 智能体服务(AI 健康教练)",
+        "uni-app Vue 2 小程序 / Vue 2 + Element UI",
+        "MySQL 8.0"
+      ],
+      "content_budget": {
+        "body_max_lines": 4,
+        "strict": true
+      },
+      "image": {
+        "needed": false,
+        "usage": null,
+        "placement": null,
+        "content_description": null,
+        "source_hint": null
+      }
+    },
+    {
+      "card_id": "c3",
+      "role": "support",
+      "card_type": "list",
+      "card_style": "filled",
+      "argument_role": "framework",
+      "headline": "AI 服务(自研)",
+      "body": [
+        "智能体服务体系:健康咨询、营养建议、报告解读、任务生成、内容推荐",
+        "家庭知识库问答(RAG):向量知识库 + 菌属/指标/食物/营养素知识库"
+      ],
+      "content_budget": {
+        "body_max_lines": 4,
+        "strict": true
+      },
+      "image": {
+        "needed": true,
+        "usage": "inline-illustration",
+        "placement": "inline",
+        "content_description": "AI 智能体服务架构意象:机器人+知识库",
+        "source_hint": "AI服务配图"
+      }
+    }
+  ],
+  "workflow_metadata": {
+    "workflow_version": "2026.04.09-v4.1",
+    "planning_schema_version": "4.1",
+    "planning_packet_version": "4.1",
+    "planning_continuity_version": "4.1"
+  },
+  "layout_hint": "primary-secondary",
+  "source_guidance": {
+    "material_ref": "docs/AI创新项目大赛-项目介绍.md §1.4",
+    "key_facts": [
+      "48/47/15",
+      "50+/29",
+      "技术栈清单"
+    ]
+  }
+}

+ 159 - 0
ppt-output/planning/planning7.json

@@ -0,0 +1,159 @@
+{
+  "slide_number": 7,
+  "page_type": "content",
+  "title": "公司实力与核心团队",
+  "page_goal": "国家级高新技术企业的工程实力 + 心理学/医疗/教育专家团队背书",
+  "narrative_role": "evidence",
+  "visual_weight": 8,
+  "density_label": "medium",
+  "density_reason": "公司实力与团队:数据卡为主,团队突出专家背景",
+  "density_contract": {
+    "deck_bias": "balanced",
+    "page_lower_bound": "mid_low",
+    "page_upper_bound": "high",
+    "max_cards": 4,
+    "max_charts": 2,
+    "min_body_font_px": 18,
+    "max_lines_per_card": 5,
+    "image_policy": "support_only",
+    "decoration_budget": "medium",
+    "overflow_strategy": "tighten_budget"
+  },
+  "director_command": {
+    "focus": "card_hierarchy",
+    "rule": "ensure_anchor_visual_dominance",
+    "note": "公司实力与核心团队"
+  },
+  "decoration_hints": {
+    "style": "tech_blue_warm_orange",
+    "elements": [
+      "title_underline",
+      "kpi_row",
+      "footer"
+    ],
+    "brand": "accent 金"
+  },
+  "resources": {
+    "layout_refs": [],
+    "block_refs": [],
+    "chart_refs": [],
+    "principle_refs": []
+  },
+  "cards": [
+    {
+      "card_id": "c1",
+      "role": "anchor",
+      "card_type": "data",
+      "card_style": "accent",
+      "argument_role": "evidence",
+      "headline": "公司实力",
+      "body": [
+        "北京安盈创卓科技有限公司(2013 年成立,中关村软件核心区)国家级高新技术企业、中石化 A 级供应商"
+      ],
+      "content_budget": {
+        "body_max_lines": 4,
+        "strict": true
+      },
+      "image": {
+        "needed": false,
+        "usage": null,
+        "placement": null,
+        "content_description": null,
+        "source_hint": null
+      },
+      "data_points": [
+        {
+          "value": "500+",
+          "label": "央企/政府/金融/军工客户",
+          "interpretation": "中国石化、国家电网、中航工业等"
+        },
+        {
+          "value": "60+",
+          "label": "软件著作权",
+          "interpretation": "ISO9001 认证,自主知识产权"
+        },
+        {
+          "value": "10 年",
+          "label": "自研\"图腾\"云原生平台",
+          "interpretation": "DDD 模型,业务与技术分离"
+        }
+      ],
+      "chart": {
+        "chart_type": "kpi",
+        "note": "公司关键实力指标"
+      }
+    },
+    {
+      "card_id": "c2",
+      "role": "support",
+      "card_type": "list",
+      "card_style": "elevated",
+      "argument_role": "evidence",
+      "headline": "业务覆盖",
+      "body": [
+        "政府:云南省办公厅、交通运输部等",
+        "石化:中国石化、长城电商等",
+        "电力:国网北京信通、北京电力及 60 余分支",
+        "军工/金融:中航工业、航天科工、银保监云南局"
+      ],
+      "content_budget": {
+        "body_max_lines": 4,
+        "strict": true
+      },
+      "image": {
+        "needed": false,
+        "usage": null,
+        "placement": null,
+        "content_description": null,
+        "source_hint": null
+      }
+    },
+    {
+      "card_id": "c3",
+      "role": "support",
+      "card_type": "people",
+      "card_style": "filled",
+      "argument_role": "evidence",
+      "headline": "9 人核心团队",
+      "body": [
+        "廖晓光·项目负责人 | 魏高峡·学术带头人 | 施建农·学术带头人",
+        "张挽时·学术带头人 | 林小朋·技术专家 | 韩庆宇·销售专家",
+        "曹丽君·心理学专家 | 闫德波·医疗专家 | 王卫东·教育专家"
+      ],
+      "content_budget": {
+        "body_max_lines": 4,
+        "strict": true
+      },
+      "image": {
+        "needed": false,
+        "usage": null,
+        "placement": null,
+        "content_description": null,
+        "source_hint": null
+      },
+      "data_points": [
+        {
+          "value": "9",
+          "label": "核心团队成员",
+          "interpretation": "工程骨干 + 心理学/医疗/教育专家"
+        }
+      ]
+    }
+  ],
+  "workflow_metadata": {
+    "workflow_version": "2026.04.09-v4.1",
+    "planning_schema_version": "4.1",
+    "planning_packet_version": "4.1",
+    "planning_continuity_version": "4.1"
+  },
+  "layout_hint": "mixed-grid",
+  "source_guidance": {
+    "material_ref": "docs/AI创新项目大赛-项目介绍.md §1.5",
+    "key_facts": [
+      "500+ 客户",
+      "60+ 著作权",
+      "9 人团队",
+      "双培强基学术背书"
+    ]
+  }
+}

+ 73 - 0
ppt-output/planning/planning8.json

@@ -0,0 +1,73 @@
+{
+  "slide_number": 8,
+  "page_type": "section",
+  "title": "Part 02 · 背景政策",
+  "page_goal": "论证\"健康中国 + 人工智能+\"双轮驱动下 AI 主动健康正处最佳窗口期",
+  "narrative_role": "section",
+  "visual_weight": 7,
+  "density_label": "mid_low",
+  "density_reason": "章节封面:窗口期结论先行",
+  "density_contract": {
+    "deck_bias": "balanced",
+    "page_lower_bound": "low",
+    "page_upper_bound": "high",
+    "max_cards": 3,
+    "max_charts": 1,
+    "min_body_font_px": 20,
+    "max_lines_per_card": 4,
+    "image_policy": "flexible",
+    "decoration_budget": "medium",
+    "overflow_strategy": "rebalance_layout"
+  },
+  "director_command": {
+    "focus": "card_hierarchy",
+    "rule": "ensure_anchor_visual_dominance",
+    "note": "Part 02 · 背景政策"
+  },
+  "decoration_hints": {
+    "style": "tech_blue_warm_orange",
+    "elements": [
+      "title_underline",
+      "grid_dots",
+      "footer"
+    ],
+    "brand": "五维色系点缀"
+  },
+  "resources": {
+    "layout_refs": [],
+    "block_refs": [],
+    "chart_refs": [],
+    "principle_refs": []
+  },
+  "cards": [
+    {
+      "card_id": "c1",
+      "role": "anchor",
+      "card_type": "text",
+      "card_style": "elevated",
+      "argument_role": "claim",
+      "headline": "国家战略 · 时代需求 · 技术就绪",
+      "body": [
+        "政策端\"鼓励 AI 赋能健康/心理服务\"、需求端\"问题高发且供给短缺\"、技术端\"大模型智能体已就绪\"——三者交汇,正是 AI 精准干预、主动健康类项目的最佳窗口期"
+      ],
+      "content_budget": {
+        "body_max_lines": 4,
+        "strict": true
+      },
+      "image": {
+        "needed": true,
+        "usage": "hero-background",
+        "placement": "right-half",
+        "content_description": "国家政策与科技趋势交汇意象,蓝色调",
+        "source_hint": "章节封面配图"
+      }
+    }
+  ],
+  "workflow_metadata": {
+    "workflow_version": "2026.04.09-v4.1",
+    "planning_schema_version": "4.1",
+    "planning_packet_version": "4.1",
+    "planning_continuity_version": "4.1"
+  },
+  "layout_hint": "free-section"
+}

+ 119 - 0
ppt-output/planning/planning9.json

@@ -0,0 +1,119 @@
+{
+  "slide_number": 9,
+  "page_type": "content",
+  "title": "国家战略与专项政策",
+  "page_goal": "从健康中国到\"人工智能+\",政策已为 AI 心理/健康干预产品打开准入大门",
+  "narrative_role": "evidence",
+  "visual_weight": 8,
+  "density_label": "medium",
+  "density_reason": "政策是硬证据:时间线展示政策演进,专项政策聚焦儿童青少年",
+  "density_contract": {
+    "deck_bias": "balanced",
+    "page_lower_bound": "mid_low",
+    "page_upper_bound": "high",
+    "max_cards": 4,
+    "max_charts": 2,
+    "min_body_font_px": 18,
+    "max_lines_per_card": 5,
+    "image_policy": "support_only",
+    "decoration_budget": "medium",
+    "overflow_strategy": "tighten_budget"
+  },
+  "director_command": {
+    "focus": "card_hierarchy",
+    "rule": "ensure_anchor_visual_dominance",
+    "note": "国家战略与专项政策"
+  },
+  "decoration_hints": {
+    "style": "tech_blue_warm_orange",
+    "elements": [
+      "title_underline",
+      "timeline",
+      "footer"
+    ],
+    "brand": "accent 蓝"
+  },
+  "resources": {
+    "layout_refs": [],
+    "block_refs": [],
+    "chart_refs": [],
+    "principle_refs": []
+  },
+  "cards": [
+    {
+      "card_id": "c1",
+      "role": "anchor",
+      "card_type": "timeline",
+      "card_style": "accent",
+      "argument_role": "evidence",
+      "headline": "政策脉络:从健康中国到 AI+",
+      "body": [
+        "2016《健康中国2030规划纲要》:\"健康优先、预防为主\"——主动健康最高纲领",
+        "2019《健康中国行动》:3 个儿童青少年专项行动",
+        "2022《家庭教育促进法》:家庭教育从\"家事\"上升为\"国事\"",
+        "2025.08 国发〔2025〕11号:\"人工智能+\"上升为国家行动",
+        "2025.10 国卫办规划发〔2025〕30号:以学生为重点,心理问题智能筛查、预警、干预,2027 年形成垂直大模型和智能体应用"
+      ],
+      "content_budget": {
+        "body_max_lines": 4,
+        "strict": true
+      },
+      "image": {
+        "needed": false,
+        "usage": null,
+        "placement": null,
+        "content_description": null,
+        "source_hint": null
+      },
+      "chart": {
+        "chart_type": "timeline",
+        "note": "5 个政策节点"
+      }
+    },
+    {
+      "card_id": "c2",
+      "role": "support",
+      "card_type": "list",
+      "card_style": "elevated",
+      "argument_role": "evidence",
+      "headline": "儿童青少年健康专项",
+      "body": [
+        "教育部等17部门《专项行动计划》:\"一生一策\"心理健康档案,四位一体体系",
+        "教基厅〔2025〕2号《十条措施》:明确\"利用人工智能赋能学生心理健康,支持探索'AI心理助手'\"",
+        "卫健委等13部门《\"五健\"促进行动计划》:防筛诊治康一体化,数智技术分级分类精细化管理"
+      ],
+      "content_budget": {
+        "body_max_lines": 4,
+        "strict": true
+      },
+      "image": {
+        "needed": false,
+        "usage": null,
+        "placement": null,
+        "content_description": null,
+        "source_hint": null
+      },
+      "data_points": [
+        {
+          "value": "2027",
+          "label": "垂直大模型应用目标",
+          "interpretation": "国卫办规划发〔2025〕30号"
+        }
+      ]
+    }
+  ],
+  "workflow_metadata": {
+    "workflow_version": "2026.04.09-v4.1",
+    "planning_schema_version": "4.1",
+    "planning_packet_version": "4.1",
+    "planning_continuity_version": "4.1"
+  },
+  "layout_hint": "asymmetric",
+  "source_guidance": {
+    "material_ref": "docs/AI创新项目大赛-项目介绍.md §2.1-2.2",
+    "key_facts": [
+      "5 份政策文件",
+      "AI 心理助手明确入政策"
+    ]
+  }
+}

BIN
ppt-output/png/slide-1.png


BIN
ppt-output/png/slide-10.png


BIN
ppt-output/png/slide-11.png


BIN
ppt-output/png/slide-12.png


BIN
ppt-output/png/slide-13.png


BIN
ppt-output/png/slide-14.png


BIN
ppt-output/png/slide-15.png


BIN
ppt-output/png/slide-16.png


BIN
ppt-output/png/slide-17.png


BIN
ppt-output/png/slide-18.png


BIN
ppt-output/png/slide-19.png


BIN
ppt-output/png/slide-2.png


BIN
ppt-output/png/slide-20.png


BIN
ppt-output/png/slide-21.png


BIN
ppt-output/png/slide-22.png


BIN
ppt-output/png/slide-23.png


BIN
ppt-output/png/slide-24.png


BIN
ppt-output/png/slide-25.png


BIN
ppt-output/png/slide-26.png


BIN
ppt-output/png/slide-3.png


BIN
ppt-output/png/slide-4.png


BIN
ppt-output/png/slide-5.png


BIN
ppt-output/png/slide-6.png


BIN
ppt-output/png/slide-7.png


BIN
ppt-output/png/slide-8.png


BIN
ppt-output/png/slide-9.png


BIN
ppt-output/presentation.pptx


BIN
ppt-output/presentation_new.pptx


Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 240 - 0
ppt-output/preview.html


+ 112 - 0
ppt-output/runtime/gen_images.py

@@ -0,0 +1,112 @@
+# -*- coding: utf-8 -*-
+"""Step 5b: 批量生成 22 张 PPT 配图(Agnes API, agnes-image-2.0-flash)"""
+import json, os, sys, time, urllib.request, urllib.error
+
+sys.stdout.reconfigure(encoding='utf-8')
+
+BASE = r"D:\workspace\cfc\ppt-output"
+OUT_DIR = os.path.join(BASE, "images")
+API_URL = "https://apihub.agnes-ai.cn/v1/images/generations"
+TOKEN = "sk-MyLS4uAAWAIGnJLK2UKuFrL1GotnsFX1XqJ57MZq0n208sNb"
+MODEL = "agnes-image-2.0-flash"
+os.makedirs(OUT_DIR, exist_ok=True)
+
+# ---- 22 张配图定义:slide_no, card_idx, usage, placement, prompt ----
+STYLE = ("Warm, premium flat illustration with soft 3D depth, family health AI platform theme. "
+         "Color palette: deep tech navy blue background (#0A1A2F) with warm orange (#FF8C42) accents "
+         "and soft golden light glow. Clean, minimalist. No text, no words, no letters, no numbers, no logos. 1024x1024.")
+
+COMPOSITION = {
+    "full-bleed": "Scene fills the entire canvas edge-to-edge with balanced composition.",
+    "right-half": "Main subject on the left side, large empty negative space on the right side for text overlay.",
+    "inline": "Centered composition, balanced, suitable for a small card illustration.",
+}
+
+IMAGES = [
+    (1, 0, "hero-background", "full-bleed",
+     "A warm family scene: parents and child sitting together in golden sunlight reading a health report on a tablet, happy and relaxed."),
+    (3, 0, "hero-background", "right-half",
+     "A family silhouette connected to floating digital health service icons (heart, pulse line, shield) via soft glowing threads, warm orange light."),
+    (4, 0, "inline-illustration", "inline",
+     "Scattered fragmented data puzzle pieces with a heart shape slowly emerging from the fragments."),
+    (5, 0, "inline-illustration", "inline",
+     "A parent holding a smartphone managing family health tasks, floating checklists and heart icons around the phone."),
+    (6, 2, "inline-illustration", "inline",
+     "AI agent service architecture: a friendly robot connected to a glowing knowledge base and data streams."),
+    (8, 0, "hero-background", "right-half",
+     "National policy documents and rising technology trend curves converging into a bright future gateway, tech blue tones."),
+    (10, 1, "inline-illustration", "inline",
+     "A confused parent looking at a thick professional medical report full of complex charts they cannot understand."),
+    (11, 0, "hero-background", "right-half",
+     "A soaring upward growth curve over a vast open city skyline, representing a trillion-yuan market opportunity, blue and orange tones."),
+    (13, 2, "inline-illustration", "inline",
+     "Competitive landscape map with one distinctive position highlighted by a warm orange beacon among blue competitors."),
+    (14, 2, "inline-illustration", "inline",
+     "Three-sided marketplace: families, planners, and activity providers connected by a glowing triangle platform."),
+    (15, 0, "hero-background", "right-half",
+     "Five-element energy system: five colored energy orbs (orange, pink, indigo, green, gold) circulating in a harmonious ring, warm orange dominant."),
+    (16, 0, "inline-illustration", "inline",
+     "Fragmented family health data scattered across devices (phone, tablet, paper reports) with disconnected wires, pain point mood."),
+    (17, 1, "inline-illustration", "inline",
+     "A five-dimensional energy dashboard sandbox: circular gauges with five colored energy rings glowing softly."),
+    (18, 1, "inline-illustration", "inline",
+     "A family health data flywheel loop: data, analysis, plan, action, improvement rotating in a continuous glowing cycle."),
+    (19, 0, "hero-background", "right-half",
+     "A glowing shield protecting flowing data streams, representing technology and data moats, tech navy blue dominant."),
+    (20, 2, "inline-illustration", "inline",
+     "Five-element mutual generation and restriction: five colored circles connected by curved arrows in dynamic balance."),
+    (21, 0, "inline-illustration", "inline",
+     "An AI health advisor avatar gently connecting to family data points floating around a home."),
+    (22, 1, "inline-illustration", "inline",
+     "Three-party network flywheel: families, planners, partners spinning together in a growing wheel."),
+    (23, 0, "hero-background", "right-half",
+     "Real achievements: a certificate of growth results with an upward progress curve and warm orange and blue tones."),
+    (24, 1, "inline-illustration", "inline",
+     "AI extracting key metrics from a thick report: a magnifying glass over dense pages revealing bright glowing key indicators."),
+    (25, 0, "inline-illustration", "inline",
+     "Authoritative academic institution endorsement: a laurel wreath and seal over a rising chart, trustworthy mood."),
+    (26, 2, "hero-background", "full-bleed",
+     "Warm family silhouettes walking toward an ascending path of light, echoing the cover, warm orange and blue tones."),
+]
+
+
+def generate_one(idx, slide, card, usage, placement, desc):
+    prompt = desc + " " + COMPOSITION[placement] + " " + STYLE
+    body = json.dumps({"model": MODEL, "prompt": prompt, "size": "1024x1024", "n": 1}).encode("utf-8")
+    req = urllib.request.Request(API_URL, data=body, headers={
+        "Content-Type": "application/json",
+        "Authorization": "Bearer " + TOKEN,
+    })
+    with urllib.request.urlopen(req, timeout=120) as resp:
+        data = json.loads(resp.read().decode("utf-8"))
+    url = data["data"][0]["url"]
+    fname = os.path.join(OUT_DIR, f"slide_{slide:02d}_{card}.png")
+    urllib.request.urlretrieve(url, fname)
+    return fname
+
+
+def main():
+    todo = []
+    for slide, card, usage, placement, desc in IMAGES:
+        fname = os.path.join(OUT_DIR, f"slide_{slide:02d}_{card}.png")
+        if os.path.exists(fname) and os.path.getsize(fname) > 1024:
+            print(f"[skip] {fname} exists")
+            continue
+        todo.append((slide, card, usage, placement, desc))
+
+    print(f"[todo] {len(todo)} images to generate")
+    for idx, (slide, card, usage, placement, desc) in enumerate(todo, 1):
+        for attempt in range(3):
+            try:
+                fname = generate_one(idx, slide, card, usage, placement, desc)
+                print(f"[{idx}/{len(todo)}] OK slide_{slide:02d}_{card}.png ({os.path.getsize(fname)} bytes)")
+                break
+            except Exception as e:
+                print(f"[{idx}/{len(todo)}] slide_{slide:02d}_{card} attempt {attempt+1} failed: {e}")
+                time.sleep(5)
+        else:
+            print(f"[{idx}/{len(todo)}] FAILED slide_{slide:02d}_{card} after 3 attempts")
+
+
+if __name__ == "__main__":
+    main()

+ 626 - 0
ppt-output/runtime/gen_planning.py

@@ -0,0 +1,626 @@
+#!/usr/bin/env python3
+"""Generate planning.json for 浠艾福 (Care Family) project intro deck (26 pages).
+Schema: planning_validator.py v4.1
+"""
+import json, sys
+from pathlib import Path
+
+OUT = Path(r"D:\workspace\cfc\ppt-output\planning.json")
+
+DECK_BIAS = "balanced"
+VERSION = {"workflow_version": "2026.04.09-v4.1", "planning_schema_version": "4.1",
+           "planning_packet_version": "4.1", "planning_continuity_version": "4.1"}
+
+def density_contract(label):
+    base = {
+        "low":      {"max_cards": 2, "max_charts": 1, "min_body_font_px": 24, "max_lines_per_card": 3, "image_policy": "flexible",  "decoration_budget": "generous", "overflow_strategy": "rebalance_layout"},
+        "mid_low":  {"max_cards": 3, "max_charts": 1, "min_body_font_px": 20, "max_lines_per_card": 4, "image_policy": "flexible",  "decoration_budget": "medium",   "overflow_strategy": "rebalance_layout"},
+        "medium":   {"max_cards": 4, "max_charts": 2, "min_body_font_px": 18, "max_lines_per_card": 5, "image_policy": "support_only", "decoration_budget": "medium", "overflow_strategy": "tighten_budget"},
+        "high":     {"max_cards": 6, "max_charts": 2, "min_body_font_px": 16, "max_lines_per_card": 4, "image_policy": "support_only", "decoration_budget": "low",     "overflow_strategy": "table_or_microchart"},
+    }[label]
+    lo, hi = {"low": ("low", "mid_low"), "mid_low": ("low", "high"), "medium": ("mid_low", "high"), "high": ("medium", "high")}[label]
+    return {"deck_bias": DECK_BIAS, "page_lower_bound": lo, "page_upper_bound": hi, **base}
+
+DEFAULT_NROLE = {"cover": "cover", "toc": "toc", "section": "section", "content": "evidence", "end": "close"}
+
+def page(num, ptype, title, goal, cards, vw, dlabel, dreason, layout=None, nrole=None, source=None, dc=None, deco=None, resources=None):
+    p = {
+        "slide_number": num, "page_type": ptype, "title": title, "page_goal": goal,
+        "narrative_role": nrole or DEFAULT_NROLE.get(ptype, "setup"),
+        "visual_weight": vw, "density_label": dlabel, "density_reason": dreason,
+        "density_contract": density_contract(dlabel),
+        "director_command": {"focus": "card_hierarchy", "rule": "ensure_anchor_visual_dominance", "note": title},
+        "decoration_hints": deco or {"style": "tech_blue_warm_orange", "elements": ["title_underline", "grid_dots", "footer"], "brand": "五维色系点缀"},
+        "resources": resources or {"layout_refs": [], "block_refs": [], "chart_refs": [], "principle_refs": []},
+        "cards": cards, "workflow_metadata": dict(VERSION),
+    }
+    if layout:
+        p["layout_hint"] = layout
+    if source:
+        p["source_guidance"] = source
+    return p
+
+def card(cid, role, ctype, style, headline, body, data=None, chart=None, budget=None, img=None, arg="evidence"):
+    c = {
+        "card_id": cid, "role": role, "card_type": ctype, "card_style": style,
+        "argument_role": arg, "headline": headline, "body": body,
+        "content_budget": budget or {"body_max_lines": 4, "strict": True},
+        "image": img or {"needed": False, "usage": None, "placement": None, "content_description": None, "source_hint": None},
+    }
+    if data:
+        c["data_points"] = data
+    if chart:
+        c["chart"] = chart
+    return c
+
+def noimg(): return {"needed": False, "usage": None, "placement": None, "content_description": None, "source_hint": None}
+
+def img(usage, placement, desc, hint):
+    return {"needed": True, "usage": usage, "placement": placement, "content_description": desc, "source_hint": hint}
+
+PAGES = []
+
+# ---------- 1. 封面 ----------
+PAGES.append(page(1, "cover",
+    "给全家的一站式幸福提案",
+    "让评委第一眼理解:这是一家以 AI 驱动家庭主动健康管理的平台,守护家庭五维成长",
+    [card("c1", "anchor", "text", "elevated", "AI 驱动的家庭主动健康管理平台",
+        ["以 AI 实现精准干预、主动健康,守护中国家庭的\"身 · 心 · 智 · 行 · 富\"五维成长",
+         "让每一个家庭都拥有\"看得懂报告、跟得上方案、做得到行动\"的专属 AI 健康教练"],
+        data=[{"value": "身·心·智·行·富", "label": "五维成长体系", "interpretation": "方法论底座"},
+              {"value": "7×24", "label": "AI 健康教练", "interpretation": "专属智能体服务"}],
+        img=img("hero-background", "full-bleed", "温暖家庭场景:父母与孩子一起在阳光下查看健康报告,科技蓝与暖橙色调", "封面主视觉"),
+        arg="claim", budget={"body_max_lines": 3})],
+    8, "low", "封面页:品牌主张 + 视觉冲击,信息克制", nrole="cover",
+    resources={"layout_refs": [], "block_refs": [], "chart_refs": [], "principle_refs": []},
+    deco={"style": "tech_blue_warm_orange", "elements": ["hero_image", "brand_mark", "five_dim_badges", "footer"]}))
+
+# ---------- 2. 目录 ----------
+PAGES.append(page(2, "toc",
+    "目录",
+    "六大部分清晰导航,让评委预知内容结构",
+    [card("c1", "anchor", "list", "elevated", "六大部分",
+        ["Part 01 项目概述:缘起 · 产品 · 规模 · 团队",
+         "Part 02 背景政策:国家战略 · 社会痛点",
+         "Part 03 市场容量:TAM/SAM/SOM · 商业模式",
+         "Part 04 需求与设计:五维方法论 · 业务闭环",
+         "Part 05 技术壁垒:溯源算法 · AI 能力栈",
+         "Part 06 应用成效:真实案例 · 权威背书"],
+        img=noimg(), arg="framework", budget={"body_max_lines": 3})],
+    6, "low", "目录页:清晰导航,每章有编号与主题", nrole="toc",
+    layout="toc-route",
+    resources={"layout_refs": [], "block_refs": [], "chart_refs": [], "principle_refs": []}))
+
+# ---------- 3. Part 01 章节封面 ----------
+PAGES.append(page(3, "section",
+    "Part 01 · 项目概述",
+    "让评委理解项目为解决什么而生、产品形态与建成规模",
+    [card("c1", "anchor", "text", "elevated", "从割裂到闭环:一个 AI 健康教练连接全家",
+        ["健康检查在医院、心理辅导在机构、素质教育在培训机构——数据与服务完全割裂",
+         "浠艾福以五维能量体系为底座、AI 智能体为引擎,构建连接家长、孩子、规划师与专业服务团队的家庭健康平台"],
+        img=img("hero-background", "right-half", "家庭与数字健康服务的连接意象,暖橙主调", "章节封面配图"), arg="claim")],
+    7, "mid_low", "章节封面:引入项目缘起与整体图景", nrole="section",
+    layout="free-section",
+    resources={"layout_refs": [], "block_refs": [], "chart_refs": [], "principle_refs": []}))
+
+# ---------- 4. 1.1 缘起与创新主张 ----------
+PAGES.append(page(4, "content",
+    "项目缘起与创新主张",
+    "家庭健康服务割裂的结构性困境 + \"AI 健康教练 + 人机协同\"的核心主张",
+    [
+        card("c1", "anchor", "text", "accent", "结构性困境:数据与服务完全割裂",
+            ["健康检查在医院、心理辅导在机构、素质教育在培训机构,一个家长需对接 5-8 个服务商才能拼凑\"这个孩子到底怎么样\"的模糊答案",
+             "内驱力不足、管理疲惫、服务商难找、效果无法量化——形成\"信息碎片化 → 干预滞后化 → 效果不可知\"的恶性循环"],
+            data=[{"value": "5-8", "label": "需对接服务商数", "interpretation": "一个家长为拼凑孩子全景需对接的服务商数量"}],
+            img=img("inline-illustration", "inline", "碎片化数据拼图的意象", "痛点配图"), arg="claim"),
+        card("c2", "support", "diagram", "elevated", "核心创新主张",
+            ["AI 不该只是一个聊天机器人,而是家庭健康的\"专属 AI 健康教练 + 后台专业服务团队(健康管家/营养师/医生)\"人机协同体系",
+             "可量化评估 → 可解释分析 → 可执行干预 → 可追踪反馈的精准干预闭环"],
+            img=noimg(), arg="claim"),
+        card("c3", "support", "process", "outline", "闭环路径",
+            ["检测/测评", "AI 分析", "个性化方案", "行为执行", "复测迭代"],
+            data=[{"value": "5", "label": "闭环环节", "interpretation": "检测→AI分析→方案→执行→复测的完整闭环"}],
+            img=noimg(), arg="method"),
+        card("c4", "context", "list", "glass", "范式转变",
+            ["从\"被动就医\"到\"主动健康\"", "从\"千人一面\"到\"千人千面\"", "AI 的精准能力注入家庭健康管理每一个环节"],
+            img=noimg(), arg="contrast"),
+    ],
+    7, "medium", "核心痛点与主张:需突出结构性困境与范式转变对比",
+    layout="hero-top",
+    source={"material_ref": "docs/AI创新项目大赛-项目介绍.md §1.1-1.2", "key_facts": ["5-8 个服务商", "恶性循环三环节", "四个可"]},
+    deco={"style": "tech_blue_warm_orange", "elements": ["title_underline", "process_flow", "footer"], "brand": "accent 橙"}))
+
+# ---------- 5. 1.2 产品形态四端 ----------
+PAGES.append(page(5, "content",
+    "产品形态:四端协同",
+    "平台连接家长、孩子、规划师、运营四方的完整产品矩阵与 AI 人机协同服务",
+    [
+        card("c1", "anchor", "list", "elevated", "家长端 · 微信小程序",
+            ["付费决策者:家庭管理、任务下发、测评预约、套餐购买", "五维能量看板、AI 健康教练"],
+            img=img("inline-illustration", "inline", "家长在手机上管理家庭健康任务", "家长端配图"), arg="framework"),
+        card("c2", "support", "list", "filled", "孩子端 · 微信小程序",
+            ["行为执行者:任务执行、小游戏训练、心愿兑换", "成长报告、健康打卡"],
+            img=noimg(), arg="framework"),
+        card("c3", "support", "list", "filled", "规划师端 · 小程序+Web",
+            ["成长规划师/营养师/活动方:套餐发布、测评结果录入", "批量任务下发、家庭健康管理"],
+            img=noimg(), arg="framework"),
+        card("c4", "support", "list", "filled", "管理端 · Vue Web",
+            ["平台运营+后台服务人员(健康管家/营养师/医生):审核、配置、数据统计", "内容与知识库管理、专业服务支持"],
+            img=noimg(), arg="framework"),
+        card("c5", "context", "quote", "glass", "人机协同服务体系",
+            ["AI 健康教练 7×24 智能应答 + 健康管家/营养师/医生后台兜底", "\"AI 智能应答 + 人工专业兜底\""],
+            img=noimg(), arg="synthesis"),
+    ],
+    8, "high", "四端产品矩阵:以四象限展示,突出人机协同体系",
+    layout="mixed-grid",
+    source={"material_ref": "docs/AI创新项目大赛-项目介绍.md §1.3", "key_facts": ["四端形态与服务对象", "后台服务体系"]},
+    deco={"style": "tech_blue_warm_orange", "elements": ["title_underline", "quadrant_grid", "footer"], "brand": "四端四色"}))
+
+# ---------- 6. 1.3 系统规模与AI服务 ----------
+PAGES.append(page(6, "content",
+    "系统规模与 AI 服务",
+    "用已上线数据证明工程成熟度与技术底座",
+    [
+        card("c1", "anchor", "data", "accent", "系统规模(已上线)",
+            ["48 个后端控制器、47 个数据库实体、15 大功能模块生产运行"],
+            data=[{"value": "48", "label": "后端控制器", "interpretation": "覆盖 15 大功能模块"},
+                  {"value": "47", "label": "数据库实体", "interpretation": "复杂数据模型支撑五维体系"},
+                  {"value": "50+", "label": "小程序页面", "interpretation": "家长/孩子/规划师三端"},
+                  {"value": "29", "label": "Web 管理页", "interpretation": "运营与服务端"}],
+            chart={"chart_type": "kpi", "note": "四组 KPI 数字"},
+            img=noimg(), arg="evidence"),
+        card("c2", "support", "list", "elevated", "技术栈",
+            ["Java 8 + Spring Boot 2.7.18 + MyBatis-Plus", "Python 智能体服务(AI 健康教练)", "uni-app Vue 2 小程序 / Vue 2 + Element UI", "MySQL 8.0"],
+            img=noimg(), arg="framework"),
+        card("c3", "support", "list", "filled", "AI 服务(自研)",
+            ["智能体服务体系:健康咨询、营养建议、报告解读、任务生成、内容推荐", "家庭知识库问答(RAG):向量知识库 + 菌属/指标/食物/营养素知识库"],
+            img=img("inline-illustration", "inline", "AI 智能体服务架构意象:机器人+知识库", "AI服务配图"), arg="framework"),
+    ],
+    8, "medium", "规模数字是核心证据:大数字 KPI 展示,技术栈为辅助",
+    layout="primary-secondary",
+    source={"material_ref": "docs/AI创新项目大赛-项目介绍.md §1.4", "key_facts": ["48/47/15", "50+/29", "技术栈清单"]},
+    deco={"style": "tech_blue_warm_orange", "elements": ["title_underline", "kpi_row", "footer"], "brand": "accent 蓝"}))
+
+# ---------- 7. 1.4 公司实力与核心团队 ----------
+PAGES.append(page(7, "content",
+    "公司实力与核心团队",
+    "国家级高新技术企业的工程实力 + 心理学/医疗/教育专家团队背书",
+    [
+        card("c1", "anchor", "data", "accent", "公司实力",
+            ["北京安盈创卓科技有限公司(2013 年成立,中关村软件核心区)国家级高新技术企业、中石化 A 级供应商"],
+            data=[{"value": "500+", "label": "央企/政府/金融/军工客户", "interpretation": "中国石化、国家电网、中航工业等"},
+                  {"value": "60+", "label": "软件著作权", "interpretation": "ISO9001 认证,自主知识产权"},
+                  {"value": "10 年", "label": "自研\"图腾\"云原生平台", "interpretation": "DDD 模型,业务与技术分离"}],
+            chart={"chart_type": "kpi", "note": "公司关键实力指标"},
+            img=noimg(), arg="evidence"),
+        card("c2", "support", "list", "elevated", "业务覆盖",
+            ["政府:云南省办公厅、交通运输部等", "石化:中国石化、长城电商等", "电力:国网北京信通、北京电力及 60 余分支", "军工/金融:中航工业、航天科工、银保监云南局"],
+            img=noimg(), arg="evidence"),
+        card("c3", "support", "people", "filled", "9 人核心团队",
+            ["廖晓光·项目负责人 | 魏高峡·学术带头人 | 施建农·学术带头人", "张挽时·学术带头人 | 林小朋·技术专家 | 韩庆宇·销售专家", "曹丽君·心理学专家 | 闫德波·医疗专家 | 王卫东·教育专家"],
+            data=[{"value": "9", "label": "核心团队成员", "interpretation": "工程骨干 + 心理学/医疗/教育专家"}],
+            img=noimg(), arg="evidence"),
+    ],
+    8, "medium", "公司实力与团队:数据卡为主,团队突出专家背景",
+    layout="mixed-grid",
+    source={"material_ref": "docs/AI创新项目大赛-项目介绍.md §1.5", "key_facts": ["500+ 客户", "60+ 著作权", "9 人团队", "双培强基学术背书"]},
+    deco={"style": "tech_blue_warm_orange", "elements": ["title_underline", "kpi_row", "footer"], "brand": "accent 金"}))
+
+# ---------- 8. Part 02 章节封面 ----------
+PAGES.append(page(8, "section",
+    "Part 02 · 背景政策",
+    "论证\"健康中国 + 人工智能+\"双轮驱动下 AI 主动健康正处最佳窗口期",
+    [card("c1", "anchor", "text", "elevated", "国家战略 · 时代需求 · 技术就绪",
+        ["政策端\"鼓励 AI 赋能健康/心理服务\"、需求端\"问题高发且供给短缺\"、技术端\"大模型智能体已就绪\"——三者交汇,正是 AI 精准干预、主动健康类项目的最佳窗口期"],
+        img=img("hero-background", "right-half", "国家政策与科技趋势交汇意象,蓝色调", "章节封面配图"), arg="claim")],
+    7, "mid_low", "章节封面:窗口期结论先行", nrole="section",
+    layout="free-section",
+    resources={"layout_refs": [], "block_refs": [], "chart_refs": [], "principle_refs": []}))
+
+# ---------- 9. 2.1 国家战略与专项政策 ----------
+PAGES.append(page(9, "content",
+    "国家战略与专项政策",
+    "从健康中国到\"人工智能+\",政策已为 AI 心理/健康干预产品打开准入大门",
+    [
+        card("c1", "anchor", "timeline", "accent", "政策脉络:从健康中国到 AI+",
+            ["2016《健康中国2030规划纲要》:\"健康优先、预防为主\"——主动健康最高纲领",
+             "2019《健康中国行动》:3 个儿童青少年专项行动",
+             "2022《家庭教育促进法》:家庭教育从\"家事\"上升为\"国事\"",
+             "2025.08 国发〔2025〕11号:\"人工智能+\"上升为国家行动",
+             "2025.10 国卫办规划发〔2025〕30号:以学生为重点,心理问题智能筛查、预警、干预,2027 年形成垂直大模型和智能体应用"],
+            chart={"chart_type": "timeline", "note": "5 个政策节点"},
+            img=noimg(), arg="evidence"),
+        card("c2", "support", "list", "elevated", "儿童青少年健康专项",
+            ["教育部等17部门《专项行动计划》:\"一生一策\"心理健康档案,四位一体体系",
+             "教基厅〔2025〕2号《十条措施》:明确\"利用人工智能赋能学生心理健康,支持探索'AI心理助手'\"",
+             "卫健委等13部门《\"五健\"促进行动计划》:防筛诊治康一体化,数智技术分级分类精细化管理"],
+            data=[{"value": "2027", "label": "垂直大模型应用目标", "interpretation": "国卫办规划发〔2025〕30号"}],
+            img=noimg(), arg="evidence"),
+    ],
+    8, "medium", "政策是硬证据:时间线展示政策演进,专项政策聚焦儿童青少年",
+    layout="asymmetric",
+    source={"material_ref": "docs/AI创新项目大赛-项目介绍.md §2.1-2.2", "key_facts": ["5 份政策文件", "AI 心理助手明确入政策"]},
+    deco={"style": "tech_blue_warm_orange", "elements": ["title_underline", "timeline", "footer"], "brand": "accent 蓝"}))
+
+# ---------- 10. 2.2 社会痛点与技术窗口期 ----------
+PAGES.append(page(10, "content",
+    "社会痛点与技术窗口期",
+    "需求端问题高发、供给短缺,技术端智能体已就绪——窗口期成立",
+    [
+        card("c1", "anchor", "data", "accent", "问题高发 · 供给严重不足",
+            ["教育部 2025 监测:初中生抑郁检出率 24.6%,高中生 29.1%;63% 家庭求助无门转向市场化服务",
+             "78% 家长认为孩子缺乏自主学习动力;隔代抚养/单亲等非传统家庭占比超 43%"],
+            data=[{"value": "24.6%", "label": "初中生抑郁检出率", "interpretation": "教育部 2025 监测数据"},
+                  {"value": "29.1%", "label": "高中生抑郁检出率", "interpretation": "问题高发,触目惊心"},
+                  {"value": "20 万+", "label": "专业人才缺口", "interpretation": "持证家庭治疗师不足 8000 人"},
+                  {"value": "78%", "label": "家长认为孩子缺乏自驱力", "interpretation": "中国教育科学研究院"}],
+            chart={"chart_type": "metric_row", "note": "痛点四组关键数据"},
+            img=noimg(), arg="evidence"),
+        card("c2", "support", "list", "elevated", "行业困境:数据有,洞察无",
+            ["一份 135 页的肠道菌群检测报告、数百项指标的测评报告,家长根本看不懂", "\"数据有,洞察无;报告有,方案无\"是行业普遍困境"],
+            img=img("inline-illustration", "inline", "看不懂的厚厚专业报告,家长困惑", "行业痛点配图"), arg="claim"),
+        card("c3", "support", "list", "filled", "技术就绪:大模型让精准干预成为可能",
+            ["多模态大模型 + 数字疗法成为学界与产业共识方向", "大模型 + RAG + 智能体(Agent)成熟:读得懂报告、说得清原理、给得出方案", "神经科学驱动的\"主动健康六阶模型\"提供行为科学标尺"],
+            img=noimg(), arg="method"),
+    ],
+    8, "medium", "痛点数据是核心冲击点:数据卡突出,技术就绪作支撑",
+    layout="primary-secondary",
+    source={"material_ref": "docs/AI创新项目大赛-项目介绍.md §2.3-2.4", "key_facts": ["24.6%/29.1%", "8000 人 vs 20 万缺口", "78%/43%", "135 页报告"]},
+    deco={"style": "tech_blue_warm_orange", "elements": ["title_underline", "metric_row", "footer"], "brand": "accent 红橙"}))
+
+# ---------- 11. Part 03 章节封面 ----------
+PAGES.append(page(11, "section",
+    "Part 03 · 市场容量",
+    "万亿赛道中的高增长细分:市场空间、用户结构、商业模式三连证",
+    [card("c1", "anchor", "text", "elevated", "4.5 万亿赛道 · 渗透率不足 3% · 97% 待开拓",
+        ["中国家庭教育消费市场 4.5 万亿元,家庭教育服务平台渗透率不足 3%——高增长细分 + 极低渗透率 = 巨大的平台化机会"],
+        img=img("hero-background", "right-half", "万亿市场规模意象:上升曲线与广阔空间,蓝橙色调", "章节封面配图"), arg="claim")],
+    7, "mid_low", "章节封面:市场规模结论先行", nrole="section",
+    layout="free-section",
+    resources={"layout_refs": [], "block_refs": [], "chart_refs": [], "principle_refs": []}))
+
+# ---------- 12. 3.1 市场空间 TAM/SAM/SOM ----------
+PAGES.append(page(12, "content",
+    "市场空间:TAM / SAM / SOM",
+    "从 4.5 万亿家庭消费到 5000 亿儿童健康管理,逐级聚焦到 97% 待开拓的平台市场",
+    [
+        card("c1", "anchor", "data", "accent", "TAM · 中国家庭教育消费市场",
+            ["2025 年规模 4.5 万亿元(艾瑞咨询)"],
+            data=[{"value": "4.5 万亿", "label": "TAM 家庭教育消费市场", "interpretation": "2025 年,艾瑞咨询"}],
+            chart={"chart_type": "kpi", "note": "TAM 大数字"},
+            img=noimg(), arg="evidence"),
+        card("c2", "support", "data", "elevated", "SAM · 细分健康管理市场",
+            ["儿童健康管理(体检/营养/心理/运动)5000 亿元+", "家庭教育服务 217 亿→500 亿(CAGR 19.3%,弗若斯特沙利文)", "心理健康教育 286.3 亿→810 亿(CAGR 23.1%,ZVZO)", "泛心理健康服务 104.1 亿(CAGR 27%,沙利文/中邮)"],
+            data=[{"value": "19.3%", "label": "家庭教育服务 CAGR", "interpretation": "217 亿→500 亿(2030)"},
+                  {"value": "27%", "label": "泛心理服务 CAGR", "interpretation": "2020-2025 复合增速"}],
+            chart={"chart_type": "comparison_bar", "note": "细分市场增速对比"},
+            img=noimg(), arg="evidence"),
+        card("c3", "support", "data", "filled", "SOM · 平台渗透率",
+            ["家庭教育服务平台渗透率不足 3%,97% 待开拓"],
+            data=[{"value": "< 3%", "label": "平台渗透率", "interpretation": "97% 市场待开拓"},
+                  {"value": "97%", "label": "待开拓空间", "interpretation": "行业测算"}],
+            img=noimg(), arg="evidence"),
+        card("c4", "context", "quote", "glass", "市场结论",
+            ["万亿赛道中的高增长细分,供给高度分散,一体化平台机会窗口打开"],
+            img=noimg(), arg="synthesis"),
+    ],
+    8, "medium", "TAM/SAM/SOM 三层漏斗:大数字逐级聚焦,增速对比",
+    layout="hero-top",
+    source={"material_ref": "docs/AI创新项目大赛-项目介绍.md §3.1", "key_facts": ["4.5 万亿", "5000 亿+", "CAGR 19.3%/27%/23.1%", "渗透率 <3%"]},
+    deco={"style": "tech_blue_warm_orange", "elements": ["title_underline", "funnel_layers", "footer"], "brand": "accent 蓝→橙渐变层级"}))
+
+# ---------- 13. 3.2 用户基数与竞争格局 ----------
+PAGES.append(page(13, "content",
+    "用户基数与竞争格局",
+    "1.2 亿户目标家庭 + 高度分散供给 = 一体化平台的卡位机会",
+    [
+        card("c1", "anchor", "data", "accent", "目标用户与付费能力",
+            ["3-18 岁儿童家庭约 1.2 亿户;2024 年小学及幼儿园在校生约 1.4 亿人次"],
+            data=[{"value": "1.2 亿", "label": "目标家庭户数", "interpretation": "3-18 岁儿童家庭"},
+                  {"value": "78.6%", "label": "城市家庭要求定制化", "interpretation": "教育目标转向综合素养"},
+                  {"value": "78.3%", "label": "心理辅导复购率", "interpretation": "服务包均价 1.2 万/年"},
+                  {"value": "1.2-3 万", "label": "家长年均教育支出/孩子", "interpretation": "90 后父母偏好数据驱动"}],
+            chart={"chart_type": "metric_row", "note": "用户结构关键数据"},
+            img=noimg(), arg="evidence"),
+        card("c2", "support", "list", "elevated", "竞争格局:高度分散,无一体化平台",
+            ["纯工具类 App:无服务闭环", "纯内容平台:无数据闭环", "纯教育机构:缺健康维度", "尚无\"健康+教育+心理\"一体化的成规模平台"],
+            img=noimg(), arg="contrast"),
+        card("c3", "support", "list", "filled", "新壁垒:AI 辅助干预 + 个性化定制",
+            ["行业从\"卖课/卖工具\"转向\"交付结果\"", "37.2% 机构已部署 AI 情绪识别", "个性化定制正成为新壁垒——正是浠艾福的差异化卡位"],
+            data=[{"value": "37.2%", "label": "机构已部署 AI 情绪识别", "interpretation": "AI 辅助干预成新壁垒"}],
+            img=img("inline-illustration", "inline", "竞争格局中的差异化卡位意象", "竞争配图"), arg="claim"),
+    ],
+    8, "medium", "用户数据+竞争格局:数据卡为核心,空白卡位为结论",
+    layout="mixed-grid",
+    source={"material_ref": "docs/AI创新项目大赛-项目介绍.md §3.2-3.3", "key_facts": ["1.2 亿户", "78.6%", "78.3% 复购", "37.2% AI 部署"]},
+    deco={"style": "tech_blue_warm_orange", "elements": ["title_underline", "metric_row", "footer"], "brand": "accent 绿"}))
+
+# ---------- 14. 3.3 商业模式与单位经济 ----------
+PAGES.append(page(14, "content",
+    "商业模式与单位经济",
+    "三边市场平台经济:四类收入 + LTV/CAC 80-330 倍,商业可行性验证",
+    [
+        card("c1", "anchor", "data", "accent", "四类收入 · 三边市场",
+            ["测评服务费:订单抽佣 15-25%,毛利率 60-70%", "规划师佣金:套餐抽佣 10-20%,毛利率 80-90%", "商品交易费:商城抽佣 5-15%,毛利率 85-95%", "会员订阅:含 AI 健康教练,毛利率 70-80%"],
+            data=[{"value": "80-90%", "label": "规划师佣金毛利率", "interpretation": "四类收入中最高"},
+                  {"value": "15-25%", "label": "测评订单抽佣", "interpretation": "平台抽佣区间"}],
+            chart={"chart_type": "stacked_bar", "note": "四类收入与毛利率"},
+            img=noimg(), arg="evidence"),
+        card("c2", "support", "data", "elevated", "单位经济模型",
+            ["微信生态社交推荐获客:单家庭 CAC 15-25 元", "单家庭年均价值 LTV 2000-5000 元"],
+            data=[{"value": "80-330 倍", "label": "LTV/CAC", "interpretation": "行业健康线为 3 倍,具备大规模投放可行性"},
+                  {"value": "15-25 元", "label": "单家庭获客成本 CAC", "interpretation": "微信生态社交推荐"},
+                  {"value": "2000-5000 元", "label": "单家庭年均价值 LTV", "interpretation": "年化价值"}],
+            chart={"chart_type": "kpi", "note": "LTV/CAC 核心指标"},
+            img=noimg(), arg="evidence"),
+        card("c3", "support", "diagram", "glass", "三边市场平台经济",
+            ["家长(需求端)— 消费 — 规划师/活动方/供应商(供给端)", "平台运营:审核 · 配置 · 数据支撑全链路"],
+            img=img("inline-illustration", "inline", "三边市场平台连接意象", "商业模式配图"), arg="framework"),
+    ],
+    8, "medium", "商业模型:收入结构+单位经济双数据卡,突出 LTV/CAC 高倍率",
+    layout="l-shape",
+    source={"material_ref": "docs/AI创新项目大赛-项目介绍.md §3.4", "key_facts": ["四类收入毛利率", "LTV/CAC 80-330 倍", "CAC 15-25 元"]},
+    deco={"style": "tech_blue_warm_orange", "elements": ["title_underline", "bar_chart", "footer"], "brand": "accent 金"}))
+
+# ---------- 15. Part 04 章节封面 ----------
+PAGES.append(page(15, "section",
+    "Part 04 · 需求与设计",
+    "以\"五维能量\"方法论重构家庭健康管理:有理论源头、有产品落点、有业务闭环",
+    [card("c1", "anchor", "text", "elevated", "五维能量体系:测得到 · 评得准 · 做得到",
+        ["基于 WHO 三维健康观与\"健康中国\"四维健康观演进,结合中国五行智慧,项目自研\"身·心·智·行·富\"五维能量体系,将理论测评与日常行为激励打通"],
+        img=img("hero-background", "right-half", "五行五维能量体系意象:五行相生的环形能量,暖橙主调", "章节封面配图"), arg="claim")],
+    7, "mid_low", "章节封面:五维方法论定位", nrole="section",
+    layout="free-section",
+    resources={"layout_refs": [], "block_refs": [], "chart_refs": [], "principle_refs": []}))
+
+# ---------- 16. 4.1 痛点与方法论演进 ----------
+PAGES.append(page(16, "content",
+    "痛点洞察与方法论演进",
+    "三大痛点 + WHO 三维到浠艾福五维的传承脉络",
+    [
+        card("c1", "anchor", "list", "accent", "三大核心痛点",
+            ["信息碎片化:健康、心理、教育数据散落各处,无全景图", "干预滞后化:只能\"事后补救\",效果缺乏数据评估", "执行断层化:拿到报告和方案,缺少推动执行机制——\"知道做不到\""],
+            img=img("inline-illustration", "inline", "家庭健康数据碎片化痛点意象", "痛点配图"), arg="claim"),
+        card("c2", "support", "process", "elevated", "方法论传承脉络",
+            ["WHO 三维健康观(1948):身体·心理·社会适应", "健康中国四维健康观:身体·心理·认知·社会性", "浠艾福五维能量体系:身·心·智·行·富"],
+            data=[{"value": "1948", "label": "WHO 三维健康观", "interpretation": "国际共识起点"},
+                  {"value": "3 维→4 维→5 维", "label": "演进路径", "interpretation": "四维×五行智慧与家庭行为实践"}],
+            img=noimg(), arg="framework"),
+        card("c3", "support", "quote", "filled", "一句话总结",
+            ["浠艾福五维 = 健康中国四维健康观 × 五行智慧与家庭行为实践——既有国家战略理论源头,又有本土化创新"],
+            img=noimg(), arg="synthesis"),
+    ],
+    8, "medium", "痛点+方法论演进:process 展示传承脉络,痛点数据突出",
+    layout="asymmetric",
+    source={"material_ref": "docs/AI创新项目大赛-项目介绍.md §4.1-4.2", "key_facts": ["三大痛点", "WHO 1948", "四维→五维"]},
+    deco={"style": "tech_blue_warm_orange", "elements": ["title_underline", "process_flow", "footer"], "brand": "accent 橙"}))
+
+# ---------- 17. 4.2 五维全景幸福矩阵 ----------
+PAGES.append(page(17, "content",
+    "五维全景幸福矩阵",
+    "五维×五行×功能:产品方法论与双轨激励机制的完整落地",
+    [
+        card("c1", "anchor", "list", "accent", "五维 · 五行 · 功能",
+            ["身(土·主动健康):肠道菌群检测、健康报告 AI 解析、饮食推荐", "心(火·大爱相随):DAN 测评、情绪打卡、家庭关系图、亲子沟通", "智(金·赋能未来):认知测评、舒尔特/数独小游戏、阅读任务", "行(木·人际和谐):任务系统、积分激励、健康打卡、家庭挑战", "富(水·利他创富):财富打卡、创客生态、心愿兑换"],
+            data=[{"value": "5 维", "label": "五行对应", "interpretation": "土火金木水"},
+                  {"value": "25+", "label": "评分数据源", "interpretation": "5 维×3-8 数据源/维"}],
+            img=noimg(), arg="framework"),
+        card("c2", "support", "diagram", "elevated", "能量沙盘 · 评分系统",
+            ["基于任务/游戏/测评/阅读/打卡行为数据,每日自动计算家庭五维健康指数(0-100%)", "维度映射比例动态可配置"],
+            data=[{"value": "0-100%", "label": "家庭五维健康指数", "interpretation": "每日自动计算"}],
+            img=img("inline-illustration", "inline", "五维能量沙盘仪表盘意象", "能量沙盘配图"), arg="method"),
+        card("c3", "support", "diagram", "filled", "能量账本 · 计量系统",
+            ["孩子完成任务获得能量积分、兑换心愿:\"努力→成长→奖励\"闭环", "全局日上限 200 能量防刷分"],
+            data=[{"value": "200", "label": "能量日上限", "interpretation": "防刷分机制"}],
+            img=noimg(), arg="method"),
+    ],
+    8, "medium", "五维矩阵是核心:五行色系区分五个维度,双轨机制并排",
+    layout="mixed-grid",
+    source={"material_ref": "docs/AI创新项目大赛-项目介绍.md §4.3", "key_facts": ["五维五行功能", "25+ 数据源", "日上限 200"]},
+    deco={"style": "tech_blue_warm_orange", "elements": ["title_underline", "five_dim_badges", "footer"], "brand": "五维五色(身橙心粉智蓝行绿富金)"}))
+
+# ---------- 18. 4.3 业务闭环 ----------
+PAGES.append(page(18, "content",
+    "业务闭环:检测到复测",
+    "五大环节构成数据飞轮,精准干预闭环可解释、可追踪、可迭代",
+    [
+        card("c1", "anchor", "process", "accent", "五环飞轮",
+            ["① 检测/测评:居家采样、测评预约", "② AI 分析:报告解析、指标解读、风险标注", "③ 个性化方案:基于个体画像的饮食/训练/亲子方案", "④ 行为执行:任务下发、能量积分、即时反馈、智能预警", "⑤ 复测迭代:周期复测、效果对比、方案调优", "回到 ① —— 数据飞轮持续积累"],
+            data=[{"value": "5 环", "label": "闭环环节", "interpretation": "检测→分析→方案→执行→复测"},
+                  {"value": "∞", "label": "数据飞轮", "interpretation": "复测迭代持续积累家庭数据资产"}],
+            chart={"chart_type": "timeline", "note": "五环节闭环"},
+            img=noimg(), arg="method"),
+        card("c2", "support", "quote", "elevated", "精准干预闭环",
+            ["可量化评估 → 可解释分析 → 可执行干预 → 可追踪反馈", "把 AI 的精准能力注入家庭健康管理的每一个环节"],
+            img=img("inline-illustration", "inline", "家庭健康数据飞轮循环意象", "闭环配图"), arg="claim"),
+    ],
+    8, "medium", "业务闭环:process 环形展示五环节,突出数据飞轮",
+    layout="primary-secondary",
+    source={"material_ref": "docs/AI创新项目大赛-项目介绍.md §4.4", "key_facts": ["五环节闭环", "数据飞轮持续积累"]},
+    deco={"style": "tech_blue_warm_orange", "elements": ["title_underline", "process_flow", "footer"], "brand": "accent 蓝"}))
+
+# ---------- 19. Part 05 章节封面 ----------
+PAGES.append(page(19, "section",
+    "Part 05 · 技术壁垒",
+    "为什么别人要追 2-3 年:方法论壁垒 + 数据壁垒 + 网络效应 + AI 能力栈",
+    [card("c1", "anchor", "text", "elevated", "四重壁垒:算法 · 数据 · 网络效应 · AI 能力栈",
+        ["自研五维相生溯源算法、长周期行为数据护城河、三方网络效应、自研 AI 健康教练智能体——构成竞争对手难以复制的复合技术纵深"],
+        img=img("hero-background", "right-half", "技术与数据壁垒意象:盾牌与数据流,科技蓝主调", "章节封面配图"), arg="claim")],
+    7, "mid_low", "章节封面:四重壁垒总览", nrole="section",
+    layout="free-section",
+    resources={"layout_refs": [], "block_refs": [], "chart_refs": [], "principle_refs": []}))
+
+# ---------- 20. 5.1 五维相生溯源算法 ----------
+PAGES.append(page(20, "content",
+    "五维相生溯源算法",
+    "核心算法壁垒:每一笔能量的来源、去向、归因都可追溯、可解释、可验证",
+    [
+        card("c1", "anchor", "data", "accent", "自研五维评价体系",
+            ["融合五行相生理论与多源加权计算,每个维度由 3-8 个异构数据源支撑", "energy_source_config 动态配置维度映射比例——差异化核心,具备\"五维健康行业标准\"演进潜力"],
+            data=[{"value": "3-8", "label": "异构数据源/维度", "interpretation": "任务/活动/课程/商品/咨询等"},
+                  {"value": "25+", "label": "总评分数据源", "interpretation": "5 维×3-8 源/维"}],
+            chart={"chart_type": "ring", "note": "五维权重动态配置"},
+            img=noimg(), arg="claim"),
+        card("c2", "support", "list", "elevated", "全链路溯源",
+            ["EnergyLog 完整记录来源类型/来源业务单/归属维度与占比权重", "从\"家庭五维健康指数\"逐级下钻到\"某一维度的某一次具体行为\"", "\"涨有据、落可查\""],
+            img=noimg(), arg="method"),
+        card("c3", "support", "list", "filled", "五行相生动态调节",
+            ["身生智→智生富→富生行→行生心→心生身:相生增益", "相克约束:动态平衡,\"一人一比例、一日一状态\"", "EnergyRuleEngine 零代码配置:维度占比、行为映射、防刷分上限", "行为—健康指标归因:回答\"过去 30 天哪些行为贡献了菌群多样性改善\""],
+            img=img("inline-illustration", "inline", "五行相生相克动态平衡意象", "算法配图"), arg="method"),
+    ],
+    8, "medium", "核心算法壁垒:溯源能力+动态调节双支撑,突出可解释性",
+    layout="mixed-grid",
+    source={"material_ref": "docs/AI创新项目大赛-项目介绍.md §5.1", "key_facts": ["3-8 数据源/维", "五行相生链", "日上限 200", "行为归因"]},
+    deco={"style": "tech_blue_warm_orange", "elements": ["title_underline", "ring_chart", "footer"], "brand": "accent 金"}))
+
+# ---------- 21. 5.2 AI 能力栈 ----------
+PAGES.append(page(21, "content",
+    "AI 能力栈(核心)",
+    "从\"没有记忆的机器人\"到\"比家长更了解这个家庭数据的健康顾问\"",
+    [
+        card("c1", "anchor", "diagram", "accent", "家庭 360° 上下文引擎",
+            ["对话前自动组装家庭全景数据:成员信息、孩子列表、任务完成率、健康报告、五维能量分数、认知测评结果、已购商品标签、历史会话记忆", "AI 不是\"没有记忆的机器人\",而是\"比家长更了解这个家庭数据的健康顾问\"——纯聊天产品无法企及的数据厚度"],
+            data=[{"value": "360°", "label": "家庭上下文", "interpretation": "8 类家庭全景数据自动组装"}],
+            img=img("inline-illustration", "inline", "AI 健康顾问与家庭数据连接意象", "AI上下文配图"), arg="claim"),
+        card("c2", "support", "list", "elevated", "AI 能力矩阵(已实现)",
+            ["智能对话:智能体对话+家庭上下文注入+人格化助手", "知识库问答(RAG):向量知识库+菌属/指标/食物/营养素定时同步", "报告智能解析:PDF→结构化指标→AI 标注风险(135 页→3 页)", "AI 生成任务:[TASK] 协议标记→自动创建成长任务", "AI 内容推荐:标签匹配+维度对齐+已购降权", "AI 每日反馈:定时生成个性化鼓励语"],
+            data=[{"value": "6 类", "label": "AI 能力", "interpretation": "对话/RAG/解析/任务/推荐/反馈"}],
+            img=noimg(), arg="framework"),
+        card("c3", "support", "process", "filled", "AI 健康教练智能体服务(自研·生产)",
+            ["意图分类器", "场景路由(咨询/营养/解读/任务/推荐)", "知识检索→大模型推理→工具调用→记忆保存", "全链路追踪+指标监控+慢请求告警"],
+            data=[{"value": "7×24", "label": "智能应答", "interpretation": "与后台专业服务团队人机协同"}],
+            img=noimg(), arg="method"),
+    ],
+    9, "medium", "AI 能力栈是全篇核心:上下文引擎+能力矩阵+智能体架构三层展示",
+    layout="l-shape",
+    source={"material_ref": "docs/AI创新项目大赛-项目介绍.md §5.4", "key_facts": ["360°上下文", "6 类 AI 能力", "135→3 页", "智能体编排"]},
+    deco={"style": "tech_blue_warm_orange", "elements": ["title_underline", "process_flow", "footer"], "brand": "accent 蓝"}))
+
+# ---------- 22. 5.3 数据护城河·六阶模型·工程安全 ----------
+PAGES.append(page(22, "content",
+    "数据护城河 · 六阶模型 · 工程安全",
+    "长周期数据不可复制 + 行为科学机制 + 生产级工程 = 多重复合壁垒",
+    [
+        card("c1", "anchor", "data", "accent", "行为数据护城河",
+            ["47 个数据库实体、25+ 评分数据源、10+ 种能量来源构成复杂数据模型", "任务记录=努力曲线、五维能量=健康趋势、测评=成长档案、游戏=认知基线", "一个孩子 6-18 岁的成长数据不是烧钱能快速复制的"],
+            data=[{"value": "47", "label": "数据库实体", "interpretation": "复杂数据模型"},
+                  {"value": "6-18 岁", "label": "成长数据周期", "interpretation": "长周期积累构成迁移壁垒"}],
+            chart={"chart_type": "kpi", "note": "数据资产规模"},
+            img=noimg(), arg="evidence"),
+        card("c2", "support", "list", "elevated", "三方网络效应(平台壁垒)",
+            ["更多家长→更多成长规划师→更优质服务→更多家庭(正向飞轮)", "已上线:服务商入驻体系(规划师/活动方/供应商)、服务商等级、团队管理、佣金分成"],
+            img=img("inline-illustration", "inline", "三方网络飞轮意象", "网络效应配图"), arg="framework"),
+        card("c3", "support", "process", "filled", "主动健康六阶模型(行为科学)",
+            ["有效启动(微行动<1s)", "持续坚持(即时多巴胺)", "获得感形成(成就显性化)", "身份重塑(健康等级/称号)", "意见领袖化(家庭互动)", "社会扩散(健康圈/分享)"],
+            data=[{"value": "6 阶", "label": "行为干预阶梯", "interpretation": "健康智能预警 6 条规则引擎+健康积分等级"}],
+            img=noimg(), arg="framework"),
+        card("c4", "support", "list", "glass", "工程化与安全合规",
+            ["JWT 认证+角色权限(parent/child/teacher/admin),敏感字段加密", "统一 Result 响应、统一 @PostMapping 规范、数据库迁移自动化", "四级地址匹配(省市区街道+区域回退);完成遗留系统全量数据迁移"],
+            img=noimg(), arg="evidence"),
+    ],
+    8, "medium", "多重壁垒并列:数据+网络效应+行为科学+工程安全四卡",
+    layout="asymmetric",
+    source={"material_ref": "docs/AI创新项目大赛-项目介绍.md §5.2-5.3,5.4(5)", "key_facts": ["47/25+/10+", "六阶模型", "JWT+权限", "全量迁移"]},
+    deco={"style": "tech_blue_warm_orange", "elements": ["title_underline", "process_flow", "footer"], "brand": "accent 绿"}))
+
+# ---------- 23. Part 06 章节封面 ----------
+PAGES.append(page(23, "section",
+    "Part 06 · 应用成效",
+    "真实案例 · 权威背书 · 系统建设:项目已从概念走向生产",
+    [card("c1", "anchor", "text", "elevated", "从真实案例到生产级系统",
+        ["135 页专业报告压缩为 3 页行动方案的真实闭环、双培强基工程\"国家队\"级学术底座、15 大模块生产运行——用事实说话"],
+        img=img("hero-background", "right-half", "真实成果与成长数据意象:成果证书与上升曲线,暖橙蓝调", "章节封面配图"), arg="claim")],
+    7, "mid_low", "章节封面:应用成效总览", nrole="section",
+    layout="free-section",
+    resources={"layout_refs": [], "block_refs": [], "chart_refs": [], "principle_refs": []}))
+
+# ---------- 24. 6.1 真实案例:精准营养闭环 ----------
+PAGES.append(page(24, "content",
+    "真实案例:精准营养闭环",
+    "135 页专业报告 → 3 页行动方案,全链路在家完成",
+    [
+        card("c1", "anchor", "data", "accent", "案例快照(匿名)",
+            ["用户 530010234,2026 年 1 月完成肠道菌群健康风险评估(募极生物,135 页报告)"],
+            data=[{"value": "73", "label": "健康总分", "interpretation": "人群均值 75,干预空间大"},
+                  {"value": "53", "label": "菌群健康", "interpretation": "偏低"},
+                  {"value": "313 种", "label": "检出菌种", "interpretation": "均值约 800 种,微生态稳定性不足"}],
+            chart={"chart_type": "metric_row", "note": "AI 分析关键指标"},
+            img=noimg(), arg="evidence"),
+        card("c2", "support", "list", "elevated", "AI 自动标注三大缺乏指标",
+            ["维生素 B12(14 分,正常>50)→ 疲劳/贫血风险", "苏氨酸(14 分)→ 肠道屏障功能受损", "丙氨酸(9 分)→ 能量代谢异常"],
+            data=[{"value": "3 项", "label": "AI 自动标注风险", "interpretation": "从 135 页报告中精准提炼"}],
+            img=img("inline-illustration", "inline", "AI 从厚报告提炼关键指标意象", "AI解析配图"), arg="evidence"),
+        card("c3", "support", "process", "filled", "平台成效:四步干预闭环",
+            ["增加膳食纤维 25-30g/天", "优选发酵蛋白", "控制风险食物", "定期复测"],
+            data=[{"value": "135→3 页", "label": "报告压缩", "interpretation": "AI 解析让报告\"看得懂\""},
+                  {"value": "4 步", "label": "拟杆菌型肠型干预策略", "interpretation": "任务系统+能量积分推动执行"}],
+            chart={"chart_type": "progress_bar", "note": "闭环推进"},
+            img=noimg(), arg="method"),
+        card("c4", "context", "quote", "glass", "价值主张兑现",
+            ["\"检测→解读→方案→执行→跟踪\"全链路在家完成,替代\"跑医院、找专家、花数千元\"的传统路径"],
+            img=noimg(), arg="synthesis"),
+    ],
+    9, "medium", "真实案例是说服力核心:数据+风险标注+干预闭环三卡",
+    layout="mixed-grid",
+    source={"material_ref": "docs/AI创新项目大赛-项目介绍.md §6.1", "key_facts": ["73/53/313 种", "B12 14 分", "135→3 页", "四步干预"]},
+    deco={"style": "tech_blue_warm_orange", "elements": ["title_underline", "metric_row", "process_flow", "footer"], "brand": "accent 橙"}))
+
+# ---------- 25. 6.2 权威背书与系统建设成效 ----------
+PAGES.append(page(25, "content",
+    "权威背书与系统建设成效",
+    "\"国家队\"级学术底座 + 15 大模块生产运行 + 商业化验证",
+    [
+        card("c1", "anchor", "list", "accent", "双培强基工程 · 权威背书",
+            ["人民日报《人民周刊》+ 中国科学院心理研究所联合发起,\"培育心智、培养志向\"", "四维测评体系(DAN 测评等)已引入平台:权威测评→解读→干预→执行→复测数字化闭环", "学术带头人魏高峡、施建农为工程学术体系核心专家", "依托工程学术委员会、素质发展研究院提供持续背书"],
+            data=[{"value": "中科院", "label": "心理研究所联合发起", "interpretation": "\"国家队\"级学术底座"}],
+            img=img("inline-illustration", "inline", "权威学术机构背书意象", "权威背书配图"), arg="evidence"),
+        card("c2", "support", "list", "elevated", "系统建设成效(可验证)",
+            ["15 大模块生产运行:认证/家庭/任务/心愿/积分/测评/成长档案/商城/服务商/AI 健康教练/健康报告/饮食推荐/文章/关系/联系人", "AI 三端落地:健康教练小程序上线,覆盖上下文/解读/营养/任务生成/推荐", "人机协同:AI 7×24 应答+健康管家/营养师/医生兜底", "工程验证:Maven 编译+启动验证+分层测试(单元/集成/E2E),完成全量数据迁移"],
+            data=[{"value": "15", "label": "生产运行模块", "interpretation": "功能闭环全部跑通"}],
+            img=noimg(), arg="evidence"),
+        card("c3", "support", "data", "filled", "商业化验证",
+            ["三边市场四类交易已设计并部分上线", "公众号案例研究内容体系验证\"专业内容+案例转化\"低成本获客"],
+            data=[{"value": "80-330 倍", "label": "LTV/CAC", "interpretation": "单位经济模型健康"},
+                  {"value": "54 岁", "label": "家长转变案例", "interpretation": "从陪读焦虑到主动健康践行者"}],
+            chart={"chart_type": "kpi", "note": "商业化关键指标"},
+            img=noimg(), arg="evidence"),
+    ],
+    8, "medium", "权威背书+系统建设+商业化:三卡并列证明可信度",
+    layout="primary-secondary",
+    source={"material_ref": "docs/AI创新项目大赛-项目介绍.md §6.2-6.5", "key_facts": ["双培强基工程", "15 大模块", "DAN 测评", "LTV/CAC"]},
+    deco={"style": "tech_blue_warm_orange", "elements": ["title_underline", "kpi_row", "footer"], "brand": "accent 蓝"}))
+
+# ---------- 26. 结束页 ----------
+PAGES.append(page(26, "end",
+    "让健康从被动等待走向主动掌握",
+    "总结回顾 + 路线图 + 三年愿景 + 行动号召",
+    [
+        card("c1", "anchor", "data", "accent", "核心回顾",
+            ["五维能量方法论 × AI 健康教练 + 人机协同 × 五维相生溯源算法", "生产级系统:48 控制器 / 50+ 页面 / 15 大模块 / 真实数据资产"],
+            data=[{"value": "身·心·智·行·富", "label": "五维能量体系", "interpretation": "方法论底座"},
+                  {"value": "80-330 倍", "label": "LTV/CAC", "interpretation": "商业可行性验证"},
+                  {"value": "10 万+", "label": "2027 年覆盖家庭目标", "interpretation": "三年愿景第一步"}],
+            chart={"chart_type": "kpi", "note": "关键成果指标"},
+            img=noimg(), arg="claim"),
+        card("c2", "support", "timeline", "elevated", "未来 12 个月路线图与三年愿景",
+            ["Q1 增强:AI 报告自动解读全覆盖、AI 个性化任务生成、健康预警上线", "Q2 增长:规划师认证线上化、活动方工具包、健康圈", "Q3 变现:会员订阅体系、推广佣金系统", "Q4 扩张:社区互动、Open API 第三方接入", "2027 覆盖 10 万+ 家庭 → 2028 五维健康行业标准 → 2029 开放生态超级入口"],
+            data=[{"value": "4 阶段", "label": "季度路线图", "interpretation": "增强→增长→变现→扩张"},
+                  {"value": "2027-2029", "label": "三年阶梯", "interpretation": "平台→标准→生态"}],
+            img=noimg(), arg="framework"),
+        card("c3", "context", "quote", "glass", "给全家的一站式幸福提案",
+            ["让每一个家庭都拥有\"看得懂报告、跟得上方案、做得到行动\"的专属 AI 健康教练", "北京安盈创卓科技有限公司 · 浠艾福(Care Family)"],
+            img=img("hero-background", "full-bleed", "温暖家庭剪影与上升光路,呼应封面,暖橙蓝调", "结束页背景"), arg="synthesis"),
+    ],
+    8, "mid_low", "结束页:核心回顾+路线图+愿景+CTA,首尾呼应",
+    layout="free-end",
+    source={"material_ref": "docs/AI创新项目大赛-项目介绍.md §7-8", "key_facts": ["12 个月路线图", "三年愿景", "行动号召"]},
+    deco={"style": "tech_blue_warm_orange", "elements": ["hero_image", "timeline", "footer"], "brand": "首尾呼应暖橙"}))
+
+# 跨页检查:连续内容页 layout 不重复
+seq = [(p["slide_number"], p.get("layout_hint")) for p in PAGES if p["page_type"] == "content"]
+for i in range(1, len(seq)):
+    if seq[i][1] == seq[i-1][1]:
+        print(f"WARN: consecutive same layout: slide {seq[i-1][0]} & {seq[i][0]} -> {seq[i][1]}", file=sys.stderr)
+
+OUT.write_text(json.dumps({"ppt_planning": {"deck_title": "浠艾福(Care Family)AI 家庭主动健康管理平台 · 项目介绍", "pages": PAGES}}, ensure_ascii=False, indent=1), encoding="utf-8")
+print(f"OK: wrote {len(PAGES)} pages to {OUT}")

+ 190 - 0
ppt-output/runtime/html-spec.md

@@ -0,0 +1,190 @@
+# HTML 页面生成统一规范(Step 5c 全体 PageAgent 必读)
+
+> 本文件是 26 页 HTML 生成的**唯一统一规范**。所有子代理必须先读本文件,再读自己负责页面的 planning JSON 与 `ppt-output/style.json`。
+
+---
+
+## 0. 输入文件位置
+
+| 项目 | 路径 |
+|------|------|
+| 全局风格 | `D:\workspace\cfc\ppt-output\style.json` |
+| 每页策划稿 | `D:\workspace\cfc\ppt-output\planning\planning{N}.json`(N = 页码)|
+| 配图目录 | `D:\workspace\cfc\ppt-output\images\` |
+| 输出目录 | `D:\workspace\cfc\ppt-output\slides\slide-{N}.html` |
+
+**图片路径映射规则**(必须严格遵守):
+- planning 中某 card 的 `image.needed == true` 时,该 card 配图文件 = `../images/slide_{页码:02d}_{card在cards数组中的索引}.png`
+- 例:slide 6 的 cards[2].image.needed=true → `../images/slide_06_2.png`
+- HTML 位于 `slides/` 目录,所以 `<img src="../images/slide_06_2.png">`
+
+---
+
+## 1. 画布物理红线(不可违反)
+
+```css
+body {
+  width: 1280px; height: 720px; overflow: hidden;
+  background: var(--bg-primary);
+  font-family: var(--font-family);
+  position: relative;
+}
+```
+
+- 画布固定 1280×720,**禁止**写 `100%` 或 `transform: scale()` 缩放 hack
+- 所有 CSS 内联在 `<style>` 标签中,禁止外部 CSS 文件
+- 所有颜色必须通过 `var(--xxx)` 引用 CSS 变量(从 style.json 的 css_variables 展开到 `:root`),**禁止硬编码色值**
+- **禁止动画**:`@keyframes` / `animation` / `transition` 一律禁用(PPTX 导出不支持)
+- 字体:中文 `'PingFang SC', 'Microsoft YaHei', system-ui, sans-serif`;数字可用 `'Inter', 'PingFang SC', ...`
+
+## 2. 统一导航骨架(跨页一致性合同)
+
+### 标题区(content / toc 页强制)
+
+```html
+<header class="slide-header">
+  <span class="overline">PART 0X &mdash; 章节名</span>
+  <h1 class="page-title">页面标题</h1>
+</header>
+```
+
+```css
+.slide-header { position: absolute; top: 20px; left: 40px; right: 40px; height: 50px;
+  display: flex; align-items: baseline; gap: 16px; z-index: 10; }
+.overline { font-size: 10px; font-weight: 700; letter-spacing: 2px;
+  color: var(--accent-1); opacity: 0.8; text-transform: uppercase; white-space: nowrap; }
+.page-title { font-size: 26px; font-weight: 700; color: var(--text-primary); line-height: 1.2; margin: 0; }
+```
+
+### 页脚区(content / toc / section 页强制)
+
+```html
+<footer class="slide-footer">
+  <span class="footer-section">章节标签</span>
+  <span class="footer-page">NN / 26</span>
+</footer>
+```
+
+```css
+.slide-footer { position: absolute; bottom: 12px; left: 40px; right: 40px; height: 20px;
+  display: flex; justify-content: space-between; align-items: center; z-index: 10; }
+.footer-section { font-size: 10px; color: var(--text-secondary); opacity: 0.5; letter-spacing: 1px; }
+.footer-page { font-size: 10px; color: var(--text-secondary); opacity: 0.5; }
+```
+
+- `cover` 页:标题自由发挥(巨大冲击力),页脚可选
+- `section` 页:标题自由发挥,**页脚强制**
+- `end` 页:标题自由发挥,页脚可选
+
+## 3. 管线兼容防偏移规则(html2svg → svg2pptx 转换保证)
+
+| 禁止 | 正确替代 |
+|------|---------|
+| `::before` / `::after` 用于视觉装饰 | 真实 `<div>` / `<span>` |
+| `background-clip: text` 渐变文字 | `color: var(--accent-1)` |
+| `mask-image` / `-webkit-mask-image` | div 遮罩(linear-gradient 背景)|
+| `conic-gradient` | 内联 SVG `<circle>` + stroke-dasharray |
+| CSS border 三角形 | 内联 SVG `<polygon>` |
+| `mix-blend-mode` | `opacity` 叠加 |
+| `filter: blur()` | `opacity` 或 `box-shadow` |
+| `content: '文字'` | 真实 `<span>` |
+| CSS `background-image: url(...)` 放图 | `<img>` 标签 |
+| SVG 内 `<text>` 元素 | HTML `<div>` 绝对定位叠加 |
+| 大小字号内嵌 span(`3.08<s>s</s>`) | flex + 独立 span |
+| `stroke-dashoffset` | `stroke-dasharray="弧长 间隔"` 两值格式 |
+
+**配图融入技法**(管线安全):渐隐融合(div 遮罩 linear-gradient)/ 色调蒙版(半透明 div)/ 氛围底图(低 opacity `<img>` 0.05-0.40)/ 裁切视窗(overflow:hidden)/ 圆形裁切(border-radius)。全部用 `<img>` 标签实现。
+
+## 4. 卡片系统
+
+### card_style 6 种存在感(每页至少混用 2 种)
+
+| 变体 | 存在感 | 实现 |
+|------|--------|------|
+| `filled` | 沉稳大地 | 实体背景 `linear-gradient(var(--card-bg-from), var(--card-bg-to))` + border |
+| `transparent` | 无界之灵 | 无背景无边框,内容裸露 |
+| `outline` | 虚境描边 | 极淡边框 `border: 1px solid rgba(255,255,255,0.08)` |
+| `accent` | 灼焰核心 | accent 渐变填充 + 反色文字,**每页最多 1 个** |
+| `glass` | 雾中幻影 | 半透明背景 `rgba(255,255,255,0.06)` + backdrop-blur |
+| `elevated` | 悬崖浮岩 | 实体背景 + 3-4 层 box-shadow + translateY(-4px),**每页最多 1 个** |
+
+### 排版阶梯
+
+| 层级 | 字号 | 字重 | 用途 |
+|------|------|------|------|
+| H0 | 48-160px | 900 | 封面主标题(尽量 ≥80px)|
+| H1 | 26-32px | 700 | 页面主标题 |
+| H2 | 16-20px | 700 | 卡片标题 |
+| Body | 13-14px | 400 | 正文(line-height 1.8)|
+| Caption | 9-12px | 400 | 辅助标注 |
+| Data-Hero | 64-120px | 900 | 核心 KPI 超级数字(数据页至少 1 个 ≥64px)|
+| Data-Sub | 28-40px | 800 | 辅助指标 |
+
+- 每页最大/最小字号倍数比尽量 ≥ 5 倍
+- **所有数字** `font-variant-numeric: tabular-nums proportional-nums`
+- 字距:大字 -0.02em~-0.045em,小标 +0.15em~+0.3em
+
+### 布局(layout_hint 决定重力场)
+
+| layout_hint | 重力场 |
+|-------------|--------|
+| `single-focus` | 全重力汇聚中心/黄金分割点 |
+| `symmetric` | 左右等量对峙 |
+| `asymmetric` | 重力偏向一侧(6:4)|
+| `three-column` | 三个均衡重力池 |
+| `primary-secondary` | 贯穿式主区 + 轻量卫星 |
+| `hero-top` | 顶部横贯大坝 + 底部溪流 |
+| `mixed-grid` | 多个异构重力点交错 |
+| `l-shape` | L 形轨迹:主体→侧面→底部 |
+
+内容区:x=40, y=80, w=1200, h=580;卡片间距 16-20px;圆角 12-14px。
+**禁止所有卡片等宽等高排一行**(那是 Word 文档)。用主副节奏 / 递减 / 交错 / 孤岛+群落制造韵律。
+
+## 5. 图表规范(planning 中 chart.chart_type 必须实现)
+
+- 纯 HTML/CSS/SVG 实现,**无 JS 运行时**
+- 所有颜色用 CSS 变量
+- SVG 内**禁止 `<text>`**,所有标签/数字/图例用 HTML div 叠加
+- 环形图:SVG circle + `stroke-dasharray="弧长 间隔"` + `transform="rotate(-90 cx cy)"`,中心文字 HTML 叠加
+- 进度条:div 填充 `width:%`
+- 时间线:HTML flex 结构 + 真实 div 连线
+- 数字必须 tabular-nums
+
+## 6. 数据卡片铁律
+
+- 每个 `data` 卡片:核心数字 ≥64px + 单位/标签 + 补充解读 + 至少一个 CSS 可视化(进度条/环形/对比柱/sparkline)
+- `data_points` 数组逐项渲染,`interpretation` 作为解读文字
+
+## 7. 对账与打标(visual_qa 检查项)
+
+- **每个 planning card 必须有对应 HTML 渲染根节点**,根节点加 `data-card-id="<card_id>"`(如 c1, c2...)
+- 每个 chart 渲染节点加 `data-chart-type="<chart_type>"`
+- **所有纯装饰节点**显式打标 `data-decoration-layer="background|floating|page-accent"` + `aria-hidden="true"`,并按 `density_contract.decoration_budget` 控制数量
+- 五层景深:背景层 z=0 / 装饰底纹 z=1 / 内容卡 z=2 / 强调浮层 z=3 / 焦点 z=4,每页至少激活 3 层
+
+## 8. 跨页一致性
+
+- 全部 26 页共享同一风格基因(CSS 变量 + 字体系统)
+- 数据页/内容页视觉风格统一骨架,但每页构图不同
+- 章节色彩递进:Part 01 用 accent-1(暖橙 #FF8C42)、Part 02 用 accent-2(亮蓝 #5AB0FF)、Part 03 accent-1/2 交替、五维页用五维色(身#FF8C42 心#FF6B9D 智#6366F1 行#10B981 富#F59E0B)点缀
+- 密度交替:章节封面(低密度留白)与内容页(中高密度)形成节奏
+
+## 9. 输出要求
+
+- 每个 HTML 文件写入 `D:\workspace\cfc\ppt-output\slides\slide-{N}.html`(N 为页码,如 slide-1.html)
+- 文件开头必须有 `<!DOCTYPE html><html lang="zh-CN"><head><meta charset="UTF-8">`
+- 文件必须是**纯净 HTML**,禁止写入任何 Prompt 指令/思考过程/注释说明
+
+## 10. 生成后自检清单
+
+- [ ] body 1280×720 overflow:hidden,无 scale hack
+- [ ] 全部颜色走 CSS 变量
+- [ ] 无动画、无伪元素装饰、无 background-image 放图
+- [ ] SVG 内无 `<text>`
+- [ ] 每个 planning card 有 `data-card-id` 根节点
+- [ ] 装饰节点有 `data-decoration-layer` + aria-hidden
+- [ ] 每页 ≥2 种 card_style,accent/elevated 各 ≤1
+- [ ] 数字全部 tabular-nums
+- [ ] data 卡片有 ≥64px 超级数字 + 可视化
+- [ ] 图片用 `<img src="../images/slide_XX_Y.png">` 且文件存在
+- [ ] 无文字溢出画布(overflow:hidden + 合理字号)

+ 23 - 0
ppt-output/runtime/image_list.txt

@@ -0,0 +1,23 @@
+needed count: 22
+1 0 hero-background | 温暖家庭场景:父母与孩子一起在阳光下查看健康报告,科技蓝与暖橙色调
+3 0 hero-background | 家庭与数字健康服务的连接意象,暖橙主调
+4 0 inline-illustration | 碎片化数据拼图的意象
+5 0 inline-illustration | 家长在手机上管理家庭健康任务
+6 2 inline-illustration | AI 智能体服务架构意象:机器人+知识库
+8 0 hero-background | 国家政策与科技趋势交汇意象,蓝色调
+10 1 inline-illustration | 看不懂的厚厚专业报告,家长困惑
+11 0 hero-background | 万亿市场规模意象:上升曲线与广阔空间,蓝橙色调
+13 2 inline-illustration | 竞争格局中的差异化卡位意象
+14 2 inline-illustration | 三边市场平台连接意象
+15 0 hero-background | 五行五维能量体系意象:五行相生的环形能量,暖橙主调
+16 0 inline-illustration | 家庭健康数据碎片化痛点意象
+17 1 inline-illustration | 五维能量沙盘仪表盘意象
+18 1 inline-illustration | 家庭健康数据飞轮循环意象
+19 0 hero-background | 技术与数据壁垒意象:盾牌与数据流,科技蓝主调
+20 2 inline-illustration | 五行相生相克动态平衡意象
+21 0 inline-illustration | AI 健康顾问与家庭数据连接意象
+22 1 inline-illustration | 三方网络飞轮意象
+23 0 hero-background | 真实成果与成长数据意象:成果证书与上升曲线,暖橙蓝调
+24 1 inline-illustration | AI 从厚报告提炼关键指标意象
+25 0 inline-illustration | 权威学术机构背书意象
+26 2 hero-background | 温暖家庭剪影与上升光路,呼应封面,暖橙蓝调

+ 29 - 0
ppt-output/runtime/interview-qa.txt

@@ -0,0 +1,29 @@
+# Interview QA(用户原意保留)
+
+## 用户回答
+- 演示场景:提交评审材料(评委自阅为主,需信息完整、逻辑自洽、可脱离讲者独立理解)
+- 目标行动:促成投资/合作意向(侧重市场规模、商业模式与变现能力,同时证明真实落地)
+- 页数偏好:18 页以上(完整版,覆盖全部 8 大章节)
+- 视觉风格:科技蓝 + 暖橙家庭感(以项目五维色系为主:身橙/心粉/智蓝/行绿/富金)
+- 配图策略:每页都配图
+- 内容取材:仅用提供的文档(docs/AI创新项目大赛-项目介绍.md),不外部搜索
+- 人工确认:只看最终成品(全程自主)
+
+## Canonical 锚点段
+- scenario: submission_review(提交评审材料)
+- audience: competition_judges(大赛评委:技术专家+商业投资视角+行业专家)
+- target_action: foster_investment_cooperation(促成投资/合作意向)
+- expected_pages: 18+(完整版,20 页左右)
+- page_density: balanced(适中——自阅材料需信息完整)
+- style: tech_blue_warm_orange(科技蓝+暖橙家庭感,五维色系)
+- brand: 北京安盈创卓科技有限公司 / 浠艾福(Care Family)/ 五维色系 身#FF8C42 心#FF6B9D 智#6366F1 行#10B981 富#F59E0B
+- must_include: 五维能量体系、AI 健康教练+人机协同、五维相生溯源算法、真实案例(135页报告→3页方案)、双培强基工程、TAM/SAM/SOM、商业模式(四类收入+LTV/CAC)、12个月路线图、三年愿景
+- must_avoid: 过度技术细节(48控制器等点到为止)、夸大未实现功能、用户隐私(案例匿名)
+- language: zh(中文为主,专有名词中英混排)
+- imagery: generate_all(每页都配图)
+- material_strategy: local_only(仅用提供的文档)
+- subagent_model_strategy: inherit(继承主代理)
+- subagent_thinking_effort: high
+- manual_audit_mode: off(只看最终成品)
+- manual_audit_scope: none
+- manual_audit_assets: summary_only

+ 41 - 0
ppt-output/runtime/prompt-interview.md

@@ -0,0 +1,41 @@
+# 采访问卷(Structured UI)
+
+主题:浠艾福(Care Family)——AI 驱动的家庭主动健康管理平台(项目介绍 PPT)
+用户背景:基于 `docs/AI创新项目大赛-项目介绍.md`(378 行完整参赛材料)制作项目介绍演示文稿。材料覆盖:项目概述(五维能量体系/四端产品形态/系统规模/公司团队)、背景政策(健康中国+人工智能+)、市场容量(TAM/SAM/SOM/商业模式)、需求设计(五维方法论/业务闭环)、技术壁垒(五维相生溯源算法/AI 能力栈/六阶模型)、应用成效(真实案例/双培强基工程)、未来展望(12 个月路线图/三年愿景)、结语。
+
+---
+
+## 共享采访核心
+
+### A. 业务场景与传达目标
+
+- `presentation_scenario`:AI 创新项目大赛的参赛项目介绍。场景推断为:评审汇报 / 现场路演(评委 = 投资人 + 行业专家 + 高校学者)。也可能需要发给大赛组委会自阅。
+- `core_audience`:大赛评委。画像推断:技术专家(看 AI 技术壁垒与工程落地)、商业/投资视角(看市场规模与商业模式)、行业专家(看政策契合与社会价值)。
+- `target_action`:让评委在有限时间内理解项目价值、认可"AI 精准干预 + 主动健康"的差异化卡位,打出高分 / 进入下一轮。
+
+### B. 结构密度与生产管线
+
+- `expected_pages`:参赛介绍 PPT 常见 12-18 页(8 大章节内容需取舍)。
+- `page_density`:评审场景 = 适中偏密(信息量大但要能讲完)。
+- `material_strategy`:`local_only` 为主——材料已极完整(含数据、政策文件号、市场规模、算法细节);可少量 `research` 补充如"心理健康市场规模"等外部数据印证。
+- `must_include`:五维能量体系(身·智·富·行·心)、AI 健康教练 + 人机协同、五维相生溯源算法、真实案例(135 页报告→3 页方案)、双培强基工程背书、TAM/SAM/SOM、商业模式、12 个月路线图与三年愿景。
+- `must_avoid`:避免过度技术细节(48 控制器等工程数字点到为止)、避免夸大未实现功能、避免泄露用户隐私(案例已匿名)。
+
+### C. 视觉审美与资产策略
+
+- `visual_style`:科技感 + 家庭温暖感融合(AI 技术 + 健康家庭主题)。候选:暗黑科技 / 蓝白商务 / 清新自然。
+- `language_mode`:中文为主(专有名词可中英混排,如 Care Family)。
+- `imagery_strategy`:generate(封面 + 章节封面配图,约 6-8 张)或 decorate(纯 CSS 装饰,更快更稳)。
+- `brand_constraints`:项目五维色系——身 `#FF8C42`(土) · 心 `#FF6B9D`(火) · 智 `#6366F1`(金) · 行 `#10B981`(木) · 富 `#F59E0B`(水);公司名"北京安盈创卓科技有限公司";产品名"浠艾福(Care Family)"。
+
+### D. 构建环境与工程卡口
+
+- `success_criteria`:内容完整传达项目价值、视觉专业、可直接用于大赛提交/宣讲。
+- `subagent_model_strategy`:继承主代理。
+- `subagent_thinking_effort`:高(涉及大量数据策划与视觉设计)。
+
+### E. 人工审计与断点控制
+
+- `manual_audit_mode`:`milestone_only`(大纲 + 策划稿确认)。
+- `manual_audit_scope`:outline / page_planning。
+- `manual_audit_assets`:summary_only。

+ 20 - 0
ppt-output/runtime/requirements-interview.txt

@@ -0,0 +1,20 @@
+# Requirements(脱水参数组)
+
+- scenario: submission_review
+- audience: competition_judges
+- target_action: foster_investment_cooperation
+- expected_pages: 20
+- page_density: balanced
+- density_bias: balanced
+- style: tech_blue_warm_orange
+- brand: 北京安盈创卓科技有限公司 / 浠艾福(Care Family)/ 五维色系 身#FF8C42 心#FF6B9D 智#6366F1 行#10B981 富#F59E0B
+- must_include: 五维能量体系; AI 健康教练+人机协同; 五维相生溯源算法; 真实案例(135页报告→3页方案); 双培强基工程; TAM/SAM/SOM; 商业模式(四类收入+LTV/CAC 80-330倍); 12个月路线图; 三年愿景
+- must_avoid: 过度技术细节; 夸大未实现功能; 用户隐私
+- language: zh
+- imagery: generate_all
+- material_strategy: local_only
+- subagent_model_strategy: inherit
+- subagent_thinking_effort: high
+- manual_audit_mode: off
+- manual_audit_scope: none
+- manual_audit_assets: summary_only

+ 18 - 0
ppt-output/runtime/split_planning.py

@@ -0,0 +1,18 @@
+# -*- coding: utf-8 -*-
+"""拆分 planning.json 为每页一个 JSON(visual_qa 需要 --planning-dir 或单页 --planning)"""
+import json, os, sys
+sys.stdout.reconfigure(encoding='utf-8')
+
+d = json.load(open(r'D:\workspace\cfc\ppt-output\planning.json', encoding='utf-8'))
+pages = d['ppt_planning']['pages']
+
+out_dir = r'D:\workspace\cfc\ppt-output\planning'
+os.makedirs(out_dir, exist_ok=True)
+
+for i, p in enumerate(pages, 1):
+    fname = os.path.join(out_dir, f'planning{i}.json')
+    with open(fname, 'w', encoding='utf-8') as f:
+        json.dump(p, f, ensure_ascii=False, indent=2)
+    print(f'wrote {fname}')
+
+print(f'OK: {len(pages)} page planning files')

+ 17 - 0
ppt-output/runtime/view_planning.py

@@ -0,0 +1,17 @@
+# -*- coding: utf-8 -*-
+"""查看 planning.json 页面结构概览"""
+import json, sys
+sys.stdout.reconfigure(encoding='utf-8')
+
+d = json.load(open(r'D:\workspace\cfc\ppt-output\planning.json', encoding='utf-8'))
+pages = d['ppt_planning']['pages']
+print('TOTAL pages:', len(pages))
+for i, p in enumerate(pages):
+    cards = p.get('cards', [])
+    charts = [c.get('chart', {}).get('chart_type') for c in cards if c.get('chart')]
+    layout = p.get('layout_hint')
+    ptype = p.get('page_type')
+    dens = p.get('density_label')
+    title = p.get('title', '')
+    nro = p.get('narrative_role')
+    print(f"{i+1:2d} | {str(ptype):10s} | {str(layout):22s} | {str(dens):8s} | cards={len(cards)} | charts={charts} | nrole={nro} | {title[:30]}")

+ 216 - 0
ppt-output/slides/slide-1.html

@@ -0,0 +1,216 @@
+<!DOCTYPE html>
+<html lang="zh-CN">
+<head>
+  <meta charset="UTF-8">
+  <style>
+    :root {
+      --bg-primary: #0A1A2F;
+      --bg-secondary: #10264A;
+      --card-bg-from: #17294A;
+      --card-bg-to: #101F3B;
+      --card-border: rgba(255,255,255,0.07);
+      --card-radius: 14px;
+      --text-primary: #F5F8FC;
+      --text-secondary: rgba(235,243,255,0.72);
+      --accent-1: #FF8C42;
+      --accent-2: #5AB0FF;
+      --accent-3: #FF6B9D;
+      --accent-4: #10B981;
+      --five-earth: #FF8C42;
+      --five-fire: #FF6B9D;
+      --five-metal: #6366F1;
+      --five-wood: #10B981;
+      --five-water: #F59E0B;
+      --grid-dot-color: #FFFFFF;
+      --grid-dot-opacity: 0.04;
+      --grid-size: 44px;
+      --font-family: 'PingFang SC', 'Microsoft YaHei', 'Noto Sans SC', system-ui, -apple-system, sans-serif;
+      --display-font: 'Inter', 'PingFang SC', 'Microsoft YaHei', system-ui, sans-serif;
+    }
+
+    body {
+      width: 1280px;
+      height: 720px;
+      overflow: hidden;
+      background: var(--bg-primary);
+      font-family: var(--font-family);
+      position: relative;
+      margin: 0;
+      padding: 0;
+      color: var(--text-primary);
+    }
+
+    /* Background Layer */
+    .hero-bg {
+      position: absolute;
+      top: 0;
+      left: 0;
+      width: 100%;
+      height: 100%;
+      z-index: 0;
+    }
+    .hero-bg img {
+      width: 100%;
+      height: 100%;
+      object-fit: cover;
+    }
+    .overlay {
+      position: absolute;
+      top: 0;
+      left: 0;
+      width: 100%;
+      height: 100%;
+      background: linear-gradient(135deg, var(--bg-primary) 30%, transparent 100%);
+      z-index: 1;
+    }
+
+    /* Brand Mark */
+    .brand-mark {
+      position: absolute;
+      top: 40px;
+      left: 40px;
+      z-index: 10;
+      display: flex;
+      align-items: center;
+      gap: 12px;
+    }
+    .brand-logo {
+      width: 32px;
+      height: 32px;
+      background: var(--accent-1);
+      border-radius: 8px;
+    }
+    .brand-name {
+      font-size: 20px;
+      font-weight: 700;
+      letter-spacing: 1px;
+      color: var(--text-primary);
+    }
+
+    /* Main Content */
+    .content-area {
+      position: absolute;
+      top: 0;
+      left: 0;
+      width: 1280px;
+      height: 720px;
+      display: flex;
+      flex-direction: column;
+      justify-content: center;
+      padding-left: 80px;
+      box-sizing: border-box;
+      z-index: 5;
+    }
+
+    .h0-title {
+      font-family: var(--display-font);
+      font-size: 96px;
+      font-weight: 900;
+      line-height: 1.1;
+      margin: 0 0 40px 0;
+      letter-spacing: -0.03em;
+      color: var(--text-primary);
+      max-width: 900px;
+    }
+
+    .card-elevated {
+      data-card-id="c1";
+      background: linear-gradient(135deg, var(--card-bg-from), var(--card-bg-to));
+      border: 1px solid var(--card-border);
+      border-radius: var(--card-radius);
+      padding: 32px 40px;
+      box-shadow: 0 20px 40px rgba(0,0,0,0.4), 0 4px 12px rgba(0,0,0,0.2);
+      max-width: 700px;
+      position: relative;
+    }
+
+    .card-headline {
+      font-size: 28px;
+      font-weight: 700;
+      color: var(--accent-1);
+      margin-bottom: 16px;
+      display: block;
+    }
+
+    .card-body {
+      font-size: 20px;
+      line-height: 1.6;
+      color: var(--text-secondary);
+      margin: 0;
+    }
+
+    .card-body p {
+      margin: 0 0 12px 0;
+    }
+
+    /* Five Dim Badges */
+    .dim-badges {
+      display: flex;
+      gap: 12px;
+      margin-top: 40px;
+    }
+    .badge {
+      padding: 6px 14px;
+      border-radius: 20px;
+      font-size: 14px;
+      font-weight: 600;
+      color: #FFF;
+      display: flex;
+      align-items: center;
+      gap: 6px;
+      border: 1px solid rgba(255,255,255,0.2);
+    }
+    .badge-earth { background: var(--five-earth); }
+    .badge-fire { background: var(--five-fire); }
+    .badge-metal { background: var(--five-metal); }
+    .badge-wood { background: var(--five-wood); }
+    .badge-water { background: var(--five-water); }
+
+    /* Decoration */
+    .grid-dots {
+      position: absolute;
+      top: 0;
+      left: 0;
+      width: 100%;
+      height: 100%;
+      background-image: radial-gradient(var(--grid-dot-color) 1px, transparent 1px);
+      background-size: var(--grid-size) var(--grid-size);
+      opacity: var(--grid-dot-opacity);
+      z-index: 1;
+      pointer-events: none;
+    }
+  </style>
+</head>
+<body>
+  <div class="hero-bg">
+    <img src="../images/slide_01_0.png" alt="Family Scene">
+    <div class="overlay"></div>
+  </div>
+  <div class="grid-dots" data-decoration-layer="background" aria-hidden="true"></div>
+
+  <div class="brand-mark">
+    <div class="brand-logo"></div>
+    <span class="brand-name">浠艾福 Care Family</span>
+  </div>
+
+  <div class="content-area">
+    <h1 class="h0-title">给全家的一站式<br>幸福提案</h1>
+    
+    <div class="card-elevated" data-card-id="c1">
+      <span class="card-headline">AI 驱动的家庭主动健康管理平台</span>
+      <div class="card-body">
+        <p>以 AI 实现精准干预、主动健康,守护中国家庭的"身 · 智 · 富 · 行 · 心"五维成长</p>
+        <p>让每一个家庭都拥有"看得懂报告、跟得上方案、做得到行动"的专属 AI 健康教练</p>
+      </div>
+    </div>
+
+    <div class="dim-badges">
+      <div class="badge badge-earth">身 · 主动健康</div>
+      <div class="badge badge-metal">智 · 赋能未来</div>
+      <div class="badge badge-water">富 · 利他创富</div>
+      <div class="badge badge-wood">行 · 人际和谐</div>
+      <div class="badge badge-fire">心 · 大爱相随</div>
+    </div>
+  </div>
+</body>
+</html>

+ 318 - 0
ppt-output/slides/slide-10.html

@@ -0,0 +1,318 @@
+<!DOCTYPE html>
+<html lang="zh-CN">
+<head>
+  <meta charset="UTF-8">
+  <style>
+    :root {
+      --bg-primary: #0A1A2F;
+      --bg-secondary: #10264A;
+      --card-bg-from: #17294A;
+      --card-bg-to: #101F3B;
+      --card-border: rgba(255,255,255,0.07);
+      --card-radius: 14px;
+      --text-primary: #F5F8FC;
+      --text-secondary: rgba(235,243,255,0.72);
+      --accent-1: #FF8C42;
+      --accent-2: #5AB0FF;
+      --accent-3: #FF6B9D;
+      --accent-4: #10B981;
+      --grid-dot-color: #FFFFFF;
+      --grid-dot-opacity: 0.04;
+      --grid-size: 44px;
+      --font-family: 'PingFang SC', 'Microsoft YaHei', 'Noto Sans SC', system-ui, -apple-system, sans-serif;
+      --display-font: 'Inter', 'PingFang SC', 'Microsoft YaHei', system-ui, sans-serif;
+    }
+
+    body {
+      width: 1280px;
+      height: 720px;
+      overflow: hidden;
+      background: var(--bg-primary);
+      font-family: var(--font-family);
+      position: relative;
+      margin: 0;
+      color: var(--text-primary);
+    }
+
+    .grid-layer {
+      position: absolute;
+      inset: 0;
+      background-image: radial-gradient(var(--grid-dot-color) 1px, transparent 1px);
+      background-size: var(--grid-size) var(--grid-size);
+      opacity: var(--grid-dot-opacity);
+      z-index: 0;
+      pointer-events: none;
+    }
+
+    .slide-header {
+      position: absolute;
+      top: 20px;
+      left: 40px;
+      right: 40px;
+      height: 50px;
+      display: flex;
+      align-items: baseline;
+      gap: 16px;
+      z-index: 10;
+    }
+
+    .overline {
+      font-size: 10px;
+      font-weight: 700;
+      letter-spacing: 2px;
+      color: var(--accent-2);
+      opacity: 0.8;
+      text-transform: uppercase;
+      white-space: nowrap;
+    }
+
+    .page-title {
+      font-size: 26px;
+      font-weight: 700;
+      color: var(--text-primary);
+      line-height: 1.2;
+      margin: 0;
+    }
+
+    .slide-footer {
+      position: absolute;
+      bottom: 12px;
+      left: 40px;
+      right: 40px;
+      height: 20px;
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      z-index: 10;
+    }
+
+    .footer-section {
+      font-size: 10px;
+      color: var(--text-secondary);
+      opacity: 0.5;
+      letter-spacing: 1px;
+    }
+
+    .footer-page {
+      font-size: 10px;
+      color: var(--text-secondary);
+      opacity: 0.5;
+    }
+
+    .content-container {
+      position: absolute;
+      top: 80px;
+      left: 40px;
+      right: 40px;
+      bottom: 40px;
+      display: grid;
+      grid-template-columns: 7fr 5fr;
+      gap: 24px;
+      z-index: 2;
+    }
+
+    /* Card Styles */
+    .card {
+      border-radius: var(--card-radius);
+      position: relative;
+      overflow: hidden;
+    }
+
+    .card-accent {
+      background: linear-gradient(135deg, var(--accent-1), #E67E22);
+      color: white;
+      border: 1px solid rgba(255,255,255,0.2);
+    }
+
+    .card-elevated {
+      background: linear-gradient(135deg, var(--card-bg-from), var(--card-bg-to));
+      border: 1px solid var(--card-border);
+      box-shadow: 0 12px 32px rgba(0,0,0,0.3);
+    }
+
+    .card-filled {
+      background: var(--bg-secondary);
+      border: 1px solid var(--card-border);
+    }
+
+    .card-headline {
+      font-size: 20px;
+      font-weight: 700;
+      margin: 0;
+      padding: 24px 24px 16px 24px;
+      display: flex;
+      align-items: center;
+      gap: 10px;
+    }
+
+    .card-accent .card-headline { color: white; }
+    .card-elevated .card-headline, .card-filled .card-headline { color: var(--text-primary); }
+
+    /* Metric Row Chart */
+    .metric-row-container {
+      padding: 0 24px 24px 24px;
+      display: grid;
+      grid-template-columns: 1fr 1fr;
+      gap: 20px;
+    }
+
+    .metric-item {
+      background: rgba(255,255,255,0.1);
+      padding: 16px;
+      border-radius: 10px;
+      display: flex;
+      flex-direction: column;
+      gap: 4px;
+    }
+
+    .metric-val {
+      font-family: var(--display-font);
+      font-size: 36px;
+      font-weight: 900;
+      color: white;
+      font-variant-numeric: tabular-nums;
+    }
+
+    .metric-label {
+      font-size: 12px;
+      color: rgba(255,255,255,0.8);
+      font-weight: 600;
+    }
+
+    .metric-interp {
+      font-size: 10px;
+      color: rgba(255,255,255,0.6);
+      margin-top: 4px;
+    }
+
+    /* List Card */
+    .list-container {
+      padding: 0 24px 24px 24px;
+    }
+
+    .list-item {
+      display: flex;
+      gap: 12px;
+      margin-bottom: 16px;
+      align-items: flex-start;
+    }
+
+    .list-marker {
+      width: 6px;
+      height: 6px;
+      background: var(--accent-1);
+      border-radius: 1px;
+      margin-top: 8px;
+      flex-shrink: 0;
+    }
+
+    .list-text {
+      font-size: 13.5px;
+      line-height: 1.8;
+      color: var(--text-secondary);
+    }
+
+    .inline-img-box {
+      margin-top: 16px;
+      border-radius: 8px;
+      overflow: hidden;
+      border: 1px solid var(--card-border);
+    }
+
+    .inline-img {
+      width: 100%;
+      display: block;
+      opacity: 0.9;
+    }
+
+    .secondary-column {
+      display: flex;
+      flex-direction: column;
+      gap: 24px;
+    }
+  </style>
+</head>
+<body>
+  <div class="grid-layer" data-decoration-layer="background" aria-hidden="true"></div>
+
+  <header class="slide-header">
+    <span class="overline">PART 02 &mdash; 背景政策</span>
+    <h1 class="page-title">社会痛点与技术窗口期</h1>
+  </header>
+
+  <div class="content-container">
+    <div class="card card-accent" data-card-id="c1">
+      <h2 class="card-headline">问题高发 · 供给严重不足</h2>
+      <div class="metric-row-container" data-chart-type="metric_row">
+        <div class="metric-item">
+          <span class="metric-val">24.6%</span>
+          <span class="metric-label">初中生抑郁检出率</span>
+          <span class="metric-interp">教育部 2025 监测数据</span>
+        </div>
+        <div class="metric-item">
+          <span class="metric-val">29.1%</span>
+          <span class="metric-label">高中生抑郁检出率</span>
+          <span class="metric-interp">问题高发,触目惊心</span>
+        </div>
+        <div class="metric-item">
+          <span class="metric-val">20 万+</span>
+          <span class="metric-label">专业人才缺口</span>
+          <span class="metric-interp">持证家庭治疗师不足 8000 人</span>
+        </div>
+        <div class="metric-item">
+          <span class="metric-val">78%</span>
+          <span class="metric-label">家长认为孩子缺乏自驱力</span>
+          <span class="metric-interp">中国教育科学研究院</span>
+        </div>
+      </div>
+      <div style="padding: 0 24px 24px 24px;">
+        <p style="font-size: 13.5px; line-height: 1.6; color: rgba(255,255,255,0.9); margin: 0;">
+          教育部 2025 监测:初中生抑郁检出率 24.6%,高中生 29.1%;63% 家庭求助无门转向市场化服务。
+        </p>
+      </div>
+    </div>
+
+    <div class="secondary-column">
+      <div class="card card-elevated" data-card-id="c2">
+        <h2 class="card-headline">行业困境:数据有,洞察无</h2>
+        <div class="list-container">
+          <div class="list-item">
+            <div class="list-marker"></div>
+            <div class="list-text">一份 135 页的肠道菌群检测报告、数百项指标的测评报告,家长根本看不懂</div>
+          </div>
+          <div class="list-item">
+            <div class="list-marker"></div>
+            <div class="list-text">"数据有,洞察无;报告有,方案无"是行业普遍困境</div>
+          </div>
+          <div class="inline-img-box">
+            <img src="../images/slide_10_1.png" class="inline-img" alt="Industry Pain Point">
+          </div>
+        </div>
+      </div>
+
+      <div class="card card-filled" data-card-id="c3">
+        <h2 class="card-headline">技术就绪:大模型让精准干预成为可能</h2>
+        <div class="list-container">
+          <div class="list-item">
+            <div class="list-marker"></div>
+            <div class="list-text">多模态大模型 + 数字疗法成为学界与产业共识方向</div>
+          </div>
+          <div class="list-item">
+            <div class="list-marker"></div>
+            <div class="list-text">大模型 + RAG + 智能体(Agent)成熟:读得懂报告、说得清原理、给得出方案</div>
+          </div>
+          <div class="list-item">
+            <div class="list-marker"></div>
+            <div class="list-text">神经科学驱动的"主动健康六阶模型"提供行为科学标尺</div>
+          </div>
+        </div>
+      </div>
+    </div>
+  </div>
+
+  <footer class="slide-footer">
+    <span class="footer-section">PART 02 — 背景政策</span>
+    <span class="footer-page">10 / 28</span>
+  </footer>
+</body>
+</html>

+ 202 - 0
ppt-output/slides/slide-11.html

@@ -0,0 +1,202 @@
+<!DOCTYPE html>
+<html lang="zh-CN">
+<head>
+  <meta charset="UTF-8">
+  <style>
+    :root {
+      --bg-primary: #0A1A2F;
+      --bg-secondary: #10264A;
+      --card-bg-from: #17294A;
+      --card-bg-to: #101F3B;
+      --card-border: rgba(255,255,255,0.07);
+      --card-radius: 14px;
+      --text-primary: #F5F8FC;
+      --text-secondary: rgba(235,243,255,0.72);
+      --accent-1: #FF8C42;
+      --accent-2: #5AB0FF;
+      --accent-3: #FF6B9D;
+      --accent-4: #10B981;
+      --five-earth: #FF8C42;
+      --five-fire: #FF6B9D;
+      --five-metal: #6366F1;
+      --five-wood: #10B981;
+      --five-water: #F59E0B;
+      --grid-dot-color: #FFFFFF;
+      --grid-dot-opacity: 0.04;
+      --grid-size: 44px;
+      --font-family: 'PingFang SC', 'Microsoft YaHei', 'Noto Sans SC', system-ui, -apple-system, sans-serif;
+      --display-font: 'Inter', 'PingFang SC', 'Microsoft YaHei', system-ui, sans-serif;
+    }
+
+    body {
+      width: 1280px;
+      height: 720px;
+      overflow: hidden;
+      background: var(--bg-primary);
+      font-family: var(--font-family);
+      position: relative;
+      margin: 0;
+      color: var(--text-primary);
+    }
+
+    /* Background Decoration */
+    .grid-overlay {
+      position: absolute;
+      inset: 0;
+      background-image: radial-gradient(var(--grid-dot-color) 1px, transparent 1px);
+      background-size: var(--grid-size) var(--grid-size);
+      opacity: var(--grid-dot-opacity);
+      z-index: 0;
+      pointer-events: none;
+    }
+
+    .hero-image-container {
+      position: absolute;
+      right: 0;
+      top: 0;
+      width: 60%;
+      height: 100%;
+      z-index: 1;
+      overflow: hidden;
+    }
+
+    .hero-image {
+      width: 100%;
+      height: 100%;
+      object-fit: cover;
+      opacity: 0.7;
+    }
+
+    .image-mask {
+      position: absolute;
+      inset: 0;
+      background: linear-gradient(to right, var(--bg-primary) 0%, transparent 30%);
+      z-index: 2;
+    }
+
+    /* Content */
+    .content-area {
+      position: absolute;
+      left: 40px;
+      top: 0;
+      bottom: 0;
+      width: 600px;
+      display: flex;
+      flex-direction: column;
+      justify-content: center;
+      z-index: 10;
+    }
+
+    .section-title-group {
+      margin-bottom: 40px;
+    }
+
+    .part-label {
+      font-size: 14px;
+      font-weight: 700;
+      color: var(--accent-1);
+      letter-spacing: 4px;
+      text-transform: uppercase;
+      margin-bottom: 12px;
+      display: block;
+    }
+
+    .main-title {
+      font-family: var(--display-font);
+      font-size: 72px;
+      font-weight: 900;
+      line-height: 1.1;
+      margin: 0;
+      letter-spacing: -0.03em;
+    }
+
+    .title-underline {
+      width: 80px;
+      height: 6px;
+      background: var(--accent-1);
+      margin-top: 24px;
+      border-radius: 3px;
+    }
+
+    .anchor-card {
+      margin-top: 48px;
+      padding: 32px;
+      background: linear-gradient(135deg, var(--card-bg-from), var(--card-bg-to));
+      border: 1px solid var(--card-border);
+      border-radius: var(--card-radius);
+      box-shadow: 0 20px 40px rgba(0,0,0,0.4);
+      position: relative;
+      max-width: 520px;
+    }
+
+    .anchor-headline {
+      font-family: var(--display-font);
+      font-size: 28px;
+      font-weight: 800;
+      color: var(--text-primary);
+      margin-bottom: 16px;
+      line-height: 1.3;
+    }
+
+    .anchor-body {
+      font-size: 18px;
+      line-height: 1.6;
+      color: var(--text-secondary);
+    }
+
+    /* Footer */
+    .slide-footer {
+      position: absolute;
+      bottom: 24px;
+      left: 40px;
+      right: 40px;
+      height: 20px;
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      z-index: 10;
+    }
+
+    .footer-section {
+      font-size: 10px;
+      color: var(--text-secondary);
+      opacity: 0.5;
+      letter-spacing: 1px;
+    }
+
+    .footer-page {
+      font-size: 10px;
+      color: var(--text-secondary);
+      opacity: 0.5;
+    }
+  </style>
+</head>
+<body>
+  <div class="grid-overlay" data-decoration-layer="background" aria-hidden="true"></div>
+  
+  <div class="hero-image-container" data-decoration-layer="background" aria-hidden="true">
+    <img src="../images/slide_11_0.png" class="hero-image" alt="Market Scale">
+    <div class="image-mask"></div>
+  </div>
+
+  <div class="content-area">
+    <div class="section-title-group">
+      <span class="part-label">PART 03</span>
+      <h1 class="main-title">市场容量</h1>
+      <div class="title-underline"></div>
+    </div>
+
+    <div class="anchor-card" data-card-id="c1">
+      <div class="anchor-headline">4.5 万亿赛道 · 渗透率不足 3% · 97% 待开拓</div>
+      <div class="anchor-body">
+        中国家庭教育消费市场 4.5 万亿元,家庭教育服务平台渗透率不足 3%——高增长细分 + 极低渗透率 = 巨大的平台化机会
+      </div>
+    </div>
+  </div>
+
+  <footer class="slide-footer">
+    <span class="footer-section">PART 03 &mdash; 市场容量</span>
+    <span class="footer-page">11 / 28</span>
+  </footer>
+</body>
+</html>

+ 366 - 0
ppt-output/slides/slide-12.html

@@ -0,0 +1,366 @@
+<!DOCTYPE html>
+<html lang="zh-CN">
+<head>
+  <meta charset="UTF-8">
+  <style>
+    :root {
+      --bg-primary: #0A1A2F;
+      --bg-secondary: #10264A;
+      --card-bg-from: #17294A;
+      --card-bg-to: #101F3B;
+      --card-border: rgba(255,255,255,0.07);
+      --card-radius: 14px;
+      --text-primary: #F5F8FC;
+      --text-secondary: rgba(235,243,255,0.72);
+      --accent-1: #FF8C42;
+      --accent-2: #5AB0FF;
+      --accent-3: #FF6B9D;
+      --accent-4: #10B981;
+      --five-earth: #FF8C42;
+      --five-fire: #FF6B9D;
+      --five-metal: #6366F1;
+      --five-wood: #10B981;
+      --five-water: #F59E0B;
+      --grid-dot-color: #FFFFFF;
+      --grid-dot-opacity: 0.04;
+      --grid-size: 44px;
+      --font-family: 'PingFang SC', 'Microsoft YaHei', 'Noto Sans SC', system-ui, -apple-system, sans-serif;
+      --display-font: 'Inter', 'PingFang SC', 'Microsoft YaHei', system-ui, sans-serif;
+    }
+
+    body {
+      width: 1280px;
+      height: 720px;
+      overflow: hidden;
+      background: var(--bg-primary);
+      font-family: var(--font-family);
+      position: relative;
+      margin: 0;
+      color: var(--text-primary);
+    }
+
+    .grid-overlay {
+      position: absolute;
+      inset: 0;
+      background-image: radial-gradient(var(--grid-dot-color) 1px, transparent 1px);
+      background-size: var(--grid-size) var(--grid-size);
+      opacity: var(--grid-dot-opacity);
+      z-index: 0;
+      pointer-events: none;
+    }
+
+    /* Header */
+    .slide-header {
+      position: absolute;
+      top: 20px;
+      left: 40px;
+      right: 40px;
+      height: 50px;
+      display: flex;
+      align-items: baseline;
+      gap: 16px;
+      z-index: 10;
+    }
+    .overline {
+      font-size: 10px;
+      font-weight: 700;
+      letter-spacing: 2px;
+      color: var(--accent-1);
+      opacity: 0.8;
+      text-transform: uppercase;
+      white-space: nowrap;
+    }
+    .page-title {
+      font-size: 26px;
+      font-weight: 700;
+      color: var(--text-primary);
+      line-height: 1.2;
+      margin: 0;
+    }
+
+    /* Layout: Hero-Top */
+    .content-container {
+      position: absolute;
+      top: 80px;
+      left: 40px;
+      right: 40px;
+      bottom: 40px;
+      display: flex;
+      flex-direction: column;
+      gap: 20px;
+      z-index: 2;
+    }
+
+    .hero-band {
+      width: 100%;
+      height: 220px;
+      background: linear-gradient(135deg, var(--accent-1), var(--accent-1) 0%, var(--bg-secondary) 100%);
+      border-radius: var(--card-radius);
+      padding: 32px;
+      box-sizing: border-box;
+      display: flex;
+      align-items: center;
+      justify-content: space-between;
+      position: relative;
+      overflow: hidden;
+      box-shadow: 0 10px 30px rgba(255, 140, 66, 0.2);
+    }
+
+    .hero-band-deco {
+      position: absolute;
+      right: -50px;
+      bottom: -50px;
+      width: 200px;
+      height: 200px;
+      background: rgba(255,255,255,0.1);
+      border-radius: 50%;
+      pointer-events: none;
+    }
+
+    .hero-text {
+      flex: 1;
+      z-index: 2;
+    }
+    .hero-headline {
+      font-size: 24px;
+      font-weight: 700;
+      color: #FFF;
+      margin-bottom: 8px;
+    }
+    .hero-body {
+      font-size: 16px;
+      color: rgba(255,255,255,0.9);
+      line-height: 1.5;
+    }
+
+    .hero-kpi {
+      text-align: right;
+      z-index: 2;
+    }
+    .data-hero {
+      font-family: var(--display-font);
+      font-size: 96px;
+      font-weight: 900;
+      color: #FFF;
+      line-height: 1;
+      font-variant-numeric: tabular-nums proportional-nums;
+      letter-spacing: -0.04em;
+    }
+    .data-label {
+      font-size: 14px;
+      color: rgba(255,255,255,0.8);
+      font-weight: 600;
+      margin-top: 4px;
+    }
+
+    .sub-grid {
+      display: grid;
+      grid-template-columns: repeat(3, 1fr);
+      gap: 20px;
+      flex: 1;
+    }
+
+    /* Card Styles */
+    .card {
+      border-radius: var(--card-radius);
+      padding: 24px;
+      box-sizing: border-box;
+      position: relative;
+      display: flex;
+      flex-direction: column;
+    }
+
+    .card-headline {
+      font-size: 18px;
+      font-weight: 700;
+      color: var(--text-primary);
+      margin-bottom: 16px;
+      display: flex;
+      align-items: center;
+      gap: 8px;
+    }
+
+    .card-body {
+      font-size: 13.5px;
+      line-height: 1.8;
+      color: var(--text-secondary);
+      margin-bottom: 20px;
+      flex: 1;
+    }
+
+    .filled {
+      background: linear-gradient(135deg, var(--card-bg-from), var(--card-bg-to));
+      border: 1px solid var(--card-border);
+    }
+    .elevated {
+      background: var(--card-bg-from);
+      border: 1px solid var(--card-border);
+      box-shadow: 0 12px 24px rgba(0,0,0,0.3);
+      transform: translateY(-4px);
+    }
+    .glass {
+      background: rgba(255,255,255,0.06);
+      backdrop-filter: blur(10px);
+      border: 1px solid rgba(255,255,255,0.05);
+    }
+
+    /* Charts */
+    .chart-container {
+      margin-top: auto;
+      padding-top: 16px;
+      border-top: 1px solid var(--card-border);
+    }
+
+    .comparison-bar-group {
+      display: flex;
+      flex-direction: column;
+      gap: 12px;
+    }
+    .bar-row {
+      display: flex;
+      flex-direction: column;
+      gap: 4px;
+    }
+    .bar-label-row {
+      display: flex;
+      justify-content: space-between;
+      font-size: 11px;
+      color: var(--text-secondary);
+    }
+    .bar-track {
+      height: 8px;
+      background: rgba(255,255,255,0.1);
+      border-radius: 4px;
+      overflow: hidden;
+    }
+    .bar-fill {
+      height: 100%;
+      background: var(--accent-2);
+      border-radius: 4px;
+    }
+    .bar-fill.accent-1 { background: var(--accent-1); }
+
+    .kpi-mini {
+      display: flex;
+      justify-content: space-between;
+      align-items: baseline;
+      gap: 8px;
+    }
+    .kpi-val {
+      font-family: var(--display-font);
+      font-size: 32px;
+      font-weight: 800;
+      color: var(--accent-1);
+      font-variant-numeric: tabular-nums;
+    }
+    .kpi-lbl {
+      font-size: 11px;
+      color: var(--text-secondary);
+    }
+
+    /* Footer */
+    .slide-footer {
+      position: absolute;
+      bottom: 12px;
+      left: 40px;
+      right: 40px;
+      height: 20px;
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      z-index: 10;
+    }
+    .footer-section {
+      font-size: 10px;
+      color: var(--text-secondary);
+      opacity: 0.5;
+      letter-spacing: 1px;
+    }
+    .footer-page {
+      font-size: 10px;
+      color: var(--text-secondary);
+      opacity: 0.5;
+    }
+  </style>
+</head>
+<body>
+  <div class="grid-overlay" data-decoration-layer="background" aria-hidden="true"></div>
+
+  <header class="slide-header">
+    <span class="overline">PART 03 &mdash; 市场容量</span>
+    <h1 class="page-title">市场空间:TAM / SAM / SOM</h1>
+  </header>
+
+  <div class="content-container">
+    <!-- TAM Hero Band -->
+    <div class="hero-band" data-card-id="c1">
+      <div class="hero-band-deco" data-decoration-layer="floating" aria-hidden="true"></div>
+      <div class="hero-text">
+        <div class="hero-headline">TAM · 中国家庭教育消费市场</div>
+        <div class="hero-body">2025 年规模 4.5 万亿元(艾瑞咨询)</div>
+      </div>
+      <div class="hero-kpi">
+        <div class="data-hero">4.5万亿</div>
+        <div class="data-label">TAM 家庭教育消费市场</div>
+      </div>
+    </div>
+
+    <div class="sub-grid">
+      <!-- SAM Card -->
+      <div class="card elevated" data-card-id="c2">
+        <div class="card-headline">SAM · 细分健康管理市场</div>
+        <div class="card-body">
+          儿童健康管理(体检/营养/心理/运动)5000 亿元+<br>
+          家庭教育服务 217 亿→500 亿 (CAGR 19.3%)<br>
+          心理健康教育 286.3 亿→810 亿 (CAGR 23.1%)
+        </div>
+        <div class="chart-container" data-chart-type="comparison_bar">
+          <div class="comparison-bar-group">
+            <div class="bar-row">
+              <div class="bar-label-row"><span>家庭教育服务 CAGR</span><span class="kpi-val" style="font-size: 18px;">19.3%</span></div>
+              <div class="bar-track"><div class="bar-fill" style="width: 60%;"></div></div>
+            </div>
+            <div class="bar-row">
+              <div class="bar-label-row"><span>泛心理服务 CAGR</span><span class="kpi-val" style="font-size: 18px; color: var(--accent-2);">27%</span></div>
+              <div class="bar-track"><div class="bar-fill accent-1" style="width: 85%;"></div></div>
+            </div>
+          </div>
+        </div>
+      </div>
+
+      <!-- SOM Card -->
+      <div class="card filled" data-card-id="c3">
+        <div class="card-headline">SOM · 平台渗透率</div>
+        <div class="card-body">
+          家庭教育服务平台渗透率不足 3%,97% 待开拓
+        </div>
+        <div class="chart-container" data-chart-type="kpi">
+          <div class="kpi-mini">
+            <div>
+              <div class="kpi-val" style="font-size: 48px;">&lt; 3%</div>
+              <div class="kpi-lbl">平台渗透率</div>
+            </div>
+            <div style="text-align: right;">
+              <div class="kpi-val" style="font-size: 48px; color: var(--text-primary);">97%</div>
+              <div class="kpi-lbl">待开拓空间</div>
+            </div>
+          </div>
+        </div>
+      </div>
+
+      <!-- Conclusion Card -->
+      <div class="card glass" data-card-id="c4">
+        <div class="card-headline">市场结论</div>
+        <div class="card-body" style="font-size: 15px; font-style: italic; color: var(--text-primary);">
+          "万亿赛道中的高增长细分,供给高度分散,一体化平台机会窗口打开"
+        </div>
+      </div>
+    </div>
+  </div>
+
+  <footer class="slide-footer">
+    <span class="footer-section">PART 03 &mdash; 市场容量</span>
+    <span class="footer-page">12 / 28</span>
+  </footer>
+</body>
+</html>

+ 524 - 0
ppt-output/slides/slide-13.html

@@ -0,0 +1,524 @@
+<!DOCTYPE html>
+<html lang="zh-CN">
+<head>
+  <meta charset="UTF-8">
+  <style>
+    :root {
+      --bg-primary: #0A1A2F;
+      --bg-secondary: #10264A;
+      --card-bg-from: #17294A;
+      --card-bg-to: #101F3B;
+      --card-border: rgba(255,255,255,0.07);
+      --card-radius: 14px;
+      --text-primary: #F5F8FC;
+      --text-secondary: rgba(235,243,255,0.72);
+      --accent-1: #FF8C42;
+      --accent-2: #5AB0FF;
+      --accent-3: #FF6B9D;
+      --accent-4: #10B981;
+      --five-earth: #FF8C42;
+      --five-fire: #FF6B9D;
+      --five-metal: #6366F1;
+      --five-wood: #10B981;
+      --five-water: #F59E0B;
+      --grid-dot-color: #FFFFFF;
+      --grid-dot-opacity: 0.04;
+      --grid-size: 44px;
+      --font-family: 'PingFang SC', 'Microsoft YaHei', 'Noto Sans SC', system-ui, -apple-system, sans-serif;
+      --display-font: 'Inter', 'PingFang SC', 'Microsoft YaHei', system-ui, sans-serif;
+    }
+
+    body {
+      width: 1280px;
+      height: 720px;
+      overflow: hidden;
+      background: var(--bg-primary);
+      font-family: var(--font-family);
+      position: relative;
+      margin: 0;
+      color: var(--text-primary);
+    }
+
+    .grid-overlay {
+      position: absolute;
+      inset: 0;
+      background-image: radial-gradient(var(--grid-dot-color) 1px, transparent 1px);
+      background-size: var(--grid-size) var(--grid-size);
+      opacity: var(--grid-dot-opacity);
+      z-index: 0;
+      pointer-events: none;
+    }
+
+    /* Header */
+    .slide-header {
+      position: absolute;
+      top: 20px;
+      left: 40px;
+      right: 40px;
+      height: 50px;
+      display: flex;
+      align-items: baseline;
+      gap: 16px;
+      z-index: 10;
+    }
+    .overline {
+      font-size: 10px;
+      font-weight: 700;
+      letter-spacing: 2px;
+      color: var(--accent-1);
+      opacity: 0.8;
+      text-transform: uppercase;
+      white-space: nowrap;
+    }
+    .page-title {
+      font-size: 26px;
+      font-weight: 700;
+      color: var(--text-primary);
+      line-height: 1.2;
+      margin: 0;
+    }
+
+    /* Layout: Two-column + bottom strength strip */
+    .content-container {
+      position: absolute;
+      top: 80px;
+      left: 40px;
+      right: 40px;
+      bottom: 40px;
+      display: grid;
+      grid-template-columns: 1.1fr 1.6fr;
+      grid-template-rows: 1fr auto;
+      gap: 16px;
+      z-index: 2;
+    }
+
+    /* Card Styles */
+    .card {
+      border-radius: var(--card-radius);
+      padding: 22px 24px;
+      box-sizing: border-box;
+      position: relative;
+      display: flex;
+      flex-direction: column;
+    }
+
+    .card-headline {
+      font-size: 18px;
+      font-weight: 700;
+      color: var(--text-primary);
+      margin-bottom: 14px;
+      display: flex;
+      align-items: center;
+      gap: 8px;
+    }
+
+    .card-body {
+      font-size: 13px;
+      line-height: 1.75;
+      color: var(--text-secondary);
+    }
+
+    .accent {
+      background: linear-gradient(135deg, var(--accent-1), var(--accent-1) 0%, var(--bg-secondary) 100%);
+      border: 1px solid rgba(255,255,255,0.1);
+      color: #FFF;
+    }
+    .accent .card-headline, .accent .card-body { color: #FFF; }
+    .accent .card-body { color: rgba(255,255,255,0.92); }
+
+    .elevated {
+      background: var(--card-bg-from);
+      border: 1px solid var(--card-border);
+      box-shadow: 0 12px 24px rgba(0,0,0,0.3);
+      transform: translateY(-4px);
+    }
+    .filled {
+      background: linear-gradient(135deg, var(--card-bg-from), var(--card-bg-to));
+      border: 1px solid var(--card-border);
+    }
+
+    /* C1: Family coverage chips */
+    .chip-row {
+      display: flex;
+      gap: 8px;
+      margin-bottom: 14px;
+    }
+    .chip {
+      flex: 1;
+      text-align: center;
+      padding: 7px 6px;
+      border-radius: 10px;
+      background: rgba(255,255,255,0.12);
+      border: 1px solid rgba(255,255,255,0.22);
+      font-size: 12px;
+      font-weight: 700;
+      color: #FFF;
+      line-height: 1.3;
+    }
+    .chip em {
+      display: block;
+      font-style: normal;
+      font-weight: 400;
+      font-size: 10px;
+      color: rgba(255,255,255,0.75);
+      margin-top: 2px;
+    }
+
+    /* C1: Family overall analysis module */
+    .family-analysis {
+      margin-top: 12px;
+      padding: 12px 14px;
+      border-radius: 10px;
+      background: rgba(255,255,255,0.08);
+      border: 1px solid rgba(255,255,255,0.16);
+    }
+    .fa-headline {
+      font-size: 12px;
+      font-weight: 700;
+      color: #FFF;
+      margin-bottom: 8px;
+      display: flex;
+      align-items: center;
+      gap: 6px;
+    }
+    .fa-headline::before {
+      content: "";
+      width: 4px;
+      height: 12px;
+      border-radius: 2px;
+      background: var(--accent-2);
+    }
+    .fa-item {
+      display: flex;
+      align-items: baseline;
+      gap: 8px;
+      margin-bottom: 6px;
+    }
+    .fa-item:last-child { margin-bottom: 0; }
+    .fa-val {
+      font-family: var(--display-font);
+      font-size: 19px;
+      font-weight: 800;
+      color: var(--accent-2);
+      white-space: nowrap;
+    }
+    .fa-txt {
+      font-size: 11.5px;
+      line-height: 1.5;
+      color: rgba(255,255,255,0.85);
+    }
+
+    /* Charts: Metric Row */
+    .metric-row {
+      display: grid;
+      grid-template-columns: repeat(4, 1fr);
+      gap: 12px;
+      margin-top: auto;
+      padding-top: 14px;
+      border-top: 1px solid rgba(255,255,255,0.14);
+    }
+    .metric-item {
+      display: flex;
+      flex-direction: column;
+      align-items: center;
+      text-align: center;
+    }
+    .metric-val {
+      font-family: var(--display-font);
+      font-size: 22px;
+      font-weight: 800;
+      color: #FFF;
+      font-variant-numeric: tabular-nums;
+    }
+    .metric-lbl {
+      font-size: 10px;
+      color: rgba(255,255,255,0.75);
+      margin-top: 3px;
+      line-height: 1.2;
+    }
+
+    /* C2: Competitive matrix table */
+    .matrix-table {
+      width: 100%;
+      border-collapse: collapse;
+      table-layout: fixed;
+      margin-top: 4px;
+    }
+    .matrix-table th {
+      font-size: 10.5px;
+      font-weight: 700;
+      color: rgba(235,243,255,0.8);
+      padding: 6px 3px;
+      text-align: center;
+      border-bottom: 1px solid rgba(255,255,255,0.14);
+      line-height: 1.25;
+    }
+    .matrix-table th.dim-col {
+      text-align: left;
+      width: 108px;
+      padding-left: 2px;
+    }
+    .matrix-table th.winner-col {
+      color: var(--accent-1);
+      background: rgba(255,140,66,0.12);
+      border-radius: 6px 6px 0 0;
+    }
+    .matrix-table td {
+      font-size: 12px;
+      padding: 7px 3px;
+      text-align: center;
+      border-bottom: 1px solid rgba(255,255,255,0.06);
+      color: var(--text-secondary);
+      line-height: 1.25;
+    }
+    .matrix-table td.dim-col {
+      text-align: left;
+      font-size: 11.5px;
+      color: rgba(235,243,255,0.85);
+    }
+    .matrix-table td.dim-col em {
+      display: block;
+      font-style: normal;
+      font-size: 9.5px;
+      color: rgba(235,243,255,0.5);
+      margin-top: 1px;
+    }
+    .matrix-table td.winner-col {
+      background: rgba(255,140,66,0.10);
+      border-left: 1px solid rgba(255,140,66,0.4);
+      border-right: 1px solid rgba(255,140,66,0.4);
+      font-weight: 800;
+      color: #FFF;
+    }
+    .matrix-table tbody tr:last-child td { border-bottom: none; }
+    .sym-yes { color: var(--accent-4); font-weight: 700; }
+    .sym-part { color: var(--five-water); font-weight: 700; }
+    .sym-no { color: rgba(235,243,255,0.28); }
+    .winner-col .sym-yes { color: var(--accent-1); }
+
+    .matrix-note {
+      margin-top: 8px;
+      font-size: 9px;
+      color: rgba(235,243,255,0.4);
+      letter-spacing: 0.3px;
+    }
+
+    /* Highlight Strip (C2 bottom callout) */
+    .highlight-strip {
+      margin-top: auto;
+      padding-top: 10px;
+      border-top: 1px dashed rgba(255,255,255,0.15);
+      font-size: 12px;
+      line-height: 1.6;
+      color: var(--accent-4);
+    }
+    .highlight-strip strong {
+      font-weight: 800;
+      color: #FFF;
+    }
+
+    /* C3: Platform strength strip */
+    .strength-row {
+      display: flex;
+      align-items: center;
+      gap: 26px;
+      flex: 1;
+    }
+    .strength-lbl {
+      font-size: 13px;
+      font-weight: 700;
+      color: var(--accent-1);
+      padding-right: 14px;
+      border-right: 1px solid rgba(255,255,255,0.12);
+      white-space: nowrap;
+      letter-spacing: 1px;
+    }
+    .strength-item {
+      display: flex;
+      align-items: baseline;
+      gap: 6px;
+      white-space: nowrap;
+    }
+    .strength-item strong {
+      font-family: var(--display-font);
+      font-size: 20px;
+      font-weight: 800;
+      color: #FFF;
+      font-variant-numeric: tabular-nums;
+    }
+    .strength-item span {
+      font-size: 11.5px;
+      color: var(--text-secondary);
+    }
+
+    /* Footer */
+    .slide-footer {
+      position: absolute;
+      bottom: 12px;
+      left: 40px;
+      right: 40px;
+      height: 20px;
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      z-index: 10;
+    }
+    .footer-section {
+      font-size: 10px;
+      color: var(--text-secondary);
+      opacity: 0.5;
+      letter-spacing: 1px;
+    }
+    .footer-page {
+      font-size: 10px;
+      color: var(--text-secondary);
+      opacity: 0.5;
+    }
+  </style>
+</head>
+<body>
+  <div class="grid-overlay" data-decoration-layer="background" aria-hidden="true"></div>
+
+  <header class="slide-header">
+    <span class="overline">PART 03 &mdash; 市场容量</span>
+    <h1 class="page-title">全家健康新蓝海:无一体化的空白市场</h1>
+  </header>
+
+  <div class="content-container">
+    <!-- C1: Family Coverage (Anchor) -->
+    <div class="card accent" data-card-id="c1">
+      <div class="card-headline">全家覆盖:一个家庭的三代人</div>
+      <div class="chip-row">
+        <span class="chip">孩子<em>3-18 岁</em></span>
+        <span class="chip">家长<em>25-45 岁</em></span>
+        <span class="chip">老人<em>55-75 岁</em></span>
+      </div>
+      <div class="card-body">
+        <strong>目标基数:</strong>3-18 岁儿童家庭约 1.2 亿户;2024 年小学及幼儿园在校生约 1.4 亿人次
+      </div>
+      <div class="family-analysis">
+        <div class="fa-headline">全家总体情况分析</div>
+        <div class="fa-item">
+          <span class="fa-val">0-100</span>
+          <span class="fa-txt">家庭五维健康指数,每日自动计算,三代同看</span>
+        </div>
+        <div class="fa-item">
+          <span class="fa-val">25+</span>
+          <span class="fa-txt">数据源 · AI 对话前自动组装全家健康画像</span>
+        </div>
+      </div>
+      <div class="metric-row" data-chart-type="metric_row">
+        <div class="metric-item">
+          <div class="metric-val">1.2亿</div>
+          <div class="metric-lbl">目标家庭户数</div>
+        </div>
+        <div class="metric-item">
+          <div class="metric-val">78.6%</div>
+          <div class="metric-lbl">要求定制化</div>
+        </div>
+        <div class="metric-item">
+          <div class="metric-val">78.3%</div>
+          <div class="metric-lbl">心理辅导复购</div>
+        </div>
+        <div class="metric-item">
+          <div class="metric-val">1.2-3万</div>
+          <div class="metric-lbl">年均教育支出</div>
+        </div>
+      </div>
+    </div>
+
+    <!-- C2: Competitive Matrix (Support) -->
+    <div class="card elevated" data-card-id="c2">
+      <div class="card-headline">竞品对比:各占一个象限,无人全家通吃</div>
+      <table class="matrix-table">
+        <thead>
+          <tr>
+            <th class="dim-col">维度 \ 竞品</th>
+            <th>作业帮</th>
+            <th>壹心理</th>
+            <th>薄荷健康</th>
+            <th>亲宝宝</th>
+            <th>平安好医生</th>
+            <th class="winner-col">浠艾福 ★</th>
+          </tr>
+        </thead>
+        <tbody>
+          <tr>
+            <td class="dim-col">孩子教育 / 成长</td>
+            <td><span class="sym-yes">✓</span></td>
+            <td><span class="sym-no">—</span></td>
+            <td><span class="sym-no">—</span></td>
+            <td><span class="sym-yes">✓</span></td>
+            <td><span class="sym-no">—</span></td>
+            <td class="winner-col"><span class="sym-yes">✓</span></td>
+          </tr>
+          <tr>
+            <td class="dim-col">心理测评</td>
+            <td><span class="sym-no">—</span></td>
+            <td><span class="sym-yes">✓</span></td>
+            <td><span class="sym-no">—</span></td>
+            <td><span class="sym-no">—</span></td>
+            <td><span class="sym-part">◐</span></td>
+            <td class="winner-col"><span class="sym-yes">✓</span></td>
+          </tr>
+          <tr>
+            <td class="dim-col">健康管理 / 报告解读</td>
+            <td><span class="sym-no">—</span></td>
+            <td><span class="sym-no">—</span></td>
+            <td><span class="sym-part">◐</span></td>
+            <td><span class="sym-part">◐</span></td>
+            <td><span class="sym-yes">✓</span></td>
+            <td class="winner-col"><span class="sym-yes">✓</span></td>
+          </tr>
+          <tr>
+            <td class="dim-col">家长 / 老人全家覆盖</td>
+            <td><span class="sym-no">—</span></td>
+            <td><span class="sym-part">◐</span></td>
+            <td><span class="sym-part">◐</span></td>
+            <td><span class="sym-part">◐</span></td>
+            <td><span class="sym-yes">✓</span></td>
+            <td class="winner-col"><span class="sym-yes">✓</span></td>
+          </tr>
+          <tr>
+            <td class="dim-col">服务闭环<em>检测→分析→方案→复测</em></td>
+            <td><span class="sym-part">◐</span></td>
+            <td><span class="sym-part">◐</span></td>
+            <td><span class="sym-part">◐</span></td>
+            <td><span class="sym-no">—</span></td>
+            <td><span class="sym-yes">✓</span></td>
+            <td class="winner-col"><span class="sym-yes">✓</span></td>
+          </tr>
+          <tr>
+            <td class="dim-col">AI 个性化</td>
+            <td><span class="sym-yes">✓</span></td>
+            <td><span class="sym-yes">✓</span></td>
+            <td><span class="sym-yes">✓</span></td>
+            <td><span class="sym-yes">✓</span></td>
+            <td><span class="sym-yes">✓</span></td>
+            <td class="winner-col"><span class="sym-yes">✓</span></td>
+          </tr>
+        </tbody>
+      </table>
+      <div class="matrix-note">注:✓ 已覆盖 ◐ 部分覆盖 — 未覆盖(基于截至 2026-08 公开信息)</div>
+      <div class="highlight-strip">平台渗透率不足 <strong>3%</strong> —— 97% 市场待开拓,全家一体化卡位窗口打开</div>
+    </div>
+
+    <!-- C3: Platform Strength (full-width strip) -->
+    <div class="card filled" data-card-id="c3" style="grid-column: 1 / -1;">
+      <div class="strength-row">
+        <span class="strength-lbl">平台实力</span>
+        <span class="strength-item"><strong>25+</strong><span>评分数据源</span></span>
+        <span class="strength-item"><strong>500+</strong><span>涉及指标</span></span>
+        <span class="strength-item"><strong>48+</strong><span>智能体</span></span>
+        <span class="strength-item"><strong>47</strong><span>类关系实体</span></span>
+        <span class="strength-item"><strong>15</strong><span>大模块</span></span>
+      </div>
+    </div>
+  </div>
+
+  <footer class="slide-footer">
+    <span class="footer-section">PART 03 &mdash; 市场容量</span>
+    <span class="footer-page">13 / 28</span>
+  </footer>
+</body>
+</html>

Niektoré súbory nie sú zobrazené, pretože je v týchto rozdielových dátach zmenené mnoho súborov