/* Rooms 1–4: format, audience, intel, strategy */

const { useState: rUseState, useEffect: rUseEffect, useMemo: rUseMemo, useRef: rUseRef } = React;

// ============================================================
// SEED BRIEF — the Marriott Marquis hospitality example. Used by
// the "Try the example" button on Room 1 so a first-time user
// can hit Generate in 90 seconds.
// ============================================================
const SEED_BRIEF = {
  formats: ['one_pager', 'deck', 'linkedin_carousel'],
  goal: 'retention',
  audience_type: 'customer',
  vertical: 'hospitality',
  persona: 'Property GM',
  product: 'one_touch_formula',
  region: 'us',
  account_name: 'Marriott Marquis NYC',
  account_domain: 'marriott.com',
  account_logo_url: '',
  current_practice: '4-step manual protocol with spray-and-wipe + UV wand on high-touch surfaces. 42-minute room turnover average; target sub-22 to support compression-night rebookings.',
  facility_layout: '1,966-key urban convention hotel, 3 F&B outlets (Broadway Lounge, Crossroads, Stir).',
  usage_volume: '~600 rooms cleaned per day in peak season; 12-person EVS team per shift.',
  competitors: ['Ecolab', 'In-house protocols'],
  protocol_gaps: 'No documented audit trail for insurance carrier; Q3 norovirus cluster in Crossroads triggered 6-day F&B shutdown and ~$1.4M revenue impact.',
  notes: 'Already piloted V-Gun with EVS team on 12th floor — strong operator response, no residue/odor complaints. GM open to single-vendor consolidation IF documentation supports insurance/audit requirements.',
  attachments: []
};
window.VENTUM_SEED_BRIEF = SEED_BRIEF;

// ============================================================
// ROOM 1 — Format & goal
// ============================================================
// ------------------------------------------------------------
// FORMAT CATEGORIES — group the 19 deliverable formats by intent
// so the picker is scannable instead of a 19-tile dump. Each entry
// lists the categories' label, short desc, and the format keys it
// contains; unknown formats fall into "Other".
// ------------------------------------------------------------
const FORMAT_CATEGORIES = [
  {
    key: 'sales_collateral',
    label: 'Sales collateral',
    desc: 'Print + leave-behind pieces sales reps hand to a prospect.',
    formats: ['one_pager', 'brochure', 'case_study_summary', 'account_brief', 'trade_show_banner', 'print_ad']
  },
  {
    key: 'content_marketing',
    label: 'Content marketing',
    desc: 'Owned-channel pieces that build authority and feed AEO / SEO discovery.',
    formats: ['blog_post', 'white_paper', 'infographic', 'info_pack']
  },
  {
    key: 'decks',
    label: 'Decks',
    desc: 'Slides for pitches, all-hands, and event presentations.',
    formats: ['deck', 'deck_slide']
  },
  {
    key: 'email_web',
    label: 'Email & web',
    desc: 'Inbox + landing-page pieces — direct response surfaces.',
    formats: ['email', 'web_hero']
  },
  {
    key: 'social_posts',
    label: 'Social posts',
    desc: 'Single-post pieces for LinkedIn, X, and Instagram feeds.',
    formats: ['linkedin_post', 'twitter_post', 'instagram_post']
  },
  {
    key: 'social_campaigns',
    label: 'Social campaigns',
    desc: 'Multi-post arcs — campaigns, threads, caption sets, carousels.',
    formats: ['linkedin_campaign', 'twitter_thread', 'instagram_caption_set', 'linkedin_carousel']
  },
  {
    key: 'partner_announcements',
    label: 'Partner announcements',
    desc: 'When a partner announces Ventum to their own people — POV inverts.',
    formats: ['partner_announcement_email', 'partner_announcement_slack', 'partner_announcement_linkedin', 'partner_announcement_press', 'partner_announcement_allhands']
  }
];

// ------------------------------------------------------------
// FORMAT PACKS — opinionated bundles for common workflows.
// One click selects all formats in the pack. Pick the pack that
// matches the launch you're shipping, then customize.
// ------------------------------------------------------------
const FORMAT_PACKS = [
  {
    key: 'partner_announcement_pack',
    name: 'Partner announcement pack',
    desc: 'A partner has selected Ventum. Email + Slack + LinkedIn + press release + all-hands talking points — same news, every channel.',
    formats: [
      'partner_announcement_email',
      'partner_announcement_slack',
      'partner_announcement_linkedin',
      'partner_announcement_press',
      'partner_announcement_allhands'
    ]
  },
  {
    key: 'social_launch_pack',
    name: 'Social launch pack',
    desc: 'Multi-platform brand campaign. LinkedIn campaign (5 posts) + X thread + Instagram caption set — coordinated across platforms.',
    formats: ['linkedin_campaign', 'twitter_thread', 'instagram_caption_set']
  },
  {
    key: 'account_outreach_pack',
    name: 'Account outreach pack',
    desc: 'Pursuing a named account. Account brief + one-pager + email + LinkedIn post — pitch-ready collateral.',
    formats: ['account_brief', 'one_pager', 'email', 'linkedin_post']
  },
  {
    key: 'sales_kit_pack',
    name: 'Sales kit pack',
    desc: 'Tradeshow / sales-call package. Deck + brochure + one-pager + case study summary.',
    formats: ['deck', 'brochure', 'one_pager', 'case_study_summary']
  }
];

