← GPT-AI-LP
CODE · 89.3 KB

src/export-html.js

Workspace snapshot · 08/31 23:51

import { normalizeWireframeType, resolveWireframeLayout } from "./wireframe-layouts.js";
import { contentModelIssues, normalizeContentModel } from "./section-content.js";

const escapeHtml = (value = "") =>
  String(value)
    .replaceAll("&", "&")
    .replaceAll("<", "&lt;")
    .replaceAll(">", "&gt;")
    .replaceAll('"', "&quot;")
    .replaceAll("'", "&#39;");

const safeId = (value, fallback) =>
  String(value || fallback)
    .toLowerCase()
    .replace(/[^a-z0-9_-]+/gu, "-")
    .replace(/^-+|-+$/gu, "") || fallback;

const safeUrl = (value, fallback = "#contact") => {
  const text = String(value || "").trim();
  if (/^(https?:\/\/|mailto:|tel:|#|\/)/iu.test(text)) return escapeHtml(text);
  return fallback;
};

const absoluteAssetUrl = (assetUrl, publicUrl) => {
  const asset = String(assetUrl || "").trim();
  const base = String(publicUrl || "").trim();
  if (/^https?:\/\//iu.test(asset)) return safeUrl(asset, "");
  if (!asset || !/^https?:\/\//iu.test(base)) return "";
  try {
    return escapeHtml(new URL(asset, base.endsWith("/") ? base : `${base}/`).href);
  } catch {
    return "";
  }
};

const luminance = (hex) => {
  const match = String(hex || "").match(/^#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/iu);
  if (!match) return 0.5;
  const linear = (value) => {
    const channel = Number.parseInt(value, 16) / 255;
    return channel <= 0.04045 ? channel / 12.92 : ((channel + 0.055) / 1.055) ** 2.4;
  };
  return 0.2126 * linear(match[1]) + 0.7152 * linear(match[2]) + 0.0722 * linear(match[3]);
};

function resolvePalette(value) {
  const palette = (Array.isArray(value) ? value : [])
    .filter((color) => /^#[0-9a-f]{6}$/iu.test(color))
    .slice(0, 3);
  if (palette.length < 3) return { ink: "#111111", accent: "#ff4f00", paper: "#ffffff" };
  const ordered = [...palette].sort((left, right) => luminance(left) - luminance(right));
  return { ink: ordered[0], accent: ordered[1], paper: ordered[2] };
}

const publicEvidence = (value) => {
  const evidence = String(value || "").trim();
  if (!evidence || /営業資料|ヒアリング回答|公開前|再照合|未確認|要確認|仮説値|確認が必要|提示する|掲載する|資料v\d/iu.test(evidence)) return "";
  return evidence;
};

const listItems = (section, limit = 4) => {
  const model = normalizeContentModel(section.contentModel);
  const typed = [...model.items, ...model.steps].map((item) => item.title).filter(Boolean);
  if (typed.length) return typed.slice(0, limit);
  // 旧データは明示的な箇条書きだけを救済する。文章の句読点分割は行わない。
  return String(section.content || "")
    .split(/\r?\n/u)
    .map((item) => item.replace(/^\s*[-・*]\s*/u, "").trim())
    .filter((item) => item.length > 2)
    .slice(0, limit);
};

const faqItems = (section) => {
  const typed = normalizeContentModel(section.contentModel).faqs;
  if (typed.length) return typed;
  const pairs = [];
  let current;
  for (const line of String(section.content || "").split(/\r?\n/u)) {
    const question = line.match(/^\s*Q[::]\s*(.+)$/u);
    const answer = line.match(/^\s*A[::]\s*(.+)$/u);
    if (question) {
      current = { question: question[1].trim(), answer: "" };
      pairs.push(current);
    } else if (answer && current) {
      current.answer = answer[1].trim();
    }
  }
  if (pairs.length && pairs.every((pair) => pair.answer)) return pairs.slice(0, 8);
  return [];
};

function imageRecordForSection(section, images, layer = "content") {
  const records = Array.isArray(images) ? images : [];
  return records.find((image) => image.sectionId === section.id && image.layer === layer)
    || (layer === "content" ? records.find((image) => image.sectionId === section.id && !image.layer) : undefined);
}

function imageForSection(section, index, images) {
  const record = imageRecordForSection(section, images, "content");
  if (!record?.src || !section.imageBrief) return "";
  const alt = section.imageBrief.subject || section.asset || section.title;
  return `<figure class="section-visual${record.textIntegrated ? " text-integrated" : ""}"><img src="${escapeHtml(record.src)}" alt="${escapeHtml(alt)}" loading="${index ? "lazy" : "eager"}" decoding="async"></figure>`;
}

function backgroundForSection(section, index, images) {
  const record = imageRecordForSection(section, images, "background");
  if (!record?.src) return "";
  return `<div class="section-background" aria-hidden="true"><img src="${escapeHtml(record.src)}" alt="" loading="${index ? "lazy" : "eager"}" decoding="async"></div>`;
}

const cta = (section, href = "#contact") =>
  section.cta && section.cta !== "なし"
    ? `<a class="button" href="${safeUrl(section.ctaUrl, href)}">${escapeHtml(section.cta)}<span aria-hidden="true">→</span></a>`
    : "";

const comparableText = (value) => String(value || "").replace(/\s+/gu, "").toLowerCase();

const heroEyebrow = (section, project) => {
  const answers = project?.answers || {};
  const serviceNames = [answers.formalServiceName, project?.name].map(comparableText).filter(Boolean);
  const label = String(section.label || "").trim();
  if (label && !serviceNames.includes(comparableText(label))) return label;
  const explicitAudience = String(answers.targetAudience || answers.audienceLabel || "").trim();
  if (explicitAudience) return explicitAudience;
  const audience = String(answers.service || "").match(/^(.{2,40}?)(?:向けに|向け)/u)?.[1]?.trim();
  return audience ? `${audience}へ` : "";
};

const heading = (section, index, project) => {
  const eyebrow = index === 0
    ? heroEyebrow(section, project)
    : `${String(index + 1).padStart(2, "0")} / ${section.label || section.type || "SECTION"}`;
  return `<div class="section-heading">${eyebrow ? `<small>${escapeHtml(eyebrow)}</small>` : ""}<${index === 0 ? "h1" : "h2"}>${escapeHtml(section.title || "見出し未設定")}</${index === 0 ? "h1" : "h2"}>${section.body ? `<p class="lead">${escapeHtml(section.body)}</p>` : ""}${publicEvidence(section.proof) ? `<p class="proof">${escapeHtml(publicEvidence(section.proof))}</p>` : ""}</div>`;
};

const cards = (items, className = "cards") =>
  `<div class="${className}">${items.map((item, itemIndex) => `<article><small>${String(itemIndex + 1).padStart(2, "0")}</small><h3>${escapeHtml(item)}</h3></article>`).join("")}</div>`;

const richItems = (items, className = "cards") =>
  `<div class="${className}">${items.map((item, index) => `<article>${item.eyebrow ? `<small>${escapeHtml(item.eyebrow)}</small>` : `<small>${String(index + 1).padStart(2, "0")}</small>`}<h3>${escapeHtml(item.title)}</h3>${item.description ? `<p>${escapeHtml(item.description)}</p>` : ""}${publicEvidence(item.proof) ? `<strong>${escapeHtml(publicEvidence(item.proof))}</strong>` : ""}</article>`).join("")}</div>`;

function renderTypedContent(section, type, variant, head, image, project) {
  const model = normalizeContentModel(section.contentModel);
  const action = cta(section);
  if (type === "comparison" && model.comparison) {
    const { columns, rows } = model.comparison;
    return `${head}<div class="table-wrap"><table><thead><tr><th>比較軸</th>${columns.map((column) => `<th>${escapeHtml(column)}</th>`).join("")}</tr></thead><tbody>${rows.map((row) => `<tr><th>${escapeHtml(row.label)}</th>${columns.map((_, index) => `<td>${escapeHtml(row.cells[index] || "")}</td>`).join("")}</tr>`).join("")}</tbody></table></div>${action}`;
  }
  if (type === "faq" && model.faqs.length) {
    return `${head}<div class="faq">${model.faqs.map((entry, index) => `<details><summary><span>Q${index + 1}</span>${escapeHtml(entry.question)}</summary><p>${escapeHtml(entry.answer)}</p></details>`).join("")}</div>`;
  }
  if (type === "pricing" && model.plans.length) {
    return `${head}<div class="plans">${model.plans.map((plan) => `<article><h3>${escapeHtml(plan.name)}</h3><p class="plan-price">${escapeHtml(plan.price)}${plan.period ? `<small>${escapeHtml(plan.period)}</small>` : ""}</p>${plan.description ? `<p>${escapeHtml(plan.description)}</p>` : ""}${plan.features.length ? `<ul>${plan.features.map((feature) => `<li>${escapeHtml(feature)}</li>`).join("")}</ul>` : ""}${plan.note ? `<p class="plan-note">${escapeHtml(plan.note)}</p>` : ""}${plan.cta ? `<a class="button" href="#contact">${escapeHtml(plan.cta)}</a>` : ""}</article>`).join("")}</div>`;
  }
  if (type === "proof" && variant === "metric-case" && model.caseStudy) {
    const study = model.caseStudy;
    return `${head}<article class="metric-case"><dl>${model.metrics.map((metric) => `<div><dt>${escapeHtml(metric.label)}</dt><dd>${escapeHtml(metric.value)}</dd>${publicEvidence(metric.source) ? `<small>${escapeHtml(publicEvidence(metric.source))}</small>` : ""}</div>`).join("")}</dl><div>${study.subject ? `<small>${escapeHtml(study.subject)}</small>` : ""}<dl class="case-story"><div><dt>課題</dt><dd>${escapeHtml(study.issue)}</dd></div><div><dt>実施</dt><dd>${escapeHtml(study.action)}</dd></div><div><dt>結果</dt><dd>${escapeHtml(study.result)}</dd></div></dl>${publicEvidence(study.source) ? `<cite>${escapeHtml(publicEvidence(study.source))}</cite>` : ""}</div></article>${image}${action}`;
  }
  if ((type === "proof" || type === "trust") && model.metrics.length) {
    return `${head}<dl class="metric-band">${model.metrics.map((metric) => `<div><dt>${escapeHtml(metric.label)}</dt><dd>${escapeHtml(metric.value)}</dd>${publicEvidence(metric.source) ? `<small>${escapeHtml(publicEvidence(metric.source))}</small>` : ""}</div>`).join("")}</dl>${image}${action}`;
  }
  if (type === "proof" && model.caseStudy) {
    const study = model.caseStudy;
    return `${head}<article class="case-featured">${image}<div>${study.subject ? `<small>${escapeHtml(study.subject)}</small>` : ""}<dl class="case-story"><div><dt>課題</dt><dd>${escapeHtml(study.issue)}</dd></div><div><dt>実施</dt><dd>${escapeHtml(study.action)}</dd></div><div><dt>結果</dt><dd>${escapeHtml(study.result)}</dd></div></dl>${study.source ? `<cite>${escapeHtml(study.source)}</cite>` : ""}</div></article>${action}`;
  }
  if (["mechanism", "process"].includes(type) && model.beforeAfter) {
    const transformation = `<div class="before-after"><article><small>BEFORE</small><h3>${escapeHtml(model.beforeAfter.before.title)}</h3><p>${escapeHtml(model.beforeAfter.before.description)}</p></article><article><small>AFTER</small><h3>${escapeHtml(model.beforeAfter.after.title)}</h3><p>${escapeHtml(model.beforeAfter.after.description)}</p></article></div>`;
    return image
      ? `${head}<div class="visual-cinema visual-cinema--transformation">${image}${transformation}</div>${action}`
      : `${head}${transformation}${action}`;
  }
  if (["mechanism", "process"].includes(type) && model.steps.length) {
    if (variant === "process-timeline") {
      return `${head}<div class="visual-pair visual-pair--process"><ol class="timeline process rich-timeline">${model.steps.map((step, index) => `<li><time>STEP ${index + 1}</time><div>${step.eyebrow ? `<small>${escapeHtml(step.eyebrow)}</small>` : ""}<h3>${escapeHtml(step.title)}</h3>${step.description ? `<p>${escapeHtml(step.description)}</p>` : ""}${publicEvidence(step.proof) ? `<strong>${escapeHtml(publicEvidence(step.proof))}</strong>` : ""}</div></li>`).join("")}</ol>${image}</div>${action}`;
    }
    const steps = `<ol class="steps">${model.steps.map((step, index) => `<li><span>${index + 1}</span><h3>${escapeHtml(step.title)}</h3>${step.description ? `<p>${escapeHtml(step.description)}</p>` : ""}${publicEvidence(step.proof) ? `<strong>${escapeHtml(publicEvidence(step.proof))}</strong>` : ""}</li>`).join("")}</ol>`;
    return image
      ? `${head}<div class="visual-cinema visual-cinema--steps">${image}${steps}</div>${action}`
      : `${head}${steps}${action}`;
  }
  if (["problem", "risk", "differentiation", "usecase", "fit", "trust"].includes(type) && model.items.length) {
    if (variant === "feature-zigzag") {
      return `${head}<div class="zigzag rich-zigzag">${model.items.map((item, index) => `<article><span>${String(index + 1).padStart(2, "0")}</span><div>${item.eyebrow ? `<small>${escapeHtml(item.eyebrow)}</small>` : ""}<h3>${escapeHtml(item.title)}</h3></div><div>${item.description ? `<p>${escapeHtml(item.description)}</p>` : ""}${publicEvidence(item.proof) ? `<strong>${escapeHtml(publicEvidence(item.proof))}</strong>` : ""}</div></article>`).join("")}</div>${image}${action}`;
    }
    if (variant === "reason-stack") {
      return `${head}<div class="visual-pair visual-pair--reason"><ol class="reason-stack rich-stack">${model.items.map((item, index) => `<li><b>${index + 1}</b><div>${item.eyebrow ? `<small>${escapeHtml(item.eyebrow)}</small>` : ""}<h3>${escapeHtml(item.title)}</h3>${item.description ? `<p>${escapeHtml(item.description)}</p>` : ""}${publicEvidence(item.proof) ? `<strong>${escapeHtml(publicEvidence(item.proof))}</strong>` : ""}</div></li>`).join("")}</ol>${image}</div>${action}`;
    }
    if (variant === "usecase-story") {
      return `${head}<div class="visual-story">${image}<div class="story-rail rich-story">${model.items.map((item, index) => `<article><small>${escapeHtml(item.eyebrow || `SCENE ${index + 1}`)}</small><h3>${escapeHtml(item.title)}</h3>${item.description ? `<p>${escapeHtml(item.description)}</p>` : ""}${publicEvidence(item.proof) ? `<strong>${escapeHtml(publicEvidence(item.proof))}</strong>` : ""}</article>`).join("")}</div></div>${action}`;
    }
    if (variant === "risk-timeline") {
      return `${head}<div class="visual-pair visual-pair--risk">${image}<ol class="timeline rich-timeline">${model.items.map((item, index) => `<li><time>0${index + 1}</time><div><h3>${escapeHtml(item.title)}</h3>${item.description ? `<p>${escapeHtml(item.description)}</p>` : ""}${publicEvidence(item.proof) ? `<strong>${escapeHtml(publicEvidence(item.proof))}</strong>` : ""}</div></li>`).join("")}</ol></div>${action}`;
    }
    if (variant === "pain-checklist") {
      return `${head}<ul class="checklist rich-checklist">${model.items.map((item) => `<li><div><h3>${escapeHtml(item.title)}</h3>${item.description ? `<p>${escapeHtml(item.description)}</p>` : ""}${publicEvidence(item.proof) ? `<strong>${escapeHtml(publicEvidence(item.proof))}</strong>` : ""}</div></li>`).join("")}</ul>${action}`;
    }
    const itemCards = richItems(model.items, `cards ${type}-items`);
    return image
      ? `${head}<div class="visual-cinema visual-cinema--cards">${image}${itemCards}</div>${action}`
      : `${head}${itemCards}${action}`;
  }
  if (model.quote) {
    return `${head}<figure class="editorial-quote"><blockquote>${escapeHtml(model.quote)}</blockquote>${model.quoteAttribution ? `<figcaption>${escapeHtml(model.quoteAttribution)}</figcaption>` : ""}</figure>${image}${action}`;
  }
  return "";
}

function renderVariant({ section, type, variant, head, items, image, project }) {
  const action = cta(section);
  const pair = (left, right, className = "split") => `<div class="${className}"><article><small>BEFORE</small><h3>${escapeHtml(left || "現状")}</h3></article><article><small>AFTER</small><h3>${escapeHtml(right || "変化後")}</h3></article></div>`;
  const [first = "項目1", second = "項目2", third = "項目3", fourth = "項目4"] = items;
  switch (variant) {
    case "hero-proof-split": return `<div class="hero-copy">${head}${action}</div><aside class="hero-proof"><strong>${escapeHtml(section.proof)}</strong>${image}${cards(items.slice(0, 3), "proof-points")}</aside>`;
    case "hero-copy-led": return `<div class="hero-copy-led">${head}${action}</div>${image}`;
    case "hero-product-stage": return `<div class="hero-visual-bg">${image}</div><div class="hero-copy">${head}${action}</div>`;
    case "logo-wall": return `${head}<div class="logo-wall" aria-label="導入・取引実績">${items.map((item) => `<span>${escapeHtml(item)}</span>`).join("")}</div>${action}`;
    case "trust-metric-band": return `${head}<dl class="metric-band">${items.map((item, i) => `<div><dt>${String(i + 1).padStart(2, "0")}</dt><dd>${escapeHtml(item)}</dd></div>`).join("")}</dl>${action}`;
    case "pain-checklist": return `${head}<ul class="checklist">${items.map((item) => `<li>${escapeHtml(item)}</li>`).join("")}</ul>${action}`;
    case "pain-narrative": return `${head}<article class="narrative"><blockquote>${escapeHtml(first)}</blockquote><p>${escapeHtml([second, third, fourth].filter(Boolean).join("。"))}</p></article>${action}`;
    case "risk-list": return `${head}<ul class="risk-list">${items.map((item, i) => `<li><b>RISK ${i + 1}</b><span>${escapeHtml(item)}</span></li>`).join("")}</ul>${action}`;
    case "risk-timeline": return `${head}<ol class="timeline">${items.map((item, i) => `<li><time>0${i + 1}</time><p>${escapeHtml(item)}</p></li>`).join("")}</ol>${action}`;
    case "risk-cost-stack": return `${head}<dl class="cost-stack">${items.map((item, i) => `<div><dt>LOSS ${i + 1}</dt><dd>${escapeHtml(item)}</dd></div>`).join("")}</dl>${action}`;
    case "step-flow": return `${head}<ol class="steps">${items.map((item, i) => `<li><span>${i + 1}</span><h3>${escapeHtml(item)}</h3></li>`).join("")}</ol>${action}`;
    case "before-after": return `${head}${pair(first, second, "before-after")}${image}${action}`;
    case "solution-map": return `${head}<div class="solution-map"><b>${escapeHtml(section.title)}</b>${items.map((item) => `<span>${escapeHtml(item)}</span>`).join("")}</div>${image}${action}`;
    case "process-circles": return `${head}<ol class="process-circles">${items.map((item, i) => `<li><span>${i + 1}</span><p>${escapeHtml(item)}</p></li>`).join("")}</ol>${action}`;
    case "process-timeline": return `${head}<ol class="timeline process">${items.map((item, i) => `<li><time>STEP ${i + 1}</time><p>${escapeHtml(item)}</p></li>`).join("")}</ol>${action}`;
    case "feature-grid": return `${head}${cards(items, "feature-grid")}${image}${action}`;
    case "feature-zigzag": return `${head}<div class="zigzag">${items.map((item, i) => `<article><span>${String(i + 1).padStart(2, "0")}</span><h3>${escapeHtml(item)}</h3>${i === 0 ? image : ""}</article>`).join("")}</div>${action}`;
    case "reason-stack": return `${head}<ol class="reason-stack">${items.map((item, i) => `<li><b>${i + 1}</b><p>${escapeHtml(item)}</p></li>`).join("")}</ol>${action}`;
    case "usecase-grid": return `${head}${cards(items, "usecase-grid")}${action}`;
    case "usecase-story": return `${head}<div class="story-rail">${items.map((item, i) => `<article><small>SCENE ${i + 1}</small><h3>${escapeHtml(item)}</h3></article>`).join("")}</div>${image}${action}`;
    case "lineup-catalog": return `${head}<div class="lineup">${items.map((item) => `<article><div class="catalog-thumb">${image}</div><h3>${escapeHtml(item)}</h3></article>`).join("")}</div>${action}`;
    case "before-after-compare": return `${head}${pair(first, second, "compare-split")}${action}`;
    case "alternative-matrix": return `${head}<div class="alternative-matrix">${items.map((item) => `<article><h3>${escapeHtml(item)}</h3><p>適合条件と違いを記載</p></article>`).join("")}</div>${action}`;
    case "metric-case": return `${head}<article class="metric-case"><dl>${items.map((item, i) => `<div><dt>${i + 1}</dt><dd>${escapeHtml(item)}</dd></div>`).join("")}</dl>${image}</article>${action}`;
    case "case-study-featured": return `${head}<article class="case-featured">${image}<div><small>ISSUE / ACTION / RESULT</small><h3>${escapeHtml(first)}</h3><p>${escapeHtml([second, third, fourth].filter(Boolean).join("。"))}</p></div></article>${action}`;
    case "logo-metrics": return `${head}<div class="logo-metrics"><div class="logo-wall">${items.map((item) => `<span>${escapeHtml(item)}</span>`).join("")}</div><strong>${escapeHtml(section.proof)}</strong></div>${action}`;
    case "review-cards": return `${head}<div class="reviews">${items.map((item) => `<figure><blockquote>${escapeHtml(item)}</blockquote><figcaption>${escapeHtml(section.proof)}</figcaption></figure>`).join("")}</div>${action}`;
    case "price-lineup": return `${head}<div class="price-lineup">${items.map((item) => `<article><h3>${escapeHtml(item)}</h3><strong>${escapeHtml(project.answers?.priceAmount || "個別見積もり")}</strong>${action}</article>`).join("")}</div>`;
    case "estimate-explainer": return `${head}<div class="estimate"><div><b>${escapeHtml(project.answers?.priceAmount || "個別見積もり")}</b><p>${escapeHtml(section.content)}</p></div><ol>${items.map((item) => `<li>${escapeHtml(item)}</li>`).join("")}</ol></div>${action}`;
    case "fit-split": return `${head}<div class="fit-split"><article><small>GOOD FIT</small><h3>${escapeHtml(first)}</h3></article><article><small>NOT FIT</small><h3>${escapeHtml(project.answers?.excludedAudience || second)}</h3></article></div>${action}`;
    case "segment-selector": return `${head}<div class="segments">${items.map((item, i) => `<article><input type="radio" disabled ${i === 0 ? "checked" : ""}><h3>${escapeHtml(item)}</h3></article>`).join("")}</div>${action}`;
    case "cta-banner": return `<div class="cta-banner visual-cta">${image}<div class="cta-copy">${head}${action}</div></div>`;
    case "sample-cta": return `<div class="sample-cta">${head}<p>${escapeHtml(section.asset || "サンプルを確認できます")}</p>${action}</div>`;
    case "form-panel": return `${head}<div class="form-panel">${formMarkup(project)}</div>`;
    case "form-split": return `<div>${head}<p>${escapeHtml(section.content)}</p></div>${formMarkup(project)}`;
    case "expert-profile": return `<article class="expert-profile">${image}<div>${head}<p>${escapeHtml(section.asset)}</p></div></article>`;
    case "company-authority": return `${head}<div class="authority"><strong>${escapeHtml(project.answers?.formalCompanyName || "")}</strong>${cards(items, "authority-points")}</div>`;
    case "editorial-message": return image
      ? `<div class="message-poster">${image}</div><article class="integrated-copy-accessible"><blockquote>${escapeHtml(section.title)}</blockquote><div><p>${escapeHtml(section.body)}</p><cite>${escapeHtml(section.proof)}</cite></div></article><article class="message-mobile-copy" aria-hidden="true"><blockquote>${escapeHtml(section.title)}</blockquote><p>${escapeHtml(section.body)}</p></article>`
      : `<article class="editorial-message"><blockquote>${escapeHtml(section.title)}</blockquote><div><p>${escapeHtml(section.body)}</p><cite>${escapeHtml(section.proof)}</cite></div></article>`;
    case "content-split": return `<div class="content-split">${head}${image}</div>${action}`;
    case "content-editorial": return `<article class="content-editorial">${head}<p>${escapeHtml(section.content)}</p>${image}</article>${action}`;
    default: return "";
  }
}

function formMarkup(project, compact = false, anchorId = "") {
  const answers = project.answers || {};
  const fields = String(answers.formFields || "会社名,お名前,メールアドレス,相談内容")
    .split(/[,、\n]/u)
    .map((field) => field.trim())
    .filter(Boolean)
    .slice(0, 5);
  const selectedFields = compact
    ? [
        fields.find((field) => /会社|法人|組織/u.test(field)),
        fields.find((field) => /メール|mail/iu.test(field)),
      ].filter(Boolean)
    : fields;
  const endpoint = safeUrl(answers.formEndpoint, "");
  const privacy = safeUrl(answers.privacyUrl, "#privacy");
  return `<form${anchorId ? ` id="${escapeHtml(anchorId)}"` : ""} class="lead-form${compact ? " compact" : ""}" action="${endpoint}" method="post" data-success="${escapeHtml(answers.formSuccess || "送信しました。担当者からご連絡します。")}">${selectedFields.map((field, index) => {
    const email = /メール|mail/iu.test(field);
    const long = /内容|相談|要望|備考/iu.test(field);
    const name = safeId(field, `field-${index + 1}`);
    return `<label><span>${escapeHtml(field)}</span>${long ? `<textarea name="${name}" rows="4" required></textarea>` : `<input name="${name}" type="${email ? "email" : "text"}" ${index < 3 ? "required" : ""}>`}</label>`;
  }).join("")}<label class="consent"><input type="checkbox" required><span><a href="${privacy}" target="_blank" rel="noopener">個人情報の取り扱い</a>に同意する</span></label><button type="submit">${escapeHtml(answers.formButton || "送信する")}</button><p class="form-note">入力時間 約2分 / 送信後に受付完了を表示します</p><p class="form-status" role="status" aria-live="polite"></p></form>`;
}

function renderSection(section, index, project, images) {
  const type = normalizeWireframeType(section);
  const variant = resolveWireframeLayout(section, index);
  const density = ["hero", "message", "cta"].includes(type)
    ? "statement"
    : ["comparison", "pricing", "faq", "form"].includes(type)
      ? "information"
      : ["proof", "usecase", "trust"].includes(type)
        ? "evidence"
        : "narrative";
  const id = safeId(section.id, `section-${index + 1}`);
  const items = listItems(section, type === "usecase" ? 6 : 4);
  const imageRecord = imageRecordForSection(section, images, "content");
  const background = backgroundForSection(section, index, images);
  const textIntegrated = imageRecord?.textIntegrated === true;
  const image = imageForSection(section, index, images);
  const head = heading(section, index, project);
  const displayHead = textIntegrated && type !== "hero"
    ? `<div class="integrated-copy-accessible">${head}</div><div class="integrated-intro"><span>${escapeHtml(`${String(index + 1).padStart(2, "0")} / ${section.label || section.type || "SECTION"}`)}</span>${section.body ? `<p class="integrated-context">${escapeHtml(section.body)}</p>` : ""}</div>`
    : head;
  let body = type === "hero" && textIntegrated
    ? `<a class="hero-poster" href="#contact" aria-label="${escapeHtml(section.cta || project.answers?.formButton || "相談フォームへ進む")}">${image}</a><div class="integrated-copy-accessible">${head}</div>${variant === "hero-form" ? `<div class="hero-form-wrap">${formMarkup(project, true, "hero-contact")}</div>` : ""}`
    : "";

  const typedBody = body ? "" : renderTypedContent(section, type, variant, displayHead, image, project);
  if (typedBody) body = typedBody;
  const variantBody = body ? "" : renderVariant({ section, type, variant, head: displayHead, items, image, project });
  if (variantBody) body = variantBody;

  if (!body && type === "hero") {
    body = variant === "hero-form"
      ? `<div class="hero-art">${image}</div><div class="hero-copy">${displayHead}${cta(section)}</div><div class="hero-form-wrap">${formMarkup(project, true, "hero-contact")}</div>`
      : `<div class="hero-copy">${displayHead}${cta(section)}</div>${image}`;
  } else if (!body && ["problem", "risk", "differentiation", "usecase", "fit"].includes(type)) {
    body = `${displayHead}<div class="cards">${items.map((item, itemIndex) => `<article><small>${String(itemIndex + 1).padStart(2, "0")}</small><h3>${escapeHtml(item)}</h3></article>`).join("")}</div>${cta(section)}`;
  } else if (!body && ["mechanism", "process"].includes(type)) {
    body = `${displayHead}<ol class="steps">${items.map((item, itemIndex) => `<li><span>${itemIndex + 1}</span><h3>${escapeHtml(item)}</h3></li>`).join("")}</ol>${image}${cta(section)}`;
  } else if (!body && type === "comparison") {
    body = `${head}<div class="table-wrap"><table><thead><tr><th>比較軸</th><th>本サービス</th><th>代替手段</th></tr></thead><tbody>${items.map((item) => `<tr><th>${escapeHtml(item)}</th><td>強みを記載</td><td>違いを記載</td></tr>`).join("")}</tbody></table></div>${cta(section)}`;
  } else if (!body && (type === "proof" || type === "trust")) {
    body = `${head}<div class="evidence">${items.map((item) => `<article><strong>${escapeHtml(section.proof || "確認済みの根拠")}</strong><p>${escapeHtml(item)}</p></article>`).join("")}</div>${image}${cta(section)}`;
  } else if (!body && type === "pricing") {
    body = `${head}<div class="plans">${items.slice(0, 3).map((item, itemIndex) => `<article><small>PLAN ${itemIndex + 1}</small><h3>${escapeHtml(item)}</h3><p>${escapeHtml(project.answers?.priceAmount || "価格は条件をご確認ください")}</p>${cta(section)}</article>`).join("")}</div>`;
  } else if (!body && type === "faq") {
    body = `${head}<div class="faq">${faqItems(section).map((item, itemIndex) => `<details><summary>Q${itemIndex + 1}. ${escapeHtml(item.question)}</summary><p>${escapeHtml(item.answer)}</p></details>`).join("")}</div>`;
  } else if (!body && type === "form") {
    body = `<div>${head}</div>${formMarkup(project)}`;
  } else if (!body && type === "cta") {
    body = `${head}<div class="cta-panel">${cta(section, "#contact")}</div>`;
  } else if (!body && type === "message") {
    body = `<blockquote>${escapeHtml(section.title || "")}</blockquote><div>${section.body ? `<p>${escapeHtml(section.body)}</p>` : ""}${section.proof ? `<cite>${escapeHtml(section.proof)}</cite>` : ""}</div>`;
  } else if (!body) {
    body = `${displayHead}${image}${section.content ? `<div class="rich-copy">${escapeHtml(section.content)}</div>` : ""}${cta(section)}`;
  }
  return `<section id="${type === "form" ? "contact" : id}" class="section type-${escapeHtml(type)} variant-${escapeHtml(variant)} density-${density}${image ? " has-generated-visual" : ""}${background ? " has-generated-background" : ""}${textIntegrated ? " has-integrated-copy" : ""}" data-section-index="${index + 1}" data-role="${escapeHtml(section.role || "")}" data-question="${escapeHtml(section.userQuestion || "")}" data-layout="${escapeHtml(section.layout || variant)}" data-implementation="${escapeHtml(section.implementation || "")}" data-asset="${escapeHtml(section.asset || "")}">${background}<div class="inner">${body}</div></section>`;
}

const fontStack = (type = "") =>
  /明朝|セリフ/u.test(type)
    ? "'Noto Serif JP',serif"
    : "'Noto Sans JP',sans-serif";

const toneMode = (tone = {}) => {
  const value = [tone.id, tone.code, tone.type, tone.image, tone.layout, tone.concept].filter(Boolean).join(" ");
  if (/タイポ|極太|bold|graphic|強訴求|\bB\b/iu.test(value)) return "graphic";
  if (/UI|製品|図解|document|evidence|product|\bC\b/iu.test(value)) return "documentary";
  return "editorial";
};

export function exportHtml(project = {}, sections = [], tone = {}, images = [], options = {}) {
  const safeSections = Array.isArray(sections) && sections.length
    ? sections
    : [{ id: "hero", type: "hero", label: "FIRST VIEW", title: project.name || "新規LP", body: "内容を入力してください", cta: "相談する" }];
  const typedIssues = safeSections
    .filter((section) => Object.hasOwn(section, "contentModel"))
    .flatMap((section) => contentModelIssues(section).map((issue) => `${section.id}:${issue}`));
  if (typedIssues.length) {
    const error = new Error("LP_EXPORT_QUALITY_GATE");
    error.issues = typedIssues;
    throw error;
  }
  const visualSections = safeSections.filter((section) => section.imageBrief);
  const imageRecords = (Array.isArray(images) ? images : []).map((image, index) =>
    typeof image === "string"
      ? { src: image, sectionId: visualSections[index]?.id || "" }
      : image,
  );
  const { ink, accent, paper } = resolvePalette(tone.palette);
  const mode = toneMode(tone);
  const toneCode = String(tone.code || tone.id || "a").trim().toLowerCase().replace(/[^a-z0-9_-]/gu, "") || "a";
  const headingFont = toneCode === "a"
    ? "'Noto Serif JP',serif"
    : toneCode === "b"
      ? "'Zen Kaku Gothic New','Noto Sans JP',sans-serif"
      : "'Noto Sans JP',sans-serif";
  const answers = project.answers || {};
  const hasForm = safeSections.some((section) => normalizeWireframeType(section) === "form");
  const formSection = hasForm ? "" : `<section id="contact" class="section type-form variant-form-split"><div class="inner"><div class="section-heading"><small>CONTACT</small><h2>${escapeHtml(answers.formHeading || "ご相談内容をお聞かせください")}</h2><p class="lead">必要事項をご入力ください。</p></div>${formMarkup(project)}</div></section>`;
  const variantStyles = `<style>.proof-points,.feature-grid,.usecase-grid,.authority-points,.reviews,.price-lineup,.segments,.alternative-matrix{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:16px}.proof-points article,.feature-grid article,.usecase-grid article,.authority-points article,.alternative-matrix article,.price-lineup article,.segments article{padding:24px;border:1px solid var(--line)}.metric-band,.cost-stack{display:grid;grid-template-columns:repeat(4,1fr);margin:36px 0}.metric-band div,.cost-stack div{padding:24px;border:1px solid var(--line)}.metric-band dt,.cost-stack dt{font-size:.72rem;letter-spacing:.12em}.metric-band dd,.cost-stack dd{margin:12px 0 0;font-size:1.2rem;font-weight:800}.logo-wall{display:flex;flex-wrap:wrap;gap:1px;margin:32px 0;background:var(--line)}.logo-wall span{flex:1 1 160px;padding:24px;background:var(--paper);text-align:center;font-weight:700}.checklist,.risk-list,.reason-stack{display:grid;gap:10px;padding:0;list-style:none;margin:32px 0}.checklist li,.risk-list li,.reason-stack li{display:flex;align-items:flex-start;gap:18px;padding:20px;border-bottom:1px solid var(--line)}.checklist li:before{content:'✓';flex:0 0 28px;display:grid;place-items:center;width:28px;height:28px;background:var(--accent);font-weight:800}.rich-stack h3,.rich-checklist h3,.rich-timeline h3{margin:.15em 0 .45em}.rich-stack p,.rich-checklist p,.rich-timeline p,.rich-zigzag p,.rich-story p{line-height:1.75}.rich-stack strong,.rich-checklist strong,.rich-timeline strong,.rich-zigzag strong,.rich-story strong{display:block;margin-top:12px;font-size:.78rem}.narrative,.editorial-message{display:grid;grid-template-columns:1.2fr .8fr;gap:48px;margin-top:36px}.narrative blockquote,.editorial-message blockquote{margin:0}.timeline{position:relative;display:grid;gap:0;padding:0;list-style:none;margin:36px 0}.timeline li{display:grid;grid-template-columns:110px 1fr;gap:20px;padding:20px;border-top:1px solid var(--line)}.timeline time{font-size:.75rem;font-weight:800}.before-after,.compare-split,.fit-split{display:grid;grid-template-columns:1fr 1fr;gap:1px;margin:36px 0;background:var(--line)}.before-after article,.compare-split article,.fit-split article{padding:32px;background:var(--paper)}.solution-map{display:grid;grid-template-columns:1.4fr repeat(3,1fr);gap:1px;margin:36px 0;background:var(--line)}.solution-map>*{padding:22px;background:var(--paper)}.process-circles{display:flex;justify-content:space-between;padding:0;list-style:none;margin:36px 0}.process-circles li{display:grid;place-items:center;width:180px;aspect-ratio:1;border:1px solid var(--line);border-radius:50%;text-align:center}.process-circles span{font-size:2rem;font-weight:800}.zigzag{display:grid;gap:20px;margin:36px 0}.zigzag article{display:grid;grid-template-columns:80px 1fr 1.2fr;align-items:start;gap:24px;padding:22px;border-top:1px solid var(--line)}.story-rail,.lineup{display:grid;grid-auto-flow:column;grid-auto-columns:minmax(280px,1fr);gap:16px;overflow:auto;margin:36px 0}.story-rail article,.lineup article{padding:24px;border:1px solid var(--line)}.catalog-thumb .section-visual{margin-bottom:18px}.case-featured,.expert-profile,.content-split,.metric-case{display:grid;grid-template-columns:1.1fr .9fr;align-items:center;gap:48px;margin-top:36px}.metric-case>dl{display:grid;grid-template-columns:repeat(2,1fr);gap:1px;background:var(--line)}.metric-case>dl>div{padding:24px;background:var(--paper)}.reviews figure{margin:0;padding:24px;border:1px solid var(--line)}.reviews blockquote{font-size:1.2rem}.estimate{display:grid;grid-template-columns:1fr 1fr;gap:40px;margin:36px 0;padding:32px;border:1px solid var(--line)}.estimate b{font-size:2rem}.cta-banner,.sample-cta{width:min(1160px,calc(100% - 40px));margin:auto;padding:clamp(64px,8vw,112px) 0;text-align:center}.hero-copy-led{max-width:680px}.variant-hero-copy-led .section-heading h1{font-size:clamp(2.6rem,4vw,4.5rem)}.section-heading h1,.section-heading h2{text-wrap:balance;word-break:auto-phrase}.product-stage{padding:20px;border:1px solid var(--line)}.content-editorial{max-width:840px}.content-editorial>p{font-size:1.15rem;line-height:2}@media(max-width:760px){.proof-points,.feature-grid,.usecase-grid,.authority-points,.reviews,.price-lineup,.segments,.alternative-matrix,.metric-band,.cost-stack,.before-after,.compare-split,.fit-split,.solution-map,.narrative,.editorial-message,.case-featured,.expert-profile,.content-split,.metric-case,.estimate{grid-template-columns:1fr}.process-circles{display:grid;grid-template-columns:1fr 1fr;gap:12px}.process-circles li{width:100%}.zigzag article{grid-template-columns:52px 1fr}.zigzag article>:last-child{grid-column:2}.zigzag article .section-visual{grid-column:1/-1}.timeline li{grid-template-columns:74px 1fr}.story-rail{grid-auto-columns:minmax(82vw,1fr)}}</style>`;
  const mobileSafetyStyles = `<style>.hero-stage{display:grid;gap:16px;min-width:0}.hero-stage .section-visual{order:0}.hero-stage .lead-form{order:1}.cards article p{margin:14px 0 0;line-height:1.75}.cards article strong{display:block;margin-top:18px;font-size:.8rem}.trust-items article{border-top:4px solid var(--accent)}.fit-items article:last-child{opacity:.72}.plan-price{display:flex;align-items:baseline;gap:6px;font-size:clamp(1.8rem,3vw,3rem);font-weight:800}.plan-price small{font-size:.8rem}.plans ul{display:grid;gap:8px;padding-left:1.2em}.plan-note{font-size:.8rem}.case-story{display:grid;gap:0;margin:24px 0}.case-story div{display:grid;grid-template-columns:72px 1fr;gap:18px;padding:15px 0;border-top:1px solid var(--line)}.case-story dt{font-size:.75rem;font-weight:800}.case-story dd{margin:0;line-height:1.75}.faq summary{display:grid;grid-template-columns:48px 1fr;gap:12px;align-items:start}.faq summary span{color:var(--accent)}.variant-hero-product-stage .inner{position:relative;isolation:isolate;display:grid;grid-template-columns:minmax(0,.92fr) minmax(0,1.08fr);overflow:hidden}.variant-hero-product-stage .hero-visual-bg{position:absolute;z-index:-1;inset:0}.variant-hero-product-stage .hero-visual-bg,.variant-hero-product-stage .section-visual,.variant-hero-product-stage img{width:100%;height:100%;margin:0}.variant-hero-product-stage img{max-height:none;object-fit:cover}.variant-hero-product-stage .hero-copy{grid-column:1;padding:clamp(28px,4vw,58px);background:linear-gradient(90deg,color-mix(in srgb,var(--paper) 96%,transparent) 0%,color-mix(in srgb,var(--paper) 88%,transparent) 74%,transparent 100%)}.variant-hero-product-stage .section-heading h1{max-width:8.5em;font-size:clamp(2.7rem,4vw,4.5rem);line-height:1.18}.process-circles li,.zigzag article>*{min-width:0}@media(max-width:760px){.variant-hero-product-stage .inner{display:flex;flex-direction:column}.variant-hero-product-stage .hero-visual-bg{position:relative;z-index:auto;order:2;aspect-ratio:4/3}.variant-hero-product-stage .hero-visual-bg img{object-position:72% center}.variant-hero-product-stage .hero-copy{order:1;padding:0;background:none}.variant-hero-product-stage .section-heading h1{font-size:clamp(2.45rem,12vw,4rem)}}@media(max-width:420px){.process-circles{grid-template-columns:1fr}.story-rail,.lineup{max-width:100%}}</style>`;
  const responsivePolishStyles = `<style>.variant-hero-form .section-heading h1{font-size:clamp(2.8rem,4.3vw,4.8rem);line-height:1.17}.lead-form.compact{gap:10px;padding:24px}.lead-form.compact .form-note{max-width:38em}@media(max-width:760px){.site-header{align-items:center;gap:12px;min-height:64px;padding:10px 16px}.brand{max-width:62%;font-size:.76rem;line-height:1.35}.site-header>.button{flex:0 0 auto;margin-top:0;padding:11px 13px;font-size:.78rem}.section-heading h1,.variant-hero-form .section-heading h1{font-size:clamp(2.15rem,10vw,3rem);line-height:1.18}.lead-form.compact{padding:20px}}</style>`;
  const visualSystemStyles = `<style>.has-generated-visual{position:relative}.visual-pair{display:grid;grid-template-columns:minmax(0,.92fr) minmax(0,1.08fr);gap:clamp(28px,5vw,72px);align-items:stretch;margin-top:42px}.visual-pair>.section-visual{min-height:520px}.visual-pair>.section-visual img{height:100%;max-height:none}.visual-pair--process{grid-template-columns:minmax(0,1fr) minmax(340px,.72fr)}.visual-pair--process>.section-visual{order:2}.visual-cinema{margin-top:42px}.visual-cinema>.section-visual{overflow:hidden;aspect-ratio:16/7}.visual-cinema>.section-visual img{height:100%;max-height:none}.visual-cinema--cards>.cards{position:relative;z-index:2;width:min(94%,1040px);margin:-72px auto 0}.visual-cinema--cards>.cards article{background:color-mix(in srgb,var(--paper) 96%,transparent);backdrop-filter:blur(14px)}.visual-cinema--steps>.steps,.visual-cinema--transformation>.before-after{position:relative;z-index:2;width:min(92%,980px);margin:-64px auto 0}.visual-cinema--steps>.steps li,.visual-cinema--transformation>.before-after article{background:var(--paper)}.visual-story{display:grid;grid-template-columns:minmax(360px,.8fr) minmax(0,1.2fr);gap:0;margin-top:42px;background:var(--ink);color:var(--paper)}.visual-story>.section-visual img{height:100%;max-height:none}.visual-story .story-rail{grid-auto-flow:row;grid-auto-columns:auto;margin:0;padding:clamp(28px,5vw,64px);overflow:visible}.visual-story .story-rail article{border-color:color-mix(in srgb,var(--paper) 25%,transparent)}.visual-cta{position:relative;isolation:isolate;overflow:hidden;width:100%;max-width:none;padding:clamp(88px,12vw,168px) max(20px,calc((100vw - 1160px)/2));text-align:left}.visual-cta>.section-visual{position:absolute;z-index:-2;inset:0}.visual-cta>.section-visual:after{content:'';position:absolute;inset:0;background:linear-gradient(90deg,color-mix(in srgb,var(--ink) 94%,transparent),color-mix(in srgb,var(--ink) 62%,transparent) 62%,transparent)}.visual-cta>.section-visual img{width:100%;height:100%;max-height:none}.visual-cta .cta-copy{max-width:760px;color:var(--paper)}.visual-cta .section-heading h2{font-size:clamp(2.5rem,6vw,6rem)}.variant-hero-form{overflow:hidden}.variant-hero-form .inner{position:relative;isolation:isolate;grid-template-columns:minmax(0,1fr) minmax(320px,.56fr);min-height:min(820px,90vh);padding-inline:clamp(24px,5vw,72px);width:min(1440px,100%)}.variant-hero-form .hero-art{position:absolute;z-index:-2;inset:0}.variant-hero-form .hero-art:after{content:'';position:absolute;inset:0;background:linear-gradient(90deg,color-mix(in srgb,var(--paper) 98%,transparent) 0%,color-mix(in srgb,var(--paper) 84%,transparent) 43%,color-mix(in srgb,var(--paper) 28%,transparent) 72%,color-mix(in srgb,var(--ink) 18%,transparent))}.variant-hero-form .hero-art .section-visual,.variant-hero-form .hero-art img{width:100%;height:100%;max-height:none}.variant-hero-form .hero-copy{max-width:680px}.variant-hero-form .hero-form-wrap{align-self:end;margin-bottom:clamp(22px,5vw,72px);box-shadow:0 24px 80px color-mix(in srgb,var(--ink) 24%,transparent)}.variant-hero-form .lead-form{background:color-mix(in srgb,var(--paper) 96%,transparent);backdrop-filter:blur(18px)}@media(max-width:760px){.visual-pair,.visual-story{grid-template-columns:1fr}.visual-pair>.section-visual{min-height:auto;aspect-ratio:4/3}.visual-pair--process>.section-visual{order:0}.visual-cinema>.section-visual{aspect-ratio:4/3}.visual-cinema--cards>.cards,.visual-cinema--steps>.steps,.visual-cinema--transformation>.before-after{width:calc(100% - 24px);margin:-28px auto 0}.visual-story>.section-visual{aspect-ratio:4/3}.variant-hero-form .inner{display:flex;min-height:auto;padding:72px 20px 24px}.variant-hero-form .hero-art:after{background:linear-gradient(180deg,color-mix(in srgb,var(--paper) 98%,transparent) 0%,color-mix(in srgb,var(--paper) 88%,transparent) 48%,color-mix(in srgb,var(--paper) 35%,transparent) 100%)}.variant-hero-form .hero-copy{min-height:58vh;display:grid;align-content:center}.variant-hero-form .hero-form-wrap{margin:0}.visual-cta{padding-inline:20px}}</style>`;
  const visualQaOverrides = `<style>
    .integrated-copy-accessible{position:absolute!important;width:1px!important;height:1px!important;padding:0!important;margin:-1px!important;overflow:hidden!important;clip:rect(0,0,0,0)!important;white-space:nowrap!important;border:0!important}
    .integrated-context{max-width:880px;margin:0 0 36px;font-size:clamp(1rem,1.35vw,1.2rem);line-height:1.9}
    .has-integrated-copy .section-visual img{object-fit:contain;max-height:none}
    .variant-hero-form.has-integrated-copy .inner{position:relative;display:grid;grid-template-columns:minmax(0,1fr) minmax(320px,.34fr);width:100%;max-width:none;min-height:auto;padding:0;gap:0;background:#fff}
    .variant-hero-form.has-integrated-copy .hero-poster{grid-area:1/1/2/-1;display:block;color:inherit}
    .variant-hero-form.has-integrated-copy .hero-poster .section-visual,.variant-hero-form.has-integrated-copy .hero-poster img{display:block;width:100%;height:auto;margin:0}
    .variant-hero-form.has-integrated-copy .hero-form-wrap{grid-area:1/2/2/3;z-index:2;align-self:end;width:calc(100% - 32px);margin:0 32px 32px 0;box-shadow:0 24px 80px color-mix(in srgb,var(--ink) 28%,transparent)}
    .variant-hero-form .hero-art:after{
      background:linear-gradient(90deg,color-mix(in srgb,var(--paper) 92%,transparent) 0%,color-mix(in srgb,var(--paper) 66%,transparent) 43%,color-mix(in srgb,var(--paper) 12%,transparent) 72%,color-mix(in srgb,var(--ink) 16%,transparent));
    }
    @media(max-width:760px){
      .variant-hero-form.has-integrated-copy .inner{display:flex;flex-direction:column;padding:0}
      .variant-hero-form.has-integrated-copy .hero-poster{width:100%}
      .variant-hero-form.has-integrated-copy .hero-form-wrap{width:calc(100% - 32px);margin:-18px 16px 24px}
      .variant-hero-form .inner{display:flex;flex-direction:column;min-height:auto;padding:64px 20px 24px}
      .variant-hero-form .hero-copy{width:100%;min-height:64vh}
      .variant-hero-form .hero-form-wrap{width:100%;align-self:stretch}
      .variant-hero-form .hero-art:after{background:linear-gradient(180deg,color-mix(in srgb,var(--paper) 88%,transparent) 0%,color-mix(in srgb,var(--paper) 62%,transparent) 58%,color-mix(in srgb,var(--paper) 28%,transparent) 100%)}
    }
  </style>`;
  const layoutQaStyles = `<style>
    .mobile-nav{display:none;position:relative}
    .mobile-nav summary{cursor:pointer;list-style:none}
    .mobile-nav summary::-webkit-details-marker{display:none}
    .type-hero .inner,.variant-hero-form .inner{align-items:start}
    .variant-hero-form .hero-form-wrap{align-self:start;margin-bottom:0}
    @media(max-width:760px){
      .site-header{align-items:center;gap:10px}
      .brand{max-width:46%;font-size:.72rem}
      .mobile-nav{display:block}
      .mobile-nav summary{display:grid;place-items:center;min-width:48px;min-height:44px;padding:8px;border:1px solid var(--line);font-size:.75rem;font-weight:800}
      .mobile-nav-panel{position:absolute;top:calc(100% + 12px);right:0;display:grid;width:min(78vw,320px);padding:8px;background:var(--paper);border:1px solid var(--line);box-shadow:0 18px 48px color-mix(in srgb,var(--ink) 18%,transparent)}
      .mobile-nav-panel a{padding:14px 12px;color:inherit;text-decoration:none;border-bottom:1px solid var(--line);font-size:.86rem}
      .mobile-nav-panel a:last-child{border-bottom:0}
      .variant-hero-form .hero-form-wrap{width:100%;margin-bottom:0}
      .table-wrap{max-width:100%;overflow-x:auto;overscroll-behavior-inline:contain}
      .table-wrap table{width:max-content;min-width:720px}
      .table-wrap th,.table-wrap td{min-width:132px}
    }
  </style>`;
  const artDirectionStyles = `<style>
    :root{
      --canvas:#f2efe8;
      --surface:#fffdf8;
      --ink-soft:color-mix(in srgb,var(--ink) 68%,transparent);
      --blue:var(--accent);
      --signal:color-mix(in srgb,var(--accent) 72%,var(--paper));
      --display-font:'Noto Sans JP',sans-serif;
      --page-gutter:clamp(20px,5vw,72px);
      --section-space:clamp(88px,11vw,168px);
    }
    body{background:var(--canvas);font-feature-settings:'palt' 1,'kern' 1}
    .site-header{min-height:76px;padding-inline:var(--page-gutter);background:color-mix(in srgb,var(--canvas) 88%,transparent);border-bottom:1px solid color-mix(in srgb,var(--ink) 22%,transparent)}
    .brand{max-width:25em;font-family:var(--display-font);font-size:.82rem;line-height:1.35;letter-spacing:.01em}
    .site-nav a{position:relative;padding:10px 0;font-size:.76rem;font-weight:700}
    .site-nav a:after{content:'';position:absolute;right:0;bottom:5px;left:0;height:2px;background:var(--accent);transform:scaleX(0);transform-origin:left;transition:transform .25s ease}
    .site-nav a:hover:after{transform:scaleX(1)}
    .site-header>.button{margin:0;padding:16px 22px;background:var(--ink);color:var(--paper)}
    .section{position:relative;overflow:hidden;border:0}
    .section:not(.type-hero):before{content:attr(data-section-index);position:absolute;top:28px;right:var(--page-gutter);font:800 clamp(2.8rem,6vw,7rem)/1 var(--display-font);letter-spacing:.12em;color:color-mix(in srgb,currentColor 7%,transparent);pointer-events:none}
    .section>.inner{width:min(1240px,calc(100% - (var(--page-gutter) * 2)));padding-block:var(--section-space)}
    .section-heading{max-width:900px}
    .section-heading small,.integrated-intro>span{font:800 .7rem/1 var(--display-font);letter-spacing:.16em;text-transform:uppercase}
    .section-heading h2{max-width:12em;margin:.28em 0 .42em;font-family:var(--display-font);font-size:clamp(2.8rem,6.6vw,7.2rem);font-weight:800;line-height:1.02;letter-spacing:0}
    .section-heading .lead{max-width:48em;margin:0;font-size:clamp(1rem,1.25vw,1.18rem);line-height:1.9}
    .integrated-intro{display:grid;grid-template-columns:minmax(120px,.28fr) minmax(0,1fr);align-items:start;gap:clamp(24px,5vw,80px);margin:0 0 clamp(36px,5vw,72px);padding-top:18px;border-top:2px solid currentColor}
    .integrated-intro>span{padding-top:8px}
    .integrated-context{max-width:36em;margin:0;font-size:clamp(1.15rem,1.7vw,1.65rem);font-weight:600;line-height:1.75;letter-spacing:0}
    .section-visual{background:transparent}
    .has-integrated-copy .section-visual img{display:block;width:100%;height:auto;background:transparent}
    .button,.lead-form button{position:relative;min-height:52px;padding:15px 22px;border:1px solid currentColor;background:var(--ink);color:var(--paper);transition:transform .2s ease,box-shadow .2s ease}
    .button:hover,.lead-form button:hover{transform:translate(-3px,-3px);box-shadow:6px 6px 0 var(--accent)}

    .type-trust{background:var(--surface)}
    .type-trust>.inner{padding-block:clamp(56px,6vw,88px)}
    .type-trust .section-heading{display:grid;grid-template-columns:minmax(220px,.7fr) minmax(0,1.3fr);gap:clamp(28px,6vw,88px);max-width:none}
    .type-trust .section-heading h2{margin:0;font-size:clamp(2rem,4vw,4.5rem)}
    .type-trust .section-heading .lead{align-self:end}
    .type-trust .logo-metrics,.type-trust .metric-band{margin-top:56px}
    .type-trust .logo-wall{background:transparent;border-top:1px solid var(--ink);border-bottom:1px solid var(--ink)}
    .type-trust .logo-wall span{background:transparent;text-align:left}

    .type-problem{background:var(--canvas)!important;color:var(--ink)}
    .type-problem .visual-cinema--cards{display:grid;grid-template-columns:minmax(0,1.18fr) minmax(310px,.82fr);align-items:start;gap:0;margin-top:0}
    .type-problem .visual-cinema--cards>.section-visual{position:sticky;top:96px;aspect-ratio:auto;overflow:visible}
    .type-problem .visual-cinema--cards>.cards{display:grid;grid-template-columns:1fr;width:auto;margin:clamp(40px,5vw,72px) 0 0 -28px;gap:0}
    .type-problem .cards article{min-height:180px;padding:clamp(28px,3vw,46px);background:var(--surface);border:1px solid var(--ink)}
    .type-problem .cards article:nth-child(2){background:var(--ink);color:var(--paper)}
    .type-problem .cards article:nth-child(3){background:var(--accent)}
    .type-problem .cards h3{max-width:14em;margin:.45em 0 0;font-size:clamp(1.45rem,2vw,2.25rem);line-height:1.35;letter-spacing:0}

    .type-risk{background:var(--blue);color:#fff}
    .type-risk .integrated-intro{border-color:#fff}
    .type-risk .visual-pair--risk{grid-template-columns:minmax(0,1.12fr) minmax(330px,.72fr);gap:clamp(24px,4vw,56px);margin-top:0}
    .type-risk .section-visual{align-self:start;padding:clamp(10px,1.4vw,20px);background:var(--signal);transform:rotate(-1.2deg)}
    .type-risk .timeline{margin:0;border-top:1px solid rgba(255,255,255,.55)}
    .type-risk .timeline li{grid-template-columns:58px 1fr;gap:20px;padding:28px 0;border-top:0;border-bottom:1px solid rgba(255,255,255,.28)}
    .type-risk .timeline time{font-size:1rem;color:var(--signal)}
    .type-risk .timeline h3{font-size:clamp(1.25rem,2vw,2rem);line-height:1.35}
    .type-risk .timeline p{color:rgba(255,255,255,.8)}

    .type-mechanism{background:var(--surface)}
    .type-mechanism:nth-of-type(even){background:var(--accent)}
    .type-mechanism .visual-cinema{margin-top:0}
    .type-mechanism .visual-cinema>.section-visual{aspect-ratio:auto;border:1px solid var(--ink)}
    .type-mechanism .visual-cinema--steps>.steps{width:calc(100% - clamp(20px,5vw,80px));margin:clamp(-56px,-4vw,-32px) 0 0 auto;gap:0;box-shadow:-14px 14px 0 var(--ink)}
    .type-mechanism .steps li{min-height:210px;padding:clamp(24px,3vw,42px);background:var(--surface);border-color:var(--ink)}
    .type-mechanism .steps li:nth-child(even){background:var(--blue);color:#fff}
    .type-mechanism .steps span{font-size:clamp(2rem,4vw,4.5rem);line-height:1;color:var(--accent)}
    .type-mechanism .before-after{gap:0;background:var(--ink);border:1px solid var(--ink);box-shadow:12px 12px 0 var(--blue)}
    .type-mechanism .before-after article{min-height:300px;padding:clamp(34px,5vw,70px);background:var(--surface)}
    .type-mechanism .before-after article:last-child{background:var(--ink);color:var(--paper)}

    .type-usecase{background:var(--canvas)}
    .type-usecase .visual-story{grid-template-columns:minmax(360px,1.08fr) minmax(0,.92fr);margin-top:0;background:var(--ink)}
    .type-usecase .visual-story>.section-visual{align-self:center;padding:clamp(16px,2.5vw,34px);background:var(--accent)}
    .type-usecase .story-rail{gap:0!important;padding:0!important}
    .type-usecase .story-rail article{min-height:200px;padding:clamp(28px,4vw,50px)!important;border:0!important;border-bottom:1px solid rgba(255,255,255,.25)!important}
    .type-usecase .story-rail article:nth-child(2){background:var(--blue)}
    .type-usecase .story-rail h3{font-size:clamp(1.35rem,2vw,2.15rem);letter-spacing:0}
    .type-usecase .usecase-items{grid-template-columns:repeat(12,1fr);gap:0;margin-top:48px;border:1px solid var(--ink)}
    .type-usecase .usecase-items article{grid-column:span 4;min-height:250px;padding:clamp(24px,3vw,42px);border:0;border-right:1px solid var(--ink)}
    .type-usecase .usecase-items article:nth-child(2){grid-column:span 5;background:var(--signal)}
    .type-usecase .usecase-items article:nth-child(3){grid-column:span 3;background:var(--ink);color:var(--paper)}

    .type-differentiation{background:var(--ink);color:var(--paper)}
    .type-differentiation .visual-pair--reason{grid-template-columns:minmax(300px,.72fr) minmax(0,1.28fr);gap:clamp(32px,6vw,88px);margin-top:0}
    .type-differentiation .reason-stack{margin:0;border-top:1px solid rgba(255,255,255,.35)}
    .type-differentiation .reason-stack li{display:grid;grid-template-columns:54px 1fr;gap:18px;padding:30px 0;border:0;border-bottom:1px solid rgba(255,255,255,.25)}
    .type-differentiation .reason-stack b{font-size:1.5rem;color:var(--accent)}
    .type-differentiation .reason-stack h3{font-size:clamp(1.3rem,2vw,2.1rem)}
    .type-differentiation .section-visual{padding:14px;background:var(--blue);transform:rotate(1deg)}

    .type-comparison{background:var(--surface)}
    .type-comparison .table-wrap{margin-top:clamp(42px,6vw,82px);border-top:4px solid var(--ink)}
    .type-comparison table{font-size:.94rem}
    .type-comparison th,.type-comparison td{padding:24px;border:0;border-bottom:1px solid var(--ink)}
    .type-comparison thead th{background:var(--ink);color:var(--paper)}
    .type-comparison tbody td:first-of-type{background:color-mix(in srgb,var(--signal) 62%,var(--surface));font-weight:800}

    .type-process{background:var(--blue);color:#fff}
    .type-process .visual-pair--process{grid-template-columns:minmax(330px,.66fr) minmax(0,1.34fr);gap:clamp(30px,5vw,72px);margin-top:0}
    .type-process .visual-pair--process>.section-visual{order:0;align-self:start;padding:12px;background:var(--signal)}
    .type-process .timeline{margin:0;border-top:1px solid rgba(255,255,255,.55)}
    .type-process .timeline li{grid-template-columns:120px 1fr;padding:34px 0;border-color:rgba(255,255,255,.28)}
    .type-process .timeline time{color:var(--signal)}
    .type-process .timeline h3{font-size:clamp(1.4rem,2.2vw,2.5rem)}

    .type-proof{background:var(--surface)}
    .type-proof .case-featured,.type-proof .metric-case{grid-template-columns:minmax(0,1.15fr) minmax(320px,.85fr);gap:0;margin-top:64px;border:1px solid var(--ink)}
    .type-proof .case-featured>.section-visual{height:100%}
    .type-proof .case-featured>.section-visual img{height:100%;max-height:none;object-fit:cover}
    .type-proof .case-featured>div{padding:clamp(32px,5vw,72px);background:var(--canvas)}
    .type-proof .case-story div{grid-template-columns:80px 1fr;padding:22px 0}

    .type-pricing{background:var(--accent)}
    .type-pricing .estimate{margin-top:56px;padding:0;border:2px solid var(--ink);background:var(--surface);box-shadow:14px 14px 0 var(--blue)}
    .type-pricing .estimate>div,.type-pricing .estimate>ol{margin:0;padding:clamp(30px,5vw,64px)}
    .type-pricing .estimate>ol{background:var(--ink);color:var(--paper)}
    .type-pricing .estimate b{font-size:clamp(2.4rem,5vw,6rem);line-height:1}
    .type-pricing .plans{grid-template-columns:repeat(2,minmax(0,1fr));gap:0;margin-top:56px;border:2px solid var(--ink);box-shadow:14px 14px 0 var(--blue)}
    .type-pricing .plans article{min-height:470px;padding:clamp(32px,5vw,64px);border:0;border-right:1px solid var(--ink);background:var(--surface)}
    .type-pricing .plans article:last-child{border-right:0;background:var(--ink);color:var(--paper)}
    .type-pricing .plans .button{margin-top:32px}

    .type-fit{background:var(--canvas)}
    .type-fit .checklist{grid-template-columns:repeat(2,minmax(0,1fr));gap:0;margin-top:56px;border:1px solid var(--ink)}
    .type-fit .checklist li{min-height:180px;padding:32px;border:0;border-right:1px solid var(--ink);border-bottom:1px solid var(--ink)}
    .type-fit .checklist li:before{background:var(--blue);color:#fff}

    .type-faq{background:var(--surface)}
    .type-faq .faq{margin-top:56px;border-top:2px solid var(--ink)}
    .type-faq .faq details{padding:28px 0;border-color:var(--ink)}
    .type-faq .faq summary{grid-template-columns:72px 1fr;font-size:clamp(1.05rem,1.5vw,1.3rem)}
    .type-faq .faq summary span{color:var(--blue)}
    .type-faq .faq details p{max-width:50em;margin-left:72px;line-height:1.9}

    .type-message{background:var(--ink);color:var(--paper)}
    .type-message .visual-cta{display:grid;align-content:center;min-height:70vh;padding-block:clamp(96px,14vw,190px)}
    .type-message .visual-cta:after{content:'→';position:absolute;right:var(--page-gutter);bottom:28px;font-size:clamp(4rem,12vw,12rem);line-height:1;color:var(--accent)}
    .type-message .visual-cta .section-heading h2{max-width:10em;font-size:clamp(3.2rem,7.5vw,8rem);line-height:.98}

    .type-form{background:var(--surface)}
    .type-form>.inner{grid-template-columns:minmax(300px,.75fr) minmax(0,1.25fr);align-items:start}
    .type-form .section-heading{position:sticky;top:120px}
    .type-form .section-heading h2{font-size:clamp(2.8rem,5.5vw,6.2rem)}
    .lead-form{gap:20px;padding:clamp(28px,5vw,64px);background:var(--canvas);border:1px solid var(--ink)}
    .lead-form label>span{font-size:.78rem;font-weight:800}
    .lead-form input,.lead-form textarea{min-height:54px;padding:14px 0;background:transparent;border:0;border-bottom:1px solid var(--ink);border-radius:0}
    .lead-form textarea{min-height:130px}
    .lead-form button{background:var(--accent);color:var(--ink)}
    .site-footer{background:var(--ink);color:var(--paper)}

    @media(max-width:900px){
      .type-problem .visual-cinema--cards,.type-risk .visual-pair--risk,.type-differentiation .visual-pair--reason,.type-process .visual-pair--process,.type-proof .case-featured,.type-proof .metric-case{grid-template-columns:1fr}
      .type-problem .visual-cinema--cards>.section-visual{position:relative;top:auto}
      .type-problem .visual-cinema--cards>.cards{margin:-24px 16px 0}
      .type-usecase .visual-story{grid-template-columns:1fr}
      .type-usecase .usecase-items article,.type-usecase .usecase-items article:nth-child(2),.type-usecase .usecase-items article:nth-child(3){grid-column:span 12}
      .type-process .visual-pair--process>.section-visual{order:0}
    }
    @media(max-width:760px){
      :root{--page-gutter:16px;--section-space:72px}
      .site-header{min-height:68px}
      .section:not(.type-hero):before{top:16px;right:16px;font-size:3.5rem}
      .section>.inner{width:calc(100% - 32px)}
      .integrated-intro{grid-template-columns:1fr;gap:14px;margin-bottom:30px}
      .integrated-context{font-size:1.06rem;line-height:1.85}
      .type-trust .section-heading{grid-template-columns:1fr}
      .type-problem .visual-cinema--cards>.cards{width:calc(100% - 24px);margin:-18px auto 0}
      .type-problem .cards article{min-height:auto}
      .type-risk .section-visual,.type-differentiation .section-visual,.type-process .visual-pair--process>.section-visual{padding:7px;transform:none}
      .type-mechanism .visual-cinema--steps>.steps{width:calc(100% - 20px);margin:-18px 0 0 auto;box-shadow:-8px 8px 0 var(--ink)}
      .type-mechanism .steps li{min-height:auto}
      .type-usecase .visual-story>.section-visual{aspect-ratio:auto}
      .type-usecase .story-rail article{min-height:auto}
      .type-pricing .estimate{grid-template-columns:1fr;box-shadow:8px 8px 0 var(--blue)}
      .type-pricing .plans{grid-template-columns:1fr;box-shadow:8px 8px 0 var(--blue)}
      .type-pricing .plans article{min-height:auto;border-right:0;border-bottom:1px solid var(--ink)}
      .type-fit .checklist{grid-template-columns:1fr}
      .type-faq .faq details p{margin-left:0}
      .type-form>.inner{grid-template-columns:1fr}
      .type-form .section-heading{position:relative;top:auto}
      .type-message .visual-cta{min-height:58vh}
    }
  </style>`;
  const designFoundationStyles = `<style>
    :root{
      --grid-max:1200px;
      --grid-gutter:clamp(20px,4vw,56px);
      --space-1:8px;--space-2:16px;--space-3:24px;--space-4:32px;--space-5:48px;--space-6:72px;--space-7:104px;--space-8:144px;
      --canvas:color-mix(in srgb,var(--paper) 94%,var(--ink));
      --surface:var(--paper);
      --muted:color-mix(in srgb,var(--ink) 64%,var(--paper));
      --soft:color-mix(in srgb,var(--paper) 90%,var(--ink));
      --inverse:var(--ink);
      --inverse-text:var(--paper);
      --blue:var(--accent);
      --signal:color-mix(in srgb,var(--accent) 72%,var(--paper));
      --page-gutter:var(--grid-gutter);
      --section-space:var(--space-7);
      --display-font:var(--heading-font);
    }
    body{background:var(--canvas);font-feature-settings:'palt' 0,'kern' 1}
    .section{isolation:isolate;background:var(--surface);color:var(--ink)}
    .section-background{position:absolute;z-index:0;inset:0;overflow:hidden;pointer-events:none}
    .section-background:after{content:'';position:absolute;inset:0;background:color-mix(in srgb,var(--surface) 78%,transparent)}
    .section-background img{display:block;width:100%;height:100%;object-fit:cover;filter:saturate(.82) contrast(.94);transform:scale(1.02)}
    .section>.inner{position:relative;z-index:1}
    .section>.inner,.cta-banner,.sample-cta{width:min(var(--grid-max),calc(100% - (var(--grid-gutter) * 2)));margin-inline:auto}
    .section>.inner{padding-block:var(--space-7)}
    .density-statement>.inner{padding-block:var(--space-8)}
    .density-information>.inner{padding-block:var(--space-6)}
    .density-evidence>.inner{padding-block:var(--space-7)}
    .section:not(.type-hero):before{display:none}
    .section-heading,.integrated-intro{width:100%;max-width:none}
    .section-heading h1,.section-heading h2,.cards h3,.rich-stack h3,.rich-checklist h3,.rich-timeline h3,.rich-zigzag h3,.rich-story h3{letter-spacing:0;line-height:1.28}
    .section h1,.section h2,.section h3,.section h4{letter-spacing:0!important}
    .section-heading h1{max-width:12em;font-size:clamp(2.7rem,5.2vw,5.9rem)}
    .section-heading h2{max-width:14em;margin:.28em 0 .45em;font-size:clamp(2.1rem,4.5vw,4.8rem)}
    .section-heading small,.integrated-intro>span,.cards small,.timeline time{letter-spacing:.14em}
    .section-heading .lead,.integrated-context,.cards p,.rich-stack p,.rich-checklist p,.rich-timeline p,.rich-zigzag p,.rich-story p,.faq p,.case-story dd{max-inline-size:40em;line-height:1.85;letter-spacing:0}
    .integrated-intro{grid-template-columns:repeat(12,minmax(0,1fr));gap:var(--space-3);margin-bottom:var(--space-5);padding-top:var(--space-2);border-top:1px solid currentColor}
    .integrated-intro>span{grid-column:1/4}
    .integrated-intro>.integrated-context{grid-column:4/10;font-size:clamp(1rem,1.3vw,1.16rem);font-weight:500}
    .button,.lead-form button{border:1px solid var(--ink);background:var(--accent);color:var(--ink);box-shadow:none}
    .button:hover,.lead-form button:hover{transform:translateY(-2px);box-shadow:0 5px 0 var(--ink)}

    /* 密度は役割で変える。画像とカードは偶然重ねず、同じ12カラムへ置く。 */
    .visual-cinema,.visual-pair,.visual-story{margin-top:0}
    .visual-cinema--cards{display:grid;grid-template-columns:repeat(12,minmax(0,1fr));gap:var(--space-4);align-items:start}
    .visual-cinema--cards>.section-visual{grid-column:1/8;position:relative;top:auto;aspect-ratio:4/3;overflow:hidden}
    .visual-cinema--cards>.cards{grid-column:8/13;width:auto;margin:0;display:grid;grid-template-columns:1fr;gap:0;border-top:1px solid var(--ink)}
    .visual-cinema--cards>.cards article{min-height:auto;padding:var(--space-4) 0;border:0;border-bottom:1px solid var(--ink);background:transparent;backdrop-filter:none}
    .visual-pair{grid-template-columns:repeat(12,minmax(0,1fr));gap:var(--space-4)}
    .visual-pair>.section-visual{grid-column:1/7;min-height:auto;aspect-ratio:4/3;overflow:hidden}
    .visual-pair>.timeline,.visual-pair>.reason-stack{grid-column:7/13}
    .visual-story{grid-template-columns:repeat(12,minmax(0,1fr));background:var(--inverse);color:var(--inverse-text)}
    .visual-story>.section-visual{grid-column:1/8;min-height:520px}
    .visual-story>.story-rail{grid-column:8/13}
    .has-generated-visual .section-visual img{height:100%;object-fit:cover}
    .type-problem .visual-cinema--cards{grid-template-columns:repeat(12,minmax(0,1fr));gap:var(--space-4);margin-top:0}
    .type-problem .visual-cinema--cards>.section-visual{grid-column:1/8;position:relative;top:auto}
    .type-problem .visual-cinema--cards>.cards{grid-column:8/13;width:auto;margin:0;gap:0}

    /* 型の意味を、文字を読まなくても判別できる形にする。 */
    .before-after,.compare-split,.fit-split{position:relative;display:grid;grid-template-columns:1fr 72px 1fr;gap:0;background:transparent;border:1px solid var(--ink)}
    .before-after:before,.compare-split:before{content:'→';grid-column:2;grid-row:1;display:grid;place-items:center;background:var(--accent);color:var(--ink);font-size:2rem;font-weight:800}
    .before-after article,.compare-split article,.fit-split article{padding:clamp(32px,4vw,56px);border:0}
    .before-after article:first-child,.compare-split article:first-child{grid-column:1;grid-row:1;background:var(--soft);color:var(--muted)}
    .before-after article:last-child,.compare-split article:last-child{grid-column:3;grid-row:1;background:var(--inverse);color:var(--inverse-text)}
    .before-after article:last-child small,.compare-split article:last-child small{color:var(--accent)}
    .timeline{border-left:2px solid var(--ink)}
    .timeline li{position:relative;grid-template-columns:80px 1fr;padding:var(--space-4);border:0;border-bottom:1px solid var(--line)}
    .timeline li:before{content:'';position:absolute;left:-7px;top:38px;width:12px;height:12px;border:2px solid var(--ink);border-radius:50%;background:var(--accent)}
    .steps{position:relative;gap:0}
    .steps:before{content:'';position:absolute;top:32px;right:8%;left:8%;height:2px;background:var(--ink)}
    .steps li{position:relative;border:0;border-top:1px solid var(--ink);background:var(--surface)}
    .steps span{position:relative;z-index:1;display:grid;place-items:center;width:64px;height:64px;background:var(--accent);border:2px solid var(--ink);border-radius:50%}
    .type-comparison .table-wrap{border-top:3px solid var(--ink)}
    .type-comparison th,.type-comparison td{padding:var(--space-3);border:0;border-bottom:1px solid var(--line)}
    .type-comparison thead th{background:var(--inverse);color:var(--inverse-text)}
    .type-comparison tbody td:first-of-type{background:color-mix(in srgb,var(--accent) 24%,var(--paper));box-shadow:inset 3px 0 0 var(--accent);font-weight:800}
    .type-fit .fit-split{grid-template-columns:1fr 1fr}
    .type-fit .fit-split article:first-child{background:color-mix(in srgb,var(--accent) 24%,var(--paper));border-top:6px solid var(--accent)}
    .type-fit .fit-split article:last-child{background:var(--soft);color:var(--muted);border-top:6px solid var(--muted)}
    .type-fit .checklist{display:grid;grid-template-columns:repeat(2,1fr);gap:0;border:1px solid var(--ink)}
    .type-fit .checklist li{min-height:auto;background:color-mix(in srgb,var(--accent) 15%,var(--paper))}
    .type-fit .checklist li:nth-last-child(-n+2){background:var(--soft);color:var(--muted)}
    .type-fit .checklist li:nth-last-child(-n+2):before{content:'—';background:var(--muted);color:var(--paper)}
    .type-fit .fit-items{display:grid;grid-template-columns:repeat(2,1fr);gap:0;border:1px solid var(--ink)}
    .type-fit .fit-items article{min-height:210px;padding:var(--space-4);border:0;border-right:1px solid var(--ink);border-bottom:1px solid var(--ink);background:color-mix(in srgb,var(--accent) 18%,var(--paper))}
    .type-fit .fit-items article:nth-child(-n+2){border-top:6px solid var(--accent)}
    .type-fit .fit-items article:nth-last-child(-n+2){background:var(--soft);color:var(--muted);border-top:6px solid var(--muted)}
    .type-fit .fit-items article:nth-last-child(-n+2) small:before{content:'NOT ';font-weight:800}

    /* A: 写真と余白を中心にした編集紙面。 */
    body.tone-editorial{--section-space:var(--space-7)}
    body.tone-editorial .section-heading h1,body.tone-editorial .section-heading h2{font-family:'Noto Serif JP',serif;font-weight:600}
    body.tone-editorial .type-trust,body.tone-editorial .type-proof,body.tone-editorial .type-faq{background:var(--paper)}
    body.tone-editorial .type-risk,body.tone-editorial .type-differentiation,body.tone-editorial .type-message{background:var(--ink);color:var(--paper)}
    body.tone-editorial .type-mechanism,body.tone-editorial .type-pricing{background:color-mix(in srgb,var(--accent) 14%,var(--paper))}
    body.tone-editorial .type-usecase .visual-story{grid-template-columns:repeat(12,minmax(0,1fr))}
    body.tone-editorial .type-usecase .visual-story>.section-visual{grid-column:1/9}
    body.tone-editorial .type-usecase .story-rail{grid-column:9/13}
    body.tone-editorial .section-background:after{background:color-mix(in srgb,var(--surface) 82%,transparent)}
    body.tone-editorial .type-message .section-background:after{background:color-mix(in srgb,var(--ink) 62%,transparent)}
    .type-message.has-integrated-copy>.inner{display:block;width:min(var(--grid-max),calc(100% - (var(--grid-gutter) * 2)));padding-block:var(--space-6)}
    .message-poster{overflow:hidden;border:1px solid color-mix(in srgb,var(--paper) 24%,transparent)}
    .message-poster .section-visual img{display:block;width:100%;height:auto;max-height:none;object-fit:contain}
    .message-mobile-copy{display:none}
    @media(max-width:760px){
      .type-message.has-integrated-copy .message-mobile-copy{display:block;margin-top:var(--space-3);padding:var(--space-3);border-left:4px solid var(--accent)}
      .type-message.has-integrated-copy .message-mobile-copy blockquote{font-size:clamp(2rem,11vw,3.4rem);line-height:1.2}
      .type-message.has-integrated-copy .message-mobile-copy p{line-height:1.9}
    }

    /* B: 日本語タイポと罫線が主役。余白だけで逃げず、情報密度を上げる。 */
    body.tone-graphic{--section-space:var(--space-6);font-family:'Zen Kaku Gothic New','Noto Sans JP',sans-serif}
    body.tone-graphic .section-heading h1,body.tone-graphic .section-heading h2{font-family:'Zen Kaku Gothic New','Noto Sans JP',sans-serif;font-weight:900;line-height:1.12}
    body.tone-graphic .section{border-bottom:1px solid var(--ink)}
    body.tone-graphic .section:not(.type-hero):before{display:block;top:var(--space-3);right:var(--grid-gutter);font-size:1rem;letter-spacing:.12em;color:var(--ink)}
    body.tone-graphic .type-problem,body.tone-graphic .type-comparison,body.tone-graphic .type-pricing{background:var(--accent);color:var(--ink)}
    body.tone-graphic .type-risk,body.tone-graphic .type-differentiation,body.tone-graphic .type-message{background:var(--ink);color:var(--paper)}
    body.tone-graphic .cards article,body.tone-graphic .plans article{border-color:var(--ink);border-radius:0}
    body.tone-graphic .integrated-intro{border-top-width:3px}
    body.tone-graphic .section-background img{filter:grayscale(1) contrast(1.2)}
    body.tone-graphic .section-background:after{background:color-mix(in srgb,var(--surface) 86%,transparent);mix-blend-mode:normal}

    /* C: 黒い検証台の上へ証拠・UI・工程を積む。 */
    body.tone-documentary{--canvas:var(--ink);--surface:var(--paper);--section-space:var(--space-6);background:var(--ink)}
    body.tone-documentary .section{background:var(--ink);color:var(--paper);border-bottom:1px solid color-mix(in srgb,var(--paper) 20%,transparent)}
    body.tone-documentary .type-trust,body.tone-documentary .type-mechanism,body.tone-documentary .type-comparison,body.tone-documentary .type-proof,body.tone-documentary .type-faq,body.tone-documentary .type-form{background:var(--paper);color:var(--ink)}
    body.tone-documentary .section-heading h1,body.tone-documentary .section-heading h2{font-family:'Noto Sans JP',sans-serif;font-weight:700}
    body.tone-documentary .section-visual{padding:var(--space-2);background:color-mix(in srgb,var(--paper) 8%,transparent);border:1px solid color-mix(in srgb,var(--paper) 24%,transparent)}
    body.tone-documentary .type-comparison .section-visual,body.tone-documentary .type-proof .section-visual,body.tone-documentary .type-form .section-visual{border-color:var(--line)}
    body.tone-documentary .timeline{border-left-color:var(--accent)}
    body.tone-documentary .timeline li:before{border-color:var(--accent)}
    body.tone-documentary .type-pricing{background:var(--accent);color:var(--ink)}
    body.tone-documentary .section-background img{filter:saturate(.5) contrast(1.2) brightness(.62)}
    body.tone-documentary .section-background:after{background:color-mix(in srgb,var(--ink) 68%,transparent)}
    body.tone-documentary .type-trust .section-background:after,body.tone-documentary .type-mechanism .section-background:after,body.tone-documentary .type-comparison .section-background:after,body.tone-documentary .type-proof .section-background:after,body.tone-documentary .type-faq .section-background:after,body.tone-documentary .type-form .section-background:after{background:color-mix(in srgb,var(--paper) 84%,transparent)}

    .has-generated-background.variant-hero-form.has-integrated-copy .inner{display:grid;grid-template-columns:minmax(0,1fr) minmax(320px,380px);gap:var(--space-4);width:min(var(--grid-max),calc(100% - (var(--grid-gutter) * 2)));padding-block:var(--space-6);background:transparent}
    .has-generated-background.variant-hero-form.has-integrated-copy .hero-poster{grid-area:auto;align-self:center;overflow:hidden;border:1px solid color-mix(in srgb,var(--ink) 30%,transparent)}
    .has-generated-background.variant-hero-form.has-integrated-copy .hero-form-wrap{grid-area:auto;align-self:end;width:100%;margin:0}

    @media(max-width:900px){
      .visual-cinema--cards,.visual-pair,.visual-story{grid-template-columns:1fr}
      .visual-cinema--cards>.section-visual,.visual-cinema--cards>.cards,.visual-pair>.section-visual,.visual-pair>.timeline,.visual-pair>.reason-stack,.visual-story>.section-visual,.visual-story>.story-rail,body.tone-editorial .type-usecase .visual-story>.section-visual,body.tone-editorial .type-usecase .story-rail{grid-column:1}
      .type-problem .visual-cinema--cards>.section-visual,.type-problem .visual-cinema--cards>.cards{grid-column:1;width:auto;margin:0}
      .visual-story>.section-visual{min-height:auto;aspect-ratio:4/3}
    }
    @media(max-width:760px){
      :root{--grid-gutter:16px;--section-space:var(--space-6)}
      .section>.inner{padding-block:var(--space-6)}
      .density-statement>.inner{padding-block:var(--space-7)}
      .integrated-intro{grid-template-columns:1fr;gap:var(--space-2)}
      .integrated-intro>span,.integrated-intro>.integrated-context{grid-column:1}
      .before-after,.compare-split{grid-template-columns:1fr}
      .before-after:before,.compare-split:before{grid-column:1;grid-row:2;min-height:52px;transform:rotate(90deg)}
      .before-after article:first-child,.compare-split article:first-child{grid-column:1;grid-row:1}
      .before-after article:last-child,.compare-split article:last-child{grid-column:1;grid-row:3}
      .steps{grid-template-columns:1fr}.steps:before{top:0;bottom:0;left:31px;width:2px;height:auto}
      .type-fit .checklist{grid-template-columns:1fr}
      .type-fit .fit-items{grid-template-columns:1fr}
      .has-generated-background.variant-hero-form.has-integrated-copy .inner{display:flex;width:calc(100% - 32px);padding-block:var(--space-5)}
      .has-generated-background.variant-hero-form.has-integrated-copy .hero-form-wrap{width:100%;margin:0}
    }
  </style>`;
  const rendered = variantStyles + mobileSafetyStyles + responsivePolishStyles + visualSystemStyles + visualQaOverrides + layoutQaStyles + artDirectionStyles + designFoundationStyles + safeSections.map((section, index) => renderSection(section, index, project, imageRecords)).join("") + formSection;
  const title = project.name || safeSections[0]?.title || "Landing Page";
  const description = answers.metaDescription || project.sourceSummary || safeSections[0]?.body || title;
  const canonical = safeUrl(answers.canonicalUrl, "");
  const nav = safeSections.filter((section) => ["mechanism", "differentiation", "proof", "pricing", "faq"].includes(normalizeWireframeType(section))).slice(0, 4);
  const logo = answers.formalServiceName || project.name || "SERVICE";
  const publicUrl = safeUrl(options.publicUrl || answers.publicUrl || answers.canonicalUrl, "");
  const heroImage = imageRecords.find((image) => image.sectionId === safeSections[0]?.id) || imageRecords[0];
  const ogImage = safeUrl(answers.ogImageUrl, "") || absoluteAssetUrl(heroImage?.src, publicUrl);
  const socialImageMeta = ogImage ? `<meta property="og:image" content="${ogImage}"><meta name="twitter:image" content="${ogImage}">` : "";
  const navLinks = nav.map((section) => `<a href="#${safeId(section.id, "section")}">${escapeHtml(section.label || section.title)}</a>`).join("");
  const jsonLd = JSON.stringify({ "@context": "https://schema.org", "@type": "WebPage", name: title, description, url: canonical || undefined }).replaceAll("<", "\\u003c");
  const gaId = /^G-[A-Z0-9]+$/u.test(String(answers.ga4Id || "")) ? answers.ga4Id : "";
  return `<!doctype html><html lang="ja"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>${escapeHtml(title)}</title><meta name="description" content="${escapeHtml(description)}"><meta property="og:type" content="website"><meta property="og:title" content="${escapeHtml(title)}"><meta property="og:description" content="${escapeHtml(description)}"><meta property="og:url" content="${canonical}">${socialImageMeta}<meta name="twitter:card" content="summary_large_image">${canonical ? `<link rel="canonical" href="${canonical}">` : ""}<link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' rx='12' fill='%23111'/%3E%3Cpath d='M17 18h30v8H27v20H17z' fill='%23fff'/%3E%3C/svg%3E"><link rel="preconnect" href="https://fonts.googleapis.com"><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin><link href="https://fonts.googleapis.com/css2?family=Noto+Sans+JP:wght@400;600;800&family=Noto+Serif+JP:wght@500;700&display=swap" rel="stylesheet"><script type="application/ld+json">${jsonLd}</script>${gaId ? `<script async src="https://www.googletagmanager.com/gtag/js?id=${gaId}"></script><script>window.dataLayer=window.dataLayer||[];function gtag(){dataLayer.push(arguments)}gtag('js',new Date());gtag('config','${gaId}');</script>` : ""}<style>:root{--ink:${escapeHtml(ink)};--accent:${escapeHtml(accent)};--paper:${escapeHtml(paper)};--line:color-mix(in srgb,var(--ink) 18%,transparent);--heading-font:${headingFont};--body-font:'Noto Sans JP',sans-serif}*{box-sizing:border-box}html{scroll-behavior:smooth}body{margin:0;background:var(--paper);color:var(--ink);font-family:var(--body-font);overflow-wrap:anywhere;word-break:normal;line-break:strict}.section-heading h1,.section-heading h2,.cards h3,blockquote{font-family:var(--heading-font)}body.tone-graphic .section-heading h1,body.tone-graphic .section-heading h2{font-weight:800;letter-spacing:-.03em}body.tone-graphic .type-problem{background:var(--accent)}body.tone-documentary .inner{padding-block:clamp(52px,6vw,84px)}body.tone-documentary .cards article{background:color-mix(in srgb,var(--paper) 94%,var(--ink));border-radius:14px}.skip{position:fixed;z-index:99;left:12px;top:12px;padding:12px 16px;background:var(--ink);color:var(--paper);transform:translateY(-160%)}.skip:focus{transform:none}:focus-visible{outline:3px solid var(--accent);outline-offset:4px}.site-header{position:sticky;z-index:20;top:0;min-height:68px;padding:12px clamp(20px,5vw,72px);display:flex;align-items:center;gap:28px;background:color-mix(in srgb,var(--paper) 92%,transparent);backdrop-filter:blur(14px);border-bottom:1px solid var(--line)}.brand{margin-right:auto;color:inherit;text-decoration:none;font-weight:800}.site-nav{display:flex;gap:18px}.site-nav a{color:inherit;text-decoration:none;font-size:.82rem}.button,.lead-form button{display:inline-flex;align-items:center;justify-content:center;gap:24px;margin-top:24px;padding:15px 20px;border:0;background:var(--accent);color:var(--ink);font:inherit;font-weight:800;text-decoration:none}.section{border-bottom:1px solid var(--line)}.inner{width:min(1160px,calc(100% - 40px));margin:auto;padding:clamp(64px,8vw,112px) 0}.type-hero .inner{min-height:min(760px,86vh);display:grid;grid-template-columns:1fr 1fr;align-items:center;gap:clamp(28px,6vw,80px)}.hero-copy .section-visual{margin-top:30px}.section-heading{max-width:760px}.section-heading small{font-size:.72rem;letter-spacing:.14em;font-weight:700}.section-heading h1{margin:.25em 0;font-size:clamp(2.6rem,6vw,6.3rem);line-height:1.15;letter-spacing:0}.section-heading h2,blockquote{margin:.3em 0;font-size:clamp(2rem,4.5vw,4.5rem);line-height:1.28;letter-spacing:.01em}.lead{font-size:clamp(1rem,1.4vw,1.25rem);line-height:1.9}.proof{display:inline-block;padding:8px 11px;border:1px solid var(--line);font-weight:700}.section-visual{margin:0}.section-visual img{display:block;width:100%;height:auto;max-height:680px;object-fit:cover}.cards,.evidence,.plans{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:16px;margin-top:36px}.cards article,.evidence article,.plans article{padding:24px;border:1px solid var(--line)}.cards h3{font-size:1.25rem;line-height:1.5}.steps{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:0;margin:36px 0;padding:0;list-style:none}.steps li{padding:24px;border:1px solid var(--line)}.steps span{font-size:2rem;font-weight:800}.table-wrap{overflow:auto;margin-top:36px}table{width:100%;border-collapse:collapse}th,td{padding:18px;border:1px solid var(--line);text-align:left}.type-form .inner,.variant-hero-form .inner{display:grid;grid-template-columns:.85fr 1.15fr;gap:clamp(28px,6vw,72px)}.lead-form{display:grid;gap:14px;padding:clamp(20px,4vw,40px);background:color-mix(in srgb,var(--paper) 86%,var(--ink))}.lead-form label{display:grid;gap:6px}.lead-form input,.lead-form textarea{width:100%;padding:12px;border:1px solid var(--line);background:var(--paper);color:var(--ink);font:inherit}.lead-form .consent{grid-template-columns:auto 1fr;align-items:start}.lead-form .consent input{width:auto}.lead-form .consent a{color:inherit}.form-note,.form-status{margin:0;font-size:.78rem;line-height:1.6}.faq{margin-top:30px}.faq details{padding:20px 0;border-top:1px solid var(--line)}.faq summary{cursor:pointer;font-weight:700}.type-cta{text-align:center;background:var(--ink);color:var(--paper)}.type-message .inner{display:grid;grid-template-columns:1.2fr .8fr;gap:50px}.variant-feature-zigzag .cards{grid-template-columns:1fr}.variant-reason-stack .cards article:first-child{grid-row:span 2}.variant-lineup-catalog .cards{grid-template-columns:repeat(4,minmax(0,1fr))}.variant-review-cards .evidence article{min-height:220px}.variant-price-lineup .plans{grid-template-columns:repeat(2,minmax(0,1fr))}.site-footer{padding:28px clamp(20px,5vw,72px);display:flex;justify-content:space-between;gap:20px}.site-footer a{color:inherit}@media(max-width:760px){.site-header{align-items:flex-start}.site-nav{display:none}.type-hero .inner,.type-form .inner,.variant-hero-form .inner,.type-message .inner{grid-template-columns:1fr}.type-hero .inner{min-height:auto}.inner{width:min(100% - 32px,1160px);padding:56px 0}.section-heading h1{font-size:clamp(2.45rem,12vw,4rem)}.cards,.evidence,.plans,.steps,.variant-lineup-catalog .cards{grid-template-columns:1fr}}@media(prefers-reduced-motion:reduce){html{scroll-behavior:auto}*,*:before,*:after{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important}}</style></head><body class="tone-${mode}"><a class="skip" href="#content">本文へ移動</a><header class="site-header"><a class="brand" href="#content">${escapeHtml(logo)}</a><nav class="site-nav" aria-label="メインナビゲーション">${navLinks}</nav><details class="mobile-nav"><summary aria-label="セクションメニューを開く">目次</summary><nav class="mobile-nav-panel" aria-label="モバイルセクションナビゲーション">${navLinks}</nav></details><a class="button" href="#contact">${escapeHtml(answers.formButton || "相談する")}</a></header><main id="content">${rendered}</main><footer class="site-footer"><span>© ${new Date().getFullYear()} ${escapeHtml(answers.formalCompanyName || logo)}</span><a id="privacy" href="${safeUrl(answers.privacyUrl, "#privacy")}">個人情報の取り扱い</a></footer><script>document.querySelectorAll('.lead-form').forEach(function(form){form.addEventListener('submit',function(event){if(!form.getAttribute('action')){event.preventDefault();form.querySelector('.form-status').textContent='送信先が未設定です。管理者へお問い合わせください。';return}form.querySelector('.form-status').textContent='送信しています…'})});${gaId ? `document.querySelectorAll('.lead-form').forEach(function(form){form.addEventListener('submit',function(){gtag('event','generate_lead')})});` : ""}</script></body></html>`;
}

export function exportThanksHtml(project = {}, tone = {}) {
  const answers = project.answers || {};
  const { ink, accent, paper } = resolvePalette(tone.palette);
  const service = answers.formalServiceName || project.name || "SERVICE";
  const company = answers.formalCompanyName || service;
  const nextStep = answers.formSuccess || "担当者が内容を確認し、折り返しご連絡します。";
  const urgent = answers.urgentContact || "";
  return `<!doctype html><html lang="ja"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><meta name="robots" content="noindex,nofollow"><title>送信完了 | ${escapeHtml(service)}</title><link rel="preconnect" href="https://fonts.googleapis.com"><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin><link href="https://fonts.googleapis.com/css2?family=Noto+Sans+JP:wght@400;600;800&display=swap" rel="stylesheet"><style>:root{--ink:${escapeHtml(ink)};--accent:${escapeHtml(accent)};--paper:${escapeHtml(paper)}}*{box-sizing:border-box}body{margin:0;background:var(--paper);color:var(--ink);font-family:'Noto Sans JP',sans-serif;line-break:strict;overflow-wrap:anywhere}.wrap{width:min(920px,calc(100% - 32px));margin:auto}.header{padding:24px 0;border-bottom:1px solid color-mix(in srgb,var(--ink) 18%,transparent);font-weight:800}.hero{min-height:58vh;display:grid;align-content:center;padding:72px 0}.eyebrow{font-size:.75rem;letter-spacing:.14em}.hero h1{max-width:12em;margin:.25em 0;font-size:clamp(2.8rem,8vw,6.8rem);line-height:1.12}.hero p{max-width:42em;font-size:1.05rem;line-height:1.9}.steps{display:grid;grid-template-columns:repeat(3,1fr);gap:1px;background:color-mix(in srgb,var(--ink) 18%,transparent);margin-bottom:64px}.steps article{padding:28px;background:var(--paper)}.steps b{display:block;margin-bottom:12px}.contact{padding:32px;background:var(--ink);color:var(--paper);margin-bottom:72px}.back{display:inline-flex;margin:24px 0;color:inherit;font-weight:700}@media(max-width:640px){.steps{grid-template-columns:1fr}.hero{min-height:auto}.hero h1{font-size:3rem}}</style></head><body><header class="wrap header">${escapeHtml(service)}</header><main class="wrap"><section class="hero"><span class="eyebrow">THANK YOU</span><h1>送信を受け付けました。</h1><p>${escapeHtml(nextStep)}</p></section><section class="steps" aria-label="今後の流れ"><article><b>01 / 受付</b><span>入力内容を受け付けました。</span></article><article><b>02 / 確認</b><span>担当者が内容を確認します。</span></article><article><b>03 / ご連絡</b><span>${escapeHtml(nextStep)}</span></article></section>${urgent ? `<aside class="contact"><b>お急ぎの場合</b><p>${escapeHtml(urgent)}</p></aside>` : ""}<a class="back" href="./index.html">サイトへ戻る →</a></main><footer class="wrap">© ${new Date().getFullYear()} ${escapeHtml(company)}</footer></body></html>`;
}