// ==========================================================================
// Generative-UI vocabulary — the card library
// Every card follows the same chrome: mono header row + content + optional footer
// ==========================================================================

const genStyles = {
  card: {
    background: 'var(--card-solid)',
    border: '1px solid var(--card-border)',
    borderRadius: 'var(--radius)',
    boxShadow: 'var(--shadow-md)',
    overflow: 'hidden',
    position: 'relative',
  },
  header: {
    display: 'flex', alignItems: 'center', justifyContent: 'space-between',
    padding: '12px 18px',
    borderBottom: '1px solid var(--line-soft)',
    fontFamily: 'var(--font-mono)',
    fontSize: 10,
    letterSpacing: '0.14em',
    textTransform: 'uppercase',
    color: 'var(--ink-mute)',
  },
  body: { padding: '18px' },
};

function CardShell({ kind, title, children, actions, flush, style }) {
  return (
    <div className="card-in" style={{ ...genStyles.card, ...(style||{}) }}>
      <div style={genStyles.header}>
        <div style={{ display: 'flex', gap: 10, alignItems: 'center' }}>
          <span style={{ color: 'var(--moss)' }}>◆</span>
          <span>{kind}</span>
          {title && <span style={{ color: 'var(--ink-soft)', textTransform: 'none', letterSpacing: 0, fontFamily: 'var(--font-serif)', fontSize: 13 }}>— {title}</span>}
        </div>
        {actions}
      </div>
      <div style={flush ? {} : genStyles.body}>{children}</div>
    </div>
  );
}

// ============================================================
// 1. FILE UPLOAD CARD — shows parsed context from a file
// ============================================================
function FileCard({ file }) {
  return (
    <div className="card-in" style={{
      display: 'flex', gap: 14, padding: 14,
      background: 'var(--card-solid)',
      border: '1px solid var(--card-border)',
      borderRadius: 12,
      alignItems: 'flex-start',
      boxShadow: 'var(--shadow-sm)',
    }}>
      <div style={{
        width: 46, height: 60, flexShrink: 0,
        background: 'var(--bg-2)',
        border: '1px solid var(--line)',
        borderRadius: 4,
        display: 'flex', alignItems: 'center', justifyContent: 'center',
        color: 'var(--moss-deep)',
      }}>
        <Icon name={file.type} size={18} />
      </div>
      <div style={{ flex: 1, minWidth: 0 }}>
        <div style={{ display: 'flex', alignItems: 'baseline', gap: 10, marginBottom: 4 }}>
          <div style={{ fontFamily: 'var(--font-serif)', fontSize: 17, color: 'var(--ink)' }}>{file.parsed.title}</div>
          <span className="label">{file.type === 'pdf' ? `${file.pages}p` : `${file.rows} rows`}</span>
        </div>
        <div style={{ fontSize: 13, color: 'var(--ink-soft)', marginBottom: 10, lineHeight: 1.5 }}>
          {file.parsed.summary}
        </div>
        <div style={{ display: 'flex', gap: 6, flexWrap: 'wrap' }}>
          {file.parsed.tags.map(t => (
            <span key={t} style={{
              fontFamily: 'var(--font-mono)', fontSize: 10, letterSpacing: '.08em',
              padding: '3px 8px', borderRadius: 99,
              background: 'var(--bg-2)', color: 'var(--ink-soft)',
              border: '1px solid var(--line-soft)',
            }}>{t}</span>
          ))}
        </div>
      </div>
    </div>
  );
}

// ============================================================
// 2. CONTEXT CARD — "here's what I understood"
// ============================================================
function ContextCard({ files, onOpenFile }) {
  return (
    <CardShell kind="context" title="what I understood from your uploads">
      <div style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
        <p style={{ margin: 0, fontSize: 15, lineHeight: 1.55, color: 'var(--ink)', fontFamily: 'var(--font-serif)' }}>
          I've read both files. You're launching a Dry January push for <strong style={{ color: 'var(--moss-deep)' }}>BRAME</strong> — the botanical aperitif, 3 variants. Last quarter your Craft Heritage series drove most of your growth (CTR 3.2%), and two creatives are flagged for fatigue.
        </p>
        <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 10 }}>
          {files.map((f,i) => (
            <div key={i} onClick={() => onOpenFile && onOpenFile(f)} style={{
              padding: '10px 12px', background: 'var(--bg)', border: '1px solid var(--line-soft)', borderRadius: 8,
              display: 'flex', gap: 10, alignItems: 'center', cursor: 'pointer',
            }}>
              <Icon name={f.type} size={14} />
              <div style={{ fontSize: 12, color: 'var(--ink-soft)', flex: 1, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{f.name}</div>
            </div>
          ))}
        </div>
      </div>
    </CardShell>
  );
}

// ============================================================
// 3. AESTHETIC TILE PICKER
// ============================================================
function AestheticTiles({ tiles, value, onChange }) {
  return (
    <CardShell kind="enrichment · 1 of 4" title="pick the emotional register">
      <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 10 }}>
        {tiles.map(t => {
          const active = value === t.id;
          return (
            <button key={t.id} className="btn-reset" onClick={() => onChange(t.id)} style={{
              height: 128, borderRadius: 10,
              background: t.bg, position: 'relative', cursor: 'pointer',
              overflow: 'hidden',
              border: active ? '2px solid var(--moss-deep)' : '1px solid var(--line)',
              outline: active ? '3px solid var(--moss-glow)' : 'none',
              outlineOffset: -5,
              transition: 'all .25s',
            }}>
              <div style={{
                position: 'absolute', inset: 0,
                display: 'flex', alignItems: 'center', justifyContent: 'center',
                fontFamily: t.sample.font || 'var(--font-serif)',
                fontSize: 32, color: t.sample.color,
                fontWeight: 400,
              }}>{t.sample.text}</div>
              <div style={{
                position: 'absolute', left: 10, bottom: 8,
                fontFamily: 'var(--font-mono)', fontSize: 9, letterSpacing: '.14em', textTransform: 'uppercase',
                color: t.sample.color, opacity: .85,
              }}>{t.label}</div>
              {active && (
                <div style={{
                  position: 'absolute', right: 10, top: 10,
                  width: 22, height: 22, borderRadius: '50%',
                  background: 'var(--moss-deep)', color: 'var(--bg)',
                  display: 'flex', alignItems: 'center', justifyContent: 'center',
                }}>
                  <Icon name="check" size={13} stroke={2.2} />
                </div>
              )}
            </button>
          );
        })}
      </div>
    </CardShell>
  );
}