function Room1_Format({ intake, setIntake, allFormats, allGoals, templates = [], onUseTemplate = () => {} }) {
  const toggleFormat = (key) => {
    const next = intake.formats?.includes(key)
      ? intake.formats.filter(f => f !== key)
      : [...(intake.formats || []), key];
    setIntake({ ...intake, formats: next });
  };

  const applyPack = (pack) => {
    setIntake({ ...intake, formats: pack.formats.slice() });
    window.__toast && window.__toast(`${pack.name} loaded · ${pack.formats.length} deliverables selected`);
  };

  const loadSeedBrief = () => {
    setIntake({ ...window.VENTUM_SEED_BRIEF });
    window.__toast && window.__toast('Example brief loaded — click Next to continue');
  };

  return (
    <div>
      <div className="room-head room-head--compact">
        <div className="room-num">Step 01 of 06</div>
        <div style={{ flex: 1 }}>
          <h1>What are we making?</h1>
          <p className="lede" style={{ marginBottom: 6 }}>Pick a goal, then choose deliverables. The studio drafts, QAs, and prepares all of them in one pass.</p>
        </div>
        <button
          className="seed-brief-btn seed-brief-btn--compact"
          onClick={loadSeedBrief}
          title="Pre-fill all wizard fields with the Marriott Marquis hospitality example"
        >
          <span style={{ fontSize: 13 }}>→</span>
          <span>Try the example</span>
        </button>
      </div>

      {templates.length > 0 && (
        <div className="field-block" style={{ borderLeft: '3px solid var(--teal)', paddingLeft: 16 }}>
          <label>
            Start from a template
            <span className="hint">prebuilt briefs your team has saved</span>
          </label>
          <div className="chip-row" style={{ margin: 0, flexWrap: 'wrap', gap: 8 }}>
            {templates.slice(0, 12).map(t => (
              <button
                key={t.id}
                className="chip"
                onClick={() => onUseTemplate(t)}
                title={t.template_description || `Created by ${(t._author_email || '').split('@')[0]}`}
                style={{ display: 'flex', flexDirection: 'column', alignItems: 'flex-start', gap: 2, padding: '8px 12px', textAlign: 'left', maxWidth: 280 }}
              >
                <span style={{ fontWeight: 500, fontSize: 12.5 }}>{t.template_label || t.title}</span>
                {t.template_description && (
                  <span className="mono" style={{ fontSize: 9, color: 'var(--ink-40)', letterSpacing: '0.06em', textTransform: 'none', whiteSpace: 'normal', maxWidth: 260, lineHeight: 1.4 }}>{t.template_description.slice(0, 100)}</span>
                )}
              </button>
            ))}
          </div>
        </div>
      )}

      <div className="field-block">
        <label>Goal · what should this piece <em>do</em>?</label>
        <div className="goal-grid">
          {allGoals.map(g => (
            <button
              key={g.key}
              className={"goal-tile" + (intake.goal === g.key ? " selected" : "")}
              onClick={() => setIntake({ ...intake, goal: g.key })}
              title={g.desc}
            >
              <span className="goal-tile__tag mono">{g.tag}</span>
              <span className="goal-tile__name">{g.name}</span>
            </button>
          ))}
        </div>
      </div>

      <div className="field-block">
        <label>
          Quick-start with a pack
          <span className="hint">one click selects all formats in the pack</span>
        </label>
        <div className="pack-grid">
          {FORMAT_PACKS.map(p => {
            const allSelected = p.formats.every(f => intake.formats?.includes(f));
            return (
              <button
                key={p.key}
                className={"pack-tile" + (allSelected ? " selected" : "")}
                onClick={() => applyPack(p)}
                title={p.formats.join(' + ')}
              >
                <div className="pack-tile__top">
                  <span className="mono pack-tile__count">{p.formats.length}</span>
                  <span className="pack-tile__name">{p.name}</span>
                </div>
                <span className="pack-tile__desc">{p.desc}</span>
              </button>
            );
          })}
        </div>
      </div>

      <div className="field-block">
        <label>
          Deliverables · pick one or more
          <span className="hint">{intake.formats?.length || 0} selected</span>
        </label>
        {(() => {
          // Build a map of format key → category so unknown formats can fall
          // into "Other" without being dropped.
          const grouped = new Set();
          FORMAT_CATEGORIES.forEach(cat => cat.formats.forEach(k => grouped.add(k)));
          const otherFormats = allFormats.filter(f => !grouped.has(f.key));
          const categories = FORMAT_CATEGORIES.map(cat => ({
            ...cat,
            items: cat.formats.map(k => allFormats.find(f => f.key === k)).filter(Boolean)
          })).filter(cat => cat.items.length > 0);
          if (otherFormats.length) {
            categories.push({ key: 'other', label: 'Other', desc: '', items: otherFormats });
          }
          const toggleCategory = (cat) => {
            const allSelected = cat.items.every(f => intake.formats?.includes(f.key));
            const next = new Set(intake.formats || []);
            cat.items.forEach(f => {
              if (allSelected) next.delete(f.key);
              else next.add(f.key);
            });
            setIntake({ ...intake, formats: Array.from(next) });
          };
          return (
            <div style={{ display: 'flex', flexDirection: 'column', gap: 22 }}>
              {categories.map(cat => {
                const selectedCount = cat.items.filter(f => intake.formats?.includes(f.key)).length;
                const allSelected = selectedCount === cat.items.length;
                return (
                  <div key={cat.key}>
                    <div style={{ display: 'flex', alignItems: 'baseline', justifyContent: 'space-between', marginBottom: 8 }}>
                      <div>
                        <div className="mono" style={{ fontSize: 10, letterSpacing: '0.16em', textTransform: 'uppercase', color: 'var(--ink-100)', fontWeight: 500 }}>
                          {cat.label}
                          {selectedCount > 0 && <span style={{ color: 'var(--teal)', marginLeft: 8 }}>· {selectedCount}/{cat.items.length}</span>}
                        </div>
                        {cat.desc && <div style={{ fontSize: 11.5, color: 'var(--ink-60)', marginTop: 2, lineHeight: 1.4 }}>{cat.desc}</div>}
                      </div>
                      <button
                        onClick={() => toggleCategory(cat)}
                        className="mono"
                        title={allSelected ? `Deselect all in ${cat.label}` : `Select all in ${cat.label}`}
                        style={{ background: 'transparent', border: '1px solid var(--ink-10)', borderRadius: 2, padding: '4px 8px', fontSize: 9, letterSpacing: '0.14em', color: 'var(--ink-60)', textTransform: 'uppercase', cursor: 'pointer', whiteSpace: 'nowrap' }}
                      >{allSelected ? 'Clear' : 'All'}</button>
                    </div>
                    <div className="format-grid">
                      {cat.items.map(f => (
                        <button
                          key={f.key}
                          className={"format-tile" + (intake.formats?.includes(f.key) ? " selected" : "")}
                          onClick={() => toggleFormat(f.key)}
                          title={f.desc}
                        >
                          <span className="format-tile__icon mono">{f.icon}</span>
                          <span className="format-tile__name">{f.name}</span>
                        </button>
                      ))}
                    </div>
                  </div>
                );
              })}
            </div>
          );
        })()}
      </div>

      {/* Text density chip — affects how much copy the agent generates
          per field. Shorter = punchier; default; longer = more body copy.
          Visible whenever any text-bearing format is selected. */}
      {(intake.formats || []).length > 0 && (
        <div className="field-block" style={{ marginTop: 6 }}>
          <label>Text density <span className="hint">how much copy the agent writes per field</span></label>
          <div className="chip-row" style={{ margin: 0 }}>
            {[
              { id: 0.85, name: 'Punchy', blurb: 'Tighter copy, fewer words, more whitespace' },
              { id: 1,    name: 'Default', blurb: 'Standard length for each format' },
              { id: 1.15, name: 'Detailed', blurb: 'Fuller copy, more body, more support text' }
            ].map(d => {
              const cur = intake.font_scale || 1;
              const selected = Math.abs(cur - d.id) < 0.001;
              return (
                <button
                  key={d.id}
                  className={'chip' + (selected ? ' selected' : '')}
                  onClick={() => setIntake({ ...intake, font_scale: d.id })}
                  title={d.blurb}
                  style={{ display: 'flex', flexDirection: 'column', alignItems: 'flex-start', gap: 2, padding: '8px 12px' }}
                >
                  <span style={{ fontWeight: 500, fontSize: 12.5 }}>{d.name}</span>
                  <span className="mono" style={{ fontSize: 9, color: selected ? 'rgba(255,255,255,0.75)' : 'var(--ink-40)', letterSpacing: '0.06em', textTransform: 'none', whiteSpace: 'normal', maxWidth: 220 }}>{d.blurb}</span>
                </button>
              );
            })}
          </div>
        </div>
      )}

      {/* Deck template picker — only shows when "deck" is selected.
          Lets the operator pick a template (size + sequence) instead of
          the default 5-slide narrative. */}
      {(intake.formats || []).includes('deck') && window.VentumAgents?.DECK_TEMPLATES && (
        <div className="field-block" style={{ borderLeft: '3px solid var(--teal)', paddingLeft: 16, marginTop: 6 }}>
          <div className="mono" style={{ fontSize: 10, letterSpacing: '0.14em', color: 'var(--teal)', textTransform: 'uppercase', marginBottom: 8 }}>Slide deck template</div>
          <div className="chip-row" style={{ margin: 0, flexWrap: 'wrap' }}>
            {Object.values(window.VentumAgents.DECK_TEMPLATES).map(t => {
              const selected = (intake.deck_template || 'narrative_5') === t.id;
              return (
                <button
                  key={t.id}
                  className={'chip' + (selected ? ' selected' : '')}
                  style={{ display: 'flex', flexDirection: 'column', alignItems: 'flex-start', gap: 2, padding: '8px 12px', textAlign: 'left' }}
                  onClick={() => setIntake({ ...intake, deck_template: t.id })}
                  title={t.blurb}
                >
                  <span style={{ fontWeight: 500, fontSize: 12.5 }}>{t.name}</span>
                  <span className="mono" style={{ fontSize: 9, color: selected ? 'rgba(255,255,255,0.75)' : 'var(--ink-40)', letterSpacing: '0.06em', textTransform: 'none', whiteSpace: 'normal', maxWidth: 240 }}>{t.blurb}</span>
                </button>
              );
            })}
          </div>
        </div>
      )}
    </div>
  );
}

// ============================================================
// ROOM 2 — Audience
// ============================================================
function Room2_Audience({ intake, setIntake }) {
  const BRAND = window.VENTUM_BRAND;
  const audienceTypes = Object.entries(BRAND.audience_types).map(([k,v]) => ({ key: k, ...v }));
  const verticals = Object.entries(BRAND.verticals).map(([k,v]) => ({ key: k, ...v }));

  const personaOptions = intake.vertical
    ? (BRAND.verticals[intake.vertical]?.audience_focus || [])
    : [];

  return (
    <div>
      <div className="room-head room-head--compact">
        <div className="room-num">Step 02 of 06</div>
        <div style={{ flex: 1 }}>
          <h1>Who is this for?</h1>
          <p className="lede">Vertical + audience type shape every downstream decision — pillar, proof, CTA, and voice register.</p>
        </div>
      </div>

      <div className="field-block">
        <label>Audience type</label>
        <div className="goal-grid">
          {audienceTypes.map(at => (
            <button
              key={at.key}
              className={"goal-tile" + (intake.audience_type === at.key ? " selected" : "")}
              onClick={() => setIntake({ ...intake, audience_type: at.key })}
              title={at.note}
            >
              <span className="goal-tile__tag mono">{at.key === 'customer' ? 'WARM' : at.key === 'prospect' ? 'COLD' : 'REL'}</span>
              <span className="goal-tile__name">{at.name}</span>
            </button>
          ))}
        </div>
      </div>

      <div className="field-block">
        <label>Vertical</label>
        <div className="vertical-grid">
          {verticals.map(v => (
            <button
              key={v.key}
              className={"vertical-tile" + (intake.vertical === v.key ? " selected" : "")}
              onClick={() => setIntake({ ...intake, vertical: v.key })}
              title={v.lead_with}
            >
              <span className="vertical-tile__name">{v.name}</span>
              <span className="vertical-tile__lead">{v.lead_with}</span>
            </button>
          ))}
        </div>
      </div>

      <div className="field-grid-2">
        <div className="field-block">
          <label>Persona <span className="hint">optional — leave on "any operator" when the audience spans multiple roles</span></label>
          <select
            className="select"
            value={intake.persona || ''}
            onChange={(e) => setIntake({ ...intake, persona: e.target.value })}
          >
            <option value="">— any operator —</option>
            {personaOptions.map(p => <option key={p} value={p}>{p}</option>)}
          </select>
        </div>
        <div className="field-block">
          <label>Region</label>
          <select
            className="select"
            value={intake.region || 'us'}
            onChange={(e) => setIntake({ ...intake, region: e.target.value })}
          >
            <option value="us">United States</option>
            <option value="eu">Europe</option>
            <option value="global">Global</option>
          </select>
        </div>
      </div>

      <div className="field-block">
        <label>Products <span className="hint">pick any combination — leave all unchecked for mixed / category-level copy</span></label>
        <div className="chip-row">
          {Object.entries(BRAND.products).map(([k, v]) => {
            const selected = (intake.products || []).includes(k)
              || (!intake.products && intake.product === k);
            return (
              <button
                key={k}
                type="button"
                className={"chip" + (selected ? " selected" : "")}
                onClick={() => {
                  // First click: migrate legacy single-select intake.product into the array form.
                  const base = Array.isArray(intake.products)
                    ? intake.products
                    : (intake.product ? [intake.product] : []);
                  const next = base.includes(k) ? base.filter(x => x !== k) : [...base, k];
                  // Keep legacy intake.product in sync with the first selection for downstream consumers
                  // that still read the singular field (renderers, imagery cascade).
                  setIntake({ ...intake, products: next, product: next[0] || '' });
                }}
                title={v.name}
              >
                {v.name}
              </button>
            );
          })}
        </div>
      </div>

      <div className="field-block">
        <label>Subject framing <span className="hint">who is the protagonist of the piece — drives whether copy leads with Ventum, the account, the partnership, or the category</span></label>
        <div className="goal-grid">
          {SUBJECT_FRAMINGS.map(sf => (
            <button
              key={sf.key}
              type="button"
              className={"goal-tile" + ((intake.subject_framing || 'product') === sf.key ? " selected" : "")}
              onClick={() => setIntake({ ...intake, subject_framing: sf.key })}
              title={sf.desc}
            >
              <span className="goal-tile__tag mono">{sf.tag}</span>
              <span className="goal-tile__name">{sf.name}</span>
            </button>
          ))}
        </div>
      </div>

      <AccountIdentityField intake={intake} setIntake={setIntake} />
      <AccountResearchField intake={intake} setIntake={setIntake} />
    </div>
  );
}

