> ## Documentation Index
> Fetch the complete documentation index at: https://hc.starbridge.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# All Starbridge Data

> Complete reference of all buyer attributes and field metadata available in Starbridge.

export const FieldMetadataTable = ({sectionBlurbs = {}}) => {
  const [data, setData] = useState([]);
  const [loading, setLoading] = useState(true);
  const [selectedBuyerType, setSelectedBuyerType] = useState('All');
  const ACCOUNTS_CATEGORIES = ['BuyerAttribute', 'OpportunityType', 'Feature', 'WebAgentTemplates', 'AI Analysis'];
  const CONTACTS_CATEGORIES = ['Contact'];
  const JOB_CHANGES_CATEGORIES = ['Job Change'];
  const COMPETITORS_CATEGORIES = ['Contract', 'Purchase Order'];
  const CONFERENCES_CATEGORIES = ['Conference'];
  const RFPS_CATEGORIES = ['RFPs'];
  const MEETING_STRATEGIC_CATEGORIES = ['Meeting Minutes'];
  const BUDGET_NOTE = 'Budget data comes from research agents that find budget URLs and custom extractors that parse line items and amounts.';
  const PROCUREMENT_NOTE = 'Procurement notes summarize sole source limits, cooperative usage, and reseller patterns to help navigate procurement.';
  const CUSTOM_WEB_NOTE = 'Custom web data uses AI web agents to extract structured signals from public websites based on natural-language prompts.';
  const BUDGET_FIELD_KEYS = new Set(['BudgetAmount', 'BudgetSbita', 'BudgetUrl', 'BudgetLatestYear', 'BudgetConfidence', 'BudgetChangeAnalysisSourceUrls']);
  const PROCUREMENT_FIELD_KEYS = new Set(['ProcurementHellScore', 'ProcurementHellSummary', 'AngelProcurementSummary', 'HigherEdProcurementThresholds']);
  useEffect(() => {
    fetch('https://dashboard.starbridge.ai/api/public/doc/attributes').then(res => res.json()).then(setData).finally(() => setLoading(false));
  }, []);
  if (loading) {
    return <div style={{
      padding: '20px',
      textAlign: 'center'
    }}>Loading field metadata...</div>;
  }
  const normalizeCountries = value => {
    if (!value) return [];
    return value.split(',').map(country => country.trim()).filter(Boolean);
  };
  const isUsSource = source => {
    if (!source || !source.country) return true;
    return normalizeCountries(source.country).includes('US');
  };
  const UI_FIELD_LABELS_BY_CATEGORY = {
    Contact: {
      'BuyerContactFull.name': 'Name',
      'BuyerContactFull.title': 'Title',
      'BuyerContactFull.email': 'Email',
      'BuyerContactFull.phone': 'Phone',
      'BuyerContactFull.department': 'Department',
      'BuyerContactFull.role': 'Role'
    },
    'Job Change': {
      'jobChange:type': 'Type',
      'jobChange:subType': 'Subtype',
      'jobChange:summary': 'Summary',
      'jobChange:effective_date': 'Effective Date',
      'jobChange:announcementDate': 'Announced Date',
      'jobChange:effectiveJobTitle': 'Job Title',
      'jobChange:previousJobTitle': 'Previous Job Title',
      'jobChange:sources': 'Sources',
      'jobChange:associatedContact': 'Contact'
    },
    Contract: {
      'Opportunity.title': 'Title',
      'Opportunity.fromDate': 'Effective Date',
      'Opportunity.untilDate': 'Expiration Date',
      'Opportunity.status': 'Status',
      'Opportunity.purchaseAmount': 'Total Amount',
      'Opportunity.buyer.name': 'Buyer',
      'Opportunity.buyerName': 'Buyer',
      'Opportunity.seller.name': 'Seller',
      'Opportunity.sellerName': 'Seller'
    },
    'Purchase Order': {
      'PurchaseOrder.name': 'Name',
      'PurchaseOrder.fromDate': 'Effective Date',
      'PurchaseOrder.contractRenewalDate': 'Expiration Date',
      'PurchaseOrder.quantity': 'Quantity',
      'PurchaseOrder.unitPrice': 'Unit Price',
      'PurchaseOrder.discount': 'Discount',
      'PurchaseOrder.purchaseAmount': 'Total Amount',
      'PurchaseOrder.sellerName': 'Seller',
      'PurchaseOrder.buyer.name': 'Buyer',
      'PurchaseOrder.buyerName': 'Buyer',
      'PurchaseOrder.seller.name': 'Seller'
    },
    Conference: {
      'Conference.name': 'Name',
      'Conference.description': 'Description',
      'Conference.startDate': 'Start date',
      'Conference.endDate': 'End date',
      'Conference.costEstimatedLow': 'Estimated cost range (low)',
      'Conference.costEstimatedHigh': 'Estimated cost range (high)',
      'Conference.city': 'City',
      'Conference.stateCode': 'State',
      'Conference.verticals': 'Buyer Types',
      'Conference.url': 'URL',
      'Conference.expectedAttendeeCount': 'Expected attendance',
      'Conference.estimatedNumVendors': 'Est. vendor count',
      'Conference.expectedPersonas': 'Expected personas / titles',
      'Conference.vendorSummary': 'Vendors Summary',
      'Conference.detectedVendors': 'Vendor List'
    },
    RFPs: {
      'Rfp.name': 'Title',
      'Rfp.summary': 'Summary',
      'Rfp.sourceUrl': 'Source',
      'Rfp.release': 'Release',
      'Rfp.close': 'Close',
      'Rfp.bidInstruction': 'Bid Instructions',
      'Rfp.buyer.name': 'Buyer',
      'Rfp.buyerName': 'Buyer',
      'Opportunity.buyer.name': 'Buyer',
      'Opportunity.buyerName': 'Buyer'
    },
    'Meeting Minutes': {
      'Opportunity.title': 'Title',
      'Opportunity.summary': 'Summary',
      'Opportunity.fromDate': 'Posted',
      'BuyerFile.name': 'Files',
      'Opportunity.buyer.name': 'Buyer',
      'Opportunity.buyerName': 'Buyer'
    },
    'Strategic Plan': {
      'Opportunity.title': 'Title',
      'Opportunity.summary': 'Summary',
      'Opportunity.fromDate': 'Posted',
      'BuyerFile.name': 'Files',
      'Opportunity.buyer.name': 'Buyer',
      'Opportunity.buyerName': 'Buyer'
    }
  };
  const BUYER_SELLER_CATEGORIES = ['Contract', 'Purchase Order', 'RFPs', 'Meeting Minutes', 'Strategic Plan'];
  const normalizeKey = value => {
    if (!value) return '';
    return String(value).toLowerCase();
  };
  const isBuyerNameField = field => {
    if (!BUYER_SELLER_CATEGORIES.includes(field.category)) return false;
    const key = normalizeKey(field.key);
    if (!key.includes('buyer')) return false;
    if (key.includes('buyerfile') || key.includes('parentbuyer')) return false;
    return key.endsWith('name') || key.endsWith('.name');
  };
  const isSellerNameField = field => {
    if (!BUYER_SELLER_CATEGORIES.includes(field.category)) return false;
    const key = normalizeKey(field.key);
    if (!key.includes('seller')) return false;
    return key.endsWith('name') || key.endsWith('.name');
  };
  const dataForDisplay = data.map(field => {
    if (!field.sources || field.sources.length === 0) return field;
    const sources = field.sources.filter(isUsSource);
    return {
      ...field,
      sources
    };
  }).filter(field => {
    const hadSources = field.sources && field.sources.length > 0;
    return !hadSources || field.sources.length > 0;
  });
  const getBuyerTypesArray = field => {
    if (!field) return [];
    if (Array.isArray(field.relatedToBuyerTypes)) {
      return field.relatedToBuyerTypes;
    }
    const sources = field.sources;
    if (!sources || sources.length === 0) return [];
    const types = sources.flatMap(source => source.relatedToBuyerTypes || []).filter(Boolean);
    return Array.from(new Set(types));
  };
  const matchesBuyerType = field => {
    if (selectedBuyerType === 'All') return true;
    const buyerTypes = getBuyerTypesArray(field);
    if (buyerTypes.length === 0) return true;
    return buyerTypes.includes(selectedBuyerType);
  };
  const accountsSourceData = dataForDisplay.filter(f => ACCOUNTS_CATEGORIES.includes(f.category));
  const availableBuyerTypes = Array.from(new Set(accountsSourceData.flatMap(field => getBuyerTypesArray(field)))).sort((a, b) => a.localeCompare(b));
  const sortByName = (a, b) => {
    const aName = a.label || a.key;
    const bName = b.label || b.key;
    return aName.localeCompare(bName);
  };
  const sortByCategoryThenName = (a, b) => {
    const categoryOrder = a.category.localeCompare(b.category);
    if (categoryOrder !== 0) return categoryOrder;
    return sortByName(a, b);
  };
  const ACCOUNTS_CATEGORY_ORDER = ['BuyerAttribute', 'Feature', 'WebAgentTemplates', 'OpportunityType', 'AI Analysis'];
  const sortAccounts = (a, b) => {
    const aIdx = ACCOUNTS_CATEGORY_ORDER.indexOf(a.category);
    const bIdx = ACCOUNTS_CATEGORY_ORDER.indexOf(b.category);
    const aOrder = aIdx === -1 ? 999 : aIdx;
    const bOrder = bIdx === -1 ? 999 : bIdx;
    if (aOrder !== bOrder) return aOrder - bOrder;
    return sortByName(a, b);
  };
  const filterToUiFields = fields => fields.filter(field => {
    const labelMap = UI_FIELD_LABELS_BY_CATEGORY[field.category];
    if (!labelMap) return true;
    return Boolean(labelMap[field.key]) || isBuyerNameField(field) || isSellerNameField(field);
  });
  const accountsData = accountsSourceData.filter(matchesBuyerType).sort(sortAccounts);
  const contactsData = filterToUiFields(dataForDisplay.filter(f => CONTACTS_CATEGORIES.includes(f.category))).sort(sortByCategoryThenName);
  const jobChangesData = filterToUiFields(dataForDisplay.filter(f => JOB_CHANGES_CATEGORIES.includes(f.category))).sort(sortByCategoryThenName);
  const competitorsData = filterToUiFields(dataForDisplay.filter(f => COMPETITORS_CATEGORIES.includes(f.category))).sort(sortByCategoryThenName);
  const conferencesData = filterToUiFields(dataForDisplay.filter(f => CONFERENCES_CATEGORIES.includes(f.category))).sort(sortByCategoryThenName);
  const rfpsData = filterToUiFields(dataForDisplay.filter(f => RFPS_CATEGORIES.includes(f.category))).sort(sortByCategoryThenName);
  const meetingStrategicData = filterToUiFields(dataForDisplay.filter(f => MEETING_STRATEGIC_CATEGORIES.includes(f.category))).sort(sortByCategoryThenName);
  const formatDate = value => {
    if (!value) return null;
    const [date] = value.split('T');
    return date;
  };
  const getSourceValues = (sources, selector) => {
    if (!sources || sources.length === 0) return [];
    const values = sources.map(selector).filter(Boolean);
    return Array.from(new Set(values));
  };
  const getLastUpdatedValues = (sources, category) => {
    const values = getSourceValues(sources, source => formatDate(source.lastUpdatedAt));
    if (values.length === 0 && category === 'BuyerAttribute') return ['2026'];
    return values;
  };
  const renderSourceValues = values => {
    if (!values || values.length === 0) return '—';
    return values.map((value, index) => <div key={`${value}-${index}`}>{value}</div>);
  };
  const formatCamelCase = value => {
    if (!value) return value;
    return value.replace(/([a-z0-9])([A-Z])/g, '$1 $2').replace(/([A-Z])([A-Z][a-z])/g, '$1 $2');
  };
  const formatDisplayLabel = field => {
    if (field.category === 'BuyerAttribute') return field.label;
    return formatCamelCase(field.label || field.key);
  };
  const formatCategoryLabel = category => formatCamelCase(category);
  const formatFieldKey = key => {
    if (!key || typeof key !== 'string') return key;
    return key.includes('.') ? key.split('.').pop() : key;
  };
  const getUiFieldLabel = field => {
    const labelMap = UI_FIELD_LABELS_BY_CATEGORY[field.category];
    if (labelMap?.[field.key]) return labelMap[field.key];
    if (isBuyerNameField(field)) return 'Buyer';
    if (isSellerNameField(field)) return 'Seller';
    return formatFieldKey(field.key);
  };
  const getBuyerTypes = field => {
    const unique = getBuyerTypesArray(field);
    if (unique.length === 0) return 'All';
    return unique.map(t => formatCamelCase(t)).join(', ');
  };
  const getAccountsNotes = field => {
    const notes = [];
    if (field.notesOnSourceData) notes.push(field.notesOnSourceData);
    if (BUDGET_FIELD_KEYS.has(field.key)) notes.push(BUDGET_NOTE);
    if (PROCUREMENT_FIELD_KEYS.has(field.key)) notes.push(PROCUREMENT_NOTE);
    if (field.category === 'WebAgentTemplates') notes.push(CUSTOM_WEB_NOTE);
    return Array.from(new Set(notes));
  };
  const thStyle = {
    textAlign: 'left',
    padding: '12px',
    fontWeight: 700,
    fontSize: '13px',
    color: 'var(--sb-heading)',
    backgroundColor: 'var(--sb-surface)',
    position: 'sticky',
    top: 0,
    zIndex: 2,
    borderBottom: '1px solid var(--sb-row-border)'
  };
  const tdStyle = {
    padding: '12px',
    color: 'var(--sb-muted)',
    fontSize: '13px',
    verticalAlign: 'top'
  };
  const navLinkStyle = {
    display: 'inline-block',
    padding: '10px 20px',
    backgroundColor: 'var(--sb-heading)',
    color: 'var(--sb-surface)',
    borderRadius: '8px',
    textDecoration: 'none',
    fontSize: '14px',
    fontWeight: 600,
    transition: 'opacity 0.2s',
    border: '1px solid var(--sb-border)'
  };
  const tableWrapperStyle = {
    width: '100%',
    overflowX: 'auto',
    border: '1px solid var(--sb-border)',
    borderRadius: '10px',
    backgroundColor: 'var(--sb-surface)',
    boxShadow: 'var(--sb-shadow)'
  };
  const renderAccountsTable = (data, id, title, blurb) => <div>
      <h2 id={id} style={{
    fontSize: '20px',
    fontWeight: 700,
    color: 'var(--sb-heading)',
    margin: '48px 0 16px',
    scrollMarginTop: '80px'
  }}>{title}</h2>
      {blurb && <div className="sb-section-blurb">{blurb}</div>}
      <div className="sb-buyer-filter" style={{
    display: 'flex',
    alignItems: 'center',
    gap: '12px',
    marginBottom: '16px',
    flexWrap: 'wrap'
  }}>
        <label htmlFor="buyer-type-filter" className="sb-buyer-filter-label">
          Filter by Buyer Type:
        </label>
        <select id="buyer-type-filter" className="sb-buyer-filter-select" value={selectedBuyerType} onChange={e => setSelectedBuyerType(e.target.value)} style={{
    appearance: 'auto',
    WebkitAppearance: 'menulist',
    padding: '10px 36px 10px 14px',
    border: '2px solid var(--sb-border)',
    borderRadius: '10px',
    backgroundColor: 'var(--sb-surface)',
    color: 'var(--sb-text)',
    fontSize: '15px',
    fontWeight: 600,
    minWidth: '240px',
    cursor: 'pointer',
    boxShadow: '0 1px 3px rgba(0,0,0,0.08)'
  }}>
          <option value="All">All buyer types</option>
          {availableBuyerTypes.map(type => <option key={type} value={type}>
              {formatCamelCase(type)}
            </option>)}
        </select>
      </div>
      {selectedBuyerType !== 'All' && <p className="sb-buyer-filter-hint">
          Showing Accounts for <strong>{formatCamelCase(selectedBuyerType)}</strong> plus fields marked as all buyer types.
        </p>}
      <div style={{
    borderTop: '1px solid var(--sb-border)',
    margin: '8px 0 16px'
  }} />
      <div style={tableWrapperStyle}>
        <table className="sb-field-metadata-table" style={{
    width: '100%',
    minWidth: '1100px',
    borderCollapse: 'separate',
    borderSpacing: 0,
    tableLayout: 'auto'
  }}>
          <thead>
            <tr>
              <th style={{
    ...thStyle,
    minWidth: '190px'
  }}>Category</th>
              <th style={thStyle}>Name</th>
              <th style={{
    ...thStyle,
    minWidth: '260px'
  }}>Description</th>
              <th style={{
    ...thStyle,
    minWidth: '110px'
  }}>Credits</th>
              <th style={{
    ...thStyle,
    minWidth: '100px'
  }}>Data Type</th>
              <th style={{
    ...thStyle,
    minWidth: '180px'
  }}>Example</th>
              <th style={{
    ...thStyle,
    minWidth: '140px'
  }}>Buyer Types</th>
              <th style={{
    ...thStyle,
    minWidth: '160px'
  }}>Data Source</th>
              <th style={{
    ...thStyle,
    minWidth: '120px'
  }}>When We Last Updated</th>
              <th style={{
    ...thStyle,
    minWidth: '140px'
  }}>Refresh Cadence</th>
              <th style={{
    ...thStyle,
    minWidth: '220px'
  }}>Data Source Notes</th>
            </tr>
          </thead>
          <tbody>
            {data.map((field, idx) => {
    const rowBg = idx % 2 === 0 ? 'var(--sb-row-even, transparent)' : 'var(--sb-row-alt, rgba(15, 23, 42, 0.04))';
    return <tr key={`${field.key}-${field.category}`} style={{
      borderBottom: '1px solid var(--sb-row-border)',
      backgroundColor: rowBg
    }}>
                  <td style={{
      ...tdStyle,
      whiteSpace: 'nowrap'
    }}>
                    {formatCategoryLabel(field.category)}
                  </td>
                  <td style={{
      ...tdStyle,
      fontWeight: 600,
      fontSize: '14px',
      color: 'var(--sb-text)'
    }}>
                    {formatDisplayLabel(field)}
                  </td>
                  <td style={{
      ...tdStyle,
      whiteSpace: 'normal',
      wordBreak: 'break-word'
    }}>
                    {field.description || '—'}
                  </td>
                  <td style={tdStyle}>
                    {field.creditsConsumed || (field.category === 'BuyerAttribute' ? 'FREE' : '—')}
                  </td>
                  <td style={tdStyle}>
                    {field.dataType || '—'}
                  </td>
                  <td style={{
      ...tdStyle,
      whiteSpace: 'normal',
      wordBreak: 'break-word'
    }}>
                    {field.example || '—'}
                  </td>
                  <td style={tdStyle}>
                    {getBuyerTypes(field)}
                  </td>
                  <td style={tdStyle}>
                    {renderSourceValues(getSourceValues(field.sources, source => source.dataSourceName))}
                  </td>
                  <td style={tdStyle}>
                    {renderSourceValues(getLastUpdatedValues(field.sources, field.category))}
                  </td>
                  <td style={tdStyle}>
                    {renderSourceValues(getSourceValues(field.sources, source => source.refreshCadence))}
                  </td>
                  <td style={{
      ...tdStyle,
      whiteSpace: 'normal',
      wordBreak: 'break-word'
    }}>
                    {renderSourceValues(getAccountsNotes(field))}
                  </td>
                </tr>;
  })}
          </tbody>
        </table>
      </div>
    </div>;
  const renderSimpleTable = (data, id, title, blurb, showCategory = true, showDataSourceNotes = true) => <div>
      <h2 id={id} style={{
    fontSize: '20px',
    fontWeight: 700,
    color: 'var(--sb-heading)',
    margin: '48px 0 16px',
    scrollMarginTop: '80px'
  }}>{title}</h2>
      {blurb && <div className="sb-section-blurb">{blurb}</div>}
      <div style={tableWrapperStyle}>
        <table className="sb-field-metadata-table" style={{
    width: '100%',
    minWidth: '800px',
    borderCollapse: 'separate',
    borderSpacing: 0,
    tableLayout: 'auto'
  }}>
          <thead>
            <tr>
              {showCategory && <th style={{
    ...thStyle,
    minWidth: '150px',
    whiteSpace: 'nowrap'
  }}>Category</th>}
              <th style={{
    ...thStyle,
    minWidth: '200px',
    whiteSpace: 'nowrap'
  }}>Field</th>
              <th style={{
    ...thStyle,
    minWidth: '280px'
  }}>Description</th>
              <th style={{
    ...thStyle,
    minWidth: '200px'
  }}>Example</th>
              <th style={{
    ...thStyle,
    minWidth: '160px'
  }}>Data Source</th>
              {showDataSourceNotes && <th style={{
    ...thStyle,
    minWidth: '200px'
  }}>Data Source Notes</th>}
            </tr>
          </thead>
          <tbody>
            {data.map((field, idx) => {
    const rowBg = idx % 2 === 0 ? 'var(--sb-row-even, transparent)' : 'var(--sb-row-alt, rgba(15, 23, 42, 0.04))';
    return <tr key={`${field.key}-${field.category}`} style={{
      borderBottom: '1px solid var(--sb-row-border)',
      backgroundColor: rowBg
    }}>
                  {showCategory && <td style={{
      ...tdStyle,
      fontWeight: 600,
      fontSize: '14px',
      color: 'var(--sb-text)',
      whiteSpace: 'nowrap'
    }}>
                      {field.category}
                    </td>}
                  <td style={{
      ...tdStyle,
      whiteSpace: 'nowrap',
      overflow: 'hidden',
      textOverflow: 'ellipsis'
    }}>
                    {getUiFieldLabel(field)}
                  </td>
                  <td style={{
      ...tdStyle,
      whiteSpace: 'normal',
      wordBreak: 'break-word'
    }}>
                    {field.tooltipExplanation || field.description || '—'}
                  </td>
                  <td style={{
      ...tdStyle,
      whiteSpace: 'normal',
      wordBreak: 'break-word'
    }}>
                    {field.example || '—'}
                  </td>
                  <td style={tdStyle}>
                    {renderSourceValues(getSourceValues(field.sources, source => source.dataSourceName))}
                  </td>
                  {showDataSourceNotes && <td style={{
      ...tdStyle,
      whiteSpace: 'normal',
      wordBreak: 'break-word'
    }}>
                      {field.notesOnSourceData || '—'}
                    </td>}
                </tr>;
  })}
          </tbody>
        </table>
      </div>
    </div>;
  const renderMergedMeetingsTable = (data, id, title, blurb) => <div>
      <h2 id={id} style={{
    fontSize: '20px',
    fontWeight: 700,
    color: 'var(--sb-heading)',
    margin: '48px 0 16px',
    scrollMarginTop: '80px'
  }}>{title}</h2>
      {blurb && <div className="sb-section-blurb">{blurb}</div>}
      <div style={tableWrapperStyle}>
        <table className="sb-field-metadata-table" style={{
    width: '100%',
    minWidth: '720px',
    borderCollapse: 'separate',
    borderSpacing: 0,
    tableLayout: 'auto'
  }}>
          <thead>
            <tr>
              <th style={{
    ...thStyle,
    minWidth: '200px',
    whiteSpace: 'nowrap'
  }}>Field</th>
              <th style={{
    ...thStyle,
    minWidth: '280px'
  }}>Description</th>
              <th style={{
    ...thStyle,
    minWidth: '200px'
  }}>Example</th>
              <th style={{
    ...thStyle,
    minWidth: '160px'
  }}>Data Source</th>
            </tr>
          </thead>
          <tbody>
            {data.map((field, idx) => {
    const rowBg = idx % 2 === 0 ? 'var(--sb-row-even, transparent)' : 'var(--sb-row-alt, rgba(15, 23, 42, 0.04))';
    return <tr key={`${field.key}-${field.category}`} style={{
      borderBottom: '1px solid var(--sb-row-border)',
      backgroundColor: rowBg
    }}>
                  <td style={{
      ...tdStyle,
      whiteSpace: 'nowrap',
      overflow: 'hidden',
      textOverflow: 'ellipsis'
    }}>
                    {getUiFieldLabel(field)}
                  </td>
                  <td style={{
      ...tdStyle,
      whiteSpace: 'normal',
      wordBreak: 'break-word'
    }}>
                    {field.tooltipExplanation || field.description || '—'}
                  </td>
                  <td style={{
      ...tdStyle,
      whiteSpace: 'normal',
      wordBreak: 'break-word'
    }}>
                    {field.example || '—'}
                  </td>
                  <td style={tdStyle}>
                    {renderSourceValues(getSourceValues(field.sources, source => source.dataSourceName))}
                  </td>
                </tr>;
  })}
          </tbody>
        </table>
      </div>
    </div>;
  return <div>
      {}
      {renderAccountsTable(accountsData, 'accounts', 'Accounts', sectionBlurbs.accounts)}
      {renderSimpleTable(contactsData, 'contacts', 'Contacts', sectionBlurbs.contacts, false, false)}
      {renderSimpleTable(jobChangesData, 'job-changes', 'Job Changes', sectionBlurbs.jobChanges, false, false)}
      {renderSimpleTable(competitorsData, 'competitors', 'Spend Data', sectionBlurbs.competitors, true, false)}
      {renderSimpleTable(conferencesData, 'conferences', 'Conferences', sectionBlurbs.conferences, false, false)}
      {renderSimpleTable(rfpsData, 'rfps', 'RFPs', sectionBlurbs.rfps, false, false)}
      {renderMergedMeetingsTable(meetingStrategicData, 'meeting-strategic', 'Meeting Minutes + Strategic Plans', sectionBlurbs.meetings)}
    </div>;
};

