const path = require('path');
const pptxgen = require('pptxgenjs');

const base = __dirname;
const outPath = path.join(base, 'prezentaciya-vampiricheskoe-tenevoe-pravitelstvo.pptx');
const assets = {
  agent: path.join(base, 'assets', 'generated', 'ai-01-snv12-agent.png'),
  medieval: path.join(base, 'assets', 'generated', 'ai-02-medieval-spread-map.png'),
  classes: path.join(base, 'assets', 'generated', 'ai-03-vampire-classes.png'),
  failed: path.join(base, 'assets', 'generated', 'ai-04-failed-infection.png'),
  economy: path.join(base, 'assets', 'generated', 'ai-05-blood-economy.png'),
  artificial: path.join(base, 'assets', 'generated', 'ai-06-live-vs-artificial-blood.png'),
  market: path.join(base, 'assets', 'generated', 'ai-07-hidden-donor-market.png'),
  zones: path.join(base, 'assets', 'generated', 'ai-08-feeding-zones-map.png')
};

const pptx = new pptxgen();
pptx.layout = 'LAYOUT_WIDE';
pptx.author = 'Hermes Agent';
pptx.company = 'Nous Research';
pptx.subject = 'Строгая псевдонаучная модель гемозависимой элиты';
pptx.title = 'Пищевая цепочка: от тебя до теневого правительства';
pptx.lang = 'ru-RU';
pptx.theme = {
  headFontFace: 'Aptos Display',
  bodyFontFace: 'Aptos',
  lang: 'ru-RU'
};

const C = {
  bg: 'F7F9FC',
  white: 'FFFFFF',
  ink: '1F2937',
  muted: '667085',
  line: 'CAD5E2',
  blue: '245B8F',
  blueSoft: 'EAF2FB',
  red: 'B42318',
  redSoft: 'FDECEC',
  graySoft: 'EEF2F6',
  dark: '102A43'
};

function footer(slide, n, dark = false) {
  slide.addText(String(n).padStart(2, '0'), {
    x: 12.48,
    y: 7.02,
    w: 0.36,
    h: 0.18,
    fontSize: 10,
    color: dark ? 'D0DAE6' : C.muted,
    align: 'right',
    margin: 0
  });
}

function put(slide, text, x, y, w, h, opts = {}) {
  slide.addText(text, {
    x, y, w, h,
    fontFace: opts.fontFace ?? 'Aptos',
    fontSize: opts.fontSize ?? 14,
    color: opts.color ?? C.ink,
    bold: opts.bold ?? false,
    italic: opts.italic ?? false,
    align: opts.align ?? 'left',
    valign: opts.valign ?? 'mid',
    margin: opts.margin ?? 0.02,
    breakLine: opts.breakLine ?? true
  });
}

function card(slide, x, y, w, h, title, body, opts = {}) {
  slide.addShape(pptx.ShapeType.roundRect, {
    x, y, w, h,
    rectRadius: 0.05,
    fill: { color: opts.fill ?? C.white },
    line: { color: opts.line ?? C.line, pt: opts.pt ?? 1.1 }
  });
  if (title) {
    put(slide, title, x + 0.18, y + 0.16, w - 0.36, 0.28, {
      fontSize: opts.titleSize ?? 16,
      bold: true,
      color: opts.titleColor ?? C.ink
    });
  }
  if (body) {
    put(slide, body, x + 0.18, y + (title ? 0.52 : 0.18), w - 0.36, h - (title ? 0.68 : 0.28), {
      fontSize: opts.bodySize ?? 13.5,
      color: opts.bodyColor ?? C.ink,
      valign: opts.bodyValign ?? 'top',
      margin: 0.04
    });
  }
}

function pill(slide, x, y, w, h, text, opts = {}) {
  slide.addShape(pptx.ShapeType.roundRect, {
    x, y, w, h,
    rectRadius: 0.08,
    fill: { color: opts.fill ?? C.graySoft },
    line: { color: opts.line ?? C.graySoft, pt: 1 }
  });
  put(slide, text, x + 0.08, y + 0.05, w - 0.16, h - 0.1, {
    fontSize: opts.fontSize ?? 12.5,
    color: opts.color ?? C.ink,
    bold: opts.bold ?? false,
    align: opts.align ?? 'center'
  });
}