// Subject framing presets — drives a routing block at the head of the
// system prompt. "product" is the legacy default (Ventum-led). The
// other modes flip the protagonist so the agents stop defaulting to
// product-feature copy when the brief is really an account case,
// co-marketing piece, or category positioning.
const SUBJECT_FRAMINGS = [
  { key: 'product',     tag: 'DEFAULT',  name: 'Product-led',     desc: 'Ventum is the protagonist. Lead with the product line and proof. Use when the piece is about Ventum (V-Gun, V-Sterilizer, V-Bot, One-Touch, V-Wipes).' },
  { key: 'account',     tag: 'ACCOUNT',  name: 'Account-led',     desc: "The named account is the protagonist. Ventum is the recommended solution. Any partner named is an endorser, not the subject. Use for case briefs like 'Royal Caribbean switches to Ventum with WHO support.'" },
  { key: 'partnership', tag: 'CO-MKT',   name: 'Partnership-led', desc: 'Ventum + the partner are co-subjects. Use for co-marketing pieces where neither side leads.' },
  { key: 'category',    tag: 'CATEGORY', name: 'Category-led',    desc: 'The category / problem is the protagonist; Ventum shows up as the science-led answer. Use for thought-leadership or AEO-positioning pieces.' }
];

// ============================================================
// Account Research — uses Anthropic's web_search tool to pull public,
// citable facts about the account/partner and stamp them on the intake
// so the strategy + copy agents see real context, not just user-typed
// notes. Manual button — costs ~$0.10-$0.16 per research call, so we
// only fire on explicit user action and cache the result for 24h.
// ============================================================
function AccountResearchField({ intake, setIntake }) {
  const [status, setStatus] = rUseState('idle'); // idle | running | done | error
  const [error, setError] = rUseState(null);
  const target = intake.account_name || intake.partner_name || '';
  const research = intake.account_research;

  // Cache check: 24h freshness on the same target
  const cachedAndFresh = !!research && research._for === target &&
    research._searched_at &&
    (Date.now() - new Date(research._searched_at).getTime()) < 24 * 60 * 60 * 1000;

  const runResearch = async () => {
    if (!target) return;
    setStatus('running');
    setError(null);
    try {
      const result = await window.VentumAgents.runAccountResearch({
        account_name: intake.account_name,
        account_domain: intake.account_domain,
        partner_name: intake.partner_name,
        partner_industry: intake.partner_industry,
        vertical: intake.vertical
      });
      setIntake({ ...intake, account_research: { ...result, _for: target } });
      setStatus('done');
    } catch (e) {
      console.error(e);
      setError(String(e.message || e));
      setStatus('error');
    }
  };
  const clearResearch = () => {
    const next = { ...intake };
    delete next.account_research;
    setIntake(next);
    setStatus('idle');
    setError(null);
  };

  if (!target) return null; // nothing to research yet

  return (
    <div className="field-block" style={{ borderLeft: '3px solid var(--teal)', paddingLeft: 16, marginTop: 20 }}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 12, marginBottom: 8 }}>
        <div className="mono" style={{ fontSize: 10, letterSpacing: '0.14em', color: 'var(--teal)', textTransform: 'uppercase' }}>Account research</div>
        <span style={{ fontSize: 11.5, color: 'var(--ink-60)' }}>
          {status === 'running' ? 'Searching the web…' :
            research ? `Researched ${research._searched_at ? new Date(research._searched_at).toLocaleString() : ''} · cached 24h` :
            'Optional — pull public facts about this account to inform strategy + copy. Costs ~$0.10–0.16 per call.'}
        </span>
        <div style={{ marginLeft: 'auto', display: 'flex', gap: 6 }}>
          {!research && (
            <button
              onClick={runResearch}
              disabled={status === 'running'}
              className="btn-primary"
              style={{ padding: '7px 14px', fontSize: 11, letterSpacing: '0.1em' }}
            >
              {status === 'running' ? 'Researching…' : '🔍 Research this account'}
            </button>
          )}
          {research && !cachedAndFresh && (
            <button onClick={runResearch} className="btn-ghost" style={{ padding: '7px 14px', fontSize: 11 }} title="Re-run research (will charge again)">↻ Refresh</button>
          )}
          {research && (
            <button onClick={clearResearch} className="btn-ghost" style={{ padding: '7px 12px', fontSize: 11 }}>Clear</button>
          )}
        </div>
      </div>

      {status === 'running' && <ResearchLoading target={target} />}

      {status === 'error' && error && (
        <div style={{ padding: '10px 12px', background: 'rgba(192,57,43,0.06)', border: '1px solid rgba(192,57,43,0.2)', borderRadius: 3, fontSize: 12, color: '#C0392B', marginBottom: 10 }}>
          Research failed: {error}
        </div>
      )}

      {research && status !== 'running' && (
        <div style={{ background: '#F8FAFC', border: '1px solid var(--ink-10)', borderRadius: 4, padding: '14px 18px', fontSize: 13, lineHeight: 1.5 }}>
          {research.company_summary && (
            <div style={{ marginBottom: 12, color: 'var(--ink-100)' }}>{research.company_summary}</div>
          )}
          <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 14 }}>
            {research.scale && (
              <ResearchFact label="Scale" value={research.scale} source={research.scale_source_url} />
            )}
            {research.positioning && (
              <ResearchFact label="Positioning" value={research.positioning} source={research.positioning_source_url} />
            )}
          </div>
          {Array.isArray(research.leadership) && research.leadership.length > 0 && (
            <ResearchSection label="Leadership">
              {research.leadership.map((l, i) => (
                <div key={i} style={{ fontSize: 12.5, marginBottom: 4 }}>
                  <strong>{l.name}</strong>, {l.title}
                  {l.relevance && <span style={{ color: 'var(--ink-60)' }}> — {l.relevance}</span>}
                  {l.source_url && <a href={l.source_url} target="_blank" rel="noopener" style={{ marginLeft: 8, fontSize: 10, color: 'var(--teal)' }}>↗ source</a>}
                </div>
              ))}
            </ResearchSection>
          )}
          {Array.isArray(research.recent_news) && research.recent_news.length > 0 && (
            <ResearchSection label="Recent news">
              {research.recent_news.map((n, i) => (
                <div key={i} style={{ fontSize: 12.5, marginBottom: 4 }}>
                  <span className="mono" style={{ fontSize: 10.5, color: 'var(--ink-40)', marginRight: 8 }}>{n.date}</span>
                  {n.headline}
                  {n.why_relevant && <span style={{ color: 'var(--ink-60)' }}> — {n.why_relevant}</span>}
                  {n.source_url && <a href={n.source_url} target="_blank" rel="noopener" style={{ marginLeft: 8, fontSize: 10, color: 'var(--teal)' }}>↗</a>}
                </div>
              ))}
            </ResearchSection>
          )}
          {Array.isArray(research.pain_signals) && research.pain_signals.length > 0 && (
            <ResearchSection label="Pain signals">
              {research.pain_signals.map((p, i) => (
                <div key={i} style={{ fontSize: 12.5, marginBottom: 4 }}>
                  {p.signal}
                  {p.source_url && <a href={p.source_url} target="_blank" rel="noopener" style={{ marginLeft: 8, fontSize: 10, color: 'var(--teal)' }}>↗</a>}
                </div>
              ))}
            </ResearchSection>
          )}
          {Array.isArray(research.existing_vendors_known) && research.existing_vendors_known.length > 0 && (
            <ResearchSection label="Known vendors">
              {research.existing_vendors_known.map((v, i) => (
                <div key={i} style={{ fontSize: 12.5, marginBottom: 4 }}>
                  <strong>{v.vendor}</strong> <span className="mono" style={{ fontSize: 10.5, color: 'var(--ink-40)' }}>· {v.category}</span>
                  {v.source_url && <a href={v.source_url} target="_blank" rel="noopener" style={{ marginLeft: 8, fontSize: 10, color: 'var(--teal)' }}>↗</a>}
                </div>
              ))}
            </ResearchSection>
          )}
          <div className="mono" style={{ fontSize: 10, color: 'var(--ink-40)', letterSpacing: '0.06em', marginTop: 12, paddingTop: 10, borderTop: '1px solid var(--ink-10)' }}>
            Research flows into Strategy + Copy agents automatically. Edit any field by clearing and re-running, or by editing the JSON in the saved library entry.
          </div>
        </div>
      )}
    </div>
  );
}
// Branded indeterminate loading panel shown while account research runs.
// The real call is a single async request, so this cycles through the phases
// it's actually doing to make the ~10–20s wait feel considered, not hung.
function ResearchLoading({ target }) {
  const phases = [
    'Searching the web for ' + (target || 'the account') + '…',
    'Reading public company sources…',
    'Extracting scale, positioning & footprint…',
    'Matching Ventum proof points to their context…',
    'Assembling the research brief…',
  ];
  const [phase, setPhase] = rUseState(0);
  rUseEffect(() => {
    const id = setInterval(() => setPhase(p => Math.min(p + 1, phases.length - 1)), 3200);
    return () => clearInterval(id);
  }, []);
  return (
    <div className="research-loading">
      <div className="research-loading__scan" aria-hidden="true">
        <span></span><span></span><span></span>
      </div>
      <div className="research-loading__body">
        <div className="research-loading__phase">{phases[phase]}</div>
        <div className="research-loading__track"><div className="research-loading__bar" /></div>
        <div className="research-loading__steps mono">
          {phases.map((_, i) => (
            <span key={i} className={'research-loading__dot' + (i <= phase ? ' is-done' : '') + (i === phase ? ' is-active' : '')} />
          ))}
        </div>
      </div>
    </div>
  );
}
function ResearchFact({ label, value, source }) {
  return (
    <div>
      <div className="mono" style={{ fontSize: 9.5, letterSpacing: '0.14em', color: 'var(--ink-40)', textTransform: 'uppercase', marginBottom: 2 }}>{label}</div>
      <div style={{ fontSize: 12.5, color: 'var(--ink-100)' }}>
        {value}
        {source && <a href={source} target="_blank" rel="noopener" style={{ marginLeft: 8, fontSize: 10, color: 'var(--teal)' }}>↗</a>}
      </div>
    </div>
  );
}
function ResearchSection({ label, children }) {
  return (
    <div style={{ marginTop: 12, paddingTop: 10, borderTop: '1px solid var(--ink-10)' }}>
      <div className="mono" style={{ fontSize: 9.5, letterSpacing: '0.14em', color: 'var(--ink-40)', textTransform: 'uppercase', marginBottom: 6 }}>{label}</div>
      {children}
    </div>
  );
}