<style>
  {`
      :root {
        --sb-text: rgb(var(--gray-900));
        --sb-heading: rgb(var(--gray-900));
        --sb-muted: rgb(var(--gray-600));
        --sb-muted-2: rgb(var(--gray-500));
        --sb-border: rgb(var(--gray-200));
        --sb-surface: rgb(var(--background-light));
        --sb-row-border: rgb(var(--gray-200));
        --sb-row-even: transparent;
        --sb-row-alt: rgba(15, 23, 42, 0.04);
        --sb-shadow: 0 1px 2px rgba(15, 23, 42, 0.06);
      }
      @media (prefers-color-scheme: dark) {
        :root {
          --sb-text: rgb(var(--gray-100));
          --sb-heading: rgb(var(--gray-100));
          --sb-muted: rgb(var(--gray-300));
          --sb-muted-2: rgb(var(--gray-400));
          --sb-border: rgb(var(--gray-800));
          --sb-surface: rgb(var(--background-dark));
          --sb-row-border: rgb(var(--gray-700));
          --sb-row-even: rgba(255, 255, 255, 0.02);
          --sb-row-alt: rgba(255, 255, 255, 0.08);
          --sb-shadow: 0 1px 2px rgba(0, 0, 0, 0.4);
        }
      }
      :root.dark,
      html.dark,
      body.dark,
      :root[data-theme="dark"],
      html[data-theme="dark"],
      body[data-theme="dark"],
      [data-color-mode="dark"] {
      --sb-text: rgb(var(--gray-100));
      --sb-heading: rgb(var(--gray-100));
      --sb-muted: rgb(var(--gray-300));
      --sb-muted-2: rgb(var(--gray-400));
      --sb-border: rgb(var(--gray-800));
      --sb-surface: rgb(var(--background-dark));
      --sb-row-border: rgb(var(--gray-700));
      --sb-row-even: rgba(255, 255, 255, 0.02);
      --sb-row-alt: rgba(255, 255, 255, 0.08);
      --sb-shadow: 0 1px 2px rgba(0, 0, 0, 0.4);
      }
      .sb-buyer-filter,
      .sb-buyer-filter * {
      box-sizing: border-box;
      }
      .sb-buyer-filter-label {
      color: var(--sb-heading) !important;
      font-weight: 600 !important;
      font-size: 14px !important;
      margin: 0 !important;
      }
      .sb-buyer-filter .sb-buyer-filter-select,
      .sb-buyer-filter select.sb-buyer-filter-select,
      select#buyer-type-filter {
      appearance: auto !important;
      -webkit-appearance: menulist !important;
      padding: 10px 36px 10px 14px !important;
      border: 2px solid var(--sb-border) !important;
      border-radius: 10px !important;
      background-color: var(--sb-surface) !important;
      color: var(--sb-text) !important;
      font-size: 15px !important;
      font-weight: 600 !important;
      min-width: 240px !important;
      cursor: pointer !important;
      box-shadow: 0 1px 3px rgba(0,0,0,0.08) !important;
      display: inline-block !important;
      }
      .sb-buyer-filter .sb-buyer-filter-select:hover,
      .sb-buyer-filter select:hover,
      select#buyer-type-filter:hover {
      border-color: var(--sb-muted-2) !important;
      box-shadow: 0 2px 6px rgba(0,0,0,0.12) !important;
      }
      .sb-buyer-filter .sb-buyer-filter-select:focus,
      select#buyer-type-filter:focus {
      outline: 2px solid var(--sb-heading) !important;
      outline-offset: 2px !important;
      }
      .sb-buyer-filter-hint {
      margin: 0 0 16px 0 !important;
      color: var(--sb-muted) !important;
      font-size: 13px !important;
      }
      .sb-section-blurb {
      margin: 0 0 16px 0 !important;
      }
      `}