function titleSlideBase(slide, title, subtitle) {
  slide.background = { color: C.bg };
  put(slide, title, 0.9, 0.78, 6.1, 1.25, { fontSize: 28, bold: true, color: C.dark, margin: 0 });
  put(slide, subtitle, 0.92, 2.18, 6.3, 0.55, { fontSize: 16, color: C.muted, margin: 0 });
}

function textSlideBase(slide, title, kicker = 'Псевдонаучная реконструкция') {
  slide.background = { color: C.bg };
  put(slide, title, 0.7, 0.52, 8.4, 0.42, { fontSize: 26, bold: true, color: C.dark, margin: 0 });
  put(slide, kicker, 10.05, 0.58, 2.55, 0.2, { fontSize: 10.5, color: C.blue, italic: true, align: 'right', margin: 0 });
}

function fullImage(slide, imagePath) {
  slide.background = { color: C.white };
  slide.addImage({ path: imagePath, x: 0, y: 0, w: 13.333, h: 7.5 });
}

// 1. title
{
  const s = pptx.addSlide();
  titleSlideBase(s,
    'Пищевая цепочка:\nот тебя до теневого правительства',
    'Модель гемозависимой элиты'
  );

  card(s, 0.92, 3.0, 5.25, 2.25, 'Исходная гипотеза',
    'Теневое правительство — не только сеть власти, но и каста с контролем отбора, хранения и распределения крови.',
    { fill: C.white, line: C.line, bodySize: 15, bodyValign: 'mid' }
  );

  card(s, 7.1, 1.2, 5.2, 4.95, 'Минимальная схема', '', { fill: C.white, line: C.line });
  pill(s, 7.6, 2.0, 1.1, 0.5, 'человек', { fill: C.blueSoft, line: C.blue, color: C.blue, bold: true });
  put(s, '→', 8.85, 2.08, 0.3, 0.2, { fontSize: 24, color: C.muted, align: 'center' });
  pill(s, 9.25, 2.0, 1.2, 0.5, 'отбор', { fill: C.graySoft, line: C.line, color: C.ink, bold: true });
  put(s, '→', 10.55, 2.08, 0.3, 0.2, { fontSize: 24, color: C.muted, align: 'center' });
  pill(s, 10.95, 2.0, 1.1, 0.5, 'кровь', { fill: C.redSoft, line: C.red, color: C.red, bold: true });
  put(s, '→', 8.85, 3.48, 0.3, 0.2, { fontSize: 24, color: C.muted, align: 'center' });
  pill(s, 9.25, 3.4, 2.05, 0.56, 'верхушка элиты', { fill: C.dark, line: C.dark, color: C.white, bold: true });
  put(s, 'долгожительство\nселекция\nскрытая логистика', 7.58, 4.28, 4.1, 0.95, {
    fontSize: 16,
    color: C.muted,
    align: 'center'
  });

  put(s, 'Псевдонаучная реконструкция', 0.95, 6.55, 6.8, 0.22, {
    fontSize: 12.5,
    color: C.muted,
    italic: true,
    margin: 0
  });
  footer(s, 1);
}

// 2. hypothesis
{
  const s = pptx.addSlide();
  textSlideBase(s, 'Исходная гипотеза');
  card(s, 0.88, 1.45, 12.0, 1.2,
    'Тезис',
    'Элита не только управляет обществом, но и питается им.',
    { fill: C.white, line: C.blue, titleColor: C.blue, bodySize: 17, bodyValign: 'mid' }
  );
  card(s, 0.88, 3.0, 2.75, 2.05, '1. Биология', 'Вампиризм = вирусная адаптация.', { fill: C.redSoft, line: C.red });
  card(s, 3.85, 3.0, 2.75, 2.05, '2. Долголетие', 'Возраст даёт власть и время.', { fill: C.blueSoft, line: C.blue });
  card(s, 6.82, 3.0, 2.75, 2.05, '3. Кровь', 'Кровь = еда, валюта, селекция.', { fill: C.white, line: C.line });
  card(s, 9.79, 3.0, 3.09, 2.05, '4. Институты', 'Система власти маскирует кормовую сеть.', { fill: C.white, line: C.line });
  pill(s, 1.7, 5.72, 9.9, 0.62, 'Ключевая формула: власть = контроль кормовой инфраструктуры', { fill: C.dark, line: C.dark, color: C.white, fontSize: 20, bold: true });
  footer(s, 2);
}

