/**
 * Delectable AI ML/Personalization Pipeline
 *
 * Interactive visualization of the actual Delectable AI models and
 * personalization pipeline powering the Your Grocer shopping assistant.
 *
 * Inline JSX + Babel pattern (no imports, no exports).
 */

const { useState, useEffect } = React;

// ── Inline SVG Icon Components ──────────────────────────────────────────────

const Database = () => (
  <svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
    <ellipse cx="12" cy="5" rx="9" ry="3" strokeWidth={2}/>
    <path d="M21 12c0 1.66-4 3-9 3s-9-1.34-9-3" strokeWidth={2}/>
    <path d="M3 5v14c0 1.66 4 3 9 3s9-1.34 9-3V5" strokeWidth={2}/>
  </svg>
);

const Brain = () => (
  <svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
    <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9.663 17h4.673M12 3v1m6.364 1.636l-.707.707M21 12h-1M4 12H3m3.343-5.657l-.707-.707m2.828 9.9a5 5 0 117.072 0l-.548.547A3.374 3.374 0 0014 18.469V19a2 2 0 11-4 0v-.531c0-.895-.356-1.754-.988-2.386l-.548-.547z" />
  </svg>
);

const Zap = () => (
  <svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
    <polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round"/>
  </svg>
);

const Target = () => (
  <svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
    <circle cx="12" cy="12" r="10" strokeWidth={2}/>
    <circle cx="12" cy="12" r="6" strokeWidth={2}/>
    <circle cx="12" cy="12" r="2" strokeWidth={2}/>
  </svg>
);

const TrendingUp = () => (
  <svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
    <polyline points="23 6 13.5 15.5 8.5 10.5 1 18" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round"/>
    <polyline points="17 6 23 6 23 12" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round"/>
  </svg>
);

const Users = () => (
  <svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
    <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197M13 7a4 4 0 11-8 0 4 4 0 018 0z" />
  </svg>
);

const ShoppingCart = () => (
  <svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
    <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 3h2l.4 2M7 13h10l4-8H5.4M7 13L5.4 5M7 13l-2.293 2.293c-.63.63-.184 1.707.707 1.707H17m0 0a2 2 0 100 4 2 2 0 000-4zm-8 2a2 2 0 11-4 0 2 2 0 014 0z" />
  </svg>
);

const GitBranch = () => (
  <svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
    <line x1="6" y1="3" x2="6" y2="15" strokeWidth={2} strokeLinecap="round"/>
    <circle cx="18" cy="6" r="3" strokeWidth={2}/>
    <circle cx="6" cy="18" r="3" strokeWidth={2}/>
    <path d="M18 9a9 9 0 01-9 9" strokeWidth={2}/>
  </svg>
);

const Layers = () => (
  <svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
    <polygon points="12 2 2 7 12 12 22 7 12 2" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round"/>
    <polyline points="2 17 12 22 22 17" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round"/>
    <polyline points="2 12 12 17 22 12" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round"/>
  </svg>
);

const Activity = () => (
  <svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
    <polyline points="22 12 18 12 15 21 9 3 6 12 2 12" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round"/>
  </svg>
);

const BarChart3 = () => (
  <svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
    <path d="M3 3v18h18" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round"/>
    <rect x="7" y="10" width="3" height="8" strokeWidth={2}/>
    <rect x="13" y="6" width="3" height="12" strokeWidth={2}/>
    <rect x="19" y="2" width="3" height="16" strokeWidth={2}/>
  </svg>
);

const Network = () => (
  <svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
    <circle cx="12" cy="12" r="3" strokeWidth={2}/>
    <circle cx="4" cy="6" r="2" strokeWidth={2}/>
    <circle cx="20" cy="6" r="2" strokeWidth={2}/>
    <circle cx="4" cy="18" r="2" strokeWidth={2}/>
    <circle cx="20" cy="18" r="2" strokeWidth={2}/>
    <line x1="9.5" y1="10" x2="6" y2="7.5" strokeWidth={2}/>
    <line x1="14.5" y1="10" x2="18" y2="7.5" strokeWidth={2}/>
    <line x1="9.5" y1="14" x2="6" y2="16.5" strokeWidth={2}/>
    <line x1="14.5" y1="14" x2="18" y2="16.5" strokeWidth={2}/>
  </svg>
);

const Sparkles = () => (
  <svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
    <path d="M12 3l1.912 5.813L20 10l-6.088 1.187L12 17l-1.912-5.813L4 10l6.088-1.187L12 3z" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round"/>
    <path d="M5 3l.5 1.5L7 5l-1.5.5L5 7l-.5-1.5L3 5l1.5-.5L5 3z" strokeWidth={2}/>
    <path d="M19 17l.5 1.5L21 19l-1.5.5L19 21l-.5-1.5L17 19l1.5-.5L19 17z" strokeWidth={2}/>
  </svg>
);

const ChevronRight = () => (
  <svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
    <polyline points="9 18 15 12 9 6" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round"/>
  </svg>
);

const Play = () => (
  <svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
    <polygon points="5 3 19 12 5 21 5 3" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round"/>
  </svg>
);

const Pause = () => (
  <svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
    <rect x="6" y="4" width="4" height="16" strokeWidth={2}/>
    <rect x="14" y="4" width="4" height="16" strokeWidth={2}/>
  </svg>
);

const Filter = () => (
  <svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
    <polygon points="22 3 2 3 10 12.46 10 19 14 21 14 12.46 22 3" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round"/>
  </svg>
);

const Cpu = () => (
  <svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
    <rect x="4" y="4" width="16" height="16" rx="2" strokeWidth={2}/>
    <rect x="9" y="9" width="6" height="6" strokeWidth={2}/>
    <line x1="9" y1="1" x2="9" y2="4" strokeWidth={2}/>
    <line x1="15" y1="1" x2="15" y2="4" strokeWidth={2}/>
    <line x1="9" y1="20" x2="9" y2="23" strokeWidth={2}/>
    <line x1="15" y1="20" x2="15" y2="23" strokeWidth={2}/>
    <line x1="20" y1="9" x2="23" y2="9" strokeWidth={2}/>
    <line x1="20" y1="14" x2="23" y2="14" strokeWidth={2}/>
    <line x1="1" y1="9" x2="4" y2="9" strokeWidth={2}/>
    <line x1="1" y1="14" x2="4" y2="14" strokeWidth={2}/>
  </svg>
);

