/* global React, ReactDOM */
const { useState, useEffect } = React;

// =====================================================================
// Google Sheets API フェッチ & パーサー
// =====================================================================
async function fetchSheetRows() {
  const { apiKey, spreadsheetId, sheetGid } = window.__SHEETS_CONFIG;

  // GID → シート名
  const metaUrl = `https://sheets.googleapis.com/v4/spreadsheets/${spreadsheetId}?fields=sheets.properties&key=${apiKey}`;
  const metaRes = await fetch(metaUrl);
  if (!metaRes.ok) throw new Error(`Sheets API ${metaRes.status}: メタデータ取得失敗`);
  const meta = await metaRes.json();
  const sheet = meta.sheets.find(s => s.properties.sheetId === sheetGid);
  if (!sheet) throw new Error(`GID ${sheetGid} のシートが見つかりません`);
  const sheetName = sheet.properties.title;

  // セルデータ取得
  const range = encodeURIComponent(`${sheetName}!A1:BJ400`);
  const dataUrl = `https://sheets.googleapis.com/v4/spreadsheets/${spreadsheetId}/values/${range}?key=${apiKey}`;
  const dataRes = await fetch(dataUrl);
  if (!dataRes.ok) throw new Error(`Sheets API ${dataRes.status}: データ取得失敗`);
  const json = await dataRes.json();
  return json.values || [];
}

function parseSheetRows(rows) {
  const c = (row, i) => (row && i < row.length ? (row[i] || '').trim() : '');
  const projects = [];

  for (let i = 0; i < rows.length; i++) {
    const row = rows[i];
    if (!/^\d+$/.test(c(row, 0)) || !/^\d{4}\/\d+$/.test(c(row, 1))) continue;

    const start = c(row, 1);
    const end   = c(row, 6);
    const ind   = c(row, 10);
    const ctype = c(row, 15).replace(/\n/g, ' ');
    const desc  = c(row, 19).replace(/\n/g, ' ');
    const proc  = c(row, 32).replace(/\n/g, '・');
    const langs = c(row, 48).replace(/\n/g, ' / ');
    const db    = c(row, 54).replace(/\n/g, ' / ');
    const tools = c(row, 59).replace(/\n/g, ' / ');

    const tRow = rows[i + 4] || [];
    const dRow = rows[i + 5] || [];
    const team = c(tRow, 19) ? c(tRow, 19) + '名' : '-';
    const dur  = (c(dRow, 1) + c(dRow, 6)).trim() || '-';

    let overview = '';
    for (let j = i + 1; j < Math.min(i + 14, rows.length); j++) {
      const txt = rows[j].join(' ');
      if (/【業\s*務\s*概\s*要】|【PJ詳細】/.test(txt)) {
        const found = rows[j].find(v => !/【業\s*務\s*概\s*要】/.test(v) && v.trim().length > 10);
        if (found) { overview = found.trim(); break; }
        if (rows[j + 1]) { overview = rows[j + 1].filter(v => v.trim()).join(' ').trim(); break; }
      }
    }

    const fmt = s => s.replace(/(\d{4})\/(\d+)/, (_, y, m) => `${y}.${m.padStart(2, '0')}`);
    const isNow = !end || end === start;
    const periodStr = fmt(start) + ' - ' + (isNow ? '現在' : fmt(end));

    const stackRaw = [langs, db, tools].join(' / ').split(/[\n/]/).map(s => s.trim()).filter(Boolean);
    const stack = [...new Set(stackRaw)];

    const type = ctype === '正社員' ? '正社員' : 'フリーランス';

    projects.push({ period: periodStr, duration: dur, title: desc, industry: ind, role: '-', contract: ctype,
                    process: proc, teamSize: team, overview, responsibilities: [], achievements: [],
                    stack, _type: type, _start: start });
  }

  projects.sort((a, b) => b._start.localeCompare(a._start));

  const freelance = projects.filter(p => p._type === 'フリーランス');
  const employee  = projects.filter(p => p._type === '正社員');
  const career = [];

  if (freelance.length) {
    const starts = freelance.map(p => p._start).sort();
    const ends   = freelance.map(p => p.period.split(' - ')[1]);
    const hasNow = ends.some(e => e === '現在');
    career.push({ period: starts[0].replace(/(\d{4})\/(\d+)/, '$1.$2') + ' - ' + (hasNow ? '現在' : ends.sort().reverse()[0]),
                  type: 'フリーランス', company: 'フリーランス（個人事業主）', role: 'エンジニア', note: '', projects: freelance });
  }
  if (employee.length) {
    const starts = employee.map(p => p._start).sort();
    const ends   = employee.map(p => p.period.split(' - ')[1]).filter(e => e !== '現在').sort();
    career.push({ period: starts[0].replace(/(\d{4})\/(\d+)/, '$1.$2') + ' - ' + (ends.length ? ends.reverse()[0] : '現在'),
                  type: '正社員', company: '各社（正社員）', role: 'エンジニア', note: '', projects: employee });
  }

  const skills = buildSkillsFromProjects(projects);
  return { career, skills };
}