// 3. biological agent
{
  const s = pptx.addSlide();
  fullImage(s, assets.agent);
  put(s, 'Биологический агент: вирус, молекула и механизм действия', 0.62, 0.23, 11.98, 0.42, {
    fontSize: 24, bold: true, color: C.dark, align: 'center'
  });
  put(s, '1. Вирион SNV-12', 1.28, 1.3, 2.45, 0.26, { fontSize: 14, bold: true, color: C.red, align: 'center' });
  put(s, '2. Гемофильный регуляторный комплекс', 5.0, 1.3, 2.75, 0.26, { fontSize: 13.6, bold: true, color: C.red, align: 'center' });
  put(s, 'HEP + MitoX + Reg\nперестройка обмена', 7.7, 2.46, 1.68, 0.78, { fontSize: 11, color: C.ink, align: 'center' });
  put(s, 'связывание → вход → РНК → транспорт', 4.58, 5.9, 3.24, 0.34, { fontSize: 11.2, color: C.blue, align: 'center', bold: true });
  put(s, '3. Клеточные мишени и системные эффекты', 9.08, 1.3, 3.9, 0.26, { fontSize: 13.2, bold: true, color: C.red, align: 'center' });
  put(s, 'ROS↑,\nпотенциал↓', 8.42, 3.18, 1.35, 0.38, { fontSize: 10.5, color: C.red, align: 'center' });
  put(s, 'сдвиг\nметаболизма гема', 9.88, 3.18, 1.62, 0.38, { fontSize: 10.5, color: C.red, align: 'center' });
  put(s, 'ускорение\nрепарации тканей', 11.52, 3.18, 1.2, 0.38, { fontSize: 10.1, color: C.red, align: 'center' });
  put(s, 'сумеречное зрение\nи фоточувствительность', 8.42, 5.15, 1.34, 0.42, { fontSize: 9.8, color: C.blue, align: 'center' });
  put(s, 'лактатный сдвиг\nи экономия энергии', 11.52, 5.15, 1.16, 0.42, { fontSize: 9.8, color: C.blue, align: 'center' });
  put(s, 'Донорская кровь: сигналы → гем → гормоны → адаптация', 8.18, 6.05, 4.1, 0.28, {
    fontSize: 10.2, color: C.blue, align: 'center', bold: true
  });
  put(s, 'Вампиризм = перенастройка обмена, регенерации и светочувствительности.', 10.63, 6.75, 2.2, 0.48, {
    fontSize: 10.2, color: C.muted, align: 'center'
  });
  footer(s, 3);
}

// 4. medieval spread map
{
  const s = pptx.addSlide();
  fullImage(s, assets.medieval);
  put(s, 'Средневековье создаёт идеальную среду', 0.22, 0.28, 4.12, 0.34, { fontSize: 21, bold: true, color: C.dark, align: 'center' });
  put(s, 'XII–XIV века: торговля, миграции, войны и эпидемии', 0.22, 0.62, 4.12, 0.22, { fontSize: 11.2, color: C.muted, align: 'center' });
  put(s, 'Торговля, войны и миграции создают коридоры распространения SNV-12.', 11.28, 5.55, 1.82, 1.34, {
    fontSize: 10.4, color: C.ink, align: 'center', valign: 'mid'
  });
  footer(s, 4);
}

// 5. physiological changes
{
  const s = pptx.addSlide();
  textSlideBase(s, 'Как вирус меняет организм');
  card(s, 0.88, 1.4, 3.88, 1.4, 'Кровяная среда', 'Норма: транспорт O₂.\nЗаражение: спрос на гем.\nАдаптация: кровь = сигнал.', { fill: C.white, line: C.red });
  card(s, 4.95, 1.4, 3.88, 1.4, 'Обмен веществ', 'Норма: базовый цикл.\nЗаражение: стресс.\nАдаптация: экономный режим.', { fill: C.white, line: C.blue });
  card(s, 9.02, 1.4, 3.43, 1.4, 'Нервная система', 'Норма: баланс.\nЗаражение: перегрузка.\nАдаптация: фокус на добыче.', { fill: C.white, line: C.line });
  card(s, 0.88, 3.15, 3.88, 1.4, 'Зрение и свет', 'Норма: дневное зрение.\nЗаражение: фотостресс.\nАдаптация: сумеречный режим.', { fill: C.white, line: C.blue });
  card(s, 4.95, 3.15, 3.88, 1.4, 'Регенерация', 'Норма: обычное заживление.\nЗаражение: воспаление.\nАдаптация: быстрая репарация.', { fill: C.white, line: C.red });
  card(s, 9.02, 3.15, 3.43, 1.4, 'Старение', 'Норма: обычный цикл.\nЗаражение: нагрузка.\nАдаптация: медленное старение.', { fill: C.white, line: C.line });
  card(s, 0.88, 5.08, 11.98, 1.08, 'Итог', 'Отсюда: кровь, свет, регенерация, долголетие.', { fill: C.dark, line: C.dark, titleColor: C.white, bodyColor: 'EAF2FB', bodySize: 15, bodyValign: 'mid' });
  footer(s, 5);
}