</style>

<div style={{ width: '100%', maxWidth: 'none', boxSizing: 'border-box', margin: '0 auto', padding: '24px var(--page-padding, 24px)', fontFamily: 'var(--font-family-body-custom, inherit)', fontSize: '15px', lineHeight: '1.6', color: 'var(--sb-text)' }}>
  <div style={{ marginBottom: '24px' }}>
    <div style={{ fontSize: '12px', fontWeight: 700, color: 'var(--sb-heading)', letterSpacing: '0.08em', textTransform: 'uppercase', marginBottom: '8px' }}>
      How We Collect Data
    </div>

    <h1 style={{ fontFamily: 'var(--font-family-headings-custom, inherit)', fontSize: '30px', lineHeight: '1.25', fontWeight: 700, color: 'var(--sb-heading)', margin: '0 0 8px' }}>
      All Starbridge Data
    </h1>

    <p style={{ fontSize: '15px', color: 'var(--sb-muted)', margin: 0 }}>
      Complete reference of all buyer attributes and field metadata available in Starbridge.
    </p>

    <p style={{ fontSize: '14px', color: 'var(--sb-muted)', margin: '12px 0 0' }}>
      This page lists every field we expose—organized by data type—along with descriptions, examples, and data source details.
    </p>

    <Note>
      All data and attributes are algorithmically derived, normalized, and inferred from public records and other lawful sources. Starbridge does not republish third-party proprietary content.
    </Note>
  </div>

  <div style={{ margin: '16px 0 24px' }}>
    <CardGroup cols={3}>
      <Card title="Accounts" icon="database" href="#accounts">
        Buyer attributes and account-level signals.
      </Card>

      <Card title="Contacts" icon="user" href="#contacts">
        Verified public sector contact data.
      </Card>

      <Card title="Job Changes" icon="briefcase" href="#job-changes">
        Track new hires, promotions, and departures at target accounts.
      </Card>

      <Card title="Spend Data" icon="shield" href="#competitors">
        Data from FOIA for Purchase Orders & Contracts along with Web Scraping Enrichments.
      </Card>

      <Card title="Conferences" icon="calendar" href="#conferences">
        Conference and event opportunities.
      </Card>

      <Card title="RFPs" icon="pen-to-square" href="#rfps">
        Active bids and procurement postings.
      </Card>

      <Card title="Meeting Minutes" icon="message" href="#meeting-strategic">
        Meeting minutes and strategic plans.
      </Card>
    </CardGroup>
  </div>

  <FieldMetadataTable
    sectionBlurbs={{
accounts: (
  <Info>
    Buyer attributes blend public datasets with Starbridge research. Learn more in{' '}
    <a href="https://hc.starbridge.ai/data/budget-data">Budget Data</a>,{' '}
    <a href="https://hc.starbridge.ai/data/how-to-navigate-procurement-data">How to Navigate Procurement Data</a>, and{' '}
    <a href="https://hc.starbridge.ai/data/custom-web-tracking-data">Custom Web Data</a>.
  </Info>
),
contacts: (
  <Info>
    Contacts are pulled from public agency and school sites, enriched via a multi-provider waterfall, and continuously verified to keep data current. Learn more in{' '}
    <a href="https://hc.starbridge.ai/data/contact-data">How We Source Contact Data</a>.
  </Info>
),
jobChanges: (
  <Info>
    Job changes track new hires, title changes, and departures at your target accounts. Starbridge monitors public sources and enrichment providers to surface these signals. Learn more in{' '}
    <a href="https://hc.starbridge.ai/builders/starbridge-for-builders/tracking-job-changes">Tracking Job Changes</a>.
  </Info>
),
competitors: (
  <Info>
    Competitor presence is detected from purchase orders, contracts, federal contract spend (FPDS/USA Spending), and web or meeting mentions to surface usage signals. Learn more in{' '}
    <a href="https://hc.starbridge.ai/data/federal-data">Federal Data</a> and{' '}
    <a href="https://hc.starbridge.ai/data/competitor-presence-spend-data">Competitors &amp; Spend</a>.
  </Info>
),
rfps: (
  <Info>
    RFPs are collected via daily scraping of thousands of portals plus partner feeds, typically within 24-48 hours of posting. Learn more in{' '}
    <a href="https://hc.starbridge.ai/data/rfp-data">RFP Data</a>.
  </Info>
),
conferences: (
  <Info>
    Conferences help you discover relevant government and education events, prioritize which to attend, and enrich lead lists for better outreach. Learn more in{' '}
    <a href="https://hc.starbridge.ai/builders/conferences">Conferences</a>.
  </Info>
),
meetings: (
  <Info>
    Meeting minutes and strategic plans are collected and refreshed on a weekly basis from public sources (websites, YouTube, third-party sites). Learn more in{' '}
    <a href="https://hc.starbridge.ai/data/meetings-data">Board Meetings &amp; Strategic Plans</a>.
  </Info>
),
}}
  />
</div>