const Box = () => (
  <svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
    <path d="M21 16V8a2 2 0 00-1-1.73l-7-4a2 2 0 00-2 0l-7 4A2 2 0 003 8v8a2 2 0 001 1.73l7 4a2 2 0 002 0l7-4A2 2 0 0021 16z" strokeWidth={2}/>
    <polyline points="3.27 6.96 12 12.01 20.73 6.96" strokeWidth={2}/>
    <line x1="12" y1="22.08" x2="12" y2="12" strokeWidth={2}/>
  </svg>
);

const ArrowRight = () => (
  <svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
    <line x1="5" y1="12" x2="19" y2="12" strokeWidth={2} strokeLinecap="round"/>
    <polyline points="12 5 19 12 12 19" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round"/>
  </svg>
);

const Beaker = () => (
  <svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
    <path d="M9 3h6M10 3v5.172a2 2 0 01-.586 1.414l-4.828 4.828A2 2 0 004 15.828V17a3 3 0 003 3h10a3 3 0 003-3v-1.172a2 2 0 00-.586-1.414l-4.828-4.828A2 2 0 0114 8.172V3" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round"/>
    <path d="M7 15h10" strokeWidth={2} strokeLinecap="round"/>
  </svg>
);

const Shield = () => (
  <svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
    <path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round"/>
  </svg>
);

const ShoppingBag = () => (
  <svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
    <path d="M6 2L3 6v14a2 2 0 002 2h14a2 2 0 002-2V6l-3-4z" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round"/>
    <line x1="3" y1="6" x2="21" y2="6" strokeWidth={2}/>
    <path d="M16 10a4 4 0 01-8 0" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round"/>
  </svg>
);

const Clock = () => (
  <svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
    <circle cx="12" cy="12" r="10" strokeWidth={2}/>
    <polyline points="12 6 12 12 16 14" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round"/>
  </svg>
);

const LineChart = () => (
  <svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
    <path d="M3 3v18h18" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round"/>
    <path d="M7 16l4-8 4 4 6-10" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round"/>
  </svg>
);

// ── Main Component ──────────────────────────────────────────────────────────