// ============================================================
// Account identity sub-field — name + domain + logo confirmation
// Uses Clearbit logo API with favicon fallback. Both are public,
// no auth required, served cross-origin.
// ============================================================
function AccountIdentityField({ intake, setIntake }) {
  // Cascade through logo sources; mark the first one that loads successfully.
  const sources = intake.account_domain ? window.VENTUM_LOGO.sources(intake.account_domain) : [];
  const [activeIdx, setActiveIdx] = rUseState(0);
  const [results, setResults] = rUseState({}); // {idx: 'ok'|'fail'}

  const [probing, setProbing] = rUseState(false);

  // Auto-identify the domain when the account name changes (only if domain is
  // empty). Reads the whole name, builds several candidate domains, and probes
  // each against Clearbit (which 404s for domains it doesn't know) — keeping the
  // highest-priority candidate that resolves to a real logo instead of stopping
  // at the first ".com" guess. Debounced so it fires after typing settles.
  rUseEffect(() => {
    if (!intake.account_name || intake.account_domain) { setProbing(false); return; }
    let cancelled = false;
    const t = setTimeout(() => {
      const cands = window.VENTUM_LOGO?.candidates(intake.account_name) || [];
      if (!cands.length) return;
      setProbing(true);
      const probe = (d) => new Promise((res) => {
        const img = new Image();
        let done = false;
        const finish = (ok) => { if (!done) { done = true; res(ok); } };
        img.onload = () => finish(img.naturalWidth > 2);
        img.onerror = () => finish(false);
        img.src = `https://logo.clearbit.com/${d}`;
        setTimeout(() => finish(false), 2500);
      });
      Promise.all(cands.map((d) => probe(d).then((ok) => ({ d, ok })))).then((results) => {
        if (cancelled) return;
        setProbing(false);
        // results preserve candidate order → first ok is the highest priority
        const hit = results.find((r) => r.ok);
        setIntake({ ...intake, account_domain: hit ? hit.d : cands[0] });
      });
    }, 600);
    return () => { cancelled = true; clearTimeout(t); };
  }, [intake.account_name]);

  // Reset cascade when domain changes
  rUseEffect(() => {
    setActiveIdx(0);
    setResults({});
  }, [intake.account_domain]);

  const onImgLoad = (i) => () => setResults(r => ({ ...r, [i]: 'ok' }));
  const onImgError = (i) => () => {
    setResults(r => ({ ...r, [i]: 'fail' }));
    // try next source
    if (i === activeIdx && i + 1 < sources.length) setActiveIdx(i + 1);
  };

  const activeSource = sources[activeIdx];
  const allFailed = sources.length > 0 && Object.values(results).filter(v => v === 'fail').length >= sources.length;
  const locked = intake.account_logo_url;
  const isFavicon = activeSource && (activeSource.name === 'google' || activeSource.name === 'duckduckgo');

  const confirmLogo = () => {
    if (!activeSource) return;
    setIntake({ ...intake, account_logo_url: activeSource.url });
    window.__toast && window.__toast('Logo locked in');
  };
  const tryNext = () => {
    if (activeIdx + 1 < sources.length) setActiveIdx(activeIdx + 1);
  };
  const clearLogo = () => {
    setIntake({ ...intake, account_logo_url: '' });
  };

  return (
    <div className="field-block">
      <label>Account or organization <span className="hint">unlocks account-aware artifacts + customer logo on the piece</span></label>
      <div className="field-grid-2">
        <input
          type="text"
          className="input"
          placeholder="e.g. Marriott · Memorial Health · Pasteur Institute"
          value={intake.account_name || ''}
          onChange={(e) => setIntake({ ...intake, account_name: e.target.value })}
        />
        <input
          type="text"
          className="input"
          placeholder="website domain (e.g. marriott.com)"
          value={intake.account_domain || ''}
          onChange={(e) => setIntake({ ...intake, account_domain: e.target.value.trim() })}
        />
      </div>
      {probing && !intake.account_domain && (
        <div className="mono" style={{ marginTop: 6, fontSize: 10, letterSpacing: '0.1em', textTransform: 'uppercase', color: 'var(--ink-40)', display: 'flex', alignItems: 'center', gap: 6 }}>
          <span className="autosave-dot pulse" style={{ background: 'var(--teal)' }} />
          Finding the right domain…
        </div>
      )}

      {intake.account_domain && !locked && (
        <div style={{
          display: 'grid', gridTemplateColumns: '88px 1fr auto', gap: 14,
          alignItems: 'center', padding: 14, marginTop: 10,
          background: 'var(--ink-05)', borderRadius: 2
        }}>
          <div style={{
            width: 88, height: 88, background: 'white',
            border: '1px solid var(--ink-10)', borderRadius: 2,
            display: 'flex', alignItems: 'center', justifyContent: 'center',
            overflow: 'hidden', padding: 8, position: 'relative'
          }}>
            {sources.map((s, i) => (
              <img
                key={s.url}
                src={s.url}
                alt=""
                onLoad={onImgLoad(i)}
                onError={onImgError(i)}
                style={{
                  maxWidth: '100%', maxHeight: '100%', objectFit: 'contain',
                  display: i === activeIdx && results[i] !== 'fail' ? 'block' : 'none'
                }}
              />
            ))}
            {allFailed && (
              <div className="mono" style={{ fontSize: 9, color: 'var(--ink-40)', textAlign: 'center', letterSpacing: '0.1em' }}>NO LOGO<br />FOUND</div>
            )}
          </div>
          <div style={{ fontSize: 13, color: 'var(--ink-80)', lineHeight: 1.5 }}>
            {!allFailed && results[activeIdx] !== 'ok' && (
              <span className="muted">Trying <span className="mono">{activeSource?.name}</span>…</span>
            )}
            {results[activeIdx] === 'ok' && !isFavicon && (
              <>
                <div style={{ fontWeight: 500 }}>Looks right?</div>
                <div className="muted" style={{ fontSize: 12, marginTop: 2 }}>
                  Source: <span className="mono">{activeSource.name}</span> · <span className="mono">{intake.account_domain}</span>
                </div>
              </>
            )}
            {results[activeIdx] === 'ok' && isFavicon && (
              <>
                <div style={{ fontWeight: 500 }}>Found a favicon — low-res, but works as a placeholder.</div>
                <div className="muted" style={{ fontSize: 12, marginTop: 2 }}>Source: <span className="mono">{activeSource.name}</span>. Try a different domain for a proper brand mark.</div>
              </>
            )}
            {allFailed && (
              <>
                <div style={{ fontWeight: 500 }}>Nothing found for <span className="mono">{intake.account_domain}</span>.</div>
                <div className="muted" style={{ fontSize: 12, marginTop: 2 }}>Try the marketing root (e.g. <span className="mono">marriott.com</span>), the parent brand, or skip the logo.</div>
              </>
            )}
          </div>
          <div style={{ display: 'flex', flexDirection: 'column', gap: 6, minWidth: 130 }}>
            {results[activeIdx] === 'ok' && (
              <button className="btn-primary" style={{ padding: '8px 12px', fontSize: 11 }} onClick={confirmLogo}>Use this logo</button>
            )}
            {activeIdx + 1 < sources.length && results[activeIdx] === 'ok' && (
              <button className="btn-ghost" style={{ padding: '7px 12px', fontSize: 10 }} onClick={tryNext}>Try another source</button>
            )}
            {allFailed && (
              <button className="btn-ghost" style={{ padding: '7px 12px', fontSize: 10 }} onClick={() => setIntake({ ...intake, account_logo_url: '' })}>Skip logo</button>
            )}
          </div>
        </div>
      )}

      {locked && (
        <>
          <div style={{
            display: 'grid', gridTemplateColumns: '64px 1fr auto', gap: 14,
            alignItems: 'center', padding: 12, marginTop: 10,
            background: 'rgba(71,184,129,0.06)', border: '1px solid rgba(71,184,129,0.25)', borderRadius: 2
          }}>
            <div style={{
              width: 64, height: 64, background: 'white',
              border: '1px solid var(--ink-10)', borderRadius: 2,
              display: 'flex', alignItems: 'center', justifyContent: 'center', padding: 6
            }}>
              <img src={locked} alt="" style={{ maxWidth: '100%', maxHeight: '100%', objectFit: 'contain' }} />
            </div>
            <div>
              <div className="mono" style={{ fontSize: 10, color: 'var(--efficacy)', letterSpacing: '0.14em', textTransform: 'uppercase' }}>✓ Logo locked</div>
              <div style={{ fontSize: 13, marginTop: 2 }}>Will render on account-aware artifacts.</div>
            </div>
            <button className="btn-ghost" style={{ padding: '7px 12px', fontSize: 10 }} onClick={clearLogo}>Change</button>
          </div>
          <div style={{
            marginTop: 8, padding: '8px 12px', fontSize: 12, lineHeight: 1.5,
            color: 'var(--ink-60)', background: 'var(--ink-05)', borderLeft: '3px solid var(--ink-20)', borderRadius: 2
          }}>
            <strong style={{ color: 'var(--ink-100)' }}>Internal use only.</strong> Customer / partner logos render fine in drafts and proposals sent directly to the named org. For anything published externally (web, paid ads, public social posts, public case studies), get written permission from the org first.
          </div>
        </>
      )}
    </div>
  );
}