// 6. dependency on blood
{
  const s = pptx.addSlide();
  textSlideBase(s, 'Почему возникает зависимость от крови');
  pill(s, 0.92, 2.1, 2.25, 0.9, '1. Вирус требует\nживые сигнальные молекулы', { fill: C.redSoft, line: C.red, color: C.red, bold: true, fontSize: 15 });
  put(s, '→', 3.3, 2.34, 0.4, 0.2, { fontSize: 28, color: C.muted, align: 'center' });
  pill(s, 3.82, 2.1, 2.25, 0.9, '2. Донорская кровь несёт\nсигналы и маркеры', { fill: C.blueSoft, line: C.blue, color: C.blue, bold: true, fontSize: 14.2 });
  put(s, '→', 6.22, 2.34, 0.4, 0.2, { fontSize: 28, color: C.muted, align: 'center' });
  pill(s, 6.72, 2.1, 2.25, 0.9, '3. Суррогат даёт\nтранспорт, но не сигналы', { fill: C.graySoft, line: C.line, color: C.ink, bold: true, fontSize: 14.2 });
  put(s, '→', 9.12, 2.34, 0.4, 0.2, { fontSize: 28, color: C.muted, align: 'center' });
  pill(s, 9.62, 2.1, 2.75, 0.9, '4. Голод = дефицит\nсигнальных компонентов', { fill: C.dark, line: C.dark, color: C.white, bold: true, fontSize: 14.2 });
  card(s, 1.15, 4.0, 3.45, 1.25, 'Живая кровь', 'Сигналы, вариативность, метаболизм.', { fill: C.white, line: C.red });
  card(s, 5.0, 4.0, 3.45, 1.25, 'Искусственная кровь', 'Транспорт есть. Сигналов почти нет.', { fill: C.white, line: C.blue });
  card(s, 8.85, 4.0, 3.45, 1.25, 'Следствие', 'Селекция, ритуалы, рынок доноров.', { fill: C.white, line: C.line });
  footer(s, 6);
}

// 7. classes
{
  const s = pptx.addSlide();
  fullImage(s, assets.classes);
  put(s, 'Классы вампирической адаптации', 0.88, 0.3, 11.6, 0.36, { fontSize: 22, bold: true, color: C.dark, align: 'center' });
  put(s, 'Выше класс — выше контроль, доступ и влияние.', 0.3, 6.9, 12.7, 0.24, { fontSize: 11.5, color: C.muted, align: 'center' });
  footer(s, 7);
}

// 8. failed infection
{
  const s = pptx.addSlide();
  fullImage(s, assets.failed);
  put(s, 'Неудачное заражение: каскад несовместимости', 1.1, 0.28, 11.1, 0.36, { fontSize: 22, bold: true, color: C.dark, align: 'center' });
  put(s, 'нейровегетативный\nсбой', 3.6, 1.78, 1.44, 0.44, { fontSize: 11.2, color: C.blue, align: 'center' });
  put(s, 'кардиореспираторный\nстресс', 3.57, 2.92, 1.5, 0.44, { fontSize: 11.2, color: C.blue, align: 'center' });
  put(s, 'абдоминальный\nсосудистый криз', 3.58, 4.12, 1.48, 0.44, { fontSize: 11.0, color: C.blue, align: 'center' });
  put(s, 'периферический\nспазм', 3.58, 5.33, 1.48, 0.4, { fontSize: 11.0, color: C.blue, align: 'center' });
  put(s, 'терминальное\nистощение', 3.58, 6.42, 1.48, 0.4, { fontSize: 11.0, color: C.blue, align: 'center' });
  put(s, 'генетическая и иммунная\nнесовместимость', 7.62, 1.7, 4.66, 0.42, { fontSize: 12.2, color: C.red, align: 'center', bold: true });
  put(s, 'сосудистый перегрев\nи гиперметаболизм', 7.62, 3.0, 4.66, 0.42, { fontSize: 12.2, color: C.red, align: 'center', bold: true });
  put(s, 'массовый гемолиз\nи высвобождение гема', 7.62, 4.25, 4.66, 0.42, { fontSize: 12.2, color: C.red, align: 'center', bold: true });
  put(s, 'воспалительный каскад\nи токсическая перегрузка', 7.62, 5.46, 4.66, 0.42, { fontSize: 12.2, color: C.red, align: 'center', bold: true });
  put(s, 'системный коллапс\nбез устойчивой адаптации', 7.62, 6.65, 4.66, 0.42, { fontSize: 12.2, color: C.red, align: 'center', bold: true });
  footer(s, 8);
}