// =====================================================================
// スキル集計
// =====================================================================
const TECH_NORMALIZE = {
  'typescript': 'TypeScript', 'javascript': 'JavaScript', 'php': 'PHP',
  'ruby': 'Ruby', 'python': 'Python', 'go': 'Go', 'sql': 'SQL',
  'react': 'React', 'vue.js': 'Vue.js', 'vuejs': 'Vue.js',
  'nuxtjs': 'NuxtJS', 'nuxt': 'NuxtJS', 'jquery': 'jQuery',
  'laravel': 'Laravel', 'ruby on rails': 'Ruby on Rails',
  'mysql': 'MySQL', 'postgresql': 'PostgreSQL', 'redis': 'Redis',
  'dynamodb': 'DynamoDB', 'bigquery': 'BigQuery',
  'aws': 'AWS', 'gcp': 'GCP', 'docker': 'Docker', 'git': 'Git',
  'swagger': 'Swagger', 'terraform': 'Terraform', 'kubernetes': 'Kubernetes',
};

const CATEGORY_MAP = {
  frontend:  ['TypeScript', 'JavaScript', 'React', 'Vue.js', 'NuxtJS', 'jQuery'],
  backend:   ['PHP', 'Ruby', 'Python', 'Go', 'Laravel', 'Ruby on Rails'],
  database:  ['MySQL', 'PostgreSQL', 'Redis', 'DynamoDB', 'BigQuery', 'SQL'],
  cloud:     ['AWS', 'GCP', 'Docker', 'Git', 'Swagger', 'Terraform', 'Kubernetes'],
};

function monthsToLevel(m) {
  if (m >= 96) return 5;
  if (m >= 60) return 4;
  if (m >= 36) return 3;
  if (m >= 12) return 2;
  return 1;
}

function monthsToYears(m) {
  const y = Math.floor(m / 12);
  const mo = m % 12;
  if (y === 0) return mo + 'ヶ月';
  if (mo === 0) return y + '年';
  return y + '年' + mo + 'ヶ月';
}

function buildSkillsFromProjects(projects) {
  const now = new Date();
  const monthsMap = {};
  for (const p of projects) {
    const [startStr, endStr] = p.period.split(' - ');
    const [sy, sm] = startStr.split('.').map(Number);
    const [ey, em] = endStr === '現在'
      ? [now.getFullYear(), now.getMonth() + 1]
      : endStr.split('.').map(Number);
    const months = Math.max(0, (ey - sy) * 12 + (em - sm));
    for (const raw of p.stack) {
      const key = TECH_NORMALIZE[raw.toLowerCase()] || null;
      if (!key) continue;
      monthsMap[key] = (monthsMap[key] || 0) + months;
    }
  }

  const result = {};
  for (const [cat, techs] of Object.entries(CATEGORY_MAP)) {
    result[cat] = techs
      .filter(t => monthsMap[t])
      .map(t => ({ name: t, level: monthsToLevel(monthsMap[t]), years: monthsToYears(monthsMap[t]) }));
  }
  return result;
}