// ============================================================
// ROOM 3 — Intel intake
// ============================================================
function Room3_Intel({ intake, setIntake }) {
  const BRAND = window.VENTUM_BRAND;
  const vert = BRAND.verticals[intake.vertical] || {};
  const commonCompetitors = vert.competitors || [];
  const [previewAttachment, setPreviewAttachment] = rUseState(null);

  const promptByVertical = {
    healthcare:    "Think turnover speed (ED bays, OR turnover), IP audit findings, EVS staffing, any documented HAI pressure.",
    life_sciences: "Class of cleanroom, batch cycle frequency, ISO standards in play, audit cadence, current fumigation protocol.",
    hospitality:   "Property type, room turnover SLA, guest sensitivity (allergens, fragrance-free), housekeeping headcount.",
    food:          "Shift count, sanitation window length, allergen program, USDA/FDA inspector cadence, current SSOP.",
    remediation:   "Job mix (water/fire/biohazard), hand-back SLA, insurance carrier expectations, current biocide.",
    ems_fire:      "Run volume, current decon-between-runs procedure, vehicle types, station-level vs. central decon.",
    transport:     "Fleet size, route volume, between-trip decon window, current vendor or in-house program.",
    defense:       "Mission profile, deployment cadence, FOB conditions, current MILSPEC protocols."
  };
  const intelPrompt = promptByVertical[intake.vertical];

  // Format-type detection — INTEL fields swap based on what the user is
  // generating. Announcements ask for an announcement brief (partner POV);
  // campaigns ask for a campaign brief (Ventum brand POV); everything else
  // uses the original account-intel block.
  const CAMPAIGN_FORMATS    = (window.VentumAgents?.CAMPAIGN_FORMATS)    || ['linkedin_campaign'];
  const ANNOUNCEMENT_FORMATS = (window.VentumAgents?.ANNOUNCEMENT_FORMATS) || [];
  const selectedFormats = intake.formats || [];
  const hasCampaign      = selectedFormats.some(f => CAMPAIGN_FORMATS.includes(f));
  const hasAnnouncement  = selectedFormats.some(f => ANNOUNCEMENT_FORMATS.includes(f));
  const hasAccountPiece  = selectedFormats.some(f => !CAMPAIGN_FORMATS.includes(f) && !ANNOUNCEMENT_FORMATS.includes(f));
  const proofPoints = Object.entries(BRAND.proof_points || {}).map(([k, v]) => ({ key: k, label: v }));
  const toggleProof = (k) => {
    const cur = intake.supporting_proofs || [];
    const next = cur.includes(k) ? cur.filter(x => x !== k) : [...cur, k];
    setIntake({ ...intake, supporting_proofs: next });
  };

  const toggleCompetitor = (c) => {
    const cur = intake.competitors || [];
    const next = cur.includes(c) ? cur.filter(x => x !== c) : [...cur, c];
    setIntake({ ...intake, competitors: next });
  };

  // Attachment handling.
  // Plain-text formats: read directly. PDFs: extract text via pdf.js so the
  // strategy + copy agents can actually reference the brief. Each attachment's
  // text is capped at ~32k chars (~8k tokens). buildAttachmentBlock in
  // agents.js does the final prompt-side budgeting across multiple files.
  const PER_ATTACHMENT_TEXT_LIMIT = 32000;

  const extractPdfText = async (file) => {
    if (!window.pdfjsLib) throw new Error('pdf.js not loaded');
    const buf = await file.arrayBuffer();
    const pdf = await window.pdfjsLib.getDocument({ data: buf }).promise;
    const parts = [];
    let total = 0;
    for (let i = 1; i <= pdf.numPages; i++) {
      const page = await pdf.getPage(i);
      const content = await page.getTextContent();
      const pageText = (content.items || []).map(it => it.str).join(' ');
      parts.push(pageText);
      total += pageText.length;
      // Bail early once we're well past the per-attachment limit
      if (total > PER_ATTACHMENT_TEXT_LIMIT * 1.5) break;
    }
    return parts.join('\n\n');
  };

  const onAttach = async (e) => {
    const files = Array.from(e.target.files || []);
    const next = [...(intake.attachments || [])];
    for (const file of files) {
      let text = '';
      let extractError = null;
      try {
        if (file.type.startsWith('text/') || /\.(txt|md|csv|json)$/i.test(file.name)) {
          text = await file.text();
        } else if (file.type === 'application/pdf' || /\.pdf$/i.test(file.name)) {
          text = await extractPdfText(file);
        } else {
          extractError = 'unsupported format — only text and PDF are read';
        }
      } catch (err) {
        console.error('attachment extract failed', err);
        extractError = err.message || 'extraction failed';
      }
      next.push({
        name: file.name,
        size: file.size,
        type: file.type,
        text: (text || '').slice(0, PER_ATTACHMENT_TEXT_LIMIT),
        chars: (text || '').length,
        extractError
      });
    }
    setIntake({ ...intake, attachments: next });
    e.target.value = '';
    const ok = next.filter(a => (a.text || '').length > 0).length;
    if (files.length) {
      window.__toast && window.__toast(`Read ${ok} attachment${ok === 1 ? '' : 's'} into the brief`);
    }
  };
  const removeAttach = (i) => {
    const next = (intake.attachments || []).filter((_, idx) => idx !== i);
    setIntake({ ...intake, attachments: next });
  };
  const fmtBytes = (b) => b < 1024 ? `${b}B` : b < 1024*1024 ? `${(b/1024).toFixed(1)}KB` : `${(b/1024/1024).toFixed(1)}MB`;

  return (
    <div>
      <div className="room-head">
        <div className="room-num">Step 03 of 06</div>
        <div>
          <h1>Intelligence intake</h1>
          <p className="lede">The more specific this is, the sharper the piece. Fill what you know — even partial intel beats generic. Drop in account notes, RFPs, or call transcripts at the bottom.</p>
        </div>
      </div>

      <BriefPills intake={intake} />

      {hasCampaign && (
        <div className="field-block" style={{ borderLeft: '3px solid var(--signal)', paddingLeft: 16, marginBottom: 24 }}>
          <div className="mono" style={{ fontSize: 10, letterSpacing: '0.14em', color: 'var(--signal)', textTransform: 'uppercase', marginBottom: 14 }}>Campaign brief · for the selected campaign format(s)</div>
          <div className="field-grid-2">
            <div className="field-block">
              <label>Campaign objective</label>
              <textarea
                className="textarea"
                rows={2}
                placeholder="What does the campaign drive? e.g. '10 hospitality pilot bookings by end of Q3'; 'Establish Ventum as the audit-grade EVS standard'."
                value={intake.campaign_objective || ''}
                onChange={(e) => setIntake({ ...intake, campaign_objective: e.target.value })}
              />
            </div>
            <div className="field-block">
              <label>Key message</label>
              <textarea
                className="textarea"
                rows={2}
                placeholder="The one thing they should remember. 1–2 sentences."
                value={intake.key_message || ''}
                onChange={(e) => setIntake({ ...intake, key_message: e.target.value })}
              />
            </div>
          </div>
          {selectedFormats.includes('linkedin_campaign') && (
            <div className="field-block">
              <label>LinkedIn campaign — number of posts <span className="hint">Auto lets the agent choose 3–7 by scope</span></label>
              <select
                className="input"
                value={intake.campaign_posts || 'auto'}
                onChange={(e) => setIntake({ ...intake, campaign_posts: e.target.value === 'auto' ? '' : e.target.value })}
              >
                <option value="auto">Auto — intelligent (3–7 posts)</option>
                <option value="3">3 posts</option>
                <option value="4">4 posts</option>
                <option value="5">5 posts</option>
                <option value="6">6 posts</option>
                <option value="7">7 posts</option>
              </select>
            </div>
          )}
          <div className="field-grid-2">
            <div className="field-block">
              <label>Time window & cadence</label>
              <input
                className="input"
                type="text"
                placeholder="e.g. '2 posts/week for 3 weeks', '5 posts over Q2'"
                value={intake.time_window || ''}
                onChange={(e) => setIntake({ ...intake, time_window: e.target.value })}
              />
            </div>
            <div className="field-block">
              <label>Partner / co-marketing <span className="hint">optional — leave blank for Ventum-only</span></label>
              <input
                className="input"
                type="text"
                placeholder="e.g. 'Marriott International', 'CleanRoom Coalition'"
                value={intake.partner || ''}
                onChange={(e) => setIntake({ ...intake, partner: e.target.value })}
              />
            </div>
          </div>
          <div className="field-block">
            <label>
              Supporting proof points
              <span className="hint">{(intake.supporting_proofs || []).length} selected — the campaign agent leans on these</span>
            </label>
            <div className="chip-row">
              {proofPoints.map(pp => (
                <button
                  key={pp.key}
                  className={"chip" + ((intake.supporting_proofs || []).includes(pp.key) ? " selected" : "")}
                  onClick={() => toggleProof(pp.key)}
                  title={pp.label}
                >
                  {pp.key}
                </button>
              ))}
            </div>
          </div>
        </div>
      )}

      {hasAnnouncement && (
        <div className="field-block" style={{ borderLeft: '3px solid #5b6cff', paddingLeft: 16, marginBottom: 24 }}>
          <div className="mono" style={{ fontSize: 10, letterSpacing: '0.14em', color: '#5b6cff', textTransform: 'uppercase', marginBottom: 14 }}>Partner announcement brief · for the selected announcement format(s)</div>
          <div className="field-grid-2">
            <div className="field-block">
              <label>Partner organization name</label>
              <input
                className="input"
                type="text"
                placeholder='e.g. "Marriott International", "Mayo Clinic Health System"'
                value={intake.partner_name || ''}
                onChange={(e) => setIntake({ ...intake, partner_name: e.target.value })}
              />
            </div>
            <div className="field-block">
              <label>Partner industry / vertical</label>
              <input
                className="input"
                type="text"
                placeholder='e.g. "hospitality", "acute-care healthcare", "food manufacturing"'
                value={intake.partner_industry || ''}
                onChange={(e) => setIntake({ ...intake, partner_industry: e.target.value })}
              />
            </div>
          </div>
          <div className="field-grid-2">
            <div className="field-block">
              <label>Partnership scope</label>
              <input
                className="input"
                type="text"
                placeholder='e.g. "5-property pilot starting Oct 2026", "system-wide rollout"'
                value={intake.partnership_scope || ''}
                onChange={(e) => setIntake({ ...intake, partnership_scope: e.target.value })}
              />
            </div>
            <div className="field-block">
              <label>Internal audience</label>
              <input
                className="input"
                type="text"
                placeholder='e.g. "operations leadership", "all property GMs", "all-hands"'
                value={intake.internal_audience || ''}
                onChange={(e) => setIntake({ ...intake, internal_audience: e.target.value })}
              />
            </div>
          </div>
          <div className="field-grid-2">
            <div className="field-block">
              <label>Why we chose Ventum (business case)</label>
              <textarea
                className="textarea"
                rows={3}
                placeholder='e.g. "Audit-grade documentation, residue-free for guest experience, fits our 30-minute room turn target."'
                value={intake.why_ventum || ''}
                onChange={(e) => setIntake({ ...intake, why_ventum: e.target.value })}
              />
            </div>
            <div className="field-block">
              <label>What changes for internal teams</label>
              <textarea
                className="textarea"
                rows={3}
                placeholder='e.g. "Housekeeping picks up V-Wipes for daily turns; central training Q4; new audit log in PMS."'
                value={intake.what_changes || ''}
                onChange={(e) => setIntake({ ...intake, what_changes: e.target.value })}
              />
            </div>
          </div>
          <div className="field-grid-2">
            <div className="field-block">
              <label>Rollout timeline</label>
              <input
                className="input"
                type="text"
                placeholder='e.g. "Pilot Nov 2026 (5 properties), Phase 1 Q1 2027 (Eastern region)"'
                value={intake.rollout_timeline || ''}
                onChange={(e) => setIntake({ ...intake, rollout_timeline: e.target.value })}
              />
            </div>
            <div className="field-block">
              <label>Partner executive quote attribution <span className="hint">optional</span></label>
              <input
                className="input"
                type="text"
                placeholder='e.g. "Avery Chen, VP Operations"'
                value={intake.partner_quote_attribution || ''}
                onChange={(e) => setIntake({ ...intake, partner_quote_attribution: e.target.value })}
              />
            </div>
          </div>
          <div className="field-block">
            <label>Co-branding level <span className="hint">how prominent Ventum should appear</span></label>
            <div className="chip-row">
              {[
                { key: 'ventum_prominent',     label: 'Prominent — name Ventum throughout' },
                { key: 'ventum_light',         label: 'Light — name once or twice, then protocol vocabulary' },
                { key: 'ventum_mentioned_once',label: 'Mentioned once — partner-led, Ventum named only as vendor' }
              ].map(opt => (
                <button
                  key={opt.key}
                  className={"chip" + ((intake.cobranding_level || 'ventum_light') === opt.key ? " selected" : "")}
                  onClick={() => setIntake({ ...intake, cobranding_level: opt.key })}
                >
                  {opt.label}
                </button>
              ))}
            </div>
          </div>
          <div className="field-block">
            <label>Rollout pattern <span className="hint">drives the All-hands rollout timeline shape</span></label>
            <div className="chip-row">
              {[
                { key: 'immediate',      label: 'Immediate — single phase, all at once' },
                { key: 'pilot_full',     label: 'Pilot → Full rollout (2 phases)' },
                { key: 'pilot_scale',    label: 'Pilot → Scale → Validate (3 phases · default)' },
                { key: 'multi_region',   label: 'Multi-region (3–5 phases by geography)' },
                { key: 'custom',         label: 'Custom — let the agent infer from the timeline text' }
              ].map(opt => (
                <button
                  key={opt.key}
                  className={"chip" + ((intake.rollout_pattern || 'pilot_scale') === opt.key ? " selected" : "")}
                  onClick={() => setIntake({ ...intake, rollout_pattern: opt.key })}
                >
                  {opt.label}
                </button>
              ))}
            </div>
          </div>
        </div>
      )}

      {hasAccountPiece && (
        <>
          <div className="field-grid-2">
            <div className="field-block">
              <label>Current disinfection practice</label>
              <textarea
                className="textarea"
                placeholder="What are they doing today? Manual wipe-down? UV-C? A specific product line?"
                value={intake.current_practice || ''}
                onChange={(e) => setIntake({ ...intake, current_practice: e.target.value })}
              />
              {intelPrompt && (
                <div className="intel-prompt">
                  <div className="ico">i</div>
                  <div>{intelPrompt}</div>
                </div>
              )}
            </div>
            <div className="field-block">
              <label>Facility layout / footprint</label>
              <textarea
                className="textarea"
                placeholder="Square footage, room counts, classification (cleanroom class, exam room, suite, etc.)"
                value={intake.facility_layout || ''}
                onChange={(e) => setIntake({ ...intake, facility_layout: e.target.value })}
              />
            </div>
          </div>

          <div className="field-grid-2">
            <div className="field-block">
              <label>Usage / volume pattern</label>
              <textarea
                className="textarea"
                placeholder="Frequency, throughput, peak vs trough. e.g. '40 OR turnovers/day', '600 guest rooms × 70% occ'."
                value={intake.usage_volume || ''}
                onChange={(e) => setIntake({ ...intake, usage_volume: e.target.value })}
              />
            </div>
            <div className="field-block">
              <label>Protocol gaps & pressures</label>
              <textarea
                className="textarea"
                placeholder="Audit findings, complaints, recent incidents, regulatory pressure, internal mandates."
                value={intake.protocol_gaps || ''}
                onChange={(e) => setIntake({ ...intake, protocol_gaps: e.target.value })}
              />
            </div>
          </div>

          <div className="field-block">
            <label>
              Known incumbents / competitors
              <span className="hint">{(intake.competitors || []).length} selected · add more in notes if needed</span>
            </label>
            <div className="chip-row">
              {commonCompetitors.map(c => (
                <button
                  key={c}
                  className={"chip" + ((intake.competitors || []).includes(c) ? " selected" : "")}
                  onClick={() => toggleCompetitor(c)}
                >
                  {c}
                </button>
              ))}
              {commonCompetitors.length === 0 && (
                <span className="muted" style={{ fontSize: 13 }}>Pick a vertical in Step 02 to see common incumbents.</span>
              )}
            </div>
          </div>
        </>
      )}

      <div className="field-block">
        <label>Additional notes <span className="hint">events, named opportunities, angle hints, anything else</span></label>
        <textarea
          className="textarea"
          rows={3}
          placeholder="e.g. Tied to HIMSS 2026 booth. Account is renewing in Q3. CFO is asking for ROI proof."
          value={intake.notes || ''}
          onChange={(e) => setIntake({ ...intake, notes: e.target.value })}
        />
      </div>

      <div className="field-block">
        <label>Attachments <span className="hint">RFPs, marketing briefs, call notes — PDF and plain-text are read into the brief</span></label>
        <input type="file" multiple accept=".pdf,.txt,.md,.csv,.json,text/*,application/pdf" onChange={onAttach} style={{ fontSize: 12, color: 'var(--ink-60)' }} />
        {(intake.attachments || []).length > 0 && (() => {
          const all = intake.attachments || [];
          const empty = all.filter(a => (a?.text || '').length === 0).length;
          return (
            <>
              {empty > 0 && (
                <div style={{ marginTop: 8, padding: '8px 12px', background: '#FDECEA', border: '1px solid #F1B8B1', borderRadius: 2, fontSize: 12.5, color: '#A33027', lineHeight: 1.45 }}>
                  <strong>{empty} attachment{empty === 1 ? '' : 's'} not parsed yet.</strong> If it was uploaded before the PDF reader loaded, the agents won't see its content. Click <strong>×</strong> on the row and re-upload it now.
                </div>
              )}
              <div className="attachments">
                {all.map((f, i) => {
                  const ext = (f.name.split('.').pop() || '').toUpperCase().slice(0,4);
                  const ok = (f.text || '').length > 0;
                  return (
                    <div key={i} className="attach-row">
                      <span className="ext" style={!ok ? { background: '#FDECEA', color: '#A33027', borderColor: '#F1B8B1' } : {}}>{ext}</span>
                      <span className="nm">
                        {f.name}{' '}
                        {ok && <span className="muted" style={{ fontSize: 11 }}>· {(f.chars || (f.text || '').length).toLocaleString()} chars read</span>}
                        {!ok && <span style={{ fontSize: 11, color: '#A33027' }}>· {f.extractError || 'not parsed — re-upload to fix'}</span>}
                      </span>
                      <span className="sz">{fmtBytes(f.size)}</span>
                      <button
                        className="x"
                        onClick={() => setPreviewAttachment(f)}
                        title={ok ? 'Preview the text the agents will see' : 'Show what (if anything) was extracted'}
                        style={{ marginRight: 4, fontSize: 11, padding: '0 6px', border: '1px solid var(--ink-20)', borderRadius: 2, background: 'transparent', color: 'var(--ink-60)', cursor: 'pointer', lineHeight: '20px' }}
                      >Preview</button>
                      <button className="x" onClick={() => removeAttach(i)}>×</button>
                    </div>
                  );
                })}
              </div>
            </>
          );
        })()}
      </div>

      {/* Attachment text preview modal */}
      {previewAttachment && ReactDOM.createPortal(
        <div
          className="modal-mask"
          onClick={(e) => { if (e.target === e.currentTarget) setPreviewAttachment(null); }}
        >
          <div className="modal" style={{ width: 'min(820px, 92vw)', maxHeight: 'calc(100vh - 96px)', display: 'flex', flexDirection: 'column' }}>
            <div className="modal-head">
              <div>
                <h3 style={{ margin: 0 }}>{previewAttachment.name}</h3>
                <span className="mono" style={{ fontSize: 10, color: 'var(--ink-40)', letterSpacing: '0.12em', textTransform: 'uppercase' }}>
                  {((previewAttachment.chars != null ? previewAttachment.chars : (previewAttachment.text || '').length) || 0).toLocaleString()} chars extracted · {(previewAttachment.text || '').length.toLocaleString()} sent to agent
                </span>
              </div>
              <button className="drawer-close" onClick={() => setPreviewAttachment(null)}>×</button>
            </div>
            <div className="modal-body" style={{ padding: '14px 18px', overflowY: 'auto', fontFamily: 'var(--mono)', fontSize: 12, lineHeight: 1.55, color: 'var(--ink-80)', whiteSpace: 'pre-wrap', wordBreak: 'break-word' }}>
              {previewAttachment.text || '(no text extracted)'}
            </div>
          </div>
        </div>,
        document.body
      )}
    </div>
  );
}