// ============================================================
// 4. AUDIENCE SLIDER (Belgian ↔ international)
// ============================================================
function AudienceSlider({ value, onChange, labels = ['Hyper-local', 'International'] }) {
  return (
    <CardShell kind="enrichment · 2 of 4" title="audience geography">
      <div style={{ display: 'flex', alignItems: 'center', gap: 16, padding: '8px 4px' }}>
        <span className="label" style={{ minWidth: 90 }}>{labels[0]}</span>
        <div style={{ flex: 1, position: 'relative', height: 30 }}>
          <input type="range" min={0} max={100} value={value} onChange={e => onChange(+e.target.value)}
            style={{ width: '100%', accentColor: 'var(--moss-deep)' }} />
          <div style={{
            position: 'absolute', left: `${value}%`, top: -4, transform: 'translateX(-50%)',
            fontFamily: 'var(--font-mono)', fontSize: 10, color: 'var(--moss-deep)',
          }}>{value}</div>
        </div>
        <span className="label" style={{ minWidth: 90, textAlign: 'right' }}>{labels[1]}</span>
      </div>
    </CardShell>
  );
}

// ============================================================
// 5. TONE SLIDER (named poles)
// ============================================================
function ToneSlider({ poles, value, onChange, kind = 'enrichment' }) {
  return (
    <CardShell kind={kind} title={`${poles[0]} ←→ ${poles[1]}`}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 16 }}>
        <span style={{ fontFamily: 'var(--font-serif)', fontSize: 16, color: 'var(--ink-soft)', minWidth: 110 }}>{poles[0]}</span>
        <div style={{ flex: 1, position: 'relative' }}>
          <input type="range" min={0} max={100} value={value} onChange={e => onChange(+e.target.value)}
            style={{ width: '100%', accentColor: 'var(--moss-deep)' }} />
        </div>
        <span style={{ fontFamily: 'var(--font-serif)', fontSize: 16, color: 'var(--ink-soft)', minWidth: 110, textAlign: 'right' }}>{poles[1]}</span>
      </div>
    </CardShell>
  );
}

// ============================================================
// 6. CHANNEL MATRIX
// ============================================================
function ChannelMatrix({ channels, value, onChange, kind = 'enrichment · 3 of 4', title = 'where should this live' }) {
  return (
    <CardShell kind={kind} title={title}>
      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 10 }}>
        {channels.map(c => {
          const active = value.includes(c.id);
          return (
            <button key={c.id} className="btn-reset" onClick={() => {
              onChange(active ? value.filter(v => v !== c.id) : [...value, c.id]);
            }} style={{
              padding: '12px 10px',
              border: active ? '1.5px solid var(--moss-deep)' : '1px solid var(--line)',
              background: active ? 'var(--moss-glow)' : 'var(--bg)',
              borderRadius: 10, cursor: 'pointer',
              display: 'flex', flexDirection: 'column', gap: 8, alignItems: 'flex-start',
              transition: 'all .2s',
              color: active ? 'var(--moss-deep)' : 'var(--ink)',
            }}>
              <div style={{ display: 'flex', gap: 8, alignItems: 'center' }}>
                <ChannelIcon name={c.name} size={14} />
                <span style={{ fontSize: 13 }}>{c.name}</span>
              </div>
              <div style={{ display: 'flex', gap: 4, flexWrap: 'wrap' }}>
                {c.formats.map(f => (
                  <span key={f} style={{
                    fontFamily: 'var(--font-mono)', fontSize: 9, letterSpacing: '.08em',
                    padding: '2px 6px', borderRadius: 3,
                    background: active ? 'rgba(63,75,37,.12)' : 'var(--bg-2)',
                    color: active ? 'var(--moss-deep)' : 'var(--ink-mute)',
                  }}>{f}</span>
                ))}
              </div>
            </button>
          );
        })}
      </div>
    </CardShell>
  );
}

// ============================================================
// 7. PERSONA CHIP RACK
// ============================================================
function PersonaRack({ personas, value, onChange, kind = 'enrichment · 2 of 3' }) {
  return (
    <CardShell kind={kind} title="who is this for">
      <div style={{ display: 'flex', gap: 10, overflowX: 'auto', paddingBottom: 4 }}>
        {personas.map(p => {
          const active = value.includes(p.id);
          return (
            <button key={p.id} className="btn-reset" onClick={() => {
              onChange(active ? value.filter(v => v !== p.id) : [...value, p.id]);
            }} style={{
              minWidth: 170, textAlign: 'left',
              padding: 12,
              border: active ? '1.5px solid var(--moss-deep)' : '1px solid var(--line)',
              background: active ? 'var(--moss-glow)' : 'var(--bg)',
              borderRadius: 10, cursor: 'pointer',
              transition: 'all .2s',
              color: active ? 'var(--moss-deep)' : 'var(--ink)',
            }}>
              <div style={{ fontSize: 22, marginBottom: 6 }}>{p.emoji}</div>
              <div style={{ fontFamily: 'var(--font-serif)', fontSize: 15, lineHeight: 1.1 }}>{p.name}</div>
              <div className="label" style={{ marginTop: 3, color: active ? 'var(--moss-deep)' : 'var(--ink-mute)' }}>{p.age}</div>
              <div style={{ fontSize: 11, color: 'var(--ink-soft)', marginTop: 6, lineHeight: 1.35 }}>{p.psy}</div>
            </button>
          );
        })}
      </div>
    </CardShell>
  );
}