const MLPersonalizationPipeline = () => {
  const [activeStage, setActiveStage] = useState(0);
  const [isAnimating, setIsAnimating] = useState(false);
  const [selectedModel, setSelectedModel] = useState('dietary-propensity');
  const [expandedCode, setExpandedCode] = useState(null);

  const stages = [
    { id: 0, name: 'Data Sources', icon: Database },
    { id: 1, name: 'Feature Engineering', icon: Cpu },
    { id: 2, name: 'Model Training', icon: Brain },
    { id: 3, name: 'Personalization', icon: Sparkles }
  ];

  useEffect(() => {
    if (isAnimating) {
      const interval = setInterval(() => {
        setActiveStage((prev) => (prev + 1) % stages.length);
      }, 4000);
      return () => clearInterval(interval);
    }
  }, [isAnimating]);

  // ── Stage 0: Data Sources ──────────────────────────────────────────────

  const dataSources = [
    {
      id: 'purchase-history',
      name: 'Purchase History (BigQuery)',
      icon: ShoppingCart,
      color: '#10b981',
      records: '48M',
      description: '2-year transaction data across all Your Grocer banners',
      features: ['Basket composition', 'Purchase frequency', 'Category affinity', 'Time patterns', 'Price sensitivity'],
      schema: {
        'loyal_card_no': 'string',
        'transaction_date': 'datetime',
        'UPC': 'string',
        'quantity': 'int',
        'price': 'float',
        'store_id': 'string'
      }
    },
    {
      id: 'product-catalog',
      name: 'Product Catalog',
      icon: Box,
      color: '#8b5cf6',
      records: '70K+',
      description: 'Enriched product data from USDA, Open Food Facts, Tufts Food Compass',
      features: ['Name & brand', 'Category taxonomy', 'Nutrition facts', 'Ingredients list', 'Health labels (organic, GF, etc.)', 'UPC mapping'],
      schema: {
        'UPC': 'string',
        'product_name': 'string',
        'brand': 'string',
        'category': 'string',
        'nutrition_json': 'json',
        'ingredients': 'text',
        'health_labels': 'array'
      }
    },
    {
      id: 'behavioral',
      name: 'Behavioral Signals',
      icon: Activity,
      color: '#f59e0b',
      records: '1.2B',
      description: 'Clickstream, search queries, and cart events from web and app',
      features: ['Clickstream events', 'Search queries', 'Cart add/remove', 'Session patterns', 'Browse-to-buy signals'],
      schema: {
        'session_id': 'string',
        'event_type': 'enum',
        'product_id': 'string',
        'timestamp': 'datetime',
        'query_text': 'string'
      }
    },
    {
      id: 'loyalty',
      name: 'Loyalty & Demographics',
      icon: Users,
      color: '#ec4899',
      records: '135K',
      description: 'Shopper profiles with loyalty tiers, household info, and sensitivity scores',
      features: ['Loyalty tier', 'Household size', 'Price sensitivity', 'Promo sensitivity', 'Rewards balance'],
      schema: {
        'loyal_card_no': 'string',
        'loyalty_tier': 'enum',
        'household_size': 'int',
        'price_sensitivity': 'float',
        'promo_sensitivity': 'float',
        'rewards_balance': 'int'
      }
    }
  ];

  // ── Stage 1: Feature Engineering ───────────────────────────────────────

  const featureEngineering = [
    {
      id: 'dietary-signals',
      name: 'Dietary Signal Extraction',
      description: 'Analyze 2-year purchase history to compute 11 dietary propensity scores per shopper. Each score represents the probability of a dietary preference.',
      icon: Shield,
      color: 'from-green-500 to-emerald-500',
      examples: ['organic_propensity_score', 'gluten_free_propensity', 'vegan_propensity', 'keto_propensity'],
      code: `# Dietary propensity extraction from purchase history
def compute_dietary_propensities(purchases_2yr):
    """Compute 11 dietary probability scores from purchase patterns."""
    total_spend = purchases_2yr.amount.sum()
    organic_ratio = purchases_2yr[purchases_2yr.is_organic].amount.sum() / total_spend
    gluten_free_ratio = purchases_2yr[purchases_2yr.is_gluten_free].amount.sum() / total_spend

    # 11 scores: vegan, vegetarian, gluten_free, dairy_free, nut_free,
    #     keto, low_sodium, low_sugar, high_protein, high_fiber, organic
    scores = {
        "organic": organic_ratio,
        "gluten_free": gluten_free_ratio,
        "vegan": vegan_ratio,
        "keto": keto_ratio,
        # ... all 11 dietary dimensions
    }
    return {
        score: sigmoid(ratio * calibration_factor)
        for score, ratio in scores.items()
    }`
    },
    {
      id: 'consumption-velocity',
      name: 'Consumption Velocity Estimation',
      description: 'Model per-product consumption rate to estimate virtual pantry stock levels. Predicts when a shopper will run out of each product.',
      icon: Clock,
      color: 'from-blue-500 to-cyan-500',
      examples: ['pantry_stock_olive_oil', 'consumption_velocity_milk', 'days_until_reorder_eggs', 'stock_level_0_to_1'],
      code: `# Virtual pantry stock estimation
def estimate_stock_level(product, purchase_history):
    """Estimate remaining stock for a product-household pair."""
    avg_purchase_interval = purchase_history.diff().mean()
    days_since_last = (now - last_purchase).days
    shelf_life = get_shelf_life(product.category)
    consumption_rate = product.typical_quantity / avg_purchase_interval
    remaining = max(0, last_quantity - consumption_rate * days_since_last)
    return min(1.0, remaining / product.typical_quantity)

# Example output:
# {"olive_oil": 0.73, "milk_2pct": 0.12, "eggs_dozen": 0.05}
#  \u2192 milk and eggs flagged for reorder`
    },
    {
      id: 'basket-composition',
      name: 'Basket Composition Analysis',
      description: 'Extract co-purchase patterns and build a knowledge graph of product affinities from transaction baskets.',
      icon: Network,
      color: 'from-purple-500 to-pink-500',
      examples: ['co_purchase_affinity_score', 'basket_category_vector', 'mission_cluster_id', 'trip_type_label'],
      code: `# Co-purchase pattern extraction via association rules
from mlxtend.frequent_patterns import apriori, association_rules

# Build transaction matrix
basket_matrix = (
    df.groupby(["transaction_id", "product_name"])["quantity"]
    .sum().unstack().fillna(0)
    .applymap(lambda x: 1 if x > 0 else 0)
)

# Mine frequent itemsets (min 0.5% support)
frequent = apriori(basket_matrix, min_support=0.005, use_colnames=True)
rules = association_rules(frequent, metric="lift", min_threshold=2.0)

# Top co-purchase pairs feed the knowledge graph
# e.g., {pasta \u2192 marinara_sauce, lift: 8.4}`
    },
    {
      id: 'embeddings',
      name: 'Multi-Aspect Embedding Generation',
      description: 'Generate 3 types of 768-dimensional embeddings per product using Vertex AI text-embedding-005. Identity, nutrition, and description aspects.',
      icon: Layers,
      color: 'from-orange-500 to-amber-500',
      examples: ['embedding_identity_768d', 'embedding_nutrition_768d', 'embedding_description_768d', 'cosine_similarity_score'],
      code: `# Generate 3-aspect embeddings via Vertex AI
from vertexai.language_models import TextEmbeddingModel

model = TextEmbeddingModel.from_pretrained("text-embedding-005")

def generate_product_embeddings(product):
    """Generate 3 x 768-dim embeddings per product."""
    identity_text = f"{product.brand} {product.name} {product.category}"
    nutrition_text = f"Calories {product.calories} Fat {product.fat}g ..."
    description_text = product.description or product.name

    embeddings = model.get_embeddings([
        identity_text, nutrition_text, description_text
    ])
    return {
        "identity": embeddings[0].values,    # 768-dim
        "nutrition": embeddings[1].values,    # 768-dim
        "description": embeddings[2].values,  # 768-dim
    }

# Stored in BigQuery for VECTOR_SEARCH
# 70K products x 3 aspects = 210K vectors`
    }
  ];

  // ── Stage 2: Model Training ────────────────────────────────────────────

  const mlModels = {
    'dietary-propensity': {
      name: 'Dietary Propensity Model',
      type: 'Ensemble (Logistic Regression + calibrated probabilities)',
      description: 'Predicts 11 dietary preference probabilities per shopper from 2-year purchase history patterns.',
      input: '2yr purchase history features: category ratios, brand preferences, nutrition profile of basket',
      output: '11 probability scores (0\u20131) for each dietary preference',
      metrics: {
        'AUC': '0.89',
        'Calibration Error': '0.03',
        'Coverage': '94%',
        'Refresh Cadence': '24h'
      },
      useCases: ['Dietary badge annotations', 'Search result filtering', 'Personalized warnings'],
      code: `# Dietary Propensity Model - Ensemble approach
from sklearn.linear_model import LogisticRegression
from sklearn.calibration import CalibratedClassifierCV

DIETARY_DIMENSIONS = [
    "vegan", "vegetarian", "gluten_free", "dairy_free",
    "nut_free", "keto", "low_sodium", "low_sugar",
    "high_protein", "high_fiber", "organic"
]

def train_dietary_model(features_df, labels_df):
    """Train calibrated logistic regression for each dietary dim."""
    models = {}
    for dim in DIETARY_DIMENSIONS:
        base = LogisticRegression(
            C=1.0, max_iter=1000, class_weight="balanced"
        )
        calibrated = CalibratedClassifierCV(base, cv=5, method="isotonic")
        calibrated.fit(features_df, labels_df[dim])
        models[dim] = calibrated
    return models

# Output per shopper:
# {"organic": 0.82, "gluten_free": 0.78, "keto": 0.15, ...}`
    },
    'virtual-pantry': {
      name: 'Virtual Pantry Estimator',
      type: 'Time-series regression per product-household',
      description: 'Estimates per-product stock levels for each household, predicting when items will need replenishment.',
      input: 'Purchase timestamps, quantities, product shelf life, household size',
      output: 'Estimated stock level (0\u20131), days until reorder, consumption velocity',
      metrics: {
        'MAE': '0.12',
        'Stock-out Accuracy': '84%',
        'Products Tracked': '2,400',
        'Refresh Cadence': '24h'
      },
      useCases: ['Shopping list auto-exclusion', 'Reorder reminders', 'Low-stock flagging'],
      code: `# Virtual Pantry Estimator - Time-series regression
import numpy as np
from scipy.optimize import curve_fit

def estimate_consumption_rate(purchase_history):
    """Fit consumption curve per product-household pair."""
    intervals = purchase_history["date"].diff().dt.days.dropna()
    quantities = purchase_history["quantity"].values

    # Exponential decay model for stock depletion
    avg_interval = intervals.mean()
    avg_quantity = quantities.mean()
    consumption_rate = avg_quantity / avg_interval  # units per day

    return {
        "consumption_rate": consumption_rate,
        "avg_interval_days": avg_interval,
        "avg_quantity": avg_quantity,
    }

def get_stock_level(product_id, household_id):
    """Current estimated stock as fraction of typical purchase."""
    rate = consumption_rates[(product_id, household_id)]
    days_elapsed = (now - last_purchase_date).days
    remaining = max(0, last_qty - rate * days_elapsed)
    return min(1.0, remaining / typical_purchase_qty)

# stock > 0.5 \u2192 exclude from list
# stock 0.1-0.5 \u2192 flag "running low"
# stock < 0.1 \u2192 add to reorder suggestions`
    },
    'mission-cluster': {
      name: 'Mission Cluster Model',
      type: 'K-means clustering (k=6)',
      description: 'Segments shopping trips into 6 mission types based on basket composition, trip frequency, and behavioral patterns.',
      input: 'Basket composition vectors, trip frequency, spend patterns, time-of-day',
      output: '6 clusters: Weekly Stock-Up, Quick Meal, Health Focus, Budget Batch, Special Occasion, Exploration',
      metrics: {
        'Silhouette Score': '0.72',
        'Clusters': '6',
        'Avg Cluster Size': '22.5K',
        'Stability': '0.89'
      },
      useCases: ['Mission-targeted promotions', 'UI personalization', 'Inventory planning'],
      code: `# Mission Cluster Model - K-means on trip features
from sklearn.cluster import KMeans
from sklearn.preprocessing import StandardScaler

MISSION_LABELS = {
    0: "Weekly Stock-Up",
    1: "Quick Meal",
    2: "Health Focus",
    3: "Budget Batch",
    4: "Special Occasion",
    5: "Exploration",
}

def train_mission_clusters(trip_features):
    """Cluster shopping trips into 6 mission types."""
    scaler = StandardScaler()
    X = scaler.fit_transform(trip_features[[
        "basket_size", "unique_categories", "avg_item_price",
        "pct_produce", "pct_protein", "pct_snacks",
        "time_of_day", "day_of_week", "trip_frequency_30d",
    ]])

    kmeans = KMeans(n_clusters=6, random_state=42, n_init=10)
    kmeans.fit(X)

    trip_features["mission"] = [
        MISSION_LABELS[c] for c in kmeans.labels_
    ]
    return kmeans, scaler

# "Health Focus" cluster \u2192 organic deals
# "Budget Batch" cluster \u2192 bulk discounts`
    },
    'embedding-generator': {
      name: 'Multi-Aspect Embedding Generator',
      type: 'Vertex AI text-embedding-005',
      description: 'Generates 3 semantic vector representations per product for identity, nutrition, and description search.',
      input: 'Product text (3 aspects: identity, nutrition, description)',
      output: '3 \u00d7 768-dimensional vectors per product, stored in BigQuery',
      metrics: {
        'Dimensions': '768',
        'Total Vectors': '210K',
        'Index Type': 'IVF',
        'Search Latency': '<50ms'
      },
      useCases: ['Semantic product search', 'Similar product discovery', 'Natural language queries'],
      code: `# Multi-Aspect Embedding Generation + BigQuery VECTOR_SEARCH

# 1. Generate embeddings (batch job, nightly)
from vertexai.language_models import TextEmbeddingModel

model = TextEmbeddingModel.from_pretrained("text-embedding-005")

for product in product_catalog:
    embeddings = model.get_embeddings([
        f"{product.brand} {product.name} {product.category}",
        format_nutrition_text(product.nutrition),
        product.description or product.name,
    ])
    store_embeddings(product.upc, embeddings)

# 2. Query via BigQuery VECTOR_SEARCH at inference time
VECTOR_SEARCH_SQL = """
SELECT base.product_name, base.brand, base.UPC,
       distance
FROM VECTOR_SEARCH(
    TABLE \`project.dataset.product_embeddings\`,
    'embedding_identity',
    (SELECT embedding FROM query_embedding),
    top_k => 20,
    distance_type => 'COSINE'
)
"""
# Returns top-20 semantically similar products in <50ms`
    },
    'food-intelligence': {
      name: 'Food Intelligence Scorer',
      type: 'Rule-based + algorithmic (Food Compass, Nutri-Score, NOVA)',
      description: 'Computes nutritional quality scores for products using established food science algorithms.',
      input: 'Nutrition facts, ingredients list, processing indicators',
      output: 'Food Compass (1\u2013100), Nutri-Score (A\u2013E), NOVA (1\u20134)',
      metrics: {
        'Food Compass': '1\u2013100 scale',
        'Nutri-Score': 'A\u2013E',
        'NOVA': '1\u20134',
        'Coverage': '42%'
      },
      useCases: ['Healthier swap suggestions', 'Ultra-processed warnings', 'Nutrition-aware ranking'],
      code: `# Food Intelligence Scoring Pipeline
def compute_food_compass(nutrition, ingredients):
    """Tufts Food Compass score (1-100, higher = healthier)."""
    # 54 attributes across 9 domains
    score = (
        nutrient_ratio_domain(nutrition)
        + vitamin_mineral_domain(nutrition)
        + food_ingredient_domain(ingredients)
        + additive_domain(ingredients)
        + processing_domain(ingredients)
        + lipid_domain(nutrition)
        + fiber_protein_domain(nutrition)
        + phytochemical_domain(ingredients)
        + specific_lipid_domain(nutrition)
    )
    return clamp(score, 1, 100)

def compute_nutri_score(nutrition):
    """EU Nutri-Score: A (best) to E (worst)."""
    negative = (nutrition.energy_pts + nutrition.sugar_pts
                + nutrition.saturated_fat_pts + nutrition.sodium_pts)
    positive = (nutrition.fiber_pts + nutrition.protein_pts
                + nutrition.fruit_veg_pts)
    score = negative - positive
    return map_to_letter(score)  # A-E

def compute_nova(ingredients):
    """NOVA classification: 1=unprocessed to 4=ultra-processed."""
    if has_ultra_processed_markers(ingredients):
        return 4
    # ... classification logic
    return nova_class`
    },
    'co-purchase-graph': {
      name: 'Co-Purchase Knowledge Graph',
      type: 'Association rule mining + graph embedding',
      description: 'Builds a product affinity graph from transaction baskets for basket completion and recipe mapping.',
      input: 'Transaction baskets (product co-occurrence)',
      output: 'Product affinity scores, "frequently bought together" edges',
      metrics: {
        'Edges': '1.2M',
        'Avg Lift': '3.4',
        'Min Support': '0.5%',
        'Update Freq': 'Weekly'
      },
      useCases: ['Basket completion suggestions', 'Recipe ingredient mapping', '"Frequently bought together"'],
      code: `# Co-Purchase Knowledge Graph Construction
from mlxtend.frequent_patterns import apriori, association_rules
import networkx as nx

# 1. Mine association rules from transaction baskets
frequent_items = apriori(
    basket_matrix, min_support=0.005, use_colnames=True
)
rules = association_rules(
    frequent_items, metric="lift", min_threshold=2.0
)

# 2. Build directed graph of product affinities
G = nx.DiGraph()
for _, rule in rules.iterrows():
    for ant in rule["antecedents"]:
        for con in rule["consequents"]:
            G.add_edge(ant, con, weight=rule["lift"],
                       confidence=rule["confidence"])

# 3. Query: "What goes with pasta?"
neighbors = sorted(
    G[\"pasta\"].items(),
    key=lambda x: x[1]["weight"], reverse=True
)[:5]
# \u2192 [("marinara_sauce", 8.4), ("parmesan", 6.2), ...]

# 4. Recipe ingredient mapping
def suggest_basket_completion(current_basket, recipe_graph):
    missing = recipe_graph.required - set(current_basket)
    return [(item, G[nearest][item]["weight"])
            for item in missing if item in G]`
    }
  };

  // ── Stage 3: Personalization Outputs ───────────────────────────────────

  const personalizationOutputs = [
    {
      type: 'Dietary-Aware Search Results',
      icon: Shield,
      description: 'Agent annotates products with dietary badges based on propensity scores > 0.5. A shopper with gluten_free=0.78 sees gluten warnings prominently.',
      model: 'Dietary Propensity Model',
      example: 'Shopper (gluten_free: 0.78) searches "pasta" \u2192 Results show GF badge on rice pasta, warning on wheat pasta. Organic items boosted for organic_propensity > 0.6.',
      color: 'from-green-500 to-emerald-600'
    },
    {
      type: 'Virtual Pantry Deductions',
      icon: ShoppingBag,
      description: 'Shopping lists automatically exclude items with stock > 0.5 and flag items at 0.1\u20130.5. The optimize_shopping_list tool handles this deterministically.',
      model: 'Virtual Pantry Estimator',
      example: 'Recipe calls for olive oil (stock: 0.73) \u2192 excluded. Milk (stock: 0.12) \u2192 flagged "running low". Eggs (stock: 0.05) \u2192 added to list.',
      color: 'from-blue-500 to-cyan-600'
    },
    {
      type: 'Mission-Targeted Promotions',
      icon: Target,
      description: 'Promos matched to the shopper\'s detected mission cluster. "Health Focus" cluster gets organic deals; "Budget Batch" sees bulk pricing.',
      model: 'Mission Cluster Model',
      example: 'Shopper in "Health Focus" cluster \u2192 Agent surfaces organic produce deals, plant-based protein promos, and wellness bundle discounts.',
      color: 'from-purple-500 to-pink-600'
    },
    {
      type: 'Personalized Product Ranking',
      icon: TrendingUp,
      description: 'Search results re-ranked by combining dietary fit, purchase affinity, food intelligence scores, and semantic similarity.',
      model: 'Multi-Aspect Embeddings + Food Intelligence',
      example: 'Query "healthy snack" \u2192 Re-ranked: (1) Almonds [Food Compass: 88, organic_fit: 0.9], (2) Greek Yogurt [FC: 76], (3) Granola Bar [NOVA: 4, flagged].',
      color: 'from-orange-500 to-amber-600'
    },
    {
      type: 'Shopping List Optimization',
      icon: ShoppingCart,
      description: 'Cross-recipe ingredient aggregation, unit conversion, and retail package mapping. All deterministic via the optimize_shopping_list tool.',
      model: 'Co-Purchase Graph + Virtual Pantry',
      example: '3 recipes need "garlic" \u2192 aggregated to 1 bulb. "400g chicken" \u2192 mapped to "1 lb pack". Missing "basil" detected from co-purchase graph.',
      color: 'from-teal-500 to-green-600'
    },
    {
      type: 'Healthier Alternative Suggestions',
      icon: Beaker,
      description: 'Food Compass scores drive "healthier swap" recommendations. NOVA flags ultra-processed items. Agent suggests alternatives proactively.',
      model: 'Food Intelligence Scorer + Embeddings',
      example: 'Cart has instant ramen (NOVA: 4, FC: 22) \u2192 Agent suggests fresh ramen noodles (NOVA: 1, FC: 67) with similar embeddings, +45 Food Compass points.',
      color: 'from-rose-500 to-red-600'
    }
  ];

  // ── Render ─────────────────────────────────────────────────────────────

  return (
    <div className="min-h-screen bg-gradient-to-br from-slate-900 via-purple-900 to-slate-900 text-white">

      {/* ── Header ──────────────────────────────────────────────────────── */}
      <div className="bg-black/30 backdrop-blur-sm border-b border-white/10 sticky top-0 z-50">
        <div className="max-w-7xl mx-auto px-6 py-4">
          <div className="flex items-center justify-between">
            <div className="flex items-center gap-3">
              <div className="p-2 bg-gradient-to-br from-purple-500 to-pink-500 rounded-lg">
                <Brain />
              </div>
              <div>
                <h1 className="text-xl font-bold">Delectable AI ML/Personalization Pipeline</h1>
                <p className="text-sm text-white/60">Your Grocer Shopping Intelligence</p>
              </div>
            </div>
            <button
              onClick={() => setIsAnimating(!isAnimating)}
              className="flex items-center gap-2 px-4 py-2 bg-white/10 hover:bg-white/20 rounded-lg transition-colors"
            >
              {isAnimating ? <Pause /> : <Play />}
              <span className="text-sm">{isAnimating ? 'Pause' : 'Animate'}</span>
            </button>
          </div>
        </div>
      </div>

      {/* ── Stage Navigation ────────────────────────────────────────────── */}
      <div className="max-w-7xl mx-auto px-6 py-8">
        <div className="flex items-center justify-between mb-12">
          {stages.map((stage, idx) => {
            const Icon = stage.icon;
            const isActive = activeStage === stage.id;
            const isPast = activeStage > stage.id;

            return (
              <React.Fragment key={stage.id}>
                <button
                  onClick={() => { setActiveStage(stage.id); setIsAnimating(false); }}
                  className={`flex flex-col items-center gap-2 transition-all duration-300 ${
                    isActive ? 'scale-110' : isPast ? 'opacity-70' : 'opacity-40'
                  }`}
                >
                  <div className={`p-4 rounded-xl transition-all duration-300 ${
                    isActive
                      ? 'bg-gradient-to-br from-purple-500 to-pink-500 shadow-lg shadow-purple-500/50'
                      : isPast
                      ? 'bg-green-500/20 border border-green-500/30'
                      : 'bg-white/10'
                  }`}>
                    <Icon />
                  </div>
                  <span className="text-sm font-medium">{stage.name}</span>
                </button>
                {idx < stages.length - 1 && (
                  <div className={`transition-opacity duration-300 ${isPast ? 'opacity-100 text-green-400' : 'opacity-20'}`}>
                    <svg className="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                      <polyline points="9 18 15 12 9 6" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round"/>
                    </svg>
                  </div>
                )}
              </React.Fragment>
            );
          })}
        </div>

        {/* ── Stage 0: Data Sources ───────────────────────────────────── */}
        {activeStage === 0 && (
          <div className="space-y-6 animate-fadeIn">
            <div className="text-center mb-8">
              <h2 className="text-3xl font-bold mb-2">Data Sources</h2>
              <p className="text-white/60">Actual data feeding Delectable AI models</p>
            </div>

            <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
              {dataSources.map((source) => {
                const Icon = source.icon;
                return (
                  <div key={source.id} className="bg-white/5 backdrop-blur-sm rounded-xl p-6 border border-white/10 hover:border-white/20 transition-all">
                    <div className="flex items-start gap-4">
                      <div className="p-3 rounded-lg" style={{ backgroundColor: source.color + '20' }}>
                        <div style={{ color: source.color }}><Icon /></div>
                      </div>
                      <div className="flex-1">
                        <div className="flex items-center justify-between mb-1">
                          <h3 className="text-lg font-semibold">{source.name}</h3>
                          <span className="text-sm px-2 py-0.5 bg-white/10 rounded-full">{source.records} records</span>
                        </div>
                        <p className="text-sm text-white/60 mb-3">{source.description}</p>

                        <div className="space-y-3">
                          <div>
                            <h4 className="text-xs font-medium text-white/50 uppercase tracking-wider mb-2">Features</h4>
                            <div className="flex flex-wrap gap-1.5">
                              {source.features.map((feature) => (
                                <span key={feature} className="px-2 py-0.5 bg-white/10 rounded text-xs text-white/80">
                                  {feature}
                                </span>
                              ))}
                            </div>
                          </div>

                          <div>
                            <h4 className="text-xs font-medium text-white/50 uppercase tracking-wider mb-2">Schema</h4>
                            <div className="bg-black/30 rounded-lg p-3 font-mono text-xs space-y-1">
                              {Object.entries(source.schema).map(([field, type]) => (
                                <div key={field} className="flex justify-between">
                                  <span className="text-blue-300">{field}</span>
                                  <span className="text-green-300">{type}</span>
                                </div>
                              ))}
                            </div>
                          </div>
                        </div>
                      </div>
                    </div>
                  </div>
                );
              })}
            </div>

            {/* Data Pipeline Architecture */}
            <div className="mt-8 bg-gradient-to-r from-purple-500/10 to-pink-500/10 rounded-xl p-6 border border-purple-500/20">
              <h3 className="text-lg font-semibold mb-4 flex items-center gap-2">
                <Layers />
                Data Pipeline Architecture
              </h3>
              <div className="grid grid-cols-5 gap-3 text-center">
                {[
                  { icon: Database, label: 'BigQuery', sub: 'Raw data' },
                  { icon: Filter, label: 'dbt Transforms', sub: 'Clean & join' },
                  { icon: Cpu, label: 'Feature Extraction', sub: 'Signals & scores' },
                  { icon: Brain, label: 'Model Training', sub: 'Vertex AI' },
                  { icon: Zap, label: 'Serving', sub: 'Real-time API' },
                ].map((step, i) => {
                  const StepIcon = step.icon;
                  return (
                    <React.Fragment key={i}>
                      <div className="space-y-2">
                        <div className="p-3 bg-white/10 rounded-lg mx-auto w-fit">
                          <StepIcon />
                        </div>
                        <p className="text-sm font-medium">{step.label}</p>
                        <p className="text-xs text-white/50">{step.sub}</p>
                      </div>
                    </React.Fragment>
                  );
                })}
              </div>
              <div className="mt-4 flex justify-center">
                <div className="flex items-center gap-2 text-xs text-white/40">
                  <span>BigQuery</span>
                  <span>\u2192</span>
                  <span>dbt</span>
                  <span>\u2192</span>
                  <span>Feature Store</span>
                  <span>\u2192</span>
                  <span>Vertex AI</span>
                  <span>\u2192</span>
                  <span>Agent Tools</span>
                </div>
              </div>
            </div>
          </div>
        )}

        {/* ── Stage 1: Feature Engineering ────────────────────────────── */}
        {activeStage === 1 && (
          <div className="space-y-6 animate-fadeIn">
            <div className="text-center mb-8">
              <h2 className="text-3xl font-bold mb-2">Feature Engineering</h2>
              <p className="text-white/60">Delectable AI-specific signal extraction from raw data</p>
            </div>

            <div className="grid grid-cols-1 gap-6">
              {featureEngineering.map((feature) => {
                const Icon = feature.icon;
                const isExpanded = expandedCode === feature.id;
                return (
                  <div key={feature.id} className="bg-white/5 backdrop-blur-sm rounded-xl p-6 border border-white/10">
                    <div className="flex items-start gap-4">
                      <div className={`p-3 bg-gradient-to-br ${feature.color} rounded-lg flex-shrink-0`}>
                        <Icon />
                      </div>
                      <div className="flex-1 min-w-0">
                        <h3 className="text-xl font-semibold mb-1">{feature.name}</h3>
                        <p className="text-white/70 mb-4 text-sm">{feature.description}</p>

                        <div>
                          <h4 className="text-xs font-medium text-white/50 uppercase tracking-wider mb-2">Example Features</h4>
                          <div className="flex flex-wrap gap-2 mb-4">
                            {feature.examples.map((ex) => (
                              <code key={ex} className="px-2 py-1 bg-black/30 rounded text-xs text-green-300 font-mono">
                                {ex}
                              </code>
                            ))}
                          </div>
                        </div>

                        <button
                          onClick={() => setExpandedCode(isExpanded ? null : feature.id)}
                          className="text-sm text-cyan-400 hover:text-cyan-300 flex items-center gap-1 transition-colors"
                        >
                          {isExpanded ? 'Hide' : 'Show'} Code Example
                          <svg className={`w-4 h-4 transition-transform duration-200 ${isExpanded ? 'rotate-90' : ''}`} fill="none" stroke="currentColor" viewBox="0 0 24 24">
                            <polyline points="9 18 15 12 9 6" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round"/>
                          </svg>
                        </button>

                        {isExpanded && (
                          <div className="mt-4 bg-black/50 rounded-lg p-4 overflow-x-auto border border-white/5">
                            <pre className="text-xs text-green-300 font-mono whitespace-pre">{feature.code}</pre>
                          </div>
                        )}
                      </div>
                    </div>
                  </div>
                );
              })}
            </div>

            {/* Feature Summary */}
            <div className="bg-gradient-to-r from-cyan-500/10 to-blue-500/10 rounded-xl p-6 border border-cyan-500/20">
              <h3 className="text-lg font-semibold mb-4">Feature Pipeline Summary</h3>
              <div className="flex items-center justify-between">
                <div className="text-center">
                  <p className="text-2xl font-bold text-cyan-400">11</p>
                  <p className="text-sm text-white/60">Dietary Scores</p>
                </div>
                <div className="text-white/30"><ArrowRight /></div>
                <div className="text-center">
                  <p className="text-2xl font-bold text-blue-400">2,400</p>
                  <p className="text-sm text-white/60">Pantry Products</p>
                </div>
                <div className="text-white/30"><ArrowRight /></div>
                <div className="text-center">
                  <p className="text-2xl font-bold text-purple-400">210K</p>
                  <p className="text-sm text-white/60">Embedding Vectors</p>
                </div>
                <div className="text-white/30"><ArrowRight /></div>
                <div className="text-center">
                  <p className="text-2xl font-bold text-pink-400">6</p>
                  <p className="text-sm text-white/60">Mission Clusters</p>
                </div>
              </div>
            </div>
          </div>
        )}

        {/* ── Stage 2: Model Training ─────────────────────────────────── */}
        {activeStage === 2 && (
          <div className="space-y-6 animate-fadeIn">
            <div className="text-center mb-8">
              <h2 className="text-3xl font-bold mb-2">Delectable AI Models</h2>
              <p className="text-white/60">Actual models powering the personalization pipeline</p>
            </div>

            {/* Model Selector */}
            <div className="flex flex-wrap gap-2 justify-center mb-8">
              {Object.entries(mlModels).map(([key, model]) => (
                <button
                  key={key}
                  onClick={() => setSelectedModel(key)}
                  className={`px-4 py-2 rounded-lg text-sm transition-all ${
                    selectedModel === key
                      ? 'bg-gradient-to-r from-purple-500 to-pink-500 shadow-lg shadow-purple-500/30'
                      : 'bg-white/10 hover:bg-white/20'
                  }`}
                >
                  {model.name}
                </button>
              ))}
            </div>

            {/* Selected Model Details */}
            {selectedModel && mlModels[selectedModel] && (() => {
              const model = mlModels[selectedModel];
              return (
                <div className="bg-white/5 backdrop-blur-sm rounded-xl p-6 border border-white/10">
                  <div className="mb-6">
                    <h3 className="text-2xl font-bold mb-1">{model.name}</h3>
                    <div className="flex items-center gap-2 mb-2">
                      <span className="px-2 py-0.5 bg-purple-500/20 text-purple-300 rounded text-xs font-mono">{model.type}</span>
                    </div>
                    <p className="text-white/70">{model.description}</p>
                  </div>

                  <div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
                    {/* Left: Input/Output & Metrics */}
                    <div className="space-y-6">
                      <div className="space-y-3">
                        <div className="bg-black/20 rounded-lg p-4">
                          <h4 className="text-xs font-medium text-white/50 uppercase tracking-wider mb-2">Input</h4>
                          <p className="text-sm text-white/80">{model.input}</p>
                        </div>
                        <div className="bg-black/20 rounded-lg p-4">
                          <h4 className="text-xs font-medium text-white/50 uppercase tracking-wider mb-2">Output</h4>
                          <p className="text-sm text-white/80">{model.output}</p>
                        </div>
                      </div>

                      <div>
                        <h4 className="text-sm font-medium text-white/80 mb-3">Performance Metrics</h4>
                        <div className="grid grid-cols-2 gap-3">
                          {Object.entries(model.metrics).map(([metric, value]) => (
                            <div key={metric} className="bg-black/30 rounded-lg p-3 text-center">
                              <p className="text-xl font-bold text-green-400">{value}</p>
                              <p className="text-xs text-white/60">{metric}</p>
                            </div>
                          ))}
                        </div>
                      </div>

                      <div>
                        <h4 className="text-sm font-medium text-white/80 mb-2">Use Cases</h4>
                        <div className="space-y-1.5">
                          {model.useCases.map((uc) => (
                            <div key={uc} className="flex items-center gap-2 text-sm text-white/70">
                              <div className="text-purple-400"><Target /></div>
                              {uc}
                            </div>
                          ))}
                        </div>
                      </div>
                    </div>

                    {/* Right: Code */}
                    <div>
                      <h4 className="text-sm font-medium text-white/80 mb-3">Implementation</h4>
                      <div className="bg-black/50 rounded-lg p-4 overflow-x-auto max-h-[500px] overflow-y-auto border border-white/5">
                        <pre className="text-xs text-green-300 font-mono whitespace-pre">{model.code}</pre>
                      </div>
                    </div>
                  </div>
                </div>
              );
            })()}

            {/* Training Pipeline */}
            <div className="bg-gradient-to-r from-orange-500/10 to-red-500/10 rounded-xl p-6 border border-orange-500/20">
              <h3 className="text-lg font-semibold mb-4 flex items-center gap-2">
                <Activity />
                Training & Serving Pipeline
              </h3>
              <div className="grid grid-cols-5 gap-4 text-center text-sm">
                {[
                  { icon: Database, label: 'BigQuery', sub: 'Feature tables' },
                  { icon: GitBranch, label: 'Train/Val Split', sub: '80/20 temporal' },
                  { icon: Brain, label: 'Vertex AI Training', sub: 'Managed training' },
                  { icon: BarChart3, label: 'Evaluation', sub: 'AUC, MAE, silhouette' },
                  { icon: Zap, label: 'Vertex AI Serving', sub: 'Online prediction' }
                ].map((step, i) => {
                  const StepIcon = step.icon;
                  return (
                    <div key={i}>
                      <div className="p-3 bg-white/10 rounded-lg mb-2 mx-auto w-fit">
                        <StepIcon />
                      </div>
                      <p className="font-medium">{step.label}</p>
                      <p className="text-xs text-white/60">{step.sub}</p>
                    </div>
                  );
                })}
              </div>
            </div>
          </div>
        )}

        {/* ── Stage 3: Personalization Outputs ────────────────────────── */}
        {activeStage === 3 && (
          <div className="space-y-6 animate-fadeIn">
            <div className="text-center mb-8">
              <h2 className="text-3xl font-bold mb-2">Personalization Outputs</h2>
              <p className="text-white/60">How Delectable AI models feed the shopping assistant agent</p>
            </div>

            <div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
              {personalizationOutputs.map((output) => {
                const Icon = output.icon;
                return (
                  <div key={output.type} className="bg-white/5 backdrop-blur-sm rounded-xl p-6 border border-white/10 hover:border-white/20 transition-all">
                    <div className="flex items-start gap-4 mb-4">
                      <div className={`p-3 bg-gradient-to-br ${output.color} rounded-lg flex-shrink-0`}>
                        <Icon />
                      </div>
                      <div className="flex-1">
                        <h3 className="text-lg font-semibold mb-1">{output.type}</h3>
                        <p className="text-sm text-white/70 mb-2">{output.description}</p>
                        <span className="text-xs px-2 py-0.5 bg-purple-500/20 text-purple-300 rounded font-mono">
                          {output.model}
                        </span>
                      </div>
                    </div>

                    <div className="bg-black/30 rounded-lg p-4">
                      <h4 className="text-xs font-medium text-white/50 uppercase tracking-wider mb-2">Example</h4>
                      <p className="text-sm text-white/80 leading-relaxed">{output.example}</p>
                    </div>
                  </div>
                );
              })}
            </div>

            {/* Production Metrics */}
            <div className="bg-gradient-to-r from-green-500/10 to-emerald-500/10 rounded-xl p-6 border border-green-500/20">
              <h3 className="text-lg font-semibold mb-4 flex items-center gap-2">
                <LineChart />
                Production Metrics
              </h3>
              <div className="grid grid-cols-2 md:grid-cols-5 gap-6 text-center">
                <div>
                  <p className="text-2xl font-bold text-green-400">24h</p>
                  <p className="text-sm text-white/60">Profile Refresh</p>
                  <p className="text-xs text-white/40">Stale-While-Revalidate</p>
                </div>
                <div>
                  <p className="text-2xl font-bold text-blue-400">210K</p>
                  <p className="text-sm text-white/60">Embedding Vectors</p>
                  <p className="text-xs text-white/40">70K x 3 aspects</p>
                </div>
                <div>
                  <p className="text-2xl font-bold text-purple-400">~4,200</p>
                  <p className="text-sm text-white/60">Tool Calls/Day</p>
                  <p className="text-xs text-white/40">Agent invocations</p>
                </div>
                <div>
                  <p className="text-2xl font-bold text-pink-400">94%</p>
                  <p className="text-sm text-white/60">Personalization Rate</p>
                  <p className="text-xs text-white/40">Responses w/ signal</p>
                </div>
                <div>
                  <p className="text-2xl font-bold text-orange-400">2.8s</p>
                  <p className="text-sm text-white/60">Avg Response Time</p>
                  <p className="text-xs text-white/40">Including tool calls</p>
                </div>
              </div>
            </div>

            {/* Context Engineering Highlight */}
            <div className="bg-gradient-to-r from-purple-500/10 via-pink-500/10 to-orange-500/10 rounded-xl p-6 border border-purple-500/20">
              <h3 className="text-xl font-bold mb-4 flex items-center gap-2">
                <Sparkles />
                How Models Feed the Agent
              </h3>
              <p className="text-white/70 mb-4">
                Delectable AI uses a tool-calling architecture where the LLM agent invokes deterministic tools enriched by ML model outputs.
                Models pre-compute shopper signals; the agent consumes them at inference time.
              </p>
              <div className="grid grid-cols-1 md:grid-cols-3 gap-4">
                <div className="bg-white/5 rounded-lg p-4">
                  <h4 className="font-semibold mb-2 text-purple-300">Shopper Profile (Pre-computed)</h4>
                  <ul className="space-y-1 text-sm text-white/70">
                    <li>- 11 dietary propensity scores</li>
                    <li>- Mission cluster assignment</li>
                    <li>- Virtual pantry stock levels</li>
                    <li>- Purchase frequency patterns</li>
                  </ul>
                </div>
                <div className="bg-white/5 rounded-lg p-4">
                  <h4 className="font-semibold mb-2 text-pink-300">Product Intelligence (Indexed)</h4>
                  <ul className="space-y-1 text-sm text-white/70">
                    <li>- 3 x 768d embedding vectors</li>
                    <li>- Food Compass / Nutri-Score / NOVA</li>
                    <li>- Co-purchase affinity edges</li>
                    <li>- Health label classifications</li>
                  </ul>
                </div>
                <div className="bg-white/5 rounded-lg p-4">
                  <h4 className="font-semibold mb-2 text-orange-300">Agent Tools (Deterministic)</h4>
                  <ul className="space-y-1 text-sm text-white/70">
                    <li>- search_products (vector + keyword)</li>
                    <li>- optimize_shopping_list</li>
                    <li>- get_shopper_profile</li>
                    <li>- lookup_promotions</li>
                  </ul>
                </div>
              </div>
              <div className="mt-6 p-4 bg-black/30 rounded-lg">
                <p className="text-sm text-white/80">
                  <span className="font-semibold text-green-400">End-to-end flow:</span>{' '}
                  Shopper profile loaded (cached, 24h SWR) &rarr;
                  Agent receives query + profile context &rarr;
                  Tool calls with profile-enriched parameters &rarr;
                  Results ranked by dietary fit + food intelligence &rarr;
                  Personalized response in ~2.8s
                </p>
              </div>
            </div>
          </div>
        )}
      </div>

      {/* ── Footer ──────────────────────────────────────────────────────── */}
      <div className="bg-black/30 border-t border-white/10 mt-12 py-6">
        <div className="max-w-7xl mx-auto px-6 text-center text-sm text-white/60">
          <p>Delectable AI ML/Personalization Pipeline &bull; Your Grocer</p>
          <p className="mt-1">Powered by Vertex AI, BigQuery ML, dbt, and the Delectable AI Agent Framework</p>
        </div>
      </div>

      {/* Inline animation styles */}
      <style>{`
        @keyframes fadeIn {
          from { opacity: 0; transform: translateY(12px); }
          to { opacity: 1; transform: translateY(0); }
        }
        .animate-fadeIn {
          animation: fadeIn 0.4s ease-out;
        }
      `}</style>
    </div>
  );
};

// ── Render ────────────────────────────────────────────────────────────────

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<MLPersonalizationPipeline />);