// =====================================================================
// データ（静的フォールバック）
// =====================================================================
const PROFILE = {
  name: "K・N",
  nameKana: "",
  title: "フリーランス フルスタックエンジニア",
  birth: "1993年11月17日（32歳）",
  location: "北海道",
  email: "kenji.nkmr.1117@gmail.com",
  phone: "",
  github: "github.com/kj-nakamura",
  portfolio: "",
  summary:
    "PHP / Ruby / Python / Go をバックエンド、React / Vue.js / NuxtJS をフロントエンドに持つフルスタックエンジニア。要件定義・設計・実装・インフラ構築（AWS / GCP / Docker）まで一気通貫で対応可能。フリーランスとして複数のWebサービス開発に携わり、0→1 フェーズのプロダクト立ち上げから既存サービスの機能拡張・パフォーマンス改善まで幅広く担当。チームへの技術的リードや、コードレビューを通じた品質向上にも積極的に貢献してきた。",
};

function calcCareerYears(careerData, type) {
  const now = new Date();
  let months = 0;
  careerData.forEach((c) => {
    if (type && c.type !== type) return;
    const [startStr, endStr] = c.period.split(" - ");
    const [sy, sm] = startStr.split(".").map(Number);
    const [ey, em] = endStr === "現在"
      ? [now.getFullYear(), now.getMonth() + 1]
      : endStr.split(".").map(Number);
    months += (ey - sy) * 12 + (em - sm);
  });
  return Math.floor(months / 12);
}

function getHighlights(career) {
  return [
    { num: String(calcCareerYears(career)),               label: "エンジニア歴（年）" },
    { num: String(calcCareerYears(career, "フリーランス")), label: "フリーランス歴（年）" },
    { num: String(career.flatMap((c) => c.projects).length), label: "プロジェクト経験" },
  ];
}

const STRENGTHS = [
  { title: "0 → 1 のプロダクト立ち上げ", body: "要件の言語化、技術選定、MVP実装、リリース後のグロース支援まで。3社のシードラウンドプロダクトに技術責任者として参画。" },
  { title: "スケーラブルなアーキテクチャ設計", body: "月間数千万PV規模のサービスにおけるドメイン設計、マイクロサービス分割、CDN/キャッシュ戦略、負荷試験計画までを実務で経験。" },
  { title: "横断的な技術スタック", body: "フロント（React/Next.js）、バックエンド（Node.js/Go/Python）、インフラ（AWS/GCP）、CI/CD、監視・運用までを一気通貫で担当可能。" },
  { title: "チームビルディング/技術ブランディング", body: "テックリードとして採用面接、技術ブログ運営、社内勉強会開催、コードレビュー文化醸成などを推進。" },
];

const SKILLS = {
  languages: [
    { name: "TypeScript", level: 5, years: "8年" }, { name: "JavaScript", level: 5, years: "10年" },
    { name: "Go", level: 4, years: "4年" }, { name: "Python", level: 4, years: "5年" },
    { name: "Ruby", level: 3, years: "3年" }, { name: "SQL", level: 5, years: "10年" },
  ],
  frontend: [
    { name: "React / Next.js", level: 5, years: "7年" }, { name: "Vue / Nuxt", level: 4, years: "3年" },
    { name: "Tailwind CSS", level: 5, years: "5年" }, { name: "GraphQL (Apollo)", level: 4, years: "4年" },
  ],
  backend: [
    { name: "Node.js (Express/Nest)", level: 5, years: "8年" }, { name: "Go (Echo/Gin)", level: 4, years: "4年" },
    { name: "Ruby on Rails", level: 3, years: "3年" }, { name: "FastAPI", level: 3, years: "2年" },
  ],
  database: [
    { name: "PostgreSQL", level: 5, years: "9年" }, { name: "MySQL", level: 5, years: "10年" },
    { name: "Redis", level: 4, years: "6年" }, { name: "DynamoDB", level: 4, years: "4年" },
    { name: "BigQuery", level: 3, years: "3年" },
  ],
  cloud: [
    { name: "AWS (ECS/Lambda/RDS)", level: 5, years: "8年" }, { name: "GCP (Cloud Run/GKE)", level: 4, years: "4年" },
    { name: "Terraform", level: 4, years: "5年" }, { name: "Docker / Kubernetes", level: 4, years: "6年" },
    { name: "GitHub Actions / CircleCI", level: 5, years: "6年" }, { name: "Datadog / Sentry", level: 4, years: "5年" },
  ],
};

// =====================================================================
// テーマ
// =====================================================================
const THEMES = {
  indigo: { label: "Indigo", accent: "oklch(0.50 0.18 265)", accentSoft: "oklch(0.94 0.04 265)", accentDeep: "oklch(0.32 0.14 265)", ink: "oklch(0.14 0.015 260)", sub: "oklch(0.36 0.012 260)", line: "oklch(0.82 0.008 260)", bg: "oklch(0.985 0.004 260)", paper: "#ffffff", chip: "oklch(0.95 0.012 260)" },
  forest: { label: "Forest", accent: "oklch(0.42 0.12 155)", accentSoft: "oklch(0.94 0.03 155)", accentDeep: "oklch(0.28 0.10 155)", ink: "oklch(0.14 0.012 150)", sub: "oklch(0.36 0.010 150)", line: "oklch(0.82 0.008 150)", bg: "oklch(0.985 0.004 150)", paper: "#ffffff", chip: "oklch(0.95 0.012 150)" },
  slate:  { label: "Slate",  accent: "oklch(0.25 0.012 260)", accentSoft: "oklch(0.94 0.005 260)", accentDeep: "oklch(0.14 0.010 260)", ink: "oklch(0.12 0.010 260)", sub: "oklch(0.36 0.008 260)", line: "oklch(0.82 0.005 260)", bg: "oklch(0.985 0.003 260)", paper: "#ffffff", chip: "oklch(0.95 0.005 260)" },
  sunset: { label: "Sunset", accent: "oklch(0.55 0.16 35)",  accentSoft: "oklch(0.94 0.04 35)",  accentDeep: "oklch(0.36 0.13 35)",  ink: "oklch(0.16 0.015 30)",  sub: "oklch(0.38 0.012 30)",  line: "oklch(0.82 0.010 30)",  bg: "oklch(0.985 0.005 60)",  paper: "#ffffff", chip: "oklch(0.95 0.015 35)"  },
  ink:    { label: "Ink",    accent: "oklch(0.16 0.015 260)", accentSoft: "oklch(0.94 0.005 260)", accentDeep: "oklch(0.08 0.010 260)", ink: "oklch(0.10 0.010 260)", sub: "oklch(0.34 0.008 260)", line: "oklch(0.80 0.005 260)", bg: "oklch(0.985 0.002 260)", paper: "#ffffff", chip: "oklch(0.94 0.005 260)" },
};

const FONT_PAIRS = {
  notoSans: { label: "Noto Sans JP",  body: "'Noto Sans JP', system-ui, sans-serif",        head: "'Noto Sans JP', system-ui, sans-serif",        mono: "'JetBrains Mono', 'Noto Sans JP', monospace" },
  serif:    { label: "Noto Serif JP", body: "'Noto Serif JP', 'Noto Sans JP', serif",       head: "'Noto Serif JP', 'Noto Sans JP', serif",       mono: "'JetBrains Mono', monospace" },
  ibm:      { label: "IBM Plex",      body: "'IBM Plex Sans JP', 'Noto Sans JP', sans-serif", head: "'IBM Plex Sans JP', 'Noto Sans JP', sans-serif", mono: "'IBM Plex Mono', monospace" },
};

// =====================================================================
// アトミック
// =====================================================================
function SectionHeader({ num, ja, en }) {
  return (
    <header className="sec-head">
      <span className="sec-num">{num}</span>
      <div className="sec-titles">
        <h2 className="sec-ja">{ja}</h2>
        <span className="sec-en">{en}</span>
      </div>
      <span className="sec-rule" />
    </header>
  );
}

function LevelBar({ level }) {
  return (
    <span className="level-bar" aria-label={`level ${level} of 5`}>
      {[1, 2, 3, 4, 5].map((i) => (
        <span key={i} className={`lb-dot ${i <= level ? "on" : ""}`} />
      ))}
    </span>
  );
}

function SkillGroup({ title, items }) {
  return (
    <div className="skill-group">
      <h3 className="skill-group-title">{title}</h3>
      <ul className="skill-list">
        {items.map((s) => (
          <li key={s.name} className="skill-row">
            <span className="skill-name">{s.name}</span>
            <span className="skill-meta">
              <span className="skill-years">{s.years}</span>
              <LevelBar level={s.level} />
            </span>
          </li>
        ))}
      </ul>
    </div>
  );
}

// =====================================================================
// セクション
// =====================================================================
function Header() {
  return (
    <section className="r-header">
      <div className="r-head-left">
        <div className="r-eyebrow">職務経歴書 / Resume</div>
        <h1 className="r-name">
          {PROFILE.name}
          <span className="r-name-kana">{PROFILE.nameKana}</span>
        </h1>
        <div className="r-title">{PROFILE.title}</div>
      </div>
      <div className="r-head-right">
        <dl className="r-meta">
          <div><dt>所在地</dt><dd>{PROFILE.location}</dd></div>
          <div><dt>生年月日</dt><dd>{PROFILE.birth}</dd></div>
          <div><dt>Email</dt><dd>{PROFILE.email}</dd></div>
          <div><dt>Phone</dt><dd>{PROFILE.phone}</dd></div>
          <div><dt>GitHub</dt><dd>{PROFILE.github}</dd></div>
          <div><dt>Portfolio</dt><dd>{PROFILE.portfolio}</dd></div>
        </dl>
      </div>
    </section>
  );
}

function Summary({ career }) {
  const highlights = getHighlights(career);
  return (
    <section className="r-section">
      <SectionHeader num="01" ja="自己PR / サマリー" en="Summary" />
      <p className="r-summary">{PROFILE.summary}</p>
      <ul className="r-highlights">
        {highlights.map((h) => (
          <li key={h.label}>
            <span className="hl-num">{h.num}</span>
            <span className="hl-label">{h.label}</span>
          </li>
        ))}
      </ul>
    </section>
  );
}

function Strengths() {
  return (
    <section className="r-section">
      <SectionHeader num="02" ja="得意分野・アピールポイント" en="Strengths" />
      <ul className="r-strengths">
        {STRENGTHS.map((s, i) => (
          <li key={s.title}>
            <span className="str-idx">0{i + 1}</span>
            <div>
              <h3 className="str-title">{s.title}</h3>
              <p className="str-body">{s.body}</p>
            </div>
          </li>
        ))}
      </ul>
    </section>
  );
}

function Skills({ skills }) {
  return (
    <section className="r-section">
      <SectionHeader num="03" ja="スキルセット" en="Technical Skills" />
      <div className="skill-grid">
        {skills.languages?.length > 0 && <SkillGroup title="言語" items={skills.languages} />}
        {skills.frontend?.length  > 0 && <SkillGroup title="フロントエンド" items={skills.frontend} />}
        {skills.backend?.length   > 0 && <SkillGroup title="バックエンド" items={skills.backend} />}
        {skills.database?.length  > 0 && <SkillGroup title="データベース" items={skills.database} />}
        {skills.cloud?.length     > 0 && <SkillGroup title="クラウド / インフラ / DevOps" items={skills.cloud} />}
      </div>
    </section>
  );
}

function ProjectCard({ p, idx }) {
  return (
    <article className="proj-card">
      <header className="proj-head">
        <div className="proj-period">
          <span className="proj-idx">CASE / {String(idx + 1).padStart(2, "0")}</span>
          <span className="proj-dates">{p.period}</span>
          <span className="proj-duration">{p.duration}</span>
        </div>
        <h3 className="proj-title">{p.title}</h3>
        <ul className="proj-tags">
          <li><span className="tag-k">契約形態</span><span className="tag-v">{p.contract || p.role}</span></li>
          <li><span className="tag-k">担当工程</span><span className="tag-v">{p.process}</span></li>
          <li><span className="tag-k">体制</span><span className="tag-v">{p.teamSize}</span></li>
        </ul>
      </header>
      <div className="proj-cols proj-cols--full">
        <div className="proj-col">
          <h4>担当業務</h4>
          <p className="proj-overview">{p.overview}</p>
        </div>
      </div>
      <div className="proj-stack">
        <span className="stack-label">使用技術</span>
        <ul>
          {p.stack.map((t) => <li key={t}>{t}</li>)}
        </ul>
      </div>
    </article>
  );
}

function CareerAndProjects({ career }) {
  const allProjects = career.flatMap((c) => c.projects);
  return (
    <section className="r-section">
      <SectionHeader num="04" ja="職務経歴 / プロジェクト詳細" en="Career & Projects" />
      <div className="proj-grid">
        {allProjects.map((p, i) => <ProjectCard key={p.title + i} p={p} idx={i} />)}
      </div>
    </section>
  );
}

function LoadingOverlay() {
  return (
    <div style={{ position: 'fixed', inset: 0, display: 'flex', alignItems: 'center', justifyContent: 'center', background: 'var(--bg)', zIndex: 100 }}>
      <div style={{ width: 32, height: 32, border: '3px solid var(--line)', borderTopColor: 'var(--accent)', borderRadius: '50%', animation: 'spin 0.8s linear infinite' }} />
      <style>{`@keyframes spin { to { transform: rotate(360deg); } }`}</style>
    </div>
  );
}

function ErrorBanner({ message }) {
  return (
    <div style={{ position: 'fixed', top: 12, right: 12, background: '#fff3cd', border: '1px solid #ffc107', borderRadius: 8, padding: '10px 16px', fontSize: 13, zIndex: 200, maxWidth: 360 }}>
      ⚠ スプシ取得失敗（静的データを表示）<br/>
      <small style={{ color: '#666' }}>{message}</small>
    </div>
  );
}

function Footer() {
  return (
    <footer className="r-footer">
      <span>以上</span>
      <span className="r-foot-meta">本書類は機密情報を含みます。取り扱いにご注意ください。</span>
    </footer>
  );
}

// =====================================================================
// メイン
// =====================================================================
const FALLBACK_CAREER = [];

function Resume() {
  const [tweaks, setTweaks] = useState(window.__TWEAKS_DEFAULTS || {
    theme: "indigo", font: "notoSans", density: "comfortable", accentBlock: true,
  });
  const [showTweaks, setShowTweaks] = useState(false);
  const [career, setCareer] = useState(FALLBACK_CAREER);
  const [skills, setSkills] = useState(SKILLS);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    fetchSheetRows()
      .then(rows => {
        const parsed = parseSheetRows(rows);
        setCareer(parsed.career);
        if (parsed.skills) setSkills(parsed.skills);
      })
      .catch(e => setError(e.message))
      .finally(() => setLoading(false));
  }, []);

  useEffect(() => {
    const onMessage = (e) => {
      if (!e.data || typeof e.data !== "object") return;
      if (e.data.type === "__activate_edit_mode") setShowTweaks(true);
      if (e.data.type === "__deactivate_edit_mode") setShowTweaks(false);
    };
    window.addEventListener("message", onMessage);
    window.parent.postMessage({ type: "__edit_mode_available" }, "*");
    return () => window.removeEventListener("message", onMessage);
  }, []);

  const setTweak = (k, v) => {
    if (typeof k === "object") {
      setTweaks((t) => ({ ...t, ...k }));
      window.parent.postMessage({ type: "__edit_mode_set_keys", edits: k }, "*");
    } else {
      setTweaks((t) => ({ ...t, [k]: v }));
      window.parent.postMessage({ type: "__edit_mode_set_keys", edits: { [k]: v } }, "*");
    }
  };

  const theme = THEMES[tweaks.theme] || THEMES.indigo;
  const font = FONT_PAIRS[tweaks.font] || FONT_PAIRS.notoSans;

  const cssVars = {
    "--accent": theme.accent, "--accent-soft": theme.accentSoft, "--accent-deep": theme.accentDeep,
    "--ink": theme.ink, "--sub": theme.sub, "--line": theme.line,
    "--bg": theme.bg, "--paper": theme.paper, "--chip": theme.chip,
    "--font-body": font.body, "--font-head": font.head, "--font-mono": font.mono,
    "--gap": tweaks.density === "compact" ? "10mm" : "14mm",
    "--cell-pad": tweaks.density === "compact" ? "6px 10px" : "9px 12px",
  };

  return (
    <div className="r-root" style={cssVars} data-density={tweaks.density}>
      {loading && <LoadingOverlay />}
      {error   && <ErrorBanner message={error} />}
      <div className="r-stage">
        <article className={`r-paper ${tweaks.accentBlock ? "with-accent-block" : ""}`}>
          <Header />
          <main className="r-main">
            <Summary career={career} />
            <Strengths />
            <Skills skills={skills} />
            {career.length > 0 && <CareerAndProjects career={career} />}
          </main>
          <Footer />
        </article>
      </div>

      {showTweaks && (
        <window.TweaksPanel title="Tweaks" onClose={() => {
          setShowTweaks(false);
          window.parent.postMessage({ type: "__edit_mode_dismissed" }, "*");
        }}>
          <window.TweakSection title="カラーテーマ">
            <window.TweakSelect
              label="Theme"
              value={tweaks.theme}
              onChange={(v) => setTweak("theme", v)}
              options={Object.entries(THEMES).map(([k, v]) => ({ value: k, label: v.label }))}
            />
            <div className="tweak-swatches">
              {Object.entries(THEMES).map(([k, t]) => (
                <button key={k} className={`swatch ${tweaks.theme === k ? "active" : ""}`}
                  onClick={() => setTweak("theme", k)} title={t.label} style={{ background: t.accent }} />
              ))}
            </div>
          </window.TweakSection>
          <window.TweakSection title="フォント">
            <window.TweakRadio label="Family" value={tweaks.font} onChange={(v) => setTweak("font", v)}
              options={Object.entries(FONT_PAIRS).map(([k, v]) => ({ value: k, label: v.label }))} />
          </window.TweakSection>
          <window.TweakSection title="密度">
            <window.TweakRadio label="Density" value={tweaks.density} onChange={(v) => setTweak("density", v)}
              options={[{ value: "comfortable", label: "標準" }, { value: "compact", label: "コンパクト" }]} />
          </window.TweakSection>
          <window.TweakSection title="装飾">
            <window.TweakToggle label="アクセントブロックを表示" value={tweaks.accentBlock}
              onChange={(v) => setTweak("accentBlock", v)} />
          </window.TweakSection>
          <window.TweakSection title="出力">
            <window.TweakButton onClick={() => window.print()}>PDF / 印刷</window.TweakButton>
          </window.TweakSection>
        </window.TweaksPanel>
      )}
    </div>
  );
}

ReactDOM.createRoot(document.getElementById("root")).render(<Resume />);