// ============================================================
// 8. BUDGET DIAL — radial control
// ============================================================
function BudgetDial({ value, onChange, kind = 'enrichment · 3 of 3' }) {
  const tiers = [
    { id: 'small', label: '$', note: '< €25K', angle: -120 },
    { id: 'mid',   label: '$$', note: '€25–100K', angle: -40 },
    { id: 'large', label: '$$$', note: '€100–500K', angle: 40 },
    { id: 'xl',    label: '$$$$', note: '€500K+', angle: 120 },
  ];
  const active = tiers.find(t => t.id === value) || tiers[1];

  return (
    <CardShell kind={kind} title="budget tier">
      <div style={{ display: 'flex', gap: 24, alignItems: 'center' }}>
        <div style={{ position: 'relative', width: 140, height: 140, flexShrink: 0 }}>
          <svg viewBox="-100 -100 200 200" width="140" height="140">
            <circle cx="0" cy="0" r="70" fill="none" stroke="var(--line)" strokeWidth="1" />
            <circle cx="0" cy="0" r="70" fill="none" stroke="var(--moss-deep)" strokeWidth="3"
              strokeDasharray={`${((active.angle + 180) / 360) * 2 * Math.PI * 70} 1000`}
              transform="rotate(-90)" strokeLinecap="round" style={{ transition: 'all .4s' }} />
            {tiers.map(t => {
              const x = 70 * Math.cos((t.angle - 90) * Math.PI / 180);
              const y = 70 * Math.sin((t.angle - 90) * Math.PI / 180);
              return <circle key={t.id} cx={x} cy={y} r="4"
                fill={t.id === active.id ? 'var(--moss-deep)' : 'var(--bg-2)'}
                stroke="var(--moss-deep)" strokeWidth="1" />;
            })}
            <text x="0" y="-8" textAnchor="middle" fontFamily="Elms Sans" fontSize="28" fill="var(--ink)" fontWeight="500">{active.label}</text>
            <text x="0" y="14" textAnchor="middle" fontFamily="JetBrains Mono" fontSize="8" letterSpacing="0.14em" fill="var(--ink-mute)">{active.note}</text>
          </svg>
        </div>
        <div style={{ display: 'flex', flexDirection: 'column', gap: 6, flex: 1 }}>
          {tiers.map(t => (
            <button key={t.id} className="btn-reset" onClick={() => onChange(t.id)} style={{
              padding: '8px 12px', textAlign: 'left',
              border: t.id === value ? '1.5px solid var(--moss-deep)' : '1px solid var(--line-soft)',
              background: t.id === value ? 'var(--moss-glow)' : 'transparent',
              borderRadius: 8, cursor: 'pointer',
              display: 'flex', justifyContent: 'space-between',
              fontSize: 13,
            }}>
              <span style={{ fontFamily: 'var(--font-serif)' }}>{t.label} <span className="label" style={{ marginLeft: 6 }}>{t.note}</span></span>
            </button>
          ))}
        </div>
      </div>
    </CardShell>
  );
}

// ============================================================
// 9. CONSTRAINT CHIPS
// ============================================================
function ConstraintChips({ value, onChange, kind = 'constraints' }) {
  const chips = [
    "Dutch-language primary",
    "Must include product in hero frame",
    "Must not feature alcohol",
    "18+ targeting",
    "No human faces",
    "Founder not on-camera",
    "Vegan-certified callout",
  ];
  return (
    <CardShell kind={kind} title="hard rules for this campaign">
      <div style={{ display: 'flex', gap: 7, flexWrap: 'wrap' }}>
        {chips.map(c => {
          const on = value.includes(c);
          return (
            <button key={c} className="btn-reset" onClick={() => {
              onChange(on ? value.filter(v => v !== c) : [...value, c]);
            }} style={{
              padding: '6px 12px', borderRadius: 99,
              border: on ? '1px solid var(--moss-deep)' : '1px solid var(--line)',
              background: on ? 'var(--moss-deep)' : 'transparent',
              color: on ? 'var(--bg)' : 'var(--ink-soft)',
              fontSize: 12, cursor: 'pointer',
              transition: 'all .2s',
            }}>
              {on && <Icon name="check" size={11} stroke={2.2} />} {on && ' '}{c}
            </button>
          );
        })}
      </div>
    </CardShell>
  );
}