// 9. recognition slide built in ppt
{
  const s = pptx.addSlide();
  textSlideBase(s, 'Как распознать вампира');
  card(s, 4.45, 1.55, 4.45, 4.4, 'Гемозависимый фенотип', 'Не монстр, а носитель устойчивых физиологических и поведенческих сдвигов.', { fill: C.white, line: C.blue, bodySize: 16, bodyValign: 'mid' });
  card(s, 0.86, 1.55, 3.1, 1.06, 'Внешний маркер 1', 'слегка увеличенные клыки', { fill: C.redSoft, line: C.red, bodySize: 14.5 });
  card(s, 0.86, 2.82, 3.1, 1.06, 'Внешний маркер 2', 'медленное старение, ровная кожа', { fill: C.redSoft, line: C.red, bodySize: 14.2 });
  card(s, 0.86, 4.09, 3.1, 1.06, 'Внешний маркер 3', 'тёмный зрачок при голоде', { fill: C.redSoft, line: C.red, bodySize: 14.2 });
  card(s, 9.33, 1.55, 3.14, 1.06, 'Поведение 1', 'интерес к крови и травмам', { fill: C.blueSoft, line: C.blue, bodySize: 14.2 });
  card(s, 9.33, 2.82, 3.14, 1.06, 'Поведение 2', 'тяга к закрытым кругам', { fill: C.blueSoft, line: C.blue, bodySize: 14.2 });
  card(s, 9.33, 4.09, 3.14, 1.06, 'Поведение 3', 'жёсткий контроль питания', { fill: C.blueSoft, line: C.blue, bodySize: 14.2 });
  pill(s, 1.62, 6.15, 10.0, 0.56, 'Оценивается не признак, а их комбинация.', { fill: C.dark, line: C.dark, color: C.white, fontSize: 16, bold: true });
  footer(s, 9);
}

// 10. blood economy
{
  const s = pptx.addSlide();
  fullImage(s, assets.economy);
  put(s, 'Экономика качественной крови', 0.42, 0.32, 12.48, 0.34, { fontSize: 22, bold: true, color: C.dark, align: 'center' });
  put(s, 'Массовая кровь — периферии.\nПиковая и династическая — элите.', 0.45, 6.3, 4.3, 0.8, { fontSize: 11.3, color: C.muted, align: 'center' });
  put(s, 'Чем древнее адаптант,\nтем уже доступ.', 5.05, 6.3, 4.3, 0.8, { fontSize: 11.3, color: C.muted, align: 'center' });
  footer(s, 10);
}

// 11. live vs artificial blood
{
  const s = pptx.addSlide();
  fullImage(s, assets.artificial);
  put(s, 'Почему искусственная кровь не подходит', 1.78, 0.2, 9.8, 0.34, { fontSize: 22, bold: true, color: C.dark, align: 'center' });
  put(s, 'вариативность\nсигналов', 0.24, 3.15, 2.05, 0.38, { fontSize: 10.8, color: C.red, align: 'center' });
  put(s, 'активная\nнейрохимия', 0.24, 4.16, 2.05, 0.38, { fontSize: 10.8, color: C.red, align: 'center' });
  put(s, 'иммунный\nотклик', 0.24, 5.18, 2.05, 0.38, { fontSize: 10.8, color: C.red, align: 'center' });
  put(s, 'эффект\nнасыщения', 0.24, 6.2, 2.05, 0.38, { fontSize: 10.8, color: C.red, align: 'center' });
  put(s, 'сигналы\nсглажены', 11.0, 3.15, 2.05, 0.38, { fontSize: 10.8, color: C.blue, align: 'center' });
  put(s, 'подпись\nстёрта', 11.0, 4.16, 2.05, 0.38, { fontSize: 10.8, color: C.blue, align: 'center' });
  put(s, 'ответ\nнизкий', 11.0, 5.18, 2.05, 0.38, { fontSize: 10.8, color: C.blue, align: 'center' });
  put(s, 'сытости\nнет', 11.0, 6.2, 2.05, 0.38, { fontSize: 10.8, color: C.blue, align: 'center' });
  put(s, 'Живая кровь: сигналы, метаболизм, насыщение.', 0.2, 6.7, 5.7, 0.48, {
    fontSize: 11.4, color: C.red, align: 'center', bold: true
  });
  put(s, 'Суррогат: транспорт без насыщения.', 7.48, 6.7, 5.7, 0.48, {
    fontSize: 11.4, color: C.blue, align: 'center', bold: true
  });
  footer(s, 11);
}