// ============================================================
// ROOM 4 — Strategy brief (Strategy Agent)
// ============================================================
function Room4_Strategy({ intake, strategy, setStrategy, status, strategyStatus, strategyError, onRun, runStrategy, strategyStale, streamChars = 0 }) {
  const BRAND = window.VENTUM_BRAND;
  const pillarName = strategy?.pillar ? (BRAND.pillars[strategy.pillar]?.name || strategy.pillar) : '';
  // Accept both old and new prop names so the room is back-compat
  const effectiveStatus = strategyStatus !== undefined ? strategyStatus : status;
  const effectiveRun = runStrategy || onRun;
  const ctaLabel = strategy?.recommended_cta ? BRAND.cta_pool[strategy.recommended_cta] : '';

  return (
    <div>
      <div className="room-head">
        <div className="room-num">Step 04 of 06</div>
        <div>
          <h1>Strategy brief</h1>
          <p className="lede">The Strategy Agent reads your intake and proposes the angle: which pillar leads, which proof points to lean on, what tension this piece resolves. Approve or rewrite before we generate copy.</p>
        </div>
      </div>

      <BriefPills intake={intake} />

      {/* Surface to the operator that uploaded briefs WILL be used as the
          primary source. Without this confirmation it's easy to assume the
          PDF got captured but ignored. */}
      {(() => {
        const usable = (intake.attachments || []).filter(a => (a?.text || '').length > 0);
        if (!usable.length) return null;
        const total = usable.reduce((sum, a) => sum + Math.min(a.text.length, 32000), 0);
        return (
          <div style={{ display: 'flex', alignItems: 'center', gap: 12, padding: '10px 14px', marginBottom: 18, background: '#EEF7F3', border: '1px solid #C7E5D6', borderRadius: 3 }}>
            <span style={{ width: 6, height: 6, borderRadius: 3, background: '#1F5A48', display: 'inline-block' }} />
            <span style={{ fontSize: 12.5, color: '#1F5A48', lineHeight: 1.4 }}>
              <strong>{usable.length} attachment{usable.length === 1 ? '' : 's'}</strong> ({total.toLocaleString()} chars) will be fed to the Strategy + Copy agents as the primary brief. {usable.map(a => a.name).join(' · ')}
            </span>
          </div>
        );
      })()}

      <div className="agent-card">
        <div className="agent-head">
          <div className="agent-icon">SA</div>
          <div>
            <div className="agent-name">Strategy Agent</div>
            <div className="agent-title">Synthesizes intake → angle, pillar, proof points, tone notes</div>
          </div>
          <div style={{ display: 'flex', alignItems: 'center', gap: 10, whiteSpace: 'nowrap' }}>
            <div className={"agent-status " + (effectiveStatus === 'live' ? 'live' : effectiveStatus === 'done' ? 'done' : effectiveStatus === 'error' ? 'error' : '')}>
              <span className="pulse"></span>
              <span>{effectiveStatus === 'live' ? 'Running' : effectiveStatus === 'done' ? 'Ready' : effectiveStatus === 'error' ? 'Error' : 'Idle'}</span>
            </div>
            {strategy && effectiveStatus !== 'live' && (
              <button
                onClick={effectiveRun}
                title="Re-run the Strategy Agent against the current intake"
                className="mono"
                style={{ padding: '6px 10px', background: 'transparent', color: 'var(--ink-60)', border: '1px solid var(--ink-20)', borderRadius: 2, fontSize: 10, letterSpacing: '0.14em', textTransform: 'uppercase', cursor: 'pointer', whiteSpace: 'nowrap', lineHeight: 1 }}
              >↻ Re-run</button>
            )}
          </div>
        </div>

        {!strategy && status !== 'live' && (
          <div style={{ paddingTop: 6, color: 'var(--ink-60)', fontSize: 14, lineHeight: 1.55 }}>
            <p>Click "Run Strategy Agent" to synthesize your intake into a working brief.</p>
            <button className="btn-primary" style={{ marginTop: 14 }} onClick={effectiveRun}>
              <span>Run Strategy Agent</span>
              <span className="arrow" />
            </button>
          </div>
        )}

        {strategy && strategyStale && effectiveStatus !== 'live' && (
          <div style={{ marginTop: 14, padding: '12px 14px', background: 'rgba(255,176,40,0.08)', borderLeft: '3px solid #d99020', borderRadius: 3, display: 'flex', alignItems: 'center', gap: 12 }}>
            <div style={{ flex: 1, fontSize: 13, color: 'var(--ink-80)' }}>
              <strong style={{ color: 'var(--ink-100)' }}>Intake has changed since this strategy was generated.</strong> The current brief on screen reflects the previous intake. Re-run to refresh.
            </div>
            <button className="btn-primary" style={{ padding: '8px 14px', fontSize: 12 }} onClick={effectiveRun}>
              <span>Re-run Strategy</span>
            </button>
          </div>
        )}

        {effectiveStatus === 'live' && (
          <div style={{ display: 'flex', alignItems: 'center', gap: 12, color: 'var(--ink-60)', fontSize: 14 }}>
            <span className="spinner" />
            Reading intake, weighing pillars, drafting the angle…
            {streamChars > 0 && (
              <span className="mono" style={{ marginLeft: 'auto', fontSize: 10, letterSpacing: '0.12em', color: 'var(--teal)', textTransform: 'uppercase' }}>
                {streamChars.toLocaleString()} chars streamed
              </span>
            )}
          </div>
        )}

        {strategy && status !== 'live' && (
          <div className="agent-body">
            <div className="kv">
              <span className="k">Angle</span>
              <span className="v"><EditableField value={strategy.angle} onChange={(v) => setStrategy({ ...strategy, angle: v })} multiline /></span>
            </div>
            <div className="kv">
              <span className="k">Lead pillar</span>
              <span className="v">
                <select className="select" style={{ maxWidth: 360 }} value={strategy.pillar || ''} onChange={(e) => setStrategy({ ...strategy, pillar: e.target.value })}>
                  {Object.entries(BRAND.pillars).map(([k,v]) => <option key={k} value={k}>{v.name}</option>)}
                </select>
                <span className="muted" style={{ marginLeft: 10, fontSize: 12 }}>{strategy.pillar_rationale}</span>
              </span>
            </div>
            <div className="kv">
              <span className="k">Proof points</span>
              <span className="v" style={{ display: 'flex', flexWrap: 'wrap', gap: 6 }}>
                {Object.entries(BRAND.proof_points).map(([k,v]) => {
                  const on = (strategy.proof_points || []).includes(k);
                  return (
                    <button
                      key={k}
                      className={"chip" + (on ? " selected" : "")}
                      onClick={() => {
                        const cur = strategy.proof_points || [];
                        setStrategy({ ...strategy, proof_points: on ? cur.filter(x => x !== k) : [...cur, k] });
                      }}
                      title={v}
                    >
                      {k}
                    </button>
                  );
                })}
              </span>
            </div>
            <div className="kv">
              <span className="k">Tension</span>
              <span className="v"><EditableField value={strategy.tension} onChange={(v) => setStrategy({ ...strategy, tension: v })} multiline /></span>
            </div>
            <div className="kv">
              <span className="k">Tone notes</span>
              <span className="v"><EditableField value={strategy.tone_notes} onChange={(v) => setStrategy({ ...strategy, tone_notes: v })} /></span>
            </div>
            <div className="kv">
              <span className="k">Recommended CTA</span>
              <span className="v">
                <select className="select" style={{ maxWidth: 360 }} value={strategy.recommended_cta || ''} onChange={(e) => setStrategy({ ...strategy, recommended_cta: e.target.value })}>
                  {Object.entries(BRAND.cta_pool).map(([k,v]) => <option key={k} value={k}>{v}</option>)}
                </select>
              </span>
            </div>
            <div className="kv">
              <span className="k">Must address</span>
              <span className="v" style={{ display: 'block', minWidth: 0 }}>
                {(strategy.must_address || []).map((m, i) => (
                  <div key={i} style={{ marginBottom: 6 }}>
                    <EditableField multiline value={m} onChange={(v) => {
                      const next = [...(strategy.must_address || [])];
                      next[i] = v;
                      setStrategy({ ...strategy, must_address: next });
                    }} />
                  </div>
                ))}
              </span>
            </div>
            <div style={{ marginTop: 12, paddingTop: 14, borderTop: '1px solid var(--ink-10)', display: 'flex', gap: 10 }}>
              <button className="btn-ghost" onClick={effectiveRun}>Re-run agent</button>
            </div>
          </div>
        )}

        {effectiveStatus === 'error' && (
          <div className="error-card">
            <div className="error-card__head">
              <span className="error-card__dot" />
              <span className="error-card__title">Strategy Agent failed</span>
            </div>
            <p className="error-card__body">{strategyError?.message || 'Something went wrong. Retry the agent or proceed without — generation will fall back to vertical defaults.'}</p>
            <div className="error-card__actions">
              <button className="btn-primary" onClick={effectiveRun}>↻ Retry</button>
            </div>
            {strategyError?.technical && (
              <details className="error-card__details">
                <summary>Technical details</summary>
                <code>{strategyError.technical}</code>
              </details>
            )}
          </div>
        )}
      </div>
    </div>
  );
}

// ---- shared bits ----
function BriefPills({ intake }) {
  const BRAND = window.VENTUM_BRAND;
  const productKeys = Array.isArray(intake.products) && intake.products.length
    ? intake.products
    : (intake.product ? [intake.product] : []);
  const productLabel = productKeys.length
    ? productKeys.map(k => BRAND.products[k]?.name || k).join(' + ')
    : null;
  const framingLabel = intake.subject_framing && intake.subject_framing !== 'product'
    ? (intake.subject_framing[0].toUpperCase() + intake.subject_framing.slice(1) + '-led')
    : null;
  const items = [
    ['Goal',     intake.goal],
    ['Audience', intake.audience_type ? BRAND.audience_types[intake.audience_type]?.name : null],
    ['Vertical', intake.vertical ? BRAND.verticals[intake.vertical]?.name : null],
    ['Persona',  intake.persona],
    ['Products', productLabel],
    ['Framing',  framingLabel],
    ['Account',  intake.account_name],
    ['Formats',  (intake.formats || []).length ? `${(intake.formats || []).length} selected` : null]
  ].filter(([_,v]) => v);
  if (items.length === 0) return null;
  return (
    <div className="brief-pill-row">
      {items.map(([k,v]) => (
        <span key={k} className="brief-pill">
          <span className="pill-k">{k}</span>
          <span className="pill-v">{v}</span>
        </span>
      ))}
    </div>
  );
}

function EditableField({ value, onChange, multiline }) {
  const [v, setV] = rUseState(value || '');
  const ref = rUseRef(null);
  rUseEffect(() => { setV(value || ''); }, [value]);
  // Auto-grow the textarea so long Strategy bullets and tone notes never get clipped.
  rUseEffect(() => {
    if (multiline && ref.current) {
      ref.current.style.height = 'auto';
      ref.current.style.height = ref.current.scrollHeight + 'px';
    }
  }, [v, multiline]);
  const Tag = multiline ? 'textarea' : 'input';
  return (
    <Tag
      ref={ref}
      className={multiline ? 'textarea' : 'input'}
      style={{ background: 'transparent', border: '1px solid transparent', padding: '4px 6px', fontSize: 14, lineHeight: 1.5, width: '100%', resize: multiline ? 'vertical' : undefined, overflow: multiline ? 'hidden' : undefined, minHeight: multiline ? 44 : undefined }}
      value={v}
      onChange={(e) => setV(e.target.value)}
      onBlur={() => onChange(v)}
      rows={multiline ? 2 : undefined}
    />
  );
}

Object.assign(window, { Room1_Format, Room2_Audience, Room3_Intel, Room4_Strategy, BriefPills, EditableField });