// ============================================================
// 10. BRIEF CARD — structured brief
// ============================================================
// Small card showing a past ad variant: thumbnail + ROAS chip + caption.
// Used inside BriefCard to surface real evidence next to the new brief.
function ReferenceVariantCard({ ref_ }) {
  const [imgErr, setImgErr] = React.useState(false);
  const [expanded, setExpanded] = React.useState(false);
  const url = ref_ && ref_.visual_url;
  const roas = ref_ && typeof ref_.roas === 'number' ? ref_.roas : null;
  const isVideo = url && /\.(mp4|mov|webm)(\?|$)/i.test(url);
  const signals = Array.isArray(ref_ && ref_.match_signals) ? ref_.match_signals : [];
  const rank = ref_ && ref_.rank;
  const winPattern = ref_ && ref_.win_pattern;
  // adaptation_note is the new field; why_referenced is the legacy field — render whichever is present.
  const adaptationNote = (ref_ && (ref_.adaptation_note || ref_.why_referenced)) || '';
  const selectionReasoning = (ref_ && ref_.selection_reasoning) || '';
  const hasMore = Boolean(winPattern || adaptationNote || signals.length > 0);
  return (
    <div style={{
      flexShrink: 0,
      width: 200,
      borderRadius: 10,
      overflow: 'hidden',
      border: '1px solid var(--line)',
      background: 'var(--card-solid)',
      display: 'flex',
      flexDirection: 'column',
    }}>
      <div style={{ position: 'relative', width: '100%', height: 150, background: 'var(--bg-2)' }}>
        {url && !imgErr && !isVideo && (
          <img
            src={url}
            alt={ref_.ad_id || 'reference'}
            onError={() => setImgErr(true)}
            style={{ width: '100%', height: '100%', objectFit: 'cover', display: 'block' }}
          />
        )}
        {(imgErr || !url || isVideo) && (
          <div style={{
            width: '100%', height: '100%',
            display: 'flex', alignItems: 'center', justifyContent: 'center',
            color: 'var(--ink-mute)', fontFamily: 'var(--font-mono)', fontSize: 10, letterSpacing: '.08em',
            textAlign: 'center', padding: 10,
          }}>
            {isVideo ? '▶ video reference' : 'no preview'}
          </div>
        )}
        {rank && typeof rank.position === 'number' && typeof rank.of === 'number' && (
          <div style={{
            position: 'absolute', top: 6, left: 6,
            background: 'rgba(0,0,0,0.65)', color: '#fff',
            padding: '2px 7px', borderRadius: 4,
            fontFamily: 'var(--font-mono)', fontSize: 10, fontWeight: 500,
            letterSpacing: '.04em',
          }}>
            #{rank.position} of {rank.of}
          </div>
        )}
        {roas !== null && (
          <div style={{
            position: 'absolute', top: 6, right: 6,
            background: 'var(--moss-deep)', color: '#fff',
            padding: '2px 7px', borderRadius: 4,
            fontFamily: 'var(--font-mono)', fontSize: 10, fontWeight: 500,
            letterSpacing: '.04em',
          }}>
            ROAS {roas.toFixed(2)}
          </div>
        )}
      </div>
      <div style={{ padding: '8px 10px', display: 'flex', flexDirection: 'column', gap: 6 }}>
        {selectionReasoning && (
          <div style={{
            padding: '5px 7px', borderRadius: 5,
            background: 'var(--moss-glow)', borderLeft: '2px solid var(--moss-deep)',
          }}>
            <div className="label label-moss" style={{ fontSize: 8, marginBottom: 1 }}>Why this card</div>
            <div style={{ fontSize: 11, color: 'var(--ink)', lineHeight: 1.35, fontWeight: 500 }}>
              {selectionReasoning}
            </div>
          </div>
        )}
        {expanded && (
          <>
            {signals.length > 0 && (
              <div style={{ display: 'flex', flexWrap: 'wrap', gap: 3 }}>
                {signals.map((s, i) => (
                  <span key={i} style={{
                    fontFamily: 'var(--font-mono)', fontSize: 9, letterSpacing: '.04em',
                    padding: '1px 5px', borderRadius: 3,
                    background: 'var(--bg-2)', color: 'var(--ink-soft)',
                    border: '1px solid var(--line-soft)',
                  }}>{s}</span>
                ))}
              </div>
            )}
            {winPattern && (
              <div>
                <div className="label label-moss" style={{ fontSize: 8, marginBottom: 1 }}>What worked</div>
                <div style={{ fontSize: 11, color: 'var(--ink)', lineHeight: 1.35 }}>
                  {winPattern}
                </div>
              </div>
            )}
            {adaptationNote && (
              <div>
                <div className="label label-moss" style={{ fontSize: 8, marginBottom: 1 }}>How to adapt</div>
                <div style={{ fontSize: 11, color: 'var(--ink-soft)', lineHeight: 1.35 }}>
                  {adaptationNote}
                </div>
              </div>
            )}
          </>
        )}
        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', gap: 6, marginTop: 2 }}>
          {ref_.ad_id ? (
            <div style={{ fontFamily: 'var(--font-mono)', fontSize: 9, color: 'var(--ink-mute)', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', flex: 1 }}>
              {ref_.ad_id}
            </div>
          ) : <div style={{ flex: 1 }} />}
          {hasMore && (
            <button
              type="button"
              onClick={() => setExpanded(v => !v)}
              style={{
                fontFamily: 'var(--font-mono)', fontSize: 9, letterSpacing: '.06em',
                padding: '2px 6px', borderRadius: 3,
                background: 'transparent', color: 'var(--ink-soft)',
                border: '1px solid var(--line-soft)', cursor: 'pointer',
                textTransform: 'uppercase',
              }}
            >
              {expanded ? '− Less' : '+ More'}
            </button>
          )}
        </div>
      </div>
    </div>
  );
}

function BriefCard({ brief, critique, onOpen, generating }) {
  const warnings = (critique && Array.isArray(critique.warnings)) ? critique.warnings : [];
  const sevColor = (sev) => {
    if (sev === 'error') return { bg: '#fdecec', border: '#e08a8a', text: '#a72020' };
    if (sev === 'warning') return { bg: '#fdf4e3', border: '#d6a85a', text: '#8a5a10' };
    return { bg: '#eef3fb', border: '#9bb6dc', text: '#345583' };
  };
  return (
    <CardShell kind="creative brief" title={(brief && brief.title) || "Creative brief"}
      actions={!generating && <OpenInInku onClick={onOpen} />}>
      {generating ? (
        <GeneratingStream lines={[
          "drafting objective...",
          "synthesising audience insight...",
          "composing message architecture...",
          "defining KPIs...",
        ]} />
      ) : (
        <div style={{ display: 'flex', flexDirection: 'column', gap: 18 }}>
          {warnings.length > 0 && (
            <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
              <div className="label label-moss" style={{ marginBottom: 2 }}>
                Grounding check · {warnings.length} flag{warnings.length === 1 ? '' : 's'}
              </div>
              {warnings.map((w, i) => {
                const c = sevColor(w.severity);
                return (
                  <div key={i} style={{
                    display: 'flex', flexDirection: 'column', gap: 2,
                    padding: '6px 9px', borderRadius: 6,
                    background: c.bg, border: `1px solid ${c.border}`, color: c.text,
                    fontSize: 11, lineHeight: 1.4,
                  }}>
                    <div style={{ fontFamily: 'var(--font-mono)', fontSize: 9, letterSpacing: '.06em', textTransform: 'uppercase', opacity: 0.85 }}>
                      {w.severity || 'warning'} · {w.field || 'unknown'}
                    </div>
                    <div style={{ fontStyle: 'italic' }}>"{w.claim}"</div>
                    <div style={{ opacity: 0.85 }}>{w.gap}</div>
                  </div>
                );
              })}
            </div>
          )}
          {brief.why_now && (
            <div style={{
              borderLeft: '3px solid var(--moss-deep)',
              padding: '8px 0 8px 12px',
              background: 'var(--moss-glow)',
              borderRadius: '0 6px 6px 0',
            }}>
              <div className="label label-moss" style={{ marginBottom: 4 }}>Why now</div>
              {typeof brief.why_now === 'string' ? (
                <div style={{ fontFamily: 'var(--font-serif)', fontSize: 14, lineHeight: 1.4, color: 'var(--ink)', fontStyle: 'italic' }}>
                  {brief.why_now}
                </div>
              ) : (
                <>
                  {brief.why_now.hook && (
                    <div style={{ fontFamily: 'var(--font-serif)', fontSize: 14, lineHeight: 1.4, color: 'var(--ink)', fontStyle: 'italic', marginBottom: 6 }}>
                      {brief.why_now.hook}
                    </div>
                  )}
                  {Array.isArray(brief.why_now.signals) && brief.why_now.signals.length > 0 && (
                    <ul style={{ margin: 0, paddingLeft: 18, fontSize: 12, color: 'var(--ink-soft)', lineHeight: 1.5 }}>
                      {brief.why_now.signals.map((s, i) => <li key={i}>{s}</li>)}
                    </ul>
                  )}
                </>
              )}
            </div>
          )}
          {brief.objective && (
            <div>
              <div className="label label-moss" style={{ marginBottom: 4 }}>Objective</div>
              <div style={{ fontSize: 14, lineHeight: 1.45, color: 'var(--ink)' }}>
                {brief.objective}
              </div>
            </div>
          )}
          {brief.audience && (
            <div>
              <div className="label label-moss" style={{ marginBottom: 4 }}>Audience</div>
              {typeof brief.audience === 'string' ? (
                <div style={{ fontSize: 14, lineHeight: 1.45, color: 'var(--ink)' }}>
                  {brief.audience}
                </div>
              ) : (
                <>
                  {brief.audience.who && (
                    <div style={{ fontSize: 14, lineHeight: 1.45, color: 'var(--ink)', marginBottom: 8 }}>
                      {brief.audience.who}
                    </div>
                  )}
                  <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 14 }}>
                    {Array.isArray(brief.audience.pains) && brief.audience.pains.length > 0 && (
                      <div>
                        <div className="label label-moss" style={{ fontSize: 9, marginBottom: 3 }}>Pains</div>
                        <ul style={{ margin: 0, paddingLeft: 16, fontSize: 12, color: 'var(--ink-soft)', lineHeight: 1.5 }}>
                          {brief.audience.pains.map((p, i) => <li key={i}>{p}</li>)}
                        </ul>
                      </div>
                    )}
                    {Array.isArray(brief.audience.desires) && brief.audience.desires.length > 0 && (
                      <div>
                        <div className="label label-moss" style={{ fontSize: 9, marginBottom: 3 }}>Desires</div>
                        <ul style={{ margin: 0, paddingLeft: 16, fontSize: 12, color: 'var(--ink-soft)', lineHeight: 1.5 }}>
                          {brief.audience.desires.map((d, i) => <li key={i}>{d}</li>)}
                        </ul>
                      </div>
                    )}
                  </div>
                </>
              )}
            </div>
          )}
          {[
            { k: 'Core insight', v: brief.insight, serif: true },
            { k: 'Message', v: brief.message, big: true },
          ].map(row => (
            <div key={row.k}>
              <div className="label label-moss" style={{ marginBottom: 4 }}>{row.k}</div>
              <div style={{
                fontFamily: row.serif || row.big ? 'var(--font-serif)' : 'inherit',
                fontSize: row.big ? 22 : (row.serif ? 17 : 14),
                lineHeight: 1.45, color: 'var(--ink)',
              }}>{row.v}</div>
            </div>
          ))}
          {brief.reference_variants && brief.reference_variants.length > 0 && (
            <div style={{ paddingTop: 14, borderTop: '1px solid var(--line-soft)' }}>
              <div className="label label-moss" style={{ marginBottom: 8 }}>
                Grounded in · {brief.reference_variants.length} past performer{brief.reference_variants.length === 1 ? '' : 's'}
              </div>
              {brief.selection_logic && (
                <div style={{
                  padding: '10px 12px',
                  marginBottom: 10,
                  borderRadius: 6,
                  background: 'var(--bg-2)',
                  border: '1px solid var(--line-soft)',
                  fontSize: 12,
                  lineHeight: 1.5,
                  color: 'var(--ink)',
                }}>
                  <div className="label label-moss" style={{ marginBottom: 4 }}>Selection logic</div>
                  {brief.selection_logic}
                </div>
              )}
              <div style={{ display: 'flex', gap: 10, overflowX: 'auto', paddingBottom: 4 }}>
                {brief.reference_variants.map((ref, i) => (
                  <ReferenceVariantCard key={ref.ad_id || i} ref_={ref} />
                ))}
              </div>
            </div>
          )}
          <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 20, paddingTop: 10, borderTop: '1px solid var(--line-soft)' }}>
            <div>
              <div className="label label-moss" style={{ marginBottom: 6 }}>Channels</div>
              <div style={{ display: 'flex', flexWrap: 'wrap', gap: 6 }}>
                {brief.channels.map(c => (
                  <span key={c} style={{
                    fontSize: 11, padding: '3px 9px', borderRadius: 99,
                    background: 'var(--bg-2)', color: 'var(--ink-soft)',
                    border: '1px solid var(--line-soft)', fontFamily: 'var(--font-mono)', letterSpacing: '.06em',
                  }}>{c}</span>
                ))}
              </div>
            </div>
            <div>
              <div className="label label-moss" style={{ marginBottom: 6 }}>KPIs</div>
              <ul style={{ margin: 0, paddingLeft: 14, fontSize: 12, color: 'var(--ink-soft)', lineHeight: 1.55 }}>
                {brief.kpis.map(k => <li key={k}>{k}</li>)}
              </ul>
            </div>
          </div>
        </div>
      )}
    </CardShell>
  );
}