// 12. hidden donor market
{
  const s = pptx.addSlide();
  fullImage(s, assets.market);
  put(s, 'Тайный рынок доноров', 0.28, 0.18, 12.75, 0.36, { fontSize: 22, bold: true, color: C.dark, align: 'center' });
  put(s, 'Поток: источник → отбор → биобанк → доставка.\nКлючевые узкие места: объём, холод, безопасность.', 9.52, 5.85, 3.1, 1.0, {
    fontSize: 10.8, color: C.ink, align: 'left', valign: 'top'
  });
  footer(s, 12);
}

// 13. feeding zones map
{
  const s = pptx.addSlide();
  fullImage(s, assets.zones);
  put(s, 'Геополитика кормовых зон', 1.02, 0.18, 10.6, 0.34, { fontSize: 22, bold: true, color: C.dark, align: 'center' });
  put(s, 'Не страны, а контуры снабжения:\nрезервуары, коридоры, убежища, эксперименты.', 11.18, 5.2, 1.74, 1.2, {
    fontSize: 10.2, color: C.ink, align: 'center'
  });
  footer(s, 13);
}

// 14. consequences matrix
{
  const s = pptx.addSlide();
  textSlideBase(s, 'Следствия: почему вампиры именно такие');
  card(s, 0.92, 1.5, 5.8, 0.62, 'Биомеханизм', '', { fill: C.dark, line: C.dark, titleColor: C.white });
  card(s, 6.82, 1.5, 5.58, 0.62, 'Наблюдаемое следствие', '', { fill: C.red, line: C.red, titleColor: C.white });
  const left = [
    'дефицит сигнальных молекул',
    'изменённые фоторецепторные пути',
    'усиленная регенеративная сигнализация',
    'перестройка клеточного цикла',
    'хронический голодный стресс',
    'высокая цена качественной крови'
  ];
  const right = [
    'питание кровью',
    'чувствительность к солнцу',
    'быстрое восстановление после травм',
    'медленное старение',
    'ритуалы и иерархия доступа',
    'элитные сети и рынок доноров'
  ];
  for (let i = 0; i < left.length; i += 1) {
    const y = 2.35 + i * 0.7;
    card(s, 0.92, y, 5.8, 0.56, '', left[i], { fill: C.white, line: C.line, bodySize: 13.3, bodyValign: 'mid' });
    card(s, 6.82, y, 5.58, 0.56, '', right[i], { fill: C.white, line: C.line, bodySize: 13.3, bodyValign: 'mid' });
  }
  footer(s, 14);
}

// 15. conclusion
{
  const s = pptx.addSlide();
  s.background = { color: C.dark };
  put(s, 'Вывод', 0.95, 0.92, 2.5, 0.42, { fontSize: 28, bold: true, color: C.white, margin: 0 });
  put(s,
    'Если теория верна, власть — это доступ к качественной крови.',
    0.95, 2.0, 7.35, 1.55,
    { fontSize: 23, color: 'EAF2FB', margin: 0.02, valign: 'mid' }
  );
  card(s, 9.0, 1.7, 3.15, 3.4, 'Ключевые формулы', '• вампиризм = зависимость + инфраструктура\n\n• власть = доступ к крови\n\n• миф скрывает систему снабжения', {
    fill: '16324F',
    line: '7FA6C9',
    titleColor: C.white,
    bodyColor: 'EAF2FB',
    bodySize: 15,
    bodyValign: 'mid'
  });
  put(s, 'Псевдонаучная реконструкция', 0.98, 6.48, 6.9, 0.2, { fontSize: 12.5, color: 'B8C7D9', italic: true, margin: 0 });
  footer(s, 15, true);
}

async function main() {
  await pptx.writeFile({ fileName: outPath });
}

main().catch((err) => {
  console.error(err.stack || err.message || String(err));
  process.exit(1);
});