// ---- Brand identity board (the generated "identity file") + asset downloads ----

function saveCanvas(canvas, filename) {
  canvas.toBlob((blob) => {
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url; a.download = filename;
    document.body.appendChild(a); a.click(); a.remove();
    setTimeout(() => URL.revokeObjectURL(url), 1000);
  });
}

function fitFont(ctx, text, family, weight, maxWidth, startPx) {
  let size = startPx;
  ctx.font = `${weight} ${size}px "${family}", sans-serif`;
  while (ctx.measureText(text).width > maxWidth && size > 12) {
    size -= 2; ctx.font = `${weight} ${size}px "${family}", sans-serif`;
  }
  return size;
}

async function exportAsset(kind, brand) {
  try { await document.fonts.load(`600 64px "${brand.fonts.display}"`); } catch (e) {}
  const W = 1200, H = 750;
  const canvas = document.createElement('canvas');
  canvas.width = W; canvas.height = H;
  const ctx = canvas.getContext('2d');
  const dark = kind === 'wordmark-dark';
  const onAccent = kind === 'monogram-accent';
  const bg = dark ? brand.ink : (onAccent ? brand.primary : brand.paper);
  const fg = dark || onAccent ? brand.paper : brand.ink;
  ctx.fillStyle = bg; ctx.fillRect(0, 0, W, H);

  if (kind === 'wordmark-light' || kind === 'wordmark-dark') {
    ctx.textAlign = 'center'; ctx.textBaseline = 'middle';
    const size = fitFont(ctx, brand.businessName, brand.fonts.display, 700, W * 0.78, 130);
    ctx.fillStyle = fg;
    ctx.font = `700 ${size}px "${brand.fonts.display}", sans-serif`;
    ctx.fillText(brand.businessName, W / 2, H / 2 - 18);
    ctx.fillStyle = dark ? brand.secondary : brand.primary;
    ctx.font = `500 30px "${brand.fonts.body}", sans-serif`;
    ctx.fillText(brand.tagline, W / 2, H / 2 + size / 2 + 24);
  } else {
    // monogram badge
    const cx = W / 2, cy = H / 2, r = 150;
    ctx.fillStyle = onAccent ? brand.paper : brand.primary;
    if (brand.pattern === 'grid' || brand.pattern === 'triangles') {
      ctx.fillRect(cx - r, cy - r, r * 2, r * 2);
    } else {
      ctx.beginPath(); ctx.arc(cx, cy, r, 0, Math.PI * 2); ctx.fill();
    }
    ctx.fillStyle = onAccent ? brand.primary : brand.paper;
    ctx.textAlign = 'center'; ctx.textBaseline = 'middle';
    ctx.font = `700 150px "${brand.fonts.display}", sans-serif`;
    ctx.fillText(brand.monogram, cx, cy + 8);
  }
  saveCanvas(canvas, brand.businessName.replace(/\s+/g, '-').toLowerCase() + '-' + kind + '.png');
}

function printBoard() {
  document.body.classList.add('print-board');
  const cleanup = () => { document.body.classList.remove('print-board'); window.removeEventListener('afterprint', cleanup); };
  window.addEventListener('afterprint', cleanup);
  setTimeout(() => window.print(), 80);
}

// Render the AI-designed logo mark as inline SVG. When there's no designed icon
// (lettermark style, or an icon brand whose generation returned no shapes) the
// `framed` flag draws a deliberately designed monogram badge instead of a bare
// glyph, so text-only logos still read as an intentional mark. Without `framed`
// (e.g. inside an avatar/dot that is already a shaped container) it stays a
// plain monogram so we don't nest a badge inside a badge.
function LogoMark({ brand, size, mono, title, framed }) {
  const px = size || 80;
  // Prefer the real Ideogram-designed logo image when present, and use it on EVERY
  // surface so the identity shows ONE logo everywhere. The raster carries its own
  // light field, so on dark/brand surfaces callers give it a light backing chip
  // (or it simply reads as a light rounded tile); `mono` now only recolors the
  // vector fallback below when no raster logo exists.
  const raster = (brand.images && brand.images.logo) ? brand.images.logo : null;
  if (raster) {
    return (
      <img src={raster} width={px} height={px}
        alt={title || (brand.businessName + ' logo')}
        style={{ display: 'block', width: px, height: px, objectFit: 'contain', borderRadius: framed ? Math.round(px * 0.18) : 0 }} />
    );
  }
  if (!hasLogoMark(brand)) {
    const disp = '"' + brand.fonts.display + '", sans-serif';
    if (framed) {
      const b = letterBadgeParts(brand, mono);
      return (
        <svg viewBox="0 0 100 100" width={px} height={px} role="img" aria-label={title || (brand.businessName + ' monogram')} style={{ display: 'block' }}>
          <rect x="6" y="6" width="88" height="88" rx={b.rad} ry={b.rad} fill={b.fill} stroke={b.stroke} strokeWidth={b.strokeWidth} />
          <text x="50" y="54" textAnchor="middle" dominantBaseline="central" fontFamily={disp} fontWeight="700" fontSize="42" letterSpacing="-1" fill={b.letter}>{brand.monogram}</text>
        </svg>
      );
    }
    return (
      <svg viewBox="0 0 100 100" width={px} height={px} role="img" aria-label={title || (brand.businessName + ' monogram')} style={{ display: 'block' }}>
        <text x="50" y="54" textAnchor="middle" dominantBaseline="central" fontFamily={disp} fontWeight="700" fontSize="46" fill={mono || brand.primary}>{brand.monogram}</text>
      </svg>
    );
  }
  const resolve = (tok) => logoColorHex(brand, tok, mono);
  return (
    <svg viewBox="0 0 100 100" width={px} height={px} role="img" aria-label={title || (brand.businessName + ' logo')} style={{ display: 'block' }}>
      {brand.logo.shapes.map((s, i) => {
        const fill = resolve(s.fill);
        const stroke = resolve(s.stroke);
        const p = { fill, stroke };
        if (s.stroke !== 'none') { p.strokeWidth = s.strokeWidth || 4; p.strokeLinecap = 'round'; p.strokeLinejoin = 'round'; }
        if (typeof s.opacity === 'number') p.opacity = s.opacity;
        switch (s.type) {
          case 'circle': return <circle key={i} cx={s.cx} cy={s.cy} r={s.r} {...p} />;
          case 'ellipse': return <ellipse key={i} cx={s.cx} cy={s.cy} rx={s.rx} ry={s.ry} {...p} />;
          case 'rect': return <rect key={i} x={s.x} y={s.y} width={s.width} height={s.height} rx={s.rounded} {...p} />;
          case 'line': return <line key={i} x1={s.x1} y1={s.y1} x2={s.x2} y2={s.y2} {...p} />;
          case 'polygon': return <polygon key={i} points={s.points} {...p} />;
          case 'polyline': return <polyline key={i} points={s.points} fill="none" stroke={stroke === 'none' ? fill : stroke} strokeWidth={s.strokeWidth || 4} strokeLinecap="round" strokeLinejoin="round" />;
          case 'path': return <path key={i} d={s.d} {...p} />;
          default: return null;
        }
      })}
    </svg>
  );
}

// Renders the brand's logo honoring its chosen style:
//  - 'icon': the AI-designed pictorial mark
//  - 'lettermark': the monogram set in the display font (no icon)
//  - 'wordmark': the full business name set in the display font (no icon)
// kind 'mark' = square slot; kind 'lockup' = horizontal name lockup.
// A deliberately designed wordmark: the business name in the display face with
// tightened tracking and a short accent rule beneath it, so a name-only logo
// reads as an intentional mark rather than plain typed text.
function WordmarkArt({ brand, color, ruleColor, fontSize, align, title }) {
  const disp = '"' + brand.fonts.display + '", sans-serif';
  return (
    <div className={'logo-wordmark-block' + (align === 'left' ? ' lwb-left' : '')}
      style={{ fontFamily: disp, color, fontSize }}
      role="img" aria-label={title || (brand.businessName + ' wordmark')}>
      <span className="lwb-name">{brand.businessName}</span>
      <span className="lwb-rule" style={{ background: ruleColor }} aria-hidden="true"></span>
    </div>
  );
}

function BrandLogo({ brand, size, mono, kind, title }) {
  const style = brand.logoStyle || 'icon';
  const disp = '"' + brand.fonts.display + '", sans-serif';
  const color = mono || brand.ink;
  const ruleColor = mono || brand.accent || brand.primary;
  if (kind === 'lockup') {
    if (style === 'wordmark' && !hasLogoMark(brand)) {
      return (
        <div className="logo-lockup">
          <WordmarkArt brand={brand} color={color} ruleColor={ruleColor}
            fontSize={Math.max(15, Math.round((size || 64) * 0.34))} align="left" title={title} />
        </div>
      );
    }
    return (
      <div className="logo-lockup">
        <LogoMark brand={brand} size={size} mono={mono} framed />
        <span className="logo-wordmark" style={{ fontFamily: disp, color }}>{brand.businessName}</span>
      </div>
    );
  }
  if (style === 'wordmark' && !hasLogoMark(brand)) {
    return <WordmarkArt brand={brand} color={color} ruleColor={ruleColor}
      fontSize={Math.max(13, Math.round((size || 80) * 0.26))} title={title} />;
  }
  return <LogoMark brand={brand} size={size} mono={mono} title={title} framed />;
}

function loadSvgImage(svgString) {
  return new Promise((resolve, reject) => {
    const blob = new Blob([svgString], { type: 'image/svg+xml;charset=utf-8' });
    const url = URL.createObjectURL(blob);
    const img = new Image();
    img.onload = () => resolve({ img, url });
    img.onerror = (e) => { URL.revokeObjectURL(url); reject(e); };
    img.src = url;
  });
}

// Draw a raster logo (the Ideogram mark) with object-fit: contain into a square
// slot on a canvas. Returns true on success so callers fall back to the vector
// mark on any failure. Used by the PNG exports so downloaded logos match the
// board.
async function drawContainedRaster(ctx, src, x, y, w, h) {
  try {
    const img = await loadRasterImage(src);
    const nw = img.naturalWidth || img.width, nh = img.naturalHeight || img.height;
    if (!nw || !nh) return false;
    const scale = Math.min(w / nw, h / nh);
    const dw = nw * scale, dh = nh * scale;
    ctx.drawImage(img, x + (w - dw) / 2, y + (h - dh) / 2, dw, dh);
    return true;
  } catch (e) { return false; }
}

function logoFileName(brand, kind) {
  return brand.businessName.replace(/\s+/g, '-').toLowerCase() + '-' + kind + '.png';
}

// Draw the designed monogram badge on a canvas (the export-side twin of the
// on-screen LogoMark badge) so PNG exports match what's shown in the app.
function drawLetterBadge(ctx, brand, mono, cx, cy, d) {
  const b = letterBadgeParts(brand, mono);
  const r = d / 2;
  ctx.save();
  ctx.beginPath();
  if (b.rounded) {
    const rr = d * 0.18;
    if (ctx.roundRect) ctx.roundRect(cx - r, cy - r, d, d, rr);
    else ctx.rect(cx - r, cy - r, d, d);
  } else {
    ctx.arc(cx, cy, r, 0, Math.PI * 2);
  }
  if (b.fill !== 'none') { ctx.fillStyle = b.fill; ctx.fill(); }
  if (b.stroke !== 'none') { ctx.strokeStyle = b.stroke; ctx.lineWidth = Math.max(3, d * (b.strokeWidth / 100)); ctx.stroke(); }
  ctx.fillStyle = b.letter;
  ctx.textAlign = 'center'; ctx.textBaseline = 'middle';
  ctx.font = `700 ${Math.round(d * 0.42)}px "${brand.fonts.display}", sans-serif`;
  ctx.fillText(brand.monogram, cx, cy + d * 0.02);
  ctx.restore();
}

const LOGO_ASSET_CONF = {
  'mark-light': { bg: '@paper', mono: null, fg: '@ink' },
  'mark-dark': { bg: '@ink', mono: '@paper', fg: '@paper' },
  'mark-brand': { bg: '@primary', mono: '@paper', fg: '@paper' },
  'lockup-light': { bg: '@paper', mono: null, fg: '@ink' },
  'lockup-dark': { bg: '@ink', mono: '@paper', fg: '@paper' },
};
function logoAssetConf(kind, brand) {
  const raw = LOGO_ASSET_CONF[kind] || LOGO_ASSET_CONF['mark-light'];
  const tok = (v) => (typeof v === 'string' && v[0] === '@') ? brand[v.slice(1)] : v;
  return { bg: tok(raw.bg), mono: tok(raw.mono), fg: tok(raw.fg) };
}

// Build the logo PNG on a canvas (shared by direct download + the source-file
// bundle). Returns the canvas; callers save it or read a blob from it.
async function buildLogoCanvas(kind, brand) {
  const conf = logoAssetConf(kind, brand);
  try { await document.fonts.load(`700 90px "${brand.fonts.display}"`); } catch (e) {}

  if (kind.indexOf('lockup') >= 0) {
    // High-resolution export (~4K wide) so the downloaded PNG stays crisp and
    // editable. Layout is fully proportional, so scaling every dimension keeps
    // the composition identical, just at higher pixel density.
    const W = 4000, H = 1500, markPx = 750, gap = 150;
    const canvas = document.createElement('canvas');
    canvas.width = W; canvas.height = H;
    const ctx = canvas.getContext('2d');
    ctx.fillStyle = conf.bg; ctx.fillRect(0, 0, W, H);
    ctx.font = `700 375px "${brand.fonts.display}", sans-serif`;
    const textW = ctx.measureText(brand.businessName).width;
    let x = Math.max(100, (W - (markPx + gap + textW)) / 2);
    // Use the Ideogram raster on EVERY plate (matching the on-screen LogoMark's
    // one-logo-everywhere behavior). On dark/brand plates it reads as a light
    // rounded tile; the vector mark is only the fallback when no raster exists.
    const raster = (brand.images && brand.images.logo) ? brand.images.logo : null;
    const drew = raster ? await drawContainedRaster(ctx, raster, x, (H - markPx) / 2, markPx, markPx) : false;
    if (!drew) {
      const svg = logoSvgString(brand, { mono: conf.mono, size: markPx });
      if (svg) {
        try { const { img, url } = await loadSvgImage(svg); ctx.drawImage(img, x, (H - markPx) / 2, markPx, markPx); URL.revokeObjectURL(url); } catch (e) {}
      } else {
        // No designed mark: draw the designed monogram badge in the mark slot.
        drawLetterBadge(ctx, brand, conf.mono, x + markPx / 2, H / 2, markPx);
      }
    }
    x += markPx + gap;
    ctx.fillStyle = conf.fg; ctx.textAlign = 'left'; ctx.textBaseline = 'middle';
    ctx.fillText(brand.businessName, x, H / 2 + 6);
    return canvas;
  }

  // High-resolution square mark export (2K) for crisp, editable downloads.
  const S = 2048, markPx = Math.round(S * 0.62);
  const canvas = document.createElement('canvas');
  canvas.width = S; canvas.height = S;
  const ctx = canvas.getContext('2d');
  ctx.fillStyle = conf.bg; ctx.fillRect(0, 0, S, S);
  const raster = (brand.images && brand.images.logo) ? brand.images.logo : null;
  const drew = raster ? await drawContainedRaster(ctx, raster, (S - markPx) / 2, (S - markPx) / 2, markPx, markPx) : false;
  if (!drew) {
    const svg = logoSvgString(brand, { mono: conf.mono, size: markPx });
    if (svg) {
      try { const { img, url } = await loadSvgImage(svg); ctx.drawImage(img, (S - markPx) / 2, (S - markPx) / 2, markPx, markPx); URL.revokeObjectURL(url); } catch (e) {}
    } else {
      drawLetterBadge(ctx, brand, conf.mono, S / 2, S / 2, markPx);
    }
  }
  return canvas;
}

// Export the logo mark (or a mark+wordmark lockup) as a PNG.
async function exportLogoAsset(kind, brand) {
  const canvas = await buildLogoCanvas(kind, brand);
  saveCanvas(canvas, logoFileName(brand, kind));
}

function downloadLogoSvg(brand) {
  const svg = logoSvgString(brand, { size: 1024 });
  if (!svg) return;
  const blob = new Blob([svg], { type: 'image/svg+xml;charset=utf-8' });
  const url = URL.createObjectURL(blob);
  const a = document.createElement('a');
  a.href = url; a.download = brand.businessName.replace(/\s+/g, '-').toLowerCase() + '-logo.svg';
  document.body.appendChild(a); a.click(); a.remove();
  setTimeout(() => URL.revokeObjectURL(url), 1000);
}

// ---- Editable source-file bundle ("Download files") ----
// Builds a fully editable kit so the user can open the brand in any program:
// vector logos (SVG), raster logos (PNG), every AI photo, plus a brand guide,
// the raw colors, and the brand JSON. Bundled into a single .zip built in the
// browser with a tiny dependency-free store-only ZIP writer.

function xmlEsc(s) {
  return String(s == null ? '' : s).replace(/[&<>"']/g, (c) => (
    { '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' }[c]
  ));
}
function slug(brand) {
  return (brand.businessName || 'brand').replace(/[^a-z0-9]+/gi, '-').replace(/^-+|-+$/g, '').toLowerCase() || 'brand';
}

// A standalone, vector-editable lockup SVG (mark/badge + business name as text)
// in light or dark colorway. Text stays live so it can be re-typed in any editor.
function lockupSvgString(brand, dark) {
  const W = 1600, H = 600, markPx = 300, gap = 60, pad = 40;
  const bg = dark ? brand.ink : brand.paper;
  const fg = dark ? brand.paper : brand.ink;
  const mono = dark ? brand.paper : null;
  const disp = xmlEsc(brand.fonts.display);
  const markSvg = logoSvgString(brand, { mono: mono, size: markPx });
  const markY = (H - markPx) / 2;
  let inner = '';
  let textX;
  if (markSvg) {
    // Strip the outer <svg ...> wrapper to get just the shape body. The mark
    // SVG uses a 0 0 100 100 viewBox, so scale it into the markPx slot.
    const body = markSvg.replace(/^<svg[^>]*>/, '').replace(/<\/svg>\s*$/, '');
    inner += `<g transform="translate(${pad} ${markY}) scale(${markPx / 100})">${body}</g>`;
    textX = pad + markPx + gap;
  } else {
    // Designed monogram badge as vector.
    const b = letterBadgeParts(brand, mono);
    const cx = pad + markPx / 2, cy = H / 2, r = markPx / 2;
    if (b.rounded) {
      inner += `<rect x="${pad}" y="${markY}" width="${markPx}" height="${markPx}" rx="${markPx * 0.18}"${b.fill !== 'none' ? ` fill="${b.fill}"` : ' fill="none"'}${b.stroke !== 'none' ? ` stroke="${b.stroke}" stroke-width="${Math.max(3, markPx * (b.strokeWidth / 100))}"` : ''}/>`;
    } else {
      inner += `<circle cx="${cx}" cy="${cy}" r="${r}"${b.fill !== 'none' ? ` fill="${b.fill}"` : ' fill="none"'}${b.stroke !== 'none' ? ` stroke="${b.stroke}" stroke-width="${Math.max(3, markPx * (b.strokeWidth / 100))}"` : ''}/>`;
    }
    inner += `<text x="${cx}" y="${cy}" text-anchor="middle" dominant-baseline="central" font-family="${disp}, sans-serif" font-weight="700" font-size="${Math.round(markPx * 0.42)}" fill="${b.letter}">${xmlEsc(brand.monogram)}</text>`;
    textX = pad + markPx + gap;
  }
  inner += `<text x="${textX}" y="${H / 2}" dominant-baseline="central" font-family="${disp}, sans-serif" font-weight="700" font-size="150" fill="${fg}">${xmlEsc(brand.businessName)}</text>`;
  return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${W} ${H}" width="${W}" height="${H}"><rect width="${W}" height="${H}" fill="${bg}"/>${inner}</svg>`;
}

// --- Minimal store-only (no-compression) ZIP writer ---
const _crcTable = (() => {
  const t = new Uint32Array(256);
  for (let n = 0; n < 256; n++) {
    let c = n;
    for (let k = 0; k < 8; k++) c = (c & 1) ? (0xEDB88320 ^ (c >>> 1)) : (c >>> 1);
    t[n] = c >>> 0;
  }
  return t;
})();
function crc32(bytes) {
  let c = 0xFFFFFFFF;
  for (let i = 0; i < bytes.length; i++) c = _crcTable[(c ^ bytes[i]) & 0xFF] ^ (c >>> 8);
  return (c ^ 0xFFFFFFFF) >>> 0;
}
function buildZip(files) {
  const enc = new TextEncoder();
  const chunks = [];
  const central = [];
  let offset = 0;
  const u16 = (n) => [n & 0xFF, (n >>> 8) & 0xFF];
  const u32 = (n) => [n & 0xFF, (n >>> 8) & 0xFF, (n >>> 16) & 0xFF, (n >>> 24) & 0xFF];
  for (const f of files) {
    const nameBytes = enc.encode(f.name);
    const data = f.data instanceof Uint8Array ? f.data : enc.encode(String(f.data));
    const crc = crc32(data);
    const local = [].concat(
      u32(0x04034b50), u16(20), u16(0), u16(0), u16(0), u16(0),
      u32(crc), u32(data.length), u32(data.length), u16(nameBytes.length), u16(0)
    );
    chunks.push(new Uint8Array(local), nameBytes, data);
    central.push({ nameBytes, crc, size: data.length, offset });
    offset += local.length + nameBytes.length + data.length;
  }
  const cdStart = offset;
  let cdSize = 0;
  for (const c of central) {
    const head = [].concat(
      u32(0x02014b50), u16(20), u16(20), u16(0), u16(0), u16(0), u16(0),
      u32(c.crc), u32(c.size), u32(c.size), u16(c.nameBytes.length),
      u16(0), u16(0), u16(0), u16(0), u32(0), u32(c.offset)
    );
    chunks.push(new Uint8Array(head), c.nameBytes);
    cdSize += head.length + c.nameBytes.length;
  }
  const end = [].concat(
    u32(0x06054b50), u16(0), u16(0), u16(central.length), u16(central.length),
    u32(cdSize), u32(cdStart), u16(0)
  );
  chunks.push(new Uint8Array(end));
  return new Blob(chunks, { type: 'application/zip' });
}

function brandGuideText(brand) {
  const k = brand.kit || {};
  const L = [];
  L.push(brand.businessName + ' — Brand Guide');
  L.push('='.repeat((brand.businessName + ' — Brand Guide').length));
  L.push('');
  if (brand.tagline) L.push('Tagline: ' + brand.tagline);
  if (k.statement) L.push('Statement: ' + k.statement);
  if (brand.domain && brand.domain.display) L.push('Domain: ' + brand.domain.display);
  L.push('');
  L.push('COLORS');
  L.push('------');
  [['Primary', brand.primary], ['Secondary', brand.secondary], ['Accent', brand.accent], ['Ink', brand.ink], ['Paper', brand.paper]]
    .forEach(([n, v]) => { if (v) L.push(n.padEnd(11) + ' ' + v); });
  L.push('');
  L.push('TYPOGRAPHY');
  L.push('----------');
  L.push('Display: ' + brand.fonts.display);
  L.push('Body:    ' + brand.fonts.body);
  L.push('Get the fonts (Google Fonts):');
  L.push('  https://fonts.google.com/specimen/' + String(brand.fonts.display).replace(/ /g, '+'));
  L.push('  https://fonts.google.com/specimen/' + String(brand.fonts.body).replace(/ /g, '+'));
  const posts = (k.socialPosts && k.socialPosts.length) ? k.socialPosts : (k.socialPost ? [k.socialPost] : []);
  const heads = (k.storyHeadlines && k.storyHeadlines.length) ? k.storyHeadlines : (k.storyHeadline ? [k.storyHeadline] : []);
  if (posts.length || heads.length) {
    L.push('');
    L.push('SOCIAL COPY');
    L.push('-----------');
    posts.forEach((p, i) => L.push('Post ' + (i + 1) + ': ' + p));
    heads.forEach((h, i) => L.push('Story ' + (i + 1) + ': ' + h));
  }
  L.push('');
  L.push('EDITING THE LOGO');
  L.push('----------------');
  L.push('The logo master is the SVG vector file in logo/. SVG is an open, editable');
  L.push('format: open it in Figma, Inkscape (free & open-source), Adobe Illustrator,');
  L.push('Affinity Designer, or Photoshop to recolor, resize or restyle with no quality');
  L.push('loss. For pixel/raster editing use logo/*-transparent.png. There is no .PSD');
  L.push('because the SVG is the editable source and opens in Photoshop too.');
  L.push('');
  L.push('FILES IN THIS BUNDLE');
  L.push('--------------------');
  L.push('logo/*.svg              Vector logos (the editable master) — open in Figma,');
  L.push('                        Inkscape, Illustrator, Affinity or Photoshop.');
  L.push('logo/*.png              High-res raster logos (4000x1500 lockups, 2048x2048 marks).');
  L.push('logo/*-transparent.png  Logo mark on a transparent background (raster editing).');
  L.push('mockups/*.png           Finished merch mockups with the logo already applied.');
  L.push('images/*                The raw brand photos / blanks (edit in any image app).');
  L.push('brand.json              The full raw identity data (the source of truth).');
  return L.join('\n');
}

// Pull every brand photo (social cover/posts/stories, stationery, merch) so the
// user gets the actual images. Served same-origin, so fetch works.
async function collectBrandImageFiles(brand) {
  const imgs = brand.images || {};
  const jobs = [];
  const push = (name, url) => { if (url) jobs.push({ name, url }); };
  const social = imgs.social;
  if (social && typeof social === 'object') {
    push('images/social-cover.png', social.cover);
    (social.posts || []).forEach((u, i) => push('images/social-post-' + (i + 1) + '.png', u));
    (social.stories || []).forEach((u, i) => push('images/social-story-' + (i + 1) + '.png', u));
  } else if (typeof social === 'string') {
    push('images/social.png', social);
  }
  push('images/stationery.png', imgs.stationery);
  if (imgs.merch) ['tee', 'tote', 'mug', 'cap'].forEach((kKind) => push('images/merch-' + kKind + '-blank.png', imgs.merch[kKind]));
  const out = [];
  await Promise.all(jobs.map(async (j) => {
    try {
      const res = await fetch(j.url, { cache: 'no-store' });
      if (!res.ok) return;
      const buf = new Uint8Array(await res.arrayBuffer());
      out.push({ name: j.name, data: buf });
    } catch (e) {}
  }));
  return out;
}

async function downloadSourceFiles(brand, onState) {
  try {
    if (onState) onState('working');
    try { await document.fonts.load(`700 90px "${brand.fonts.display}"`); } catch (e) {}
    const files = [];
    // Vector logos (editable in any vector program).
    files.push({ name: 'logo/' + slug(brand) + '-lockup-light.svg', data: lockupSvgString(brand, false) });
    files.push({ name: 'logo/' + slug(brand) + '-lockup-dark.svg', data: lockupSvgString(brand, true) });
    const markLight = logoSvgString(brand, { size: 1024 });
    const markDark = logoSvgString(brand, { size: 1024, mono: brand.paper });
    if (markLight) files.push({ name: 'logo/' + slug(brand) + '-mark.svg', data: markLight });
    if (markDark) files.push({ name: 'logo/' + slug(brand) + '-mark-dark.svg', data: markDark });
    // Raster logos.
    const pngKinds = ['lockup-light', 'lockup-dark', 'mark-light', 'mark-dark', 'mark-brand'];
    // Sequential, not parallel: at 2K-4K each canvas is large (a 4000x1500
    // lockup is ~24MB of raw pixels), so rendering all five at once risks
    // freezes / OOM on low-memory devices. One at a time keeps peak memory low.
    for (const kind of pngKinds) {
      try {
        const canvas = await buildLogoCanvas(kind, brand);
        const blob = await new Promise((r) => canvas.toBlob(r, 'image/png'));
        if (blob) files.push({ name: 'logo/' + logoFileName(brand, kind), data: new Uint8Array(await blob.arrayBuffer()) });
      } catch (e) {}
    }
    // Transparent-background mark PNG so the logo can be re-edited in any raster app.
    try {
      const tcanvas = await buildTransparentMarkCanvas(brand);
      const tblob = await new Promise((r) => tcanvas.toBlob(r, 'image/png'));
      if (tblob) files.push({ name: 'logo/' + slug(brand) + '-mark-transparent.png', data: new Uint8Array(await tblob.arrayBuffer()) });
    } catch (e) {}
    // The real Ideogram-designed logo mark (icon brands), exactly as shown on the board.
    if (brand.images && brand.images.logo) {
      try {
        const res = await fetch(brand.images.logo, { cache: 'no-store' });
        if (res.ok) files.push({ name: 'logo/' + slug(brand) + '-logo.png', data: new Uint8Array(await res.arrayBuffer()) });
      } catch (e) {}
    }
    // Finished merch mockups: the product photo with the logo composited on, so the
    // downloaded merch matches the board (the raw blanks, if any, live in images/).
    for (const m of MERCH_ITEMS) {
      try {
        const mcanvas = await buildMerchCanvas(m.kind, brand, m.ink);
        if (!mcanvas) continue;
        const mblob = await new Promise((r) => mcanvas.toBlob(r, 'image/png'));
        if (mblob) files.push({ name: 'mockups/merch-' + m.kind + '.png', data: new Uint8Array(await mblob.arrayBuffer()) });
      } catch (e) {}
    }
    // Brand photos.
    const imgFiles = await collectBrandImageFiles(brand);
    files.push.apply(files, imgFiles);
    // Docs / source data.
    files.push({ name: 'brand-guide.txt', data: brandGuideText(brand) });
    files.push({ name: 'colors.txt', data: ['primary ' + brand.primary, 'secondary ' + brand.secondary, 'accent ' + brand.accent, 'ink ' + brand.ink, 'paper ' + brand.paper].join('\n') });
    files.push({ name: 'brand.json', data: JSON.stringify(brand, null, 2) });

    const zip = buildZip(files);
    const url = URL.createObjectURL(zip);
    const a = document.createElement('a');
    a.href = url; a.download = slug(brand) + '-brand-files.zip';
    document.body.appendChild(a); a.click(); a.remove();
    setTimeout(() => URL.revokeObjectURL(url), 2000);
  } finally {
    if (onState) onState('idle');
  }
}

function LogoTile({ label, brand, bg, fg, mono, onDownload }) {
  return (
    <div className="logo-tile" style={{ background: bg, color: fg }}>
      <div className="logo-tile-art">
        {mono ? (
          <div className="logo-badge" style={{ background: fg, color: bg, borderRadius: (brand.pattern === 'grid' || brand.pattern === 'triangles') ? '14px' : '50%', fontFamily: '"' + brand.fonts.display + '", sans-serif' }}>{brand.monogram}</div>
        ) : (
          <div className="logo-wordmark" style={{ fontFamily: '"' + brand.fonts.display + '", sans-serif', color: fg }}>{brand.businessName}</div>
        )}
      </div>
      <div className="logo-tile-foot">
        <span>{label}</span>
        <button className="asset-dl" onClick={onDownload} style={{ color: fg, borderColor: fg }}>↓ PNG</button>
      </div>
    </div>
  );
}

// Merch uses REAL product photos (public/mockups/*.png — studio shots of blank
// white blanks) with the brand logo overlaid, so each item reads as an actual
// printed product rather than a flat drawing.
const MOCKUP_SRC = {
  tee: 'mockups/tee.png',
  tote: 'mockups/tote.png',
  mug: 'mockups/mug.png',
  cap: 'mockups/cap.png',
};

// Where the logo sits on each product photo (% of the square stage) + its size.
const LOGO_POS = {
  tee: { top: '45%', left: '50%', size: 40 },
  tote: { top: '57%', left: '50%', size: 48 },
  mug: { top: '50%', left: '39%', size: 30 },
  cap: { top: '43%', left: '50%', size: 26 },
};

// Logo placement for the REAL AI product photos, which are centered, front-facing
// blanks — so the logo sits in the front-center clear area of each item.
const REAL_LOGO_POS = {
  tee: { top: '46%', left: '50%', size: 38 },
  tote: { top: '52%', left: '50%', size: 44 },
  mug: { top: '50%', left: '44%', size: 28 },
  cap: { top: '47%', left: '50%', size: 24 },
};

function ProductMock({ brand, kind, label, ink }) {
  const realSrc = brand.images && brand.images.merch && brand.images.merch[kind];
  // New AI merch photos have the print baked into the product, so we must NOT
  // overlay the flat logo (it would double up). Legacy/CSS-mockup items still get
  // the overlay.
  const bakedIn = !!(realSrc && brand.images && brand.images.merchPrinted);
  const pos = (realSrc ? REAL_LOGO_POS[kind] : LOGO_POS[kind]) || LOGO_POS.tee;
  return (
    <div className="product">
      <div className="product-art">
        <img className="product-img" src={realSrc || MOCKUP_SRC[kind] || MOCKUP_SRC.tee} alt={label + ' mockup'} loading="lazy" />
        {!bakedIn && (
          <div className="product-logo" style={{ top: pos.top, left: pos.left }}>
            <BrandLogo brand={brand} size={pos.size} mono={ink} kind="mark" />
          </div>
        )}
      </div>
      <span className="product-label">{label}</span>
    </div>
  );
}

// On-screen merch tiles are ~234px wide (board 1080 max-width, .bb-section 26px
// padding, 4-col grid with 14px gaps); the LOGO_POS sizes are tuned against that
// width, so we scale them by the export canvas size to keep proportions identical.
const MERCH_STAGE_PX = 234;
const MERCH_ITEMS = [
  { kind: 'tee', ink: 'primary' },
  { kind: 'tote', ink: 'ink' },
  { kind: 'mug', ink: 'primary' },
  { kind: 'cap', ink: 'ink' },
];

function loadRasterImage(src) {
  return new Promise((resolve, reject) => {
    const img = new Image();
    img.crossOrigin = 'anonymous';
    img.onload = () => resolve(img);
    img.onerror = reject;
    img.src = src;
  });
}

// Composite the brand logo onto a product photo exactly as the board shows it, so
// the downloaded merch file is the finished, logo'd mockup — not a blank product.
async function buildMerchCanvas(kind, brand, inkToken) {
  const realSrc = brand.images && brand.images.merch && brand.images.merch[kind];
  const bakedIn = !!(realSrc && brand.images && brand.images.merchPrinted);
  const src = realSrc || MOCKUP_SRC[kind] || MOCKUP_SRC.tee;
  let img;
  try { img = await loadRasterImage(src); } catch (e) { return null; }
  const S = 1600;
  const canvas = document.createElement('canvas');
  canvas.width = S; canvas.height = S;
  const ctx = canvas.getContext('2d');
  ctx.fillStyle = '#fff'; ctx.fillRect(0, 0, S, S);   // matches .product-art bg
  // object-fit: contain — letterbox the product photo, centered.
  const nw = img.naturalWidth || img.width, nh = img.naturalHeight || img.height;
  const scale = Math.min(S / nw, S / nh);
  const dw = nw * scale, dh = nh * scale;
  ctx.drawImage(img, (S - dw) / 2, (S - dh) / 2, dw, dh);
  // Baked-in print: the photo already shows the finished merch, nothing to overlay.
  if (bakedIn) return canvas;
  // Logo overlay, positioned/sized to mirror the on-screen ProductMock.
  const pos = (realSrc ? REAL_LOGO_POS[kind] : LOGO_POS[kind]) || LOGO_POS.tee;
  const mono = brand[inkToken];
  const d = (pos.size / MERCH_STAGE_PX) * S;
  const cx = (parseFloat(pos.left) / 100) * S;
  const cy = (parseFloat(pos.top) / 100) * S;
  // Prefer the one Ideogram raster logo (matching the on-screen ProductMock, which
  // overlays it via LogoMark); fall back to the vector mark / letter badge.
  const raster = (brand.images && brand.images.logo) ? brand.images.logo : null;
  const drew = raster ? await drawContainedRaster(ctx, raster, cx - d / 2, cy - d / 2, d, d) : false;
  if (!drew) {
    const svg = logoSvgString(brand, { mono: mono, size: Math.round(d) });
    if (svg) {
      try { const { img: limg, url } = await loadSvgImage(svg); ctx.drawImage(limg, cx - d / 2, cy - d / 2, d, d); URL.revokeObjectURL(url); } catch (e) {}
    } else {
      drawLetterBadge(ctx, brand, mono, cx, cy, d);
    }
  }
  return canvas;
}

// A transparent-background mark PNG (no colored plate) for raster logo editing.
async function buildTransparentMarkCanvas(brand) {
  const S = 2048, markPx = Math.round(S * 0.62);
  const canvas = document.createElement('canvas');
  canvas.width = S; canvas.height = S;
  const ctx = canvas.getContext('2d');
  const svg = logoSvgString(brand, { size: markPx });
  if (svg) {
    try { const { img, url } = await loadSvgImage(svg); ctx.drawImage(img, (S - markPx) / 2, (S - markPx) / 2, markPx, markPx); URL.revokeObjectURL(url); } catch (e) {}
  } else {
    drawLetterBadge(ctx, brand, null, S / 2, S / 2, markPx);
  }
  return canvas;
}

function BrandBoard({ brand, onBack }) {
  React.useEffect(() => { ensureBrandFonts(brand); }, [brand]);
  const [filesState, setFilesState] = React.useState('idle');
  const disp = '"' + brand.fonts.display + '", sans-serif';
  const body = '"' + brand.fonts.body + '", sans-serif';
  // The single generated logo (Ideogram raster) used across every slot. When set,
  // logo containers on dark/brand surfaces switch to a light backing so the one
  // logo reads identically everywhere; when absent we fall back to the vector mark.
  const rasterLogo = (brand.images && brand.images.logo) ? brand.images.logo : null;

  // Build the richer social set: each format gets its OWN photo + copy when
  // available, falling back gracefully (legacy single image, or CSS pattern).
  const simg = brand.images && brand.images.social;
  const sPosts = (simg && simg.posts) || [];
  const sStories = (simg && simg.stories) || [];
  const socialCover = simg ? (simg.cover || sPosts[0] || sStories[0]) : undefined;
  const kitPosts = (brand.kit.socialPosts && brand.kit.socialPosts.length) ? brand.kit.socialPosts : [brand.kit.socialPost];
  const kitHeads = (brand.kit.storyHeadlines && brand.kit.storyHeadlines.length) ? brand.kit.storyHeadlines : [brand.kit.storyHeadline];
  const postItems = Array.from({ length: 2 }, (_, i) => ({
    img: sPosts[i] || sPosts[0] || socialCover,
    caption: kitPosts[i] || kitPosts[0],
    headline: kitHeads[i] || kitHeads[0],
  }));
  const storyItems = Array.from({ length: 2 }, (_, i) => ({
    img: sStories[i] || sStories[0] || socialCover,
    headline: kitHeads[i] || kitHeads[0],
  }));

  return (
    <div className="board-wrap">
      <div className="board-bar">
        <button className="btn-link board-back" onClick={onBack}>‹ All identities</button>
        <div className="board-bar-actions">
          <button className="btn-ghost btn-sm" onClick={() => exportLogoAsset('lockup-light', brand)}>Download logo</button>
          <button className="btn-ghost btn-sm" disabled={filesState === 'working'} onClick={() => downloadSourceFiles(brand, setFilesState)}>{filesState === 'working' ? 'Preparing…' : 'Download files'}</button>
          <button className="btn-primary btn-sm" onClick={printBoard}>Download PDF</button>
        </div>
      </div>

      <div className="brand-board" id="brandPrint">
        {/* Cover */}
        <section className="bb-cover" style={{ background: brand.paper, color: brand.ink }}>
          <div className="bb-cover-mark"><BrandLogo brand={brand} size={88} kind="mark" title={brand.businessName + ' logo'} /></div>
          <div className="bb-cover-tag" style={{ color: brand.primary }}>BRAND IDENTITY</div>
          <h1 className="bb-name" style={{ fontFamily: disp }}>{brand.businessName}</h1>
          <p className="bb-tagline" style={{ fontFamily: body, color: brand.primary }}>{brand.tagline}</p>
          <div className="bb-cover-meta" style={{ fontFamily: body }}>
            <span className="bb-chip" style={{ borderColor: brand.ink }}>{brand.domain.display}</span>
            <span className="bb-chip" style={{ borderColor: brand.ink }}>{brand.vibe}</span>
          </div>
          <div className="bb-cover-pattern" style={patternCss(brand, brand.paper, brand.secondary)}></div>
        </section>

        {/* Logo */}
        <section className="bb-section">
          <div className="bb-h"><span className="bb-num">01</span> Logo</div>
          <div className="logo-grid">
            <div className="logo-tile" style={{ background: brand.paper, color: brand.ink }}>
              <BrandLogo brand={brand} size={64} kind="lockup" />
              <div className="logo-tile-foot">
                <span>Primary lockup</span>
                <button className="asset-dl" onClick={() => exportLogoAsset('lockup-light', brand)}>↓ PNG</button>
              </div>
            </div>
            <div className="logo-tile" style={{ background: brand.paper, color: brand.ink }}>
              <div className="logo-tile-art"><BrandLogo brand={brand} size={104} kind="mark" /></div>
              <div className="logo-tile-foot">
                <span>{brand.logoStyle === 'wordmark' ? 'Wordmark' : (brand.logoStyle === 'lettermark' ? 'Lettermark' : 'Logo mark')}</span>
                <span className="logo-dl-group">
                  <button className="asset-dl" onClick={() => exportLogoAsset('mark-light', brand)}>↓ PNG</button>
                  {hasLogoMark(brand) && <button className="asset-dl" onClick={() => downloadLogoSvg(brand)}>↓ SVG</button>}
                </span>
              </div>
            </div>
            <div className="logo-tile" style={{ background: brand.ink, color: brand.paper }}>
              <BrandLogo brand={brand} size={64} mono={brand.paper} kind="lockup" />
              <div className="logo-tile-foot">
                <span>Reversed lockup</span>
                <button className="asset-dl asset-dl-dark" onClick={() => exportLogoAsset('lockup-dark', brand)}>↓ PNG</button>
              </div>
            </div>
            <div className="logo-tile" style={{ background: brand.primary, color: brand.paper }}>
              <div className="logo-tile-art"><BrandLogo brand={brand} size={104} mono={brand.paper} kind="mark" /></div>
              <div className="logo-tile-foot">
                <span>{brand.logoStyle === 'wordmark' ? 'Wordmark on brand' : 'Mark on brand'}</span>
                <button className="asset-dl asset-dl-dark" onClick={() => exportLogoAsset('mark-brand', brand)}>↓ PNG</button>
              </div>
            </div>
          </div>
        </section>

        {/* Colors */}
        <section className="bb-section">
          <div className="bb-h"><span className="bb-num">02</span> Color</div>
          <div className="swatch-row">
            {brand.colors.map(c => (
              <div className="swatch" key={c.role}>
                <div className="swatch-chip" style={{ background: c.hex, borderColor: c.hex.toLowerCase() === brand.paper.toLowerCase() ? '#E0DCD3' : c.hex }}></div>
                <div className="swatch-role">{c.role}</div>
                <div className="swatch-hex">{c.hex.toUpperCase()}</div>
              </div>
            ))}
          </div>
        </section>

        {/* Typography */}
        <section className="bb-section">
          <div className="bb-h"><span className="bb-num">03</span> Typography</div>
          <div className="type-grid">
            <div className="type-card type-card-display" style={{ background: brand.ink, color: brand.paper }}>
              <div className="type-meta" style={{ color: brand.secondary }}>Display · {brand.fonts.display} · {brand.fonts.dWeight || 700}</div>
              <div className="type-headline" style={{ fontFamily: disp, fontWeight: brand.fonts.dWeight || 700 }}>{brand.kit.storyHeadline}</div>
              <div className="type-alpha" style={{ fontFamily: disp, color: brand.secondary }}>AaBbCcDd · 0123456789</div>
            </div>
            <div className="type-card" style={{ background: brand.paper, color: brand.ink, border: '1px solid #ECE8DF' }}>
              <div className="type-meta" style={{ color: brand.primary }}>Body · {brand.fonts.body}</div>
              <p className="type-body" style={{ fontFamily: body }}>{brand.kit.statement}</p>
              <div className="type-weights" style={{ fontFamily: body }}>
                <span style={{ fontWeight: 400 }}>Regular</span>
                <span style={{ fontWeight: 500 }}>Medium</span>
                <span style={{ fontWeight: 700 }}>Bold</span>
              </div>
            </div>
          </div>
        </section>

        {/* Pattern */}
        <section className="bb-section">
          <div className="bb-h"><span className="bb-num">04</span> Pattern &amp; texture</div>
          <div className="pattern-grid">
            {patternSet(brand).map((p, i) => (
              <div className="pattern-cell" key={i}>
                <div className="pattern-tile" style={p.style}></div>
                <div className="pattern-name" style={{ fontFamily: body }}>{p.pattern} · {p.label}</div>
              </div>
            ))}
          </div>
        </section>

        {/* Social */}
        <section className="bb-section">
          <div className="bb-h"><span className="bb-num">05</span> Social media</div>
          <div className="social-grid">
            {/* Profile mockup */}
            <div className="social-profile" style={{ background: brand.paper, color: brand.ink }}>
              <div className="prof-cover" style={socialCover ? { backgroundImage: `url("${socialCover}")`, backgroundSize: 'cover', backgroundPosition: 'center' } : patternCss(brand, brand.primary, brand.paper)}></div>
              <div className="prof-row">
                <div className="prof-avatar" style={{ background: rasterLogo ? brand.paper : brand.primary }}><LogoMark brand={brand} size={48} mono={rasterLogo ? null : brand.paper} /></div>
                <button className="prof-follow" style={{ background: brand.primary, color: brand.paper, fontFamily: body }}>Follow</button>
              </div>
              <div className="prof-name" style={{ fontFamily: disp }}>{brand.businessName}</div>
              <div className="prof-handle" style={{ fontFamily: body, color: brand.primary }}>@{(brand.domain.name || brand.businessName).replace(/[^a-z0-9]/gi, '').toLowerCase()}</div>
              <div className="prof-bio" style={{ fontFamily: body }}>{brand.kit.statement}</div>
              <div className="prof-link" style={{ fontFamily: body, color: brand.primary }}>{brand.domain.display}</div>
            </div>
            {/* Feed posts — one per idea, each its own photo + caption */}
            {postItems.map((post, i) => (
              <div className="social-post" key={i} style={{ background: brand.paper, color: brand.ink }}>
                <div className="sp-head">
                  <span className="sp-dot" style={{ background: rasterLogo ? brand.paper : brand.primary }}><LogoMark brand={brand} size={20} mono={rasterLogo ? null : brand.paper} /></span>
                  <span className="sp-handle" style={{ fontFamily: body }}>{brand.businessName}</span>
                </div>
                {post.img ? (
                  <div className="sp-canvas" style={{ backgroundImage: `url("${post.img}")`, backgroundSize: 'cover', backgroundPosition: 'center', position: 'relative', aspectRatio: '1 / 1', overflow: 'hidden' }}>
                    <div style={{ position: 'absolute', inset: 0, display: 'flex', alignItems: 'flex-end', padding: '16px', background: 'linear-gradient(180deg, rgba(0,0,0,0) 45%, rgba(0,0,0,0.6))' }}>
                      <div className="sp-headline" style={{ fontFamily: disp, color: '#fff', margin: 0 }}>{post.headline}</div>
                    </div>
                  </div>
                ) : (
                  <div className="sp-canvas" style={patternCss(brand, brand.primary, brand.paper)}>
                    <div className="sp-canvas-inner" style={{ background: brand.ink, color: brand.paper }}>
                      <div className="sp-headline" style={{ fontFamily: disp }}>{post.headline}</div>
                    </div>
                  </div>
                )}
                <div className="sp-caption" style={{ fontFamily: body }}><strong>{brand.businessName}</strong> {post.caption}</div>
              </div>
            ))}
          </div>
          {/* Stories — separate row, one photo per story */}
          <div className="social-stories">
            {storyItems.map((story, i) => (
              <div className="social-story" key={i} style={{ background: brand.primary, color: brand.paper }}>
                <div className="ss-pattern" style={story.img ? { backgroundImage: `linear-gradient(180deg, rgba(0,0,0,0.12), rgba(0,0,0,0.55)), url("${story.img}")`, backgroundSize: 'cover', backgroundPosition: 'center' } : patternCss(brand, brand.primary, brand.paper)}></div>
                <div className="ss-badge" style={rasterLogo ? { background: brand.paper, borderRadius: '50%', overflow: 'hidden', display: 'grid', placeItems: 'center' } : undefined}><LogoMark brand={brand} size={44} mono={rasterLogo ? null : brand.paper} /></div>
                <div className="ss-headline" style={{ fontFamily: disp }}>{story.headline}</div>
                <div className="ss-foot">
                  <span style={{ fontFamily: body }}>{brand.domain.display}</span>
                  <span className="ss-cta" style={{ fontFamily: body, background: brand.paper, color: brand.primary }}>Swipe up</span>
                </div>
              </div>
            ))}
          </div>
        </section>

        {/* Stationery */}
        <section className="bb-section">
          <div className="bb-h"><span className="bb-num">06</span> Stationery</div>
          <div className="stationery-stage" style={(brand.images && brand.images.stationery) ? { backgroundImage: `url("${brand.images.stationery}")`, backgroundSize: 'cover', backgroundPosition: 'center', borderRadius: '16px', padding: 'clamp(20px, 4vw, 52px)' } : undefined}>
          <div className="stationery-grid">
            <div className="letterhead" style={{ background: brand.paper, color: brand.ink, ...((brand.images && brand.images.stationery) ? { boxShadow: '0 36px 70px -24px rgba(0,0,0,0.5)' } : {}) }}>
              <div className="lh-band" style={{ background: brand.primary }}></div>
              <div className="lh-head">
                <div className="lh-lockup">
                  <LogoMark brand={brand} size={36} />
                  <span className="lh-logo" style={{ fontFamily: disp }}>{brand.businessName}</span>
                </div>
                <div className="lh-contact" style={{ fontFamily: body, color: brand.primary }}>{brand.domain.display}</div>
              </div>
              <div className="lh-pattern" style={patternCss(brand, brand.paper, brand.secondary)}></div>
              <div className="lh-body" style={{ fontFamily: body }}>
                <div className="lh-date">Dear friend,</div>
                <p className="lh-statement" style={{ color: brand.ink }}>{brand.kit.statement}</p>
                <p>{brand.kit.socialPost}</p>
                <div className="lh-sign">
                  <div className="lh-sign-name" style={{ fontFamily: disp, color: brand.primary }}>{brand.kit.contactName}</div>
                  <div className="lh-sign-role">{brand.kit.contactRole}</div>
                </div>
              </div>
              <div className="lh-foot" style={{ background: brand.ink, color: brand.paper, fontFamily: body }}>{brand.domain.display}</div>
            </div>
            <div className="bizcard-stack">
              <div className="bizcard bizcard-front" style={{ background: brand.primary, color: brand.paper, ...((brand.images && brand.images.stationery) ? { boxShadow: '0 26px 48px -18px rgba(0,0,0,0.55)' } : {}) }}>
                <div className="bc-pattern" style={patternCss(brand, brand.primary, brand.paper)}></div>
                <div className="bc-front-logo" style={rasterLogo ? { background: brand.paper, borderRadius: '12px', overflow: 'hidden', display: 'inline-flex' } : undefined}><LogoMark brand={brand} size={46} mono={rasterLogo ? null : brand.paper} /></div>
                <div className="bc-name" style={{ fontFamily: disp }}>{brand.businessName}</div>
                <div className="bc-tag" style={{ fontFamily: body, color: brand.secondary }}>{brand.tagline}</div>
              </div>
              <div className="bizcard bizcard-back" style={{ background: brand.paper, color: brand.ink, border: '1px solid #E5E1D8', ...((brand.images && brand.images.stationery) ? { boxShadow: '0 26px 48px -18px rgba(0,0,0,0.55)' } : {}) }}>
                <div className="bc-back-top">
                  <LogoMark brand={brand} size={28} />
                  <span style={{ fontFamily: disp }}>{brand.businessName}</span>
                </div>
                <div className="bc-person" style={{ fontFamily: body }}>
                  <strong>{brand.kit.contactName}</strong>
                  <span>{brand.kit.contactRole}</span>
                  <span style={{ color: brand.primary }}>{brand.domain.display}</span>
                </div>
              </div>
            </div>
          </div>
          </div>
        </section>

        {/* Merch */}
        <section className="bb-section">
          <div className="bb-h"><span className="bb-num">07</span> Merchandise</div>
          <div className="merch-grid">
            <ProductMock brand={brand} kind="tee" label="T-shirt" ink={brand.primary} />
            <ProductMock brand={brand} kind="tote" label="Tote bag" ink={brand.ink} />
            <ProductMock brand={brand} kind="mug" label="Ceramic mug" ink={brand.primary} />
            <ProductMock brand={brand} kind="cap" label="Cap" ink={brand.ink} />
          </div>
        </section>

        <div className="bb-footer" style={{ fontFamily: body }}>
          {brand.businessName} — Brand identity generated by namit. · {brand.domain.display}
        </div>
      </div>
    </div>
  );
}

Object.assign(window, { BrandBoard, exportAsset, printBoard });