// ============================================================
// 11. MESSAGING ARCHITECTURE — hierarchy diagram
// ============================================================
function MessagingArchitecture({ pillars, onOpen }) {
  return (
    <CardShell kind="messaging architecture" title="three pillars → proof → activation"
      actions={<OpenInInku onClick={onOpen} />}>
      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 12 }}>
        {pillars.map((p,i) => (
          <div key={p.pillar} className="card-in" style={{
            padding: 14, border: '1px solid var(--line)', borderRadius: 10,
            background: 'var(--bg)', animationDelay: `${i * 0.15}s`,
            display: 'flex', flexDirection: 'column', gap: 10,
          }}>
            <div className="label label-moss">Pillar 0{i+1}</div>
            <div style={{ fontFamily: 'var(--font-serif)', fontSize: 20, color: 'var(--moss-deep)', lineHeight: 1.1 }}>{p.pillar}</div>
            <div>
              <div className="label" style={{ marginBottom: 3 }}>Proof</div>
              <div style={{ fontSize: 12, color: 'var(--ink-soft)', lineHeight: 1.45 }}>{p.proof}</div>
            </div>
            <div style={{ borderTop: '1px dashed var(--line)', paddingTop: 10 }}>
              <div className="label" style={{ marginBottom: 3 }}>Activation</div>
              <div style={{ fontSize: 12, color: 'var(--ink)', lineHeight: 1.45 }}>{p.activation}</div>
            </div>
          </div>
        ))}
      </div>
    </CardShell>
  );
}

// ============================================================
// 12. AD CONCEPT CARD — single ad mockup
// ============================================================
function AdConcept({ concept, onClick, mini = false }) {
  const h = mini ? 180 : 280;
  const isVert = concept.channel.includes('9:16') || concept.channel.includes('Story');
  const aspect = isVert ? 9/16 : (concept.channel.includes('OOH') ? 16/9 : 1);
  const w = h * aspect;

  return (
    <button className="btn-reset card-in" onClick={onClick} style={{
      background: 'var(--card-solid)',
      border: '1px solid var(--card-border)',
      borderRadius: 12, overflow: 'hidden', textAlign: 'left',
      cursor: 'pointer', transition: 'all .25s',
      boxShadow: 'var(--shadow-sm)',
    }}
    onMouseEnter={e => e.currentTarget.style.transform = 'translateY(-2px)'}
    onMouseLeave={e => e.currentTarget.style.transform = 'translateY(0)'}>
      <div style={{
        height: h, width: '100%', position: 'relative',
        background: concept.bg,
        color: concept.accent,
        padding: mini ? 14 : 22,
        display: 'flex', flexDirection: 'column', justifyContent: 'space-between',
        overflow: 'hidden',
      }}>
        {/* bottle illustration absolute */}
        <div style={{ position: 'absolute', right: -10, bottom: -20, opacity: 0.85 }}>
          <BrameBottle w={mini ? 38 : 58}
            fill={concept.accent === '#1F1A13' ? '#6E2A14' : (concept.bg.includes('F3EADB') ? '#6E2A14' : '#F3EADB')}
            label={concept.accent === '#1F1A13' ? '#F3EADB' : '#1F1A13'} />
        </div>
        <div style={{ fontFamily: 'var(--font-mono)', fontSize: 9, letterSpacing: '.14em', textTransform: 'uppercase', opacity: 0.75 }}>
          BRAME · {concept.hook}
        </div>
        <div style={{ maxWidth: '78%' }}>
          <div style={{ fontFamily: 'var(--font-display)', fontWeight: 300, fontSize: mini ? 16 : 24, lineHeight: 1.1, letterSpacing: '-0.015em' }}>
            {concept.headline}
          </div>
          {!mini && <div style={{ fontSize: 11, marginTop: 8, opacity: 0.85, fontFamily: 'var(--font-mono)', letterSpacing: '.02em' }}>
            {concept.sub}
          </div>}
        </div>
      </div>
      <div style={{ padding: '10px 14px', fontFamily: 'var(--font-mono)', fontSize: 10, letterSpacing: '.12em', textTransform: 'uppercase', color: 'var(--ink-mute)', display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
        <span>{concept.channel}</span>
        <Icon name="arrow-up-right" size={11} />
      </div>
    </button>
  );
}

// ============================================================
// 12. AD CONCEPT GRID
// ============================================================
//
// TODO[image-pipeline, Claude Code]:
//   Each concept currently renders with `c.bg` as a CSS gradient/solid color —
//   a placeholder for the generated creative. When the image pipeline is live:
//     1. Extend the `concept` shape from api.generateConcepts() to include:
//          imageUrl   : string (signed URL to the generated image asset)
//          imagePrompt: string (the prompt used — kept for regen + transparency)
//          imageModel : string ('imagen-4' / 'flux-pro' / 'internal-brand-lora')
//     2. Replace the `background: c.bg` inline style below with a layered
//        `background: url(${c.imageUrl}) center/cover, ${c.bg}` (bg as fallback
//        while the image loads or if gen fails).
//     3. The "Open in Inku" handoff on HandoffCard should carry imagePrompt +
//        imageModel so the creative team can regenerate variants downstream.
//     4. If the brand-LoRA is trained (per-org), route gen through that
//        endpoint and tag each artifact with the LoRA version for audit.
//   Seam lives in api.js → generateConcepts() and generateVariants().
function AdConceptGrid({ concepts, onOpen }) {
  return (
    <CardShell kind="ad concepts" title={`${concepts.length} directions across hooks`}
      actions={<OpenInInku label="Open set" onClick={onOpen} />}>
      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 12 }}>
        {concepts.map((c, i) => (
          <div key={c.id} style={{ animationDelay: `${i * 0.08}s` }} className="card-in">
            <AdConcept concept={c} onClick={() => onOpen && onOpen(c)} />
          </div>
        ))}
      </div>
    </CardShell>
  );
}

// ============================================================
// 13. VARIANT MATRIX — 12 variants laid out in a grid
// ============================================================
// TODO[image-pipeline, Claude Code]: same seam as AdConceptGrid above —
// each variant's `bg` is a placeholder for a real generated image. Extend
// the variant shape with imageUrl / imagePrompt / imageModel and swap the
// background in the grid cell below. Per-org brand-LoRA routing applies.
function VariantMatrix({ variants, onOpen }) {
  return (
    <CardShell kind="variant matrix · 12" title="persona × channel × hook"
      actions={<OpenInInku label="Open set" onClick={onOpen} />}>
      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: 10 }}>
        {variants.map((v, i) => (
          <div key={i} style={{ animationDelay: `${i * 0.05}s` }} className="card-in">
            <div style={{
              aspectRatio: '1 / 1.3',
              background: v.bg,
              color: v.accent,
              borderRadius: 8,
              padding: 10,
              position: 'relative', overflow: 'hidden',
              display: 'flex', flexDirection: 'column', justifyContent: 'space-between',
              cursor: 'pointer',
              boxShadow: 'var(--shadow-sm)',
            }} onClick={() => onOpen && onOpen(v)}>
              <div style={{ fontFamily: 'var(--font-mono)', fontSize: 8, letterSpacing: '.14em', textTransform: 'uppercase', opacity: 0.7 }}>BRAME</div>
              <div style={{ position: 'absolute', right: -4, bottom: -8 }}>
                <BrameBottle w={26} fill={v.bg.includes('F3E') || v.bg.includes('EDE') || v.bg.includes('D9C') ? '#6E2A14' : '#F3EADB'}
                  label={v.bg.includes('F3E') || v.bg.includes('EDE') || v.bg.includes('D9C') ? '#F3EADB' : '#1F1A13'} />
              </div>
              <div style={{ fontFamily: 'var(--font-display)', fontSize: 13, lineHeight: 1.1, letterSpacing: '-0.015em', maxWidth: '88%', fontWeight: 300 }}>
                {v.copy}
              </div>
            </div>
            <div style={{ marginTop: 6, padding: '0 2px' }}>
              <div style={{ fontFamily: 'var(--font-mono)', fontSize: 9, letterSpacing: '.08em', color: 'var(--ink-mute)', display: 'flex', flexDirection: 'column', gap: 2 }}>
                <div>{v.persona}</div>
                <div style={{ opacity: 0.7 }}>{v.channel} · {v.hook}</div>
              </div>
            </div>
          </div>
        ))}
      </div>
    </CardShell>
  );
}

// ============================================================
// 14. CONTENT CALENDAR
// ============================================================
function ContentCalendar({ onOpen }) {
  // 4 weeks × 7 days = 28 cells. Mix of filled/empty
  const posts = [
    { day: 1, ch: 'Meta', label: 'Teaser' },
    { day: 3, ch: 'TikTok', label: 'Hero' },
    { day: 4, ch: 'Email', label: 'Drop' },
    { day: 6, ch: 'Pinterest', label: 'Pin' },
    { day: 8, ch: 'Meta', label: 'Reel' },
    { day: 10, ch: 'TikTok', label: 'Creator' },
    { day: 12, ch: 'OOH', label: 'Wild' },
    { day: 14, ch: 'Email', label: 'Week2' },
    { day: 15, ch: 'Meta', label: 'UGC' },
    { day: 17, ch: 'TikTok', label: 'Ritual' },
    { day: 19, ch: 'Pinterest', label: 'Pin' },
    { day: 22, ch: 'Meta', label: 'Proof' },
    { day: 23, ch: 'Email', label: 'Recap' },
    { day: 25, ch: 'TikTok', label: 'Story' },
    { day: 28, ch: 'OOH', label: 'Refresh' },
    { day: 30, ch: 'Email', label: 'Finale' },
  ];
  const channelColor = { Meta: '#1877F2', TikTok: '#1F1A13', Pinterest: '#E60023', OOH: '#C67B4A', Email: '#4E5A2F' };
  const dayNames = ['M', 'T', 'W', 'T', 'F', 'S', 'S'];

  return (
    <CardShell kind="content calendar · january 2026" title="31 days"
      actions={<OpenInInku onClick={onOpen} />}>
      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(7, 1fr)', gap: 4, marginBottom: 6 }}>
        {dayNames.map((d, i) => (
          <div key={i} className="label" style={{ textAlign: 'center', padding: 4 }}>{d}</div>
        ))}
      </div>
      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(7, 1fr)', gap: 4 }}>
        {Array.from({ length: 31 }).map((_, i) => {
          const day = i + 1;
          const dayPosts = posts.filter(p => p.day === day);
          return (
            <div key={day} style={{
              aspectRatio: '1',
              background: dayPosts.length ? 'var(--bg-2)' : 'var(--bg)',
              border: '1px solid var(--line-soft)',
              borderRadius: 6,
              padding: 4,
              position: 'relative',
              display: 'flex', flexDirection: 'column', justifyContent: 'space-between',
              fontFamily: 'var(--font-mono)',
              fontSize: 9,
              overflow: 'hidden',
            }}>
              <div style={{ color: 'var(--ink-mute)', fontSize: 9 }}>{day}</div>
              {dayPosts.slice(0,2).map((p, j) => (
                <div key={j} style={{
                  background: channelColor[p.ch], color: '#fff',
                  padding: '1px 4px', borderRadius: 3,
                  fontSize: 7, letterSpacing: '.08em', textTransform: 'uppercase',
                  overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap',
                }}>{p.label}</div>
              ))}
            </div>
          );
        })}
      </div>
      <div style={{ display: 'flex', gap: 12, marginTop: 10, flexWrap: 'wrap' }}>
        {Object.entries(channelColor).map(([ch, color]) => (
          <div key={ch} style={{ display: 'flex', gap: 5, alignItems: 'center', fontSize: 10, color: 'var(--ink-soft)' }}>
            <span style={{ width: 8, height: 8, borderRadius: 2, background: color }}></span>
            {ch}
          </div>
        ))}
      </div>
    </CardShell>
  );
}

// ============================================================
// 15. HANDOFF CARD — Open in Inku
// ============================================================
function HandoffCard({ brief, onOpen }) {
  return (
    <div className="card-in" style={{
      background: 'var(--moss-deep)',
      color: 'var(--bg)',
      borderRadius: 14,
      padding: 22,
      display: 'flex', justifyContent: 'space-between', alignItems: 'center',
      boxShadow: 'var(--shadow-md)',
      gap: 20,
      position: 'relative', overflow: 'hidden',
    }}>
      <Aura x="20%" y="50%" size={300} color="var(--moss-bright)" opacity={0.2} />
      <div style={{ position: 'relative', zIndex: 1 }}>
        <div className="label" style={{ color: 'var(--moss-glow)', marginBottom: 6 }}>Ready to produce</div>
        <div style={{ fontFamily: 'var(--font-display)', fontWeight: 300, fontSize: 24, lineHeight: 1.1, marginBottom: 4 }}>
          Send this package to Inku production
        </div>
        <div style={{ fontSize: 13, opacity: 0.75 }}>
          1 brief · 3 pillars · 6 concepts · 31-day calendar · 12 variants queued
        </div>
      </div>
      <button className="btn-reset" onClick={onOpen} style={{
        background: 'var(--bg)', color: 'var(--moss-deep)',
        padding: '12px 18px', borderRadius: 10,
        fontSize: 13, display: 'flex', gap: 8, alignItems: 'center',
        whiteSpace: 'nowrap', cursor: 'pointer',
        fontWeight: 500,
        position: 'relative', zIndex: 1,
      }}>
        Open in Inku <Icon name="arrow-up-right" size={14} stroke={2} />
      </button>
    </div>
  );
}

// ============================================================
// Open-in-Inku mini button (used in many card actions)
// ============================================================
function OpenInInku({ onClick, label = 'Open in Inku' }) {
  return (
    <button className="btn-reset" onClick={onClick} style={{
      display: 'flex', gap: 6, alignItems: 'center',
      padding: '5px 10px', borderRadius: 6,
      border: '1px solid var(--line)',
      color: 'var(--ink-soft)',
      cursor: 'pointer',
      fontFamily: 'var(--font-mono)',
      fontSize: 10, letterSpacing: '.1em', textTransform: 'uppercase',
      transition: 'all .2s',
    }}
    onMouseEnter={e => { e.currentTarget.style.background = 'var(--moss-glow)'; e.currentTarget.style.color = 'var(--moss-deep)'; }}
    onMouseLeave={e => { e.currentTarget.style.background = 'transparent'; e.currentTarget.style.color = 'var(--ink-soft)'; }}>
      {label} <Icon name="arrow-up-right" size={11} />
    </button>
  );
}

// ============================================================
// Performance hypothesis card — predicted uplift
// ============================================================
function PerfHypothesis({ concept, lift = 23, because }) {
  return (
    <CardShell kind="performance hypothesis" title={`predicted +${lift}% vs baseline`}>
      <div style={{ display: 'flex', alignItems: 'flex-start', gap: 14 }}>
        <div style={{ flexShrink: 0, width: 70, height: 70, borderRadius: '50%',
          background: `conic-gradient(var(--moss-deep) ${lift * 3.6}deg, var(--bg-2) 0)`,
          display: 'flex', alignItems: 'center', justifyContent: 'center',
          position: 'relative',
        }}>
          <div style={{ width: 56, height: 56, borderRadius: '50%', background: 'var(--card-solid)',
            display: 'flex', alignItems: 'center', justifyContent: 'center',
            fontFamily: 'var(--font-display)', fontSize: 20, color: 'var(--moss-deep)' }}>
            +{lift}%
          </div>
        </div>
        <div style={{ flex: 1 }}>
          <div style={{ fontFamily: 'var(--font-serif)', fontSize: 16, color: 'var(--ink)', lineHeight: 1.4 }}>
            "{because}"
          </div>
          <div className="label" style={{ marginTop: 8 }}>Based on 412 rows of last-quarter performance + 3 matched cohorts</div>
        </div>
      </div>
    </CardShell>
  );
}

// ============================================================
// Helper: a streaming "drafting..." loader
// ============================================================
function GeneratingStream({ lines }) {
  const [idx, setIdx] = React.useState(0);
  React.useEffect(() => {
    if (idx >= lines.length) return;
    const t = setTimeout(() => setIdx(idx + 1), 600);
    return () => clearTimeout(t);
  }, [idx, lines.length]);
  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 8, padding: '14px 2px' }}>
      {lines.map((l, i) => (
        <div key={i} style={{
          display: 'flex', gap: 10, alignItems: 'center',
          opacity: i < idx ? 1 : (i === idx ? 0.6 : 0.25),
          transition: 'opacity .4s',
          fontFamily: 'var(--font-mono)', fontSize: 11, color: 'var(--ink-soft)',
        }}>
          {i < idx ? <Icon name="check" size={12} stroke={2} /> : (i === idx ? <Orbit size={10}/> : <span style={{ width: 10 }}/>)}
          <span>{l}</span>
        </div>
      ))}
    </div>
  );
}

Object.assign(window, {
  CardShell, FileCard, ContextCard, AestheticTiles, AudienceSlider, ToneSlider,
  ChannelMatrix, PersonaRack, BudgetDial, ConstraintChips, BriefCard,
  MessagingArchitecture, AdConcept, AdConceptGrid, VariantMatrix, ContentCalendar,
  HandoffCard, OpenInInku, PerfHypothesis, GeneratingStream,
});
