/* global React */
// Bike-Konfigurator — UI-Komponenten

const { useState, useEffect, useMemo, useCallback, useRef } = React;
const { fmtPrice, fmtNum, SLOT_LABELS, SLOT_TO_POOL, PRIMARY_SLOTS, SECONDARY_SLOTS, TERRAIN_OPTIONS, STYLE_OPTIONS, SKILL_OPTIONS } = window.BikeFmt;
const Icon = window.BikeIcon;

// ----- Brand registry -----
const BRAND_MAP = {
  "Shimano":      { url: "https://www.shimano.com",           logo: "https://logo.clearbit.com/shimano.com" },
  "SRAM":         { url: "https://www.sram.com",              logo: "https://logo.clearbit.com/sram.com" },
  "RockShox":     { url: "https://www.sram.com/en/rockshox",  logo: "https://logo.clearbit.com/sram.com" },
  "Fox":          { url: "https://www.ridefox.com",           logo: "https://logo.clearbit.com/ridefox.com" },
  "Maxxis":       { url: "https://www.maxxis.com",            logo: "https://logo.clearbit.com/maxxis.com" },
  "Schwalbe":     { url: "https://www.schwalbe.com",          logo: "https://logo.clearbit.com/schwalbe.com" },
  "Crankbrothers":{ url: "https://www.crankbrothers.com",     logo: "https://logo.clearbit.com/crankbrothers.com" },
  "OneUp":        { url: "https://www.oneupcomponents.com",   logo: "https://logo.clearbit.com/oneupcomponents.com" },
  "Ergon":        { url: "https://www.ergon-bike.com",        logo: "https://logo.clearbit.com/ergon-bike.com" },
  "BikeYoke":     { url: "https://bikeyoke.de",               logo: "https://logo.clearbit.com/bikeyoke.de" },
  "Bosch":        { url: "https://www.bosch-ebike.com",       logo: "https://logo.clearbit.com/bosch-ebike.com" },
  "DT Swiss":     { url: "https://www.dtswiss.com",           logo: "https://logo.clearbit.com/dtswiss.com" },
  "Suntour":      { url: "https://www.srsuntour-cycling.com", logo: "https://logo.clearbit.com/srsuntour-cycling.com" },
  "ODI":          { url: "https://www.odigrips.com",          logo: "https://logo.clearbit.com/odigrips.com" },
  "SQlab":        { url: "https://www.sq-lab.com",            logo: "https://logo.clearbit.com/sq-lab.com" },
  "X-Fusion":     { url: "https://www.xfusionshox.com",       logo: "https://logo.clearbit.com/xfusionshox.com" },
  "KS":           { url: "https://www.kindshock.com",         logo: "https://logo.clearbit.com/kindshock.com" },
  "Magura":       { url: "https://www.magura.com",            logo: "https://logo.clearbit.com/magura.com" },
  "TRP":          { url: "https://www.trpcycling.com",        logo: "https://logo.clearbit.com/trpcycling.com" },
  "Enve":         { url: "https://www.enve.com",              logo: "https://logo.clearbit.com/enve.com" },
  "Brand-X":      { url: "https://www.chainreactioncycles.com", logo: "https://logo.clearbit.com/chainreactioncycles.com" },
};

const FALLBACK_MARKETPLACE_PARTNERS = [
  {
    id: "bike_discount",
    name: "Bike-Discount",
    status: "pending",
    search_url_template: "https://www.bike-discount.de/de/search?sSearch={query_encoded}",
    tracking_url_template: "",
    priority: 10
  },
  {
    id: "bike24",
    name: "Bike24",
    status: "pending",
    search_url_template: "https://www.bike24.de/search?searchTerm={query_encoded}",
    tracking_url_template: "",
    priority: 20
  },
  {
    id: "mantel",
    name: "Mantel",
    status: "pending",
    search_url_template: "https://www.mantel.com/de/search?search={query_encoded}",
    tracking_url_template: "",
    priority: 50
  }
];

function detectBrand(name) {
  for (const brand of Object.keys(BRAND_MAP)) {
    if (name && name.toLowerCase().includes(brand.toLowerCase())) return brand;
  }
  return null;
}

function getMarketplacePartners() {
  const registry = window.BikeAffiliatePartners;
  const partners = Array.isArray(registry?.partners) ? registry.partners : FALLBACK_MARKETPLACE_PARTNERS;
  const usable = partners
    .filter(p => p.status !== "disabled" && p.search_url_template)
    .sort((a, b) => (a.priority || 999) - (b.priority || 999));
  return usable.length ? usable : FALLBACK_MARKETPLACE_PARTNERS;
}

function fillMarketplaceTemplate(template, item, partner, destinationUrl) {
  const rawQuery = item?.name || "mountainbike komponenten";
  const encodedQuery = encodeURIComponent(rawQuery);
  const campaign = "component_offer";
  const content = item?.id || "component";
  return template
    .replaceAll("{query}", rawQuery)
    .replaceAll("{query_encoded}", encodedQuery)
    .replaceAll("{item_id}", content)
    .replaceAll("{partner_id}", partner.id || partner.name || "partner")
    .replaceAll("{campaign}", campaign)
    .replaceAll("{content}", content)
    .replaceAll("{destination_url}", destinationUrl)
    .replaceAll("{destination_url_encoded}", encodeURIComponent(destinationUrl));
}

function appendMarketplaceParams(urlText, item, partner) {
  const url = new URL(urlText);
  const defaults = window.BikeAffiliatePartners?.tracking_defaults || {};
  url.searchParams.set("utm_source", defaults.utm_source || "bikeforge");
  url.searchParams.set("utm_medium", defaults.utm_medium || "marketplace");
  url.searchParams.set("utm_campaign", defaults.utm_campaign || "component_offer");
  url.searchParams.set("utm_content", item?.id || "component");
  url.searchParams.set("partner", partner.id || partner.name || "partner");
  return url.toString();
}

function marketplaceUrlFor(item, partner = getMarketplacePartners()[0]) {
  const destination = fillMarketplaceTemplate(partner.search_url_template, item, partner, "");
  const destinationWithParams = appendMarketplaceParams(destination, item, partner);
  if (partner.status === "active" && partner.tracking_url_template) {
    return fillMarketplaceTemplate(partner.tracking_url_template, item, partner, destinationWithParams);
  }
  return destinationWithParams;
}

function MarketplaceButton({ item, compact = false }) {
  if (!item) return null;
  return (
    <a
      href={marketplaceUrlFor(item)}
      target="_blank"
      rel="noopener noreferrer sponsored"
      onClick={e => {
        e.stopPropagation();
        window.BikeAnalytics?.track("marketplace_click", {
          source: "component_card",
          item_id: item.id,
          item_name: item.name,
          partner: getMarketplacePartners()[0]?.id || getMarketplacePartners()[0]?.name || "partner"
        });
      }}
      title={`${item.name} Angebote bei ${getMarketplacePartners()[0]?.name || "Partnern"} prüfen`}
      style={{
        display: "inline-flex", alignItems: "center", gap: compact ? 4 : 7,
        background: compact ? "transparent" : "var(--accent-soft)",
        color: compact ? "var(--accent)" : "var(--ink)",
        border: compact ? "none" : "1px solid var(--accent)",
        padding: compact ? 0 : "6px 10px",
        textDecoration: "none",
        font: `700 ${compact ? 10 : 11}px/1 var(--font-sans)`,
        letterSpacing: "0.7px",
        textTransform: "uppercase",
        whiteSpace: "nowrap"
      }}
    >
      Angebote
      <svg width={compact ? 8 : 10} height={compact ? 8 : 10} viewBox="0 0 10 10" fill="none" aria-hidden="true">
        <path d="M2 8L8 2M8 2H4M8 2V6" stroke="currentColor" strokeWidth="1.5" strokeLinecap="square"/>
      </svg>
    </a>
  );
}

function BrandBadge({ name, size = "sm" }) {
  const brand = detectBrand(name);
  if (!brand) return null;
  const { url, logo } = BRAND_MAP[brand];
  const [imgOk, setImgOk] = useState(true);
  const isLarge = size === "lg";
  return (
    <a href={url} target="_blank" rel="noopener noreferrer"
      onClick={e => e.stopPropagation()}
      title={`${brand} Website`}
      style={{
        display: "inline-flex", alignItems: "center", gap: isLarge ? 8 : 5,
        background: "rgba(255,255,255,0.06)", border: "1px solid var(--hairline)",
        padding: isLarge ? "6px 12px" : "3px 8px",
        textDecoration: "none", flexShrink: 0,
      }}>
      {imgOk && (
        <img src={logo} alt={brand}
          onError={() => setImgOk(false)}
          style={{ height: isLarge ? 18 : 12, width: "auto", objectFit: "contain", filter: "brightness(0) invert(1)" }} />
      )}
      <span style={{ font: `600 ${isLarge ? 11 : 9}px/1 var(--font-sans)`, letterSpacing: "0.6px", color: "var(--ink-muted)", textTransform: "uppercase" }}>
        {imgOk ? "" : brand}
      </span>
      <svg width={isLarge ? 10 : 8} height={isLarge ? 10 : 8} viewBox="0 0 10 10" fill="none" style={{ opacity: 0.5 }}>
        <path d="M2 8L8 2M8 2H4M8 2V6" stroke="currentColor" strokeWidth="1.5" strokeLinecap="square"/>
      </svg>
    </a>
  );
}

// ----- Brand Ticker -----
const TICKER_BRANDS = Object.entries(BRAND_MAP).map(([name, { url, logo }]) => ({ name, url, logo }));

function BrandTicker() {
  // Inject keyframes once
  useEffect(() => {
    if (document.getElementById("bf-ticker-style")) return;
    const s = document.createElement("style");
    s.id = "bf-ticker-style";
    s.textContent = `
      @keyframes bf-marquee {
        0%   { transform: translateX(0); }
        100% { transform: translateX(-50%); }
      }
      .bf-ticker-track { animation: bf-marquee 28s linear infinite; }
      .bf-ticker-track:hover { animation-play-state: paused; }
    `;
    document.head.appendChild(s);
  }, []);

  const items = [...TICKER_BRANDS, ...TICKER_BRANDS]; // seamless loop

  return (
    <div style={{
      overflow: "hidden",
      borderTop: "1px solid var(--hairline)",
      borderBottom: "1px solid var(--hairline)",
      padding: "18px 0",
      background: "rgba(0,0,0,0.25)",
      position: "relative",
    }}>
      {/* fade edges */}
      <div style={{ position: "absolute", inset: 0, zIndex: 2, pointerEvents: "none",
        background: "linear-gradient(90deg, var(--canvas) 0%, transparent 8%, transparent 92%, var(--canvas) 100%)" }} />
      <div className="bf-ticker-track" style={{ display: "flex", gap: 0, width: "max-content" }}>
        {items.map(({ name, url, logo }, i) => (
          <a key={i} href={url} target="_blank" rel="noopener noreferrer"
            title={name}
            style={{
              display: "inline-flex", alignItems: "center", gap: 10,
              padding: "0 36px", textDecoration: "none", flexShrink: 0,
              borderRight: "1px solid var(--hairline)",
            }}>
            <img
              src={logo} alt={name}
              style={{ height: 22, width: "auto", maxWidth: 80, objectFit: "contain",
                filter: "brightness(0) invert(1)", opacity: 0.55,
                transition: "opacity 0.2s" }}
              onMouseEnter={e => e.target.style.opacity = "1"}
              onMouseLeave={e => e.target.style.opacity = "0.55"}
              onError={e => {
                e.target.style.display = "none";
                e.target.nextSibling.style.display = "block";
              }}
            />
            <span style={{ display: "none", font: "700 11px/1 var(--font-sans)", letterSpacing: "1.2px",
              textTransform: "uppercase", color: "var(--ink-muted)" }}>{name}</span>
          </a>
        ))}
      </div>
    </div>
  );
}

// ----- Top Nav -----
function TopIconButton({ title, icon, onClick, href, active, badge }) {
  const className = "btn btn-ghost btn-sm icon-btn" + (active ? " is-active" : "");
  const content = (
    <>
      <Icon name={icon} size={15} />
      {badge ? <span className="icon-badge">{badge}</span> : null}
    </>
  );
  if (href) {
    return <a href={href} className={className} title={title} aria-label={title}>{content}</a>;
  }
  return <button className={className} onClick={onClick} title={title} aria-label={title}>{content}</button>;
}

function TopNav({
  onOpenGarage, savedCount, onReset, user, onLogout, plan, onUpgrade,
  onOpenExistingBike, onOpenCoach, installReady, isInstalled, onInstallApp,
  onOpenBikePass, onOpenBodySetup, onOpenSuspensionSetup
}) {
  return (
    <div className="topnav">
      <div className="brand-mark brand-mark-app" aria-label="BikeForge">
        <img className="brand-logo brand-logo-nav" src="assets/bikeforge-logo.png" alt="BikeForge" />
      </div>
      <div className="topnav-actions">
        {user && (
          <span className="topnav-user" style={{
            font: "600 11px/1 var(--font-sans)",
            letterSpacing: "0.6px",
            color: "var(--ink-soft)",
            marginRight: 8,
            textTransform: "uppercase"
          }}>
            {user.name}
          </span>
        )}
        {plan === "pro" ? (
          <span className="topnav-plan" style={{
            background: "var(--accent)", color: "var(--accent-ink)",
            font: "700 10px/1 var(--font-sans)", letterSpacing: "1.2px",
            textTransform: "uppercase", padding: "5px 10px", marginRight: 4
          }}>PRO</span>
        ) : (
          <TopIconButton title="Pro freischalten" icon="bolt" onClick={onUpgrade} active />
        )}
        {installReady && !isInstalled && (
          <TopIconButton title="BikeForge installieren" icon="download" onClick={onInstallApp} />
        )}
        <TopIconButton title="Fahrradpass" icon="pass" onClick={onOpenBikePass} />
        <TopIconButton title="Körperdaten einstellen" icon="body" onClick={onOpenBodySetup} />
        <TopIconButton title="Gabel und Dämpfer einstellen" icon="tune" onClick={onOpenSuspensionSetup} />
        <TopIconButton title="Bestandsbike analysieren" icon="upload" onClick={onOpenExistingBike} />
        <TopIconButton title="KI Custombike-Coach" icon="chat" onClick={onOpenCoach} />
        <TopIconButton title="Profil zurücksetzen" icon="refresh" onClick={onReset} />
        <TopIconButton title="Garage" icon="list" onClick={onOpenGarage} badge={savedCount > 0 ? savedCount : null} />
        {onLogout && (
          <TopIconButton title="Abmelden" icon="close" onClick={onLogout} />
        )}
        <TopIconButton title="Datenschutz und Rechtliches" icon="info" href="/legal/datenschutz.html" />
      </div>
    </div>
  );
}

function ToolDrawer({ title, eyebrow, icon, onClose, children, footer }) {
  return (
    <>
      <div className="drawer-backdrop" onClick={onClose} />
      <div className="drawer scroll-area">
        <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", padding: "24px 28px", borderBottom: "1px solid var(--hairline)", flex: "0 0 auto" }}>
          <div>
            <div className="eyebrow eyebrow-accent">{eyebrow}</div>
            <h3 style={{ marginTop: 6, display: "flex", alignItems: "center", gap: 10 }}>
              <Icon name={icon} size={18} /> {title}
            </h3>
          </div>
          <button className="btn btn-ghost btn-sm icon-btn" onClick={onClose} title="Schließen" aria-label="Schließen">
            <Icon name="close" size={14} />
          </button>
        </div>
        <div style={{ padding: 24, display: "grid", gap: 18 }}>
          {children}
        </div>
        {footer && (
          <div style={{ position: "sticky", bottom: 0, padding: "18px 24px", borderTop: "1px solid var(--hairline)", background: "rgba(15,15,15,0.96)" }}>
            {footer}
          </div>
        )}
      </div>
    </>
  );
}

const BIKE_PASS_PART_TYPES = [
  { value: "frame", label: "Rahmen" },
  { value: "fork", label: "Federgabel" },
  { value: "shock", label: "Daempfer" },
  { value: "wheels", label: "Laufraeder" },
  { value: "brakes", label: "Bremsen" },
  { value: "drivetrain", label: "Antrieb" },
  { value: "cockpit", label: "Cockpit" },
  { value: "dropper", label: "Dropper" },
  { value: "tires", label: "Reifen" },
  { value: "other", label: "Sonstiges" }
];

function BikePassDrawer({ profile, result, onAddPart, onRemovePart, onClose }) {
  const setup = profile.suspension_setup || {};
  const manualParts = profile.bike_pass_parts || [];
  const [draft, setDraft] = useState({
    type: "fork",
    brand: "",
    model: "",
    serial: "",
    year: "",
    notes: ""
  });
  const [lookup, setLookup] = useState({ loading: false, result: null, error: "" });
  const set = (key, value) => setDraft(prev => ({ ...prev, [key]: value }));
  const identifyPart = async () => {
    const query = [draft.brand, draft.model, draft.serial, draft.year].filter(Boolean).join(" ").trim();
    if (!query) {
      setLookup({ loading: false, result: null, error: "Bitte Marke, Modell oder Seriennummer eintragen." });
      return null;
    }
    setLookup({ loading: true, result: null, error: "" });
    try {
      const response = await fetch("/api/parts/identify", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ ...draft, query })
      });
      const data = await response.json();
      if (!response.ok) throw new Error(data.error || "Teil konnte nicht erkannt werden.");
      setLookup({ loading: false, result: data, error: "" });
      return data;
    } catch (error) {
      const message = error.message || "Teil konnte nicht erkannt werden.";
      setLookup({ loading: false, result: null, error: message });
      return null;
    }
  };
  const savePart = async () => {
    const identification = lookup.result || await identifyPart();
    if (!identification && !draft.model.trim() && !draft.serial.trim()) return;
    const best = identification?.match || {};
    onAddPart({
      type: draft.type,
      type_label: BIKE_PASS_PART_TYPES.find(item => item.value === draft.type)?.label || draft.type,
      brand: draft.brand.trim() || best.brand || "",
      model: draft.model.trim() || best.name || "",
      serial: draft.serial.trim(),
      year: draft.year ? Number(draft.year) || draft.year : "",
      notes: draft.notes.trim(),
      identification: identification || {
        status: "manual",
        confidence: 0,
        message: "Manuell eingetragen, noch nicht gegen Datenbank erkannt."
      }
    });
    setDraft({ type: "fork", brand: "", model: "", serial: "", year: "", notes: "" });
    setLookup({ loading: false, result: null, error: "" });
  };
  const rider = [
    profile.rider_name ? String(profile.rider_name).trim() : "",
    profile.rider_age ? `${profile.rider_age} Jahre` : "",
    `${profile.height} cm / ${profile.weight} kg`
  ].filter(Boolean).join(" · ");
  const rows = [
    ["Rahmen", result.build.frame?.name],
    ["Einsatz", (profile.terrain || "").toUpperCase()],
    ["Fahrer", rider],
    ["Antrieb", result.build.drivetrain?.name],
    ["Gabel", result.build.fork?.name],
    ["Dämpfer", result.build.shock?.name],
    ["Bremsen", result.build.brakes?.name],
    ["Gesamt", fmtPrice(result.total_price)]
  ];
  return (
    <ToolDrawer title="Fahrradpass" eyebrow="BikeForge Pass" icon="pass" onClose={onClose}>
      <div style={{ display: "grid", gridTemplateColumns: "repeat(3, 1fr)", gap: 1, background: "var(--hairline)" }}>
        <SpecMini label="Fit" value={result.scores.fit_score.toFixed(1)} />
        <SpecMini label="Performance" value={result.scores.performance_score.toFixed(1)} />
        <SpecMini label="Value" value={result.scores.value_score.toFixed(1)} />
      </div>
      <div style={{ display: "grid", gap: 1, background: "var(--hairline)" }}>
        {rows.map(([label, value]) => (
          <div key={label} style={{ display: "grid", gridTemplateColumns: "110px 1fr", gap: 14, padding: "12px 14px", background: "rgba(255,255,255,0.03)" }}>
            <span className="spec-label">{label}</span>
            <span style={{ color: "var(--ink)", font: "600 13px/1.35 var(--font-sans)" }}>{value || "—"}</span>
          </div>
        ))}
      </div>
      <div style={{ border: "1px solid var(--hairline)", padding: 14, display: "grid", gap: 8 }}>
        <div className="spec-label">Fahrwerk</div>
        <div style={{ color: "var(--ink-soft)", font: "400 12px/1.5 var(--font-sans)" }}>
          SAG {setup.sag ?? 28}% · Gabel-Rebound {setup.fork_rebound ?? 8} Klicks · Dämpfer-Rebound {setup.shock_rebound ?? 7} Klicks
        </div>
      </div>

      <div style={{ border: "1px solid var(--hairline-strong)", padding: 14, display: "grid", gap: 12, background: "rgba(255,255,255,0.035)" }}>
        <div>
          <div className="spec-label">Eigenes Teil eintragen</div>
          <div style={{ marginTop: 6, color: "var(--ink-muted)", font: "400 12px/1.45 var(--font-sans)" }}>
            Marke, Modell und Seriennummer erfassen. BikeForge gleicht den Eintrag mit der vorhandenen Teile-Datenbank ab und speichert unbekannte Teile als pruefpflichtigen Pass-Eintrag.
          </div>
        </div>
        <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 10 }}>
          <label className="field">
            <span className="field-label">Teiltyp</span>
            <select className="field-input" value={draft.type} onChange={event => set("type", event.target.value)}>
              {BIKE_PASS_PART_TYPES.map(option => <option key={option.value} value={option.value}>{option.label}</option>)}
            </select>
          </label>
          <label className="field">
            <span className="field-label">Marke optional</span>
            <input className="field-input" value={draft.brand} onChange={event => set("brand", event.target.value)} placeholder="z.B. RockShox" />
          </label>
        </div>
        <label className="field">
          <span className="field-label">Modell / Freitext</span>
          <input className="field-input" value={draft.model} onChange={event => set("model", event.target.value)} placeholder="z.B. Pike Ultimate Charger 3 29 160 mm" />
        </label>
        <div style={{ display: "grid", gridTemplateColumns: "1fr 120px", gap: 10 }}>
          <label className="field">
            <span className="field-label">{draft.type === "wheels" ? "Seriennummer Laufradsatz" : "Seriennummer optional"}</span>
            <input className="field-input" value={draft.serial} onChange={event => set("serial", event.target.value)} placeholder="Seriennummer / ID" />
          </label>
          <label className="field">
            <span className="field-label">Baujahr</span>
            <input className="field-input" value={draft.year} onChange={event => set("year", event.target.value)} placeholder="z.B. 2018" inputMode="numeric" />
          </label>
        </div>
        <label className="field">
          <span className="field-label">Notiz optional</span>
          <input className="field-input" value={draft.notes} onChange={event => set("notes", event.target.value)} placeholder="Zustand, Service, Rechnung, Besonderheiten" />
        </label>
        {lookup.result && (
          <div style={{ border: "1px solid var(--hairline)", padding: 10, display: "grid", gap: 4, color: "var(--ink-soft)", font: "400 12px/1.45 var(--font-sans)" }}>
            <strong style={{ color: "var(--ink)" }}>{lookup.result.match?.name || "Kein sicherer Treffer"}</strong>
            <span>{lookup.result.message}</span>
            {lookup.result.match?.details && <span>{lookup.result.match.details}</span>}
          </div>
        )}
        {lookup.error && <div style={{ color: "var(--accent)", font: "600 12px/1.4 var(--font-sans)" }}>{lookup.error}</div>}
        <div style={{ display: "flex", gap: 8, flexWrap: "wrap" }}>
          <button className="btn btn-ghost btn-sm" onClick={identifyPart} disabled={lookup.loading}>
            <Icon name="search" size={14} /> {lookup.loading ? "Erkennung laeuft ..." : "Teil erkennen"}
          </button>
          <button className="btn btn-primary btn-sm" onClick={savePart}>
            <Icon name="check" size={14} /> Teil speichern
          </button>
        </div>
      </div>

      <div style={{ display: "grid", gap: 10 }}>
        <div className="spec-label">Eingetragene eigene Teile</div>
        {manualParts.length ? manualParts.map(part => (
          <div key={part.id} style={{ border: "1px solid var(--hairline)", padding: 12, display: "grid", gap: 7, background: "rgba(255,255,255,0.03)" }}>
            <div style={{ display: "flex", justifyContent: "space-between", gap: 12 }}>
              <strong style={{ color: "var(--ink)", font: "700 13px/1.3 var(--font-sans)" }}>
                {part.type_label || part.type}: {[part.brand, part.model].filter(Boolean).join(" ") || "Unbenanntes Teil"}
              </strong>
              <button className="btn btn-ghost btn-sm icon-btn" title="Teil entfernen" aria-label="Teil entfernen" onClick={() => onRemovePart(part.id)}>
                <Icon name="close" size={14} />
              </button>
            </div>
            <div style={{ color: "var(--ink-soft)", font: "400 12px/1.45 var(--font-sans)" }}>
              {part.serial ? `Seriennummer: ${part.serial}` : "Keine Seriennummer hinterlegt"}{part.year ? ` · Baujahr: ${part.year}` : ""}
            </div>
            <div style={{ color: "var(--ink-muted)", font: "400 12px/1.45 var(--font-sans)" }}>
              {part.identification?.message || "Manueller Fahrradpass-Eintrag."}
            </div>
          </div>
        )) : (
          <div style={{ border: "1px solid var(--hairline)", padding: 12, color: "var(--ink-muted)", font: "400 12px/1.45 var(--font-sans)" }}>
            Noch keine eigenen Teile eingetragen.
          </div>
        )}
      </div>
    </ToolDrawer>
  );
}

function BodySetupDrawer({ profile, onApply, onClose }) {
  const [draft, setDraft] = useState({
    rider_name: profile.rider_name || "",
    rider_age: profile.rider_age || "",
    height: profile.height || 181,
    weight: profile.weight || 92
  });
  const set = (key, value) => setDraft(prev => ({ ...prev, [key]: value }));
  return (
    <ToolDrawer
      title="Körperdaten"
      eyebrow="Fit Setup"
      icon="body"
      onClose={onClose}
      footer={(
        <button className="btn btn-primary" onClick={() => onApply(draft)}>
          <Icon name="check" size={14} /> Übernehmen
        </button>
      )}
    >
      <div style={{ display: "grid", gridTemplateColumns: "1fr 120px", gap: 14 }}>
        <label className="field">
          <span className="field-label">Name optional</span>
          <input
            className="field-input"
            value={draft.rider_name}
            onChange={event => set("rider_name", event.target.value)}
            placeholder="z.B. Alex"
          />
        </label>
        <label className="field">
          <span className="field-label">Alter optional</span>
          <input
            className="field-input"
            type="number"
            min="8"
            max="90"
            value={draft.rider_age}
            onChange={event => set("rider_age", event.target.value)}
            placeholder="—"
          />
        </label>
      </div>
      <SliderField label="Körpergröße" min={150} max={205} step={1} unit="cm" value={draft.height} onChange={(v) => set("height", v)} large />
      <SliderField label="Gewicht" min={45} max={140} step={1} unit="kg" value={draft.weight} onChange={(v) => set("weight", v)} large />
      <div style={{ color: "var(--ink-muted)", font: "400 12px/1.5 var(--font-sans)" }}>
        Diese Werte beeinflussen Reach-Empfehlung, Bremsreserve, Belastung und Teilepriorität.
      </div>
    </ToolDrawer>
  );
}

function SuspensionSetupDrawer({ profile, result, onApply, onClose }) {
  const current = profile.suspension_setup || {};
  const [draft, setDraft] = useState({
    sag: current.sag ?? 28,
    fork_rebound: current.fork_rebound ?? 8,
    fork_compression: current.fork_compression ?? 3,
    shock_rebound: current.shock_rebound ?? 7,
    shock_compression: current.shock_compression ?? 2
  });
  const set = (key, value) => setDraft(prev => ({ ...prev, [key]: value }));
  const source = current.source === "body_auto" ? "Automatisch aus Körperdaten vorgeschlagen." : "Manuell gesetztes Setup.";
  return (
    <ToolDrawer
      title="Fahrwerk-Setup"
      eyebrow="Gabel & Dämpfer"
      icon="tune"
      onClose={onClose}
      footer={(
        <button className="btn btn-primary" onClick={() => onApply(draft)}>
          <Icon name="check" size={14} /> Setup speichern
        </button>
      )}
    >
      <div style={{ display: "grid", gap: 8, padding: 14, border: "1px solid var(--hairline)" }}>
        <div className="spec-label">Aktuelle Komponenten</div>
        <div style={{ color: "var(--ink-soft)", font: "400 12px/1.5 var(--font-sans)" }}>
          Gabel: {result.build.fork?.name || "—"}<br />
          Dämpfer: {result.build.shock?.name || "—"}<br />
          Fahrer: {profile.height} cm / {profile.weight} kg · {source}
        </div>
      </div>
      <SliderField label="SAG" min={15} max={35} step={1} unit="%" value={draft.sag} onChange={(v) => set("sag", v)} />
      <SliderField label="Gabel Rebound" min={0} max={18} step={1} unit="Klicks" value={draft.fork_rebound} onChange={(v) => set("fork_rebound", v)} />
      <SliderField label="Gabel Compression" min={0} max={12} step={1} unit="Klicks" value={draft.fork_compression} onChange={(v) => set("fork_compression", v)} />
      <SliderField label="Dämpfer Rebound" min={0} max={18} step={1} unit="Klicks" value={draft.shock_rebound} onChange={(v) => set("shock_rebound", v)} />
      <SliderField label="Dämpfer Compression" min={0} max={12} step={1} unit="Klicks" value={draft.shock_compression} onChange={(v) => set("shock_compression", v)} />
      <div style={{ color: "var(--ink-muted)", font: "400 12px/1.5 var(--font-sans)" }}>
        Startwert: Rebound von geschlossen aus zählen. Vor echter Fahrt Herstellerdruck, SAG und Sicherheitscheck prüfen.
      </div>
    </ToolDrawer>
  );
}

const EXISTING_BIKE_FIELDS = [
  { key: "fork", label: "Gabel", placeholder: "Fox 36 Performance" },
  { key: "shock", label: "Dämpfer", placeholder: "Fox Float X" },
  { key: "brakes", label: "Bremsen", placeholder: "SRAM Code R" },
  { key: "drivetrain", label: "Antrieb", placeholder: "SRAM GX Eagle" },
  { key: "wheels", label: "Laufräder", placeholder: "DT Swiss EX1700" },
  { key: "tires", label: "Reifen", placeholder: "Maxxis Minion DHF" },
  { key: "dropper", label: "Dropper", placeholder: "OneUp V2 150" },
  { key: "cockpit", label: "Cockpit", placeholder: "Race Face Turbine" },
  { key: "grips", label: "Griffe", placeholder: "ODI Elite Pro" },
  { key: "saddle", label: "Sattel", placeholder: "Ergon SM Pro" },
  { key: "chain", label: "Kette", placeholder: "SRAM GX Eagle Chain" },
  { key: "pedals", label: "Pedale", placeholder: "Crankbrothers Stamp" }
];

const PHOTO_HINTS_BY_SLOT = {
  fork: "Gabel-Decals und linke/rechte Seite freier sichtbar fotografieren.",
  shock: "Daempferbereich im Rahmendreieck heller aufnehmen.",
  brakes: "Bremssattel und Bremshebel naeher sichtbar machen.",
  drivetrain: "Rechte Seite mit Schaltwerk, Kassette und Kurbel fotografieren.",
  wheels: "Naben und Felgenprofil seitlich klar sichtbar lassen.",
  tires: "Reifenflanke lesbar aufnehmen.",
  dropper: "Sattelstuetze und Remote-Hebel heller zeigen.",
  cockpit: "Lenker, Vorbau und Hebel nicht vom Rahmen verdecken lassen.",
  grips: "Lenkerenden und Griffe naeher sichtbar machen.",
  saddle: "Sattel seitlich und von oben nicht verdecken.",
  chain: "Kette und Kassette auf der Antriebsseite sichtbar machen.",
  pedals: "Pedale seitlich freistellen, nicht vom Kurbelarm verdecken lassen."
};

function confidenceLevel(value) {
  const confidence = Math.max(0, Math.min(1, Number(value || 0)));
  if (confidence >= 0.75) return "ok";
  if (confidence >= 0.45) return "check";
  return "low";
}

function confidenceHintFor(field, confidence) {
  const percent = Math.round(Math.max(0, Math.min(1, Number(confidence || 0))) * 100);
  const base = PHOTO_HINTS_BY_SLOT[field.key] || "Komponente manuell pruefen.";
  return `${field.label}: ${percent}% - ${base}`;
}

function ExistingBikeModal({ profile, onApply, onClose }) {
  const [preview, setPreview] = useState(null);
  const [modelName, setModelName] = useState("");
  const [terrain, setTerrain] = useState(profile.terrain || "trail");
  const [bikeType, setBikeType] = useState(profile.bike_type || "analog");
  const [components, setComponents] = useState({});
  const [componentActions, setComponentActions] = useState({});
  const [componentConfidence, setComponentConfidence] = useState({});
  const [analyzing, setAnalyzing] = useState(false);
  const [detailAnalyzingSlot, setDetailAnalyzingSlot] = useState(null);
  const [detailMessage, setDetailMessage] = useState(null);
  const [analysisError, setAnalysisError] = useState(null);
  const [analysisMeta, setAnalysisMeta] = useState(null);

  const setComponent = (key, value) => {
    setComponents(prev => ({ ...prev, [key]: value }));
    if (String(value || "").trim()) {
      setComponentActions(prev => ({ ...prev, [key]: prev[key] || "keep" }));
    } else {
      setComponentConfidence(prev => {
        const next = { ...prev };
        delete next[key];
        return next;
      });
    }
  };

  const applyVisionAnalysis = (payload = {}) => {
    const analysis = payload.analysis || {};
    if (analysis.bike_model_name) setModelName(analysis.bike_model_name);
    if (analysis.frame_category) setTerrain(analysis.frame_category);
    setBikeType(analysis.is_ebike ? "ebike" : "analog");

    const recognized = Object.entries(analysis.components || {})
      .map(([key, value]) => [key, String(value || "").trim()])
      .filter(([, value]) => value);

    if (recognized.length) {
      setComponents(prev => ({ ...prev, ...Object.fromEntries(recognized) }));
      setComponentActions(prev => ({
        ...prev,
        ...Object.fromEntries(recognized.map(([key]) => [key, prev[key] || "keep"]))
      }));
      setComponentConfidence(prev => ({
        ...prev,
        ...Object.fromEntries(
          recognized.map(([key]) => [key, Math.max(0, Math.min(1, Number(analysis.component_confidence?.[key] || 0)))])
        )
      }));
    }

    setAnalysisMeta({
      provider: payload.provider || "vision",
      model: payload.model || "",
      confidence: Number(analysis.confidence || 0),
      observations: Array.isArray(analysis.observations) ? analysis.observations.filter(Boolean) : []
    });
  };

  const applyDetailAnalysis = (slot, payload = {}) => {
    const analysis = payload.analysis || {};
    const value = String(analysis.components?.[slot] || "").trim();
    const confidence = Math.max(0, Math.min(1, Number(analysis.component_confidence?.[slot] || 0)));
    const field = EXISTING_BIKE_FIELDS.find(item => item.key === slot);

    if (!value) {
      setDetailMessage({
        kind: "warn",
        text: `${field?.label || slot}: Detailfoto konnte kein eindeutiges Teil erkennen. Feld bitte manuell pruefen.`
      });
      setComponentConfidence(prev => ({ ...prev, [slot]: confidence }));
      return;
    }

    setComponents(prev => ({ ...prev, [slot]: value }));
    setComponentActions(prev => ({ ...prev, [slot]: prev[slot] || "keep" }));
    setComponentConfidence(prev => ({ ...prev, [slot]: confidence }));
    setDetailMessage({
      kind: confidence >= 0.75 ? "ok" : "warn",
      text: `${field?.label || slot}: Detailfoto erkannt - ${value} (${Math.round(confidence * 100)}%).`
    });
  };

  const analyzeImage = async (file) => {
    setAnalyzing(true);
    setAnalysisError(null);
    setDetailMessage(null);
    setAnalysisMeta(null);
    try {
      const body = new FormData();
      body.append("image", file);
      const response = await fetch(`${window.BIKEFORGE_API_URL}/vision/analyze`, { method: "POST", body });
      const payload = await response.json().catch(() => ({}));
      if (!response.ok) {
        throw new Error(payload.detail || payload.error || "Bildanalyse fehlgeschlagen.");
      }
      applyVisionAnalysis(payload);
    } catch (error) {
      setAnalysisError(error.message || "Bildanalyse fehlgeschlagen.");
    } finally {
      setAnalyzing(false);
    }
  };

  const analyzeDetailImage = async (slot, file) => {
    setDetailAnalyzingSlot(slot);
    setAnalysisError(null);
    setDetailMessage(null);
    try {
      const body = new FormData();
      body.append("image", file);
      body.append("slot", slot);
      const response = await fetch(`${window.BIKEFORGE_API_URL}/vision/analyze`, { method: "POST", body });
      const payload = await response.json().catch(() => ({}));
      if (!response.ok) {
        throw new Error(payload.detail || payload.error || "Detailanalyse fehlgeschlagen.");
      }
      applyDetailAnalysis(slot, payload);
    } catch (error) {
      const field = EXISTING_BIKE_FIELDS.find(item => item.key === slot);
      setDetailMessage({
        kind: "error",
        text: `${field?.label || slot}: ${error.message || "Detailanalyse fehlgeschlagen."}`
      });
    } finally {
      setDetailAnalyzingSlot(null);
    }
  };

  const handleFile = (event) => {
    const file = event.target.files?.[0];
    if (!file) return;
    const reader = new FileReader();
    reader.onload = () => setPreview(reader.result);
    reader.readAsDataURL(file);
    analyzeImage(file);
    event.target.value = "";
  };

  const handleDetailFile = (slot, event) => {
    const file = event.target.files?.[0];
    if (!file) return;
    analyzeDetailImage(slot, file);
    event.target.value = "";
  };

  const apply = () => {
    onApply({
      model_name: modelName.trim() || "Bestandsbike",
      terrain,
      is_ebike: bikeType === "ebike",
      bike_type: bikeType,
      style: profile.riding_style || "balanced",
      photo_preview: preview,
      components: Object.fromEntries(
        Object.entries(components)
          .map(([key, value]) => [key, String(value || "").trim()])
          .filter(([, value]) => value)
      ),
      component_actions: Object.fromEntries(
        Object.entries(components)
          .map(([key, value]) => [key, String(value || "").trim()])
          .filter(([, value]) => value)
          .map(([key]) => [key, componentActions[key] || "keep"])
      ),
      component_confidence: Object.fromEntries(
        Object.entries(components)
          .map(([key, value]) => [key, String(value || "").trim()])
          .filter(([, value]) => value)
          .map(([key]) => [key, Math.max(0, Math.min(1, Number(componentConfidence[key] || 0)))])
      )
    });
  };

  const confidenceHints = EXISTING_BIKE_FIELDS
    .filter(field => components[field.key] && componentConfidence[field.key] > 0 && componentConfidence[field.key] < 0.75)
    .map(field => ({
      key: field.key,
      level: confidenceLevel(componentConfidence[field.key]),
      text: confidenceHintFor(field, componentConfidence[field.key])
    }));
  const lowConfidenceCount = confidenceHints.filter(hint => hint.level === "low").length;

  return (
    <>
      <div className="drawer-backdrop" onClick={onClose} style={{ zIndex: 920 }} />
      <div style={{
        position: "fixed", inset: "5vh auto 5vh 50%", transform: "translateX(-50%)",
        zIndex: 921, width: "min(980px, 94vw)", overflow: "hidden",
        background: "var(--canvas-card)", border: "1px solid var(--hairline-strong)",
        display: "grid", gridTemplateColumns: "minmax(280px, 0.85fr) 1.15fr"
      }}>
        <div style={{ background: "var(--canvas-deep)", minHeight: 620, position: "relative", borderRight: "1px solid var(--hairline)" }}>
          {preview ? (
            <img src={preview} alt="Bestandsbike Vorschau" style={{ position: "absolute", inset: 0, width: "100%", height: "100%", objectFit: "cover", filter: "brightness(0.72) saturate(0.95)" }} />
          ) : (
            <div style={{ position: "absolute", inset: 0, display: "grid", placeItems: "center", padding: 28, color: "var(--ink-muted)" }}>
              <img
                src="assets/bike-photo-guide.svg"
                alt="Foto-Anleitung: Bike vollstaendig seitlich, hell und auf Nabenhoehe fotografieren"
                style={{ width: "100%", maxWidth: 430, border: "1px solid var(--hairline-strong)", background: "rgba(0,0,0,0.22)" }}
              />
            </div>
          )}
          <div style={{ position: "absolute", inset: 0, background: "linear-gradient(180deg, rgba(0,0,0,0.15), rgba(0,0,0,0.82))" }} />
          <label style={{
            position: "absolute", left: 28, right: 28, bottom: 28,
            display: "flex", alignItems: "center", justifyContent: "center", gap: 10,
            padding: "16px 18px", background: "rgba(24,24,24,0.82)",
            border: "1px solid var(--hairline-strong)", color: "var(--ink)",
            font: "700 12px/1 var(--font-sans)", letterSpacing: "1.2px",
            textTransform: "uppercase", cursor: "pointer"
          }}>
            <Icon name="upload" size={14} /> {analyzing ? "Analysiere ..." : "Foto wählen"}
            <input type="file" accept="image/*" onChange={handleFile} style={{ display: "none" }} />
          </label>
        </div>

        <div className="scroll-area" style={{ maxHeight: "90vh", padding: "32px 36px 36px" }}>
          <div style={{ display: "flex", alignItems: "flex-start", justifyContent: "space-between", gap: 18, marginBottom: 28 }}>
            <div>
              <div className="eyebrow eyebrow-accent">Bestandsbike</div>
              <h2 style={{ marginTop: 8, fontSize: 30, lineHeight: 1.1 }}>Ausstattung als Startpunkt.</h2>
              <p style={{ marginTop: 12, color: "var(--ink-soft)", maxWidth: 560 }}>
                Foto hochladen, Vision-Analyse prüfen und sichtbare Teile korrigieren. BikeForge behandelt den Rahmen als vorhandene Basis.
              </p>
              <div style={{
                marginTop: 14, display: "flex", gap: 8, flexWrap: "wrap",
                color: "var(--ink-muted)", font: "700 10px/1 var(--font-sans)",
                letterSpacing: "0.8px", textTransform: "uppercase"
              }}>
                <span style={{ border: "1px solid var(--hairline)", padding: "7px 9px" }}>Seitlich fotografieren</span>
                <span style={{ border: "1px solid var(--hairline)", padding: "7px 9px" }}>Ganzes Bike sichtbar</span>
                <span style={{ border: "1px solid var(--hairline)", padding: "7px 9px" }}>Helles Licht</span>
                <span style={{ border: "1px solid var(--hairline)", padding: "7px 9px" }}>ruhiger Hintergrund</span>
              </div>
            </div>
            <button className="btn btn-ghost btn-sm" onClick={onClose}><Icon name="close" size={16} /></button>
          </div>

          {(analysisMeta || analysisError || analyzing) && (
            <div style={{
              marginBottom: 22, padding: 14, border: `1px solid ${analysisError ? "rgba(255,98,98,0.45)" : "var(--hairline)"}`,
              background: analysisError ? "rgba(255,98,98,0.08)" : "rgba(255,255,255,0.035)",
              color: analysisError ? "var(--danger)" : "var(--ink-soft)",
              font: "400 13px/1.5 var(--font-sans)"
            }}>
              {analyzing && "Vision-Analyse laeuft. Du kannst die Felder danach weiter bearbeiten."}
              {analysisError && `Vision-Analyse nicht verfuegbar: ${analysisError}`}
              {analysisMeta && !analysisError && (
                <>
                  <strong style={{ color: "var(--ink)" }}>
                    Vision erkannt ({Math.round(Math.max(0, Math.min(1, analysisMeta.confidence)) * 100)}% Sicherheit)
                  </strong>
                  {analysisMeta.observations.length > 0 && (
                    <ul style={{ margin: "8px 0 0", paddingLeft: 18 }}>
                      {analysisMeta.observations.map((item, index) => <li key={index}>{item}</li>)}
                    </ul>
                  )}
                </>
              )}
            </div>
          )}

          {confidenceHints.length > 0 && (
            <div style={{
              marginBottom: 22, padding: 14, border: "1px solid rgba(218,41,28,0.38)",
              background: "rgba(218,41,28,0.07)", color: "var(--ink-soft)",
              font: "400 13px/1.5 var(--font-sans)"
            }}>
              <strong style={{ color: "var(--ink)" }}>
                {lowConfidenceCount >= 3 ? "Foto wahrscheinlich neu aufnehmen" : "Bitte manuell pruefen"}
              </strong>
              <div style={{ marginTop: 6 }}>
                {lowConfidenceCount >= 3
                  ? "Mehrere Teile wurden unsicher erkannt. Seitlicher, heller und mit ruhigem Hintergrund fotografieren."
                  : "Einige erkannte Teile sind unsicher. Pruefe diese Felder vor dem Uebernehmen."}
              </div>
              <ul style={{ margin: "10px 0 0", paddingLeft: 18 }}>
                {confidenceHints.slice(0, 5).map(hint => (
                  <li key={hint.key}>{hint.text}</li>
                ))}
              </ul>
            </div>
          )}

          {detailMessage && (
            <div style={{
              marginBottom: 22, padding: 12, border: `1px solid ${detailMessage.kind === "error" ? "rgba(255,98,98,0.45)" : "var(--hairline)"}`,
              background: detailMessage.kind === "ok" ? "rgba(255,255,255,0.04)" : "rgba(218,41,28,0.07)",
              color: detailMessage.kind === "error" ? "var(--danger)" : "var(--ink-soft)",
              font: "500 13px/1.45 var(--font-sans)"
            }}>
              {detailMessage.text}
            </div>
          )}

          <div style={{ display: "grid", gridTemplateColumns: "1.1fr 0.9fr 0.9fr", gap: 14, marginBottom: 22 }}>
            <div className="field">
              <div className="field-label">Bike / Modell</div>
              <input className="field-input" value={modelName} onChange={e => setModelName(e.target.value)} placeholder="z.B. Cube Stereo Hybrid 140" />
            </div>
            <div className="field">
              <div className="field-label">Typ</div>
              <Segmented options={[{ value: "analog", label: "Bio" }, { value: "ebike", label: "E-Bike" }]} value={bikeType} onChange={setBikeType} />
            </div>
            <div className="field">
              <div className="field-label">Einsatz</div>
              <Segmented options={[{ value: "trail", label: "Trail" }, { value: "enduro", label: "Enduro" }, { value: "xc", label: "XC" }]} value={terrain} onChange={setTerrain} />
            </div>
          </div>

          <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 14 }}>
            {EXISTING_BIKE_FIELDS.map(field => (
              <div className="field" key={field.key}>
                <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", gap: 10, marginBottom: 8 }}>
                  <div style={{ display: "flex", alignItems: "center", gap: 8, minWidth: 0 }}>
                    <div className="field-label" style={{ margin: 0 }}>{field.label}</div>
                    {componentConfidence[field.key] > 0 && (
                      <span title="Vision-Sicherheit fuer diese Erkennung" style={{
                        border: "1px solid var(--hairline)", color: componentConfidence[field.key] >= 0.75 ? "var(--accent)" : componentConfidence[field.key] < 0.45 ? "var(--danger)" : "var(--ink-muted)",
                        padding: "3px 6px", font: "700 9px/1 var(--font-sans)", letterSpacing: "0.6px",
                        textTransform: "uppercase", whiteSpace: "nowrap"
                      }}>
                        {Math.round(componentConfidence[field.key] * 100)}%
                      </span>
                    )}
                  </div>
                  <Segmented
                    options={[{ value: "keep", label: "Behalten" }, { value: "replace", label: "Ersetzen" }]}
                    value={componentActions[field.key] || "keep"}
                    onChange={value => setComponentActions(prev => ({ ...prev, [field.key]: value }))}
                  />
                </div>
                <input
                  className="field-input"
                  value={components[field.key] || ""}
                  onChange={e => setComponent(field.key, e.target.value)}
                  placeholder={field.placeholder}
                  style={{ fontSize: 14 }}
                />
                {components[field.key] && componentConfidence[field.key] > 0 && componentConfidence[field.key] < 0.75 && (
                  <label style={{
                    marginTop: 8, display: "inline-flex", alignItems: "center", justifyContent: "center", gap: 8,
                    border: "1px solid var(--hairline-strong)", padding: "8px 10px",
                    color: "var(--ink-soft)", font: "700 10px/1 var(--font-sans)",
                    letterSpacing: "0.8px", textTransform: "uppercase", cursor: "pointer"
                  }}>
                    <Icon name="upload" size={12} />
                    {detailAnalyzingSlot === field.key ? "Detailanalyse ..." : "Detailfoto"}
                    <input type="file" accept="image/*" onChange={event => handleDetailFile(field.key, event)} style={{ display: "none" }} />
                  </label>
                )}
              </div>
            ))}
          </div>

          <div style={{
            marginTop: 24, padding: 16, border: "1px solid var(--hairline)",
            background: "rgba(255,255,255,0.03)", color: "var(--ink-soft)",
            font: "400 13px/1.5 var(--font-sans)"
          }}>
            Die Vision-Analyse ist ein Startpunkt, keine technische Garantie. Markiere gute Teile mit Behalten; bei Ersetzen nutzt BikeForge die erkannten Daten nur als Kontext und sucht eine passende neue Komponente.
          </div>

          <div style={{ display: "flex", justifyContent: "flex-end", gap: 10, marginTop: 28 }}>
            <button className="btn btn-ghost" onClick={onClose}>Abbrechen</button>
            <button className="btn btn-primary" onClick={apply}>
              <Icon name="check" size={14} /> Als Basis übernehmen
            </button>
          </div>
        </div>
      </div>
    </>
  );
}

// ----- Upgrade Modal -----
const COACH_QUICK_PROMPTS = [
  "Erstelle aus meinem Briefing einen finalen Custom-Plan.",
  "Welche Teile soll ich in welcher Reihenfolge tauschen?",
  "Welche Teile sollte ich aus Sicherheitsgruenden nicht nur wegen Optik tauschen?"
];

const COACH_LOOK_OPTIONS = [
  { value: "rosso", label: "Rosso", hint: "Rot / Schwarz" },
  { value: "stealth", label: "Stealth", hint: "Schwarz / Gunmetal" },
  { value: "neon", label: "Neon", hint: "Lauter Trail-Look" },
  { value: "oil", label: "Oil Slick", hint: "Details / Schrauben" },
  { value: "raw", label: "Raw", hint: "Metall / clean" }
];

const COACH_BUDGET_OPTIONS = [
  { value: "low", label: "50-120 EUR" },
  { value: "balanced", label: "120-250 EUR" },
  { value: "premium", label: "250-500 EUR" }
];

const COACH_PRIORITY_OPTIONS = [
  { value: "style", label: "Optik" },
  { value: "comfort", label: "Komfort" },
  { value: "performance", label: "Performance" },
  { value: "durability", label: "Haltbarkeit" }
];

function summarizeResultForCoach(result) {
  if (!result) return null;
  const parts = Object.entries(result.build || {})
    .filter(([slot, item]) => item && typeof item === "object" && !slot.startsWith("__"))
    .map(([slot, item]) => ({
      slot,
      label: SLOT_LABELS[slot] || slot,
      name: item.name,
      price: item.price || 0,
      level: item.level || item.category || null,
      color: item.color || null
    }));
  return {
    total_price: result.total_price,
    scores: result.scores,
    warnings: result.warnings || [],
    explanation: result.explanation || null,
    parts
  };
}

function priceCapForBriefing(level) {
  if (level === "low") return 120;
  if (level === "premium") return 500;
  return 250;
}

function catalogScore(item, slot, preferences, currentItem) {
  const name = `${item.name || ""}`.toLowerCase();
  let score = 0;
  if (item.id === currentItem?.id) score -= 20;
  if ((preferences.noGos || "").toLowerCase().includes("carbon") && name.includes("carbon")) score -= 30;
  if (preferences.priority === "comfort" && /(ergon|sqlab|esi|saddle|grip)/i.test(item.name || "")) score += 12;
  if (preferences.priority === "performance" && (item.score || item.performance || 0) >= 8) score += 10;
  if (preferences.priority === "durability" && /(hope|odi|oneup|race face|crankbrothers|renthal)/i.test(item.name || "")) score += 8;
  if (preferences.look === "stealth" && /(black|alloy|race face|oneup|hope|renthal|spank)/i.test(item.name || "")) score += 5;
  if (preferences.look === "raw" && /(alloy|silver|spank|race face|pro koryak)/i.test(item.name || "")) score += 5;
  if (preferences.look === "oil" && /(hope|crankbrothers|oneup)/i.test(item.name || "")) score += 4;
  if (preferences.look === "neon" && /(pnw|race face|dmr|sdg)/i.test(item.name || "")) score += 4;
  if (slot === "saddle" && preferences.noGos?.toLowerCase().includes("komfort") && /(ergon|sqlab|fizik)/i.test(item.name || "")) score += 10;
  score += Math.min(8, Number(item.score || item.performance || 0));
  score += Math.max(0, 6 - Math.min(6, Number(item.price || 0) / 60));
  return score;
}

function enrichCoachSuggestions(suggestions, components, preferences, result) {
  const cap = priceCapForBriefing(preferences.budgetLevel);
  return (suggestions || []).map(suggestion => {
    const poolName = SLOT_TO_POOL[suggestion.slot];
    const pool = poolName ? components?.[poolName] : null;
    if (!Array.isArray(pool) || !pool.length) return suggestion;
    const currentItem = result?.build?.[suggestion.slot];
    const noCarbon = (preferences.noGos || "").toLowerCase().includes("carbon");
    const candidates = pool
      .filter(item => item.id !== currentItem?.id)
      .filter(item => !noCarbon || !`${item.name || ""}`.toLowerCase().includes("carbon"))
      .filter(item => preferences.budgetLevel === "premium" || Number(item.price || 0) <= cap)
      .sort((a, b) => catalogScore(b, suggestion.slot, preferences, currentItem) - catalogScore(a, suggestion.slot, preferences, currentItem));
    const item = candidates[0] || pool.find(candidate => candidate.id !== currentItem?.id) || pool[0];
    return {
      ...suggestion,
      item,
      item_id: item?.id || null,
      item_name: item?.name || null,
      item_price: item?.price || null,
      price_range: item?.price != null ? fmtPrice(item.price) : suggestion.price_range,
      reason: item
        ? `${suggestion.reason} Katalog-Match: ${item.name} passt in diese Stufe und kann direkt uebernommen werden.`
        : suggestion.reason
    };
  });
}

function CoachSuggestionCards({ suggestions, onApplyPart, result }) {
  if (!Array.isArray(suggestions) || !suggestions.length) return null;
  return (
    <div style={{ display: "grid", gap: 8, marginTop: 12 }}>
      {suggestions.map((suggestion, index) => {
        const catalogItem = suggestion.item || null;
        const searchItem = catalogItem || { id: `coach-${suggestion.slot || index}`, name: suggestion.query || suggestion.title };
        const isApplied = Boolean(catalogItem && result?.build?.[suggestion.slot]?.id === catalogItem.id);
        return (
          <div key={`${suggestion.slot || "item"}-${index}`} style={{
            border: "1px solid var(--hairline)",
            background: "rgba(0,0,0,0.2)",
            padding: 12,
            display: "grid",
            gap: 8
          }}>
            <div style={{ display: "flex", justifyContent: "space-between", gap: 12, alignItems: "flex-start" }}>
              <div>
                <div style={{ font: "700 11px/1.3 var(--font-sans)", letterSpacing: "0.8px", textTransform: "uppercase", color: "var(--accent)" }}>
                  {suggestion.label || suggestion.slot || "Upgrade"}
                </div>
                <div style={{ marginTop: 4, font: "600 14px/1.35 var(--font-sans)", color: "var(--ink)" }}>
                  {suggestion.title}
                </div>
                {catalogItem && (
                  <div style={{ marginTop: 6, color: "var(--ink)", font: "700 12px/1.35 var(--font-sans)" }}>
                    Empfohlen: {catalogItem.name}
                  </div>
                )}
              </div>
              <span style={{
                border: "1px solid var(--hairline-strong)",
                color: suggestion.risk === "low" ? "var(--ink-soft)" : "var(--accent)",
                padding: "5px 7px",
                font: "700 9px/1 var(--font-sans)",
                letterSpacing: "0.7px",
                textTransform: "uppercase",
                whiteSpace: "nowrap"
              }}>
                {suggestion.risk === "low" ? "Low Risk" : "Check"}
              </span>
            </div>
            <p style={{ margin: 0, color: "var(--ink-soft)", font: "400 12px/1.45 var(--font-sans)" }}>
              {suggestion.reason}
            </p>
            <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", gap: 10, flexWrap: "wrap" }}>
              <span style={{ color: "var(--ink-muted)", font: "700 10px/1 var(--font-sans)", letterSpacing: "0.8px", textTransform: "uppercase" }}>
                {suggestion.price_range || "Preis pruefen"}
              </span>
              <div style={{ display: "flex", gap: 8, flexWrap: "wrap", justifyContent: "flex-end" }}>
                {catalogItem && onApplyPart && (
                  <button
                    className={isApplied ? "btn btn-outline btn-sm" : "btn btn-primary btn-sm"}
                    onClick={() => onApplyPart(suggestion.slot, catalogItem)}
                    disabled={isApplied}
                    style={{ padding: "8px 10px" }}
                  >
                    <Icon name={isApplied ? "check" : "plus"} size={13} />
                    {isApplied ? "Im Build" : "Teil übernehmen"}
                  </button>
                )}
                <a
                  href={marketplaceUrlFor(searchItem)}
                  target="_blank"
                  rel="noopener noreferrer sponsored"
                  className="btn btn-ghost btn-sm"
                  onClick={() => window.BikeAnalytics?.track("marketplace_click", {
                    source: "coach_suggestion",
                    slot: suggestion.slot,
                    item_name: catalogItem?.name || suggestion.title,
                    query: suggestion.query
                  })}
                  style={{ textDecoration: "none", color: "var(--accent)", padding: "8px 10px" }}
                >
                  Angebote
                </a>
              </div>
            </div>
          </div>
        );
      })}
    </div>
  );
}

function CoachPlanPanel({ plan, onApply, applied }) {
  if (!plan) return null;
  const palette = Array.isArray(plan.palette) ? plan.palette : [];
  const priorities = Array.isArray(plan.priority) ? plan.priority : [];
  const checks = Array.isArray(plan.checks) ? plan.checks : [];
  const variants = Array.isArray(plan.variants) ? plan.variants : [];
  return (
    <div style={{
      marginTop: 12,
      border: "1px solid var(--hairline-strong)",
      background: "rgba(218,41,28,0.08)",
      padding: 14,
      display: "grid",
      gap: 12
    }}>
      <div style={{ display: "flex", justifyContent: "space-between", gap: 12, alignItems: "flex-start" }}>
        <div>
          <div style={{ font: "700 10px/1 var(--font-sans)", letterSpacing: "1px", textTransform: "uppercase", color: "var(--accent)" }}>
            Custom-Plan
          </div>
          <div style={{ marginTop: 5, font: "600 15px/1.25 var(--font-sans)", color: "var(--ink)" }}>
            {plan.name || "Individueller Look"}
          </div>
        </div>
        <div style={{ color: "var(--ink)", font: "700 11px/1 var(--font-sans)", letterSpacing: "0.8px", textTransform: "uppercase", whiteSpace: "nowrap" }}>
          {plan.estimated_total || "Budget pruefen"}
        </div>
      </div>

      {palette.length > 0 && (
        <div style={{ display: "flex", gap: 8, alignItems: "center", flexWrap: "wrap" }}>
          {palette.map(color => (
            <span key={`${color.name}-${color.hex}`} title={color.name} style={{
              width: 28,
              height: 28,
              border: "1px solid var(--hairline-strong)",
              background: color.hex || "transparent",
              boxShadow: color.hex === "#ffffff" ? "inset 0 0 0 1px rgba(0,0,0,0.35)" : "none"
            }} />
          ))}
          <span style={{ color: "var(--ink-muted)", font: "700 10px/1.35 var(--font-sans)", letterSpacing: "0.8px", textTransform: "uppercase" }}>
            {palette.map(color => color.name).join(" / ")}
          </span>
        </div>
      )}

      {checks.length > 0 && (
        <div style={{ display: "grid", gap: 6 }}>
          <div className="spec-label">Technik-Check</div>
          {checks.map(check => (
            <div key={check.key || check.label} style={{
              display: "grid",
              gridTemplateColumns: "86px 1fr",
              gap: 10,
              alignItems: "start",
              border: "1px solid var(--hairline)",
              background: "rgba(0,0,0,0.18)",
              padding: "8px 10px"
            }}>
              <span style={{
                color: check.status === "ok" ? "var(--ink-soft)" : "var(--accent)",
                font: "700 9px/1.3 var(--font-sans)",
                letterSpacing: "0.8px",
                textTransform: "uppercase"
              }}>
                {check.status === "ok" ? "OK" : "Check"} · {check.label}
              </span>
              <span style={{ color: "var(--ink-soft)", font: "400 12px/1.4 var(--font-sans)" }}>
                {check.message}
              </span>
            </div>
          ))}
        </div>
      )}

      {variants.length > 0 && (
        <div style={{ display: "grid", gap: 8 }}>
          <div className="spec-label">Plan-Varianten</div>
          <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(112px, 1fr))", gap: 8 }}>
            {variants.map(variant => (
              <div key={variant.key || variant.name} style={{
                border: variant.recommended ? "1px solid var(--accent)" : "1px solid var(--hairline)",
                background: variant.recommended ? "rgba(218,41,28,0.14)" : "rgba(0,0,0,0.18)",
                padding: 10,
                display: "grid",
                gap: 7,
                minWidth: 0
              }}>
                <div style={{ display: "flex", justifyContent: "space-between", gap: 8, alignItems: "center" }}>
                  <span style={{
                    color: "var(--ink)",
                    font: "700 10px/1.2 var(--font-sans)",
                    letterSpacing: "0.8px",
                    textTransform: "uppercase"
                  }}>
                    {variant.name}
                  </span>
                  {variant.recommended && (
                    <Icon name="check" size={13} color="var(--accent)" />
                  )}
                </div>
                <div style={{ color: "var(--ink-soft)", font: "700 12px/1.2 var(--font-sans)" }}>
                  {variant.estimated_total}
                </div>
                <div style={{ color: "var(--ink-muted)", font: "400 11px/1.35 var(--font-sans)" }}>
                  {variant.focus}
                </div>
              </div>
            ))}
          </div>
        </div>
      )}

      {priorities.length > 0 && (
        <ol style={{ margin: 0, paddingLeft: 18, display: "grid", gap: 6, color: "var(--ink-soft)", font: "400 12px/1.45 var(--font-sans)" }}>
          {priorities.map(item => <li key={item}>{item}</li>)}
        </ol>
      )}

      {onApply && (
        <button
          className={applied ? "btn btn-outline btn-sm" : "btn btn-primary btn-sm"}
          onClick={() => onApply(plan)}
          disabled={applied}
          style={{ justifySelf: "start" }}
        >
          <Icon name={applied ? "check" : "save"} size={14} />
          {applied ? "Plan übernommen" : "Plan übernehmen"}
        </button>
      )}
    </div>
  );
}

function coachRiderContext(profile = {}) {
  const setup = profile.suspension_setup || {};
  const rider = [
    profile.rider_name ? String(profile.rider_name).trim() : "",
    profile.rider_age ? `${profile.rider_age} Jahre` : "",
    `${profile.height || "?"} cm / ${profile.weight || "?"} kg`
  ].filter(Boolean).join(" · ");
  const suspension = setup.sag
    ? `SAG ${setup.sag}% · Gabel ${setup.fork_rebound ?? "?"} · Daempfer ${setup.shock_rebound ?? "?"}${setup.source === "body_auto" ? " · Auto" : ""}`
    : "Fahrwerk noch nicht gesetzt";
  return { rider, suspension };
}

function CoachBriefingPanel({ preferences, profile, onChange, onSubmit, disabled }) {
  const set = (key, value) => onChange({ ...preferences, [key]: value });
  const coachContext = coachRiderContext(profile);
  return (
    <div style={{
      border: "1px solid var(--hairline-strong)",
      background: "rgba(255,255,255,0.035)",
      padding: 14,
      display: "grid",
      gap: 14
    }}>
      <div style={{ display: "flex", justifyContent: "space-between", gap: 12 }}>
        <div>
          <div className="eyebrow eyebrow-accent">Beratungsbriefing</div>
          <p style={{ margin: "7px 0 0", color: "var(--ink-soft)", font: "400 12px/1.45 var(--font-sans)" }}>
            Ziel-Look, Budget und Prioritaet steuern den finalen Custom-Plan.
          </p>
        </div>
      </div>

      <div style={{
        border: "1px solid var(--hairline)",
        background: "rgba(0,0,0,0.18)",
        padding: "10px 12px",
        display: "grid",
        gap: 5
      }}>
        <div className="spec-label">Coach-Kontext</div>
        <div style={{ color: "var(--ink)", font: "600 12px/1.35 var(--font-sans)" }}>{coachContext.rider}</div>
        <div style={{ color: "var(--ink-soft)", font: "400 12px/1.35 var(--font-sans)" }}>{coachContext.suspension}</div>
      </div>

      <div>
        <div className="spec-label" style={{ marginBottom: 8 }}>Ziel-Look</div>
        <div style={{ display: "grid", gridTemplateColumns: "repeat(5, minmax(0, 1fr))", gap: 6 }}>
          {COACH_LOOK_OPTIONS.map(option => (
            <button
              key={option.value}
              className={preferences.look === option.value ? "btn btn-primary btn-sm" : "btn btn-ghost btn-sm"}
              onClick={() => set("look", option.value)}
              style={{ padding: "9px 7px", minWidth: 0, whiteSpace: "normal", lineHeight: 1.25 }}
              title={option.hint}
            >
              {option.label}
            </button>
          ))}
        </div>
      </div>

      <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12 }}>
        <div>
          <div className="spec-label" style={{ marginBottom: 8 }}>Budgetstufe</div>
          <div style={{ display: "grid", gap: 6 }}>
            {COACH_BUDGET_OPTIONS.map(option => (
              <button
                key={option.value}
                className={preferences.budgetLevel === option.value ? "btn btn-primary btn-sm" : "btn btn-ghost btn-sm"}
                onClick={() => set("budgetLevel", option.value)}
                style={{ justifyContent: "flex-start" }}
              >
                {option.label}
              </button>
            ))}
          </div>
        </div>
        <div>
          <div className="spec-label" style={{ marginBottom: 8 }}>Prioritaet</div>
          <div style={{ display: "grid", gap: 6 }}>
            {COACH_PRIORITY_OPTIONS.map(option => (
              <button
                key={option.value}
                className={preferences.priority === option.value ? "btn btn-primary btn-sm" : "btn btn-ghost btn-sm"}
                onClick={() => set("priority", option.value)}
                style={{ justifyContent: "flex-start" }}
              >
                {option.label}
              </button>
            ))}
          </div>
        </div>
      </div>

      <label style={{ display: "grid", gap: 8 }}>
        <span className="spec-label">No-Gos</span>
        <input
          value={preferences.noGos || ""}
          onChange={event => set("noGos", event.target.value)}
          placeholder="z.B. kein Carbon, keine Neonfarben, Komfort darf nicht leiden"
          style={{
            width: "100%",
            border: "1px solid var(--hairline-strong)",
            background: "rgba(0,0,0,0.22)",
            color: "var(--ink)",
            padding: "11px 12px",
            font: "400 12px/1.4 var(--font-sans)"
          }}
        />
      </label>

      <button className="btn btn-primary btn-sm" onClick={onSubmit} disabled={disabled} style={{ justifySelf: "start" }}>
        <Icon name="chat" size={14} /> Briefing auswerten
      </button>
    </div>
  );
}

function CustomCoachDrawer({ plan, token, profile, result, components, onClose, onUpgrade, onApplyPlan, onApplyPart }) {
  const isPro = plan === "pro";
  const [preferences, setPreferences] = useState({
    look: "rosso",
    budgetLevel: "balanced",
    priority: "style",
    noGos: ""
  });
  const [messages, setMessages] = useState(() => ([{
    role: "assistant",
    content: "Ich bin dein BikeForge Pro-Coach. Fuell kurz das Beratungsbriefing aus; danach erstelle ich dir einen Custom-Plan mit Look, Budget, Tausch-Reihenfolge und technischen Grenzen."
  }]));
  const [input, setInput] = useState("");
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);
  const scrollerRef = useRef(null);

  useEffect(() => {
    const node = scrollerRef.current;
    if (node) node.scrollTop = node.scrollHeight;
  }, [messages, loading]);

  const send = async (text = input) => {
    const message = String(text || "").trim();
    if (!message || loading || !isPro) return;
    window.BikeAnalytics?.track("coach_message_sent", {
      has_custom_plan: Boolean(profile.custom_plan),
      terrain: profile.terrain,
      budget: profile.budget,
      height: profile.height,
      weight: profile.weight,
      rider_age: profile.rider_age || null,
      has_rider_name: Boolean(profile.rider_name),
      has_suspension_setup: Boolean(profile.suspension_setup),
      briefing_look: preferences.look,
      briefing_budget: preferences.budgetLevel,
      briefing_priority: preferences.priority,
      briefing_no_gos: preferences.noGos ? preferences.noGos.slice(0, 140) : ""
    });
    const nextMessages = [...messages, { role: "user", content: message }];
    setMessages(nextMessages);
    setInput("");
    setLoading(true);
    setError(null);
    try {
      const res = await fetch("/api/ai/chat", {
        method: "POST",
        headers: { "Content-Type": "application/json", "Authorization": `Bearer ${token || ""}` },
        body: JSON.stringify({
          message,
          history: nextMessages.slice(-8),
          context: {
            profile,
            build: summarizeResultForCoach(result),
            coach_preferences: preferences
          }
        })
      });
      const data = await res.json();
      if (!res.ok) throw new Error(data.error || "Coach nicht erreichbar.");
      window.BikeAnalytics?.track("coach_response_received", {
        provider: data.provider,
        model: data.model,
        suggestions: Array.isArray(data.suggestions) ? data.suggestions.length : 0,
        plan: data.plan?.name || null,
        briefing_look: preferences.look
      });
      setMessages(prev => [...prev, {
        role: "assistant",
        content: data.reply || "Ich konnte dazu keine Empfehlung erzeugen.",
        suggestions: enrichCoachSuggestions(data.suggestions || [], components, preferences, result),
        plan: data.plan || null
      }]);
    } catch (err) {
      const fallback = "Der KI-Dienst ist gerade nicht erreichbar. Technisch sichere Farb-Upgrades sind meist Griffe, Pedale, Sattel, Ventile, Spacer, Decals und Cockpit-Akzente. Bremsen, Laufräder und Antrieb bitte nur nach Kompatibilitaet tauschen.";
      setError(err.message || "Netzwerkfehler.");
      setMessages(prev => [...prev, { role: "assistant", content: fallback, suggestions: [] }]);
    } finally {
      setLoading(false);
    }
  };

  return (
    <>
      <div className="drawer-backdrop" onClick={onClose} />
      <div className="drawer">
        <div style={{ padding: "28px 32px", borderBottom: "1px solid var(--hairline)", display: "flex", justifyContent: "space-between", gap: 16 }}>
          <div>
            <div className="eyebrow eyebrow-accent">BikeForge Pro</div>
            <h2 style={{ font: "500 28px/1.1 var(--font-sans)", letterSpacing: "-0.56px", marginTop: 8 }}>
              Custombike-Coach
            </h2>
            <p style={{ marginTop: 10, color: "var(--ink-soft)", font: "400 13px/1.5 var(--font-sans)" }}>
              Teile, Farben und Upgrade-Pfade passend zu deinem aktuellen Build.
            </p>
          </div>
          <button className="btn btn-ghost btn-sm" onClick={onClose}><Icon name="close" size={16} /></button>
        </div>

        {!isPro ? (
          <div style={{ padding: 32, display: "flex", flexDirection: "column", gap: 18 }}>
            <div style={{ border: "1px solid var(--hairline-strong)", background: "rgba(255,255,255,0.04)", padding: 22 }}>
              <div className="eyebrow eyebrow-accent">Pro Feature</div>
              <h3 style={{ marginTop: 10, font: "500 24px/1.2 var(--font-sans)" }}>Persoenliche Custombike-Beratung</h3>
              <p style={{ marginTop: 12, color: "var(--ink-soft)", font: "400 14px/1.55 var(--font-sans)" }}>
                Der Coach diskutiert mit dir ueber farbige Teile, sinnvolle Tausch-Reihenfolge, Budget und technische Grenzen deines Bikes.
              </p>
            </div>
            <button className="btn btn-primary" onClick={onUpgrade} style={{ justifyContent: "center" }}>
              Upgrade → Pro
            </button>
          </div>
        ) : (
          <>
            <div ref={scrollerRef} className="scroll-area" style={{ flex: 1, padding: 24, display: "flex", flexDirection: "column", gap: 14 }}>
              <CoachBriefingPanel
                preferences={preferences}
                profile={profile}
                onChange={setPreferences}
                onSubmit={() => send("Erstelle aus meinem Beratungsbriefing einen finalen Custom-Plan.")}
                disabled={loading}
              />
              {messages.map((msg, index) => (
                <div key={index} style={{
                  alignSelf: msg.role === "user" ? "flex-end" : "flex-start",
                  maxWidth: "88%",
                  border: msg.role === "user" ? "1px solid var(--accent)" : "1px solid var(--hairline)",
                  background: msg.role === "user" ? "var(--accent-soft)" : "rgba(255,255,255,0.04)",
                  color: "var(--ink)",
                  padding: "12px 14px",
                  font: "400 13px/1.55 var(--font-sans)",
                  whiteSpace: "pre-wrap"
                }}>
                  {msg.content}
                  {msg.role === "assistant" && (
                    <CoachPlanPanel
                      plan={msg.plan}
                      onApply={onApplyPlan}
                      applied={Boolean(msg.plan && profile.custom_plan?.name === msg.plan.name && profile.custom_plan?.estimated_total === msg.plan.estimated_total)}
                    />
                  )}
                  {msg.role === "assistant" && (
                    <CoachSuggestionCards
                      suggestions={msg.suggestions}
                      onApplyPart={onApplyPart}
                      result={result}
                    />
                  )}
                </div>
              ))}
              {loading && (
                <div style={{ alignSelf: "flex-start", border: "1px solid var(--hairline)", padding: "12px 14px", color: "var(--ink-soft)", font: "400 13px/1.4 var(--font-sans)" }}>
                  Coach denkt ueber deinen Build nach ...
                </div>
              )}
            </div>

            <div style={{ padding: 24, borderTop: "1px solid var(--hairline)", display: "flex", flexDirection: "column", gap: 14 }}>
              <div style={{ display: "flex", flexWrap: "wrap", gap: 8 }}>
                {COACH_QUICK_PROMPTS.map(prompt => (
                  <button key={prompt} className="btn btn-ghost btn-sm" onClick={() => send(prompt)} disabled={loading} style={{ flex: "1 1 160px", justifyContent: "center" }}>
                    {prompt}
                  </button>
                ))}
              </div>
              {error && <div style={{ color: "var(--accent)", font: "600 12px/1.4 var(--font-sans)" }}>{error}</div>}
              <div style={{ display: "grid", gridTemplateColumns: "1fr auto", gap: 10, alignItems: "end" }}>
                <textarea
                  value={input}
                  onChange={event => setInput(event.target.value)}
                  onKeyDown={event => {
                    if (event.key === "Enter" && !event.shiftKey) {
                      event.preventDefault();
                      send();
                    }
                  }}
                  placeholder="Frag z.B. nach roten Pedalen, farbigen Griffen oder einem Stealth-Look ..."
                  rows={3}
                  style={{
                    width: "100%",
                    resize: "vertical",
                    minHeight: 76,
                    border: "1px solid var(--hairline-strong)",
                    background: "rgba(255,255,255,0.04)",
                    color: "var(--ink)",
                    padding: 12,
                    font: "400 13px/1.5 var(--font-sans)"
                  }}
                />
                <button className="btn btn-primary" onClick={() => send()} disabled={loading || !input.trim()}>
                  <Icon name="chat" size={14} /> Senden
                </button>
              </div>
            </div>
          </>
        )}
      </div>
    </>
  );
}

function UpgradeModal({ plan, token, onClose }) {
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);

  const handleCheckout = async () => {
    window.BikeAnalytics?.track("checkout_started", { plan });
    setLoading(true);
    setError(null);
    try {
      const res = await fetch("/api/checkout/session", {
        method: "POST",
        headers: { "Authorization": `Bearer ${token}`, "Content-Type": "application/json" }
      });
      const data = await res.json();
      if (!res.ok) { setError(data.error || "Fehler beim Erstellen der Checkout-Session."); setLoading(false); return; }
      window.location.href = data.url;
    } catch {
      setError("Netzwerkfehler. Bitte versuche es erneut.");
      setLoading(false);
    }
  };

  const features = [
    { label: "Builds speichern",     free: "3 Builds",    pro: "Unbegrenzt" },
    { label: "Komponenten-Tausch",   free: true,          pro: true },
    { label: "Marketplace-Angebote", free: true,          pro: true },
    { label: "Garage & Verlauf",     free: true,          pro: true },
    { label: "KI Custombike-Coach",   free: false,         pro: true },
    { label: "Build-Vergleich",      free: false,         pro: true },
    { label: "PDF-Export",           free: false,         pro: true },
    { label: "Prioritäts-Support",   free: false,         pro: true },
  ];

  return (
    <>
      <div className="drawer-backdrop" onClick={onClose} style={{ zIndex: 900 }} />
      <div style={{
        position: "fixed", top: "50%", left: "50%",
        transform: "translate(-50%, -50%)",
        zIndex: 901, background: "var(--canvas-card)",
        border: "1px solid var(--hairline)",
        width: "min(520px, 95vw)", maxHeight: "90vh", overflowY: "auto"
      }}>
        <div style={{ padding: "32px 40px 40px" }}>
          <div style={{ display: "flex", justifyContent: "space-between", alignItems: "flex-start", marginBottom: 32 }}>
            <div>
              <div className="eyebrow eyebrow-accent" style={{ marginBottom: 8 }}>Upgrade</div>
              <h2 style={{ font: "500 28px/1.1 var(--font-sans)", letterSpacing: "-0.56px" }}>BikeForge Pro</h2>
            </div>
            <button className="btn btn-ghost btn-sm" onClick={onClose}><Icon name="close" size={16} /></button>
          </div>

          <div style={{ display: "grid", gridTemplateColumns: "1fr auto auto", gap: "0 24px", marginBottom: 32 }}>
            <div style={{ font: "600 10px/1 var(--font-sans)", letterSpacing: "1px", color: "var(--ink-muted)", textTransform: "uppercase", paddingBottom: 10, borderBottom: "1px solid var(--hairline)" }}>Feature</div>
            <div style={{ font: "600 10px/1 var(--font-sans)", letterSpacing: "1px", color: "var(--ink-muted)", textTransform: "uppercase", paddingBottom: 10, borderBottom: "1px solid var(--hairline)", textAlign: "center" }}>Free</div>
            <div style={{ font: "600 10px/1 var(--font-sans)", letterSpacing: "1px", color: "var(--accent)", textTransform: "uppercase", paddingBottom: 10, borderBottom: "1px solid var(--accent)" }}>Pro</div>
            {features.map((f, i) => (
              <React.Fragment key={i}>
                <div style={{ padding: "14px 0", borderBottom: "1px solid var(--hairline)", font: "400 14px/1 var(--font-sans)", color: "var(--ink)" }}>{f.label}</div>
                <div style={{ padding: "14px 0", borderBottom: "1px solid var(--hairline)", textAlign: "center", color: "var(--ink-muted)", font: "500 13px/1 var(--font-sans)" }}>
                  {f.free === true ? "✓" : f.free === false ? "—" : f.free}
                </div>
                <div style={{ padding: "14px 0", borderBottom: "1px solid var(--hairline)", textAlign: "center", color: "var(--accent)", font: "700 13px/1 var(--font-sans)" }}>
                  {f.pro === true ? "✓" : f.pro === false ? "—" : f.pro}
                </div>
              </React.Fragment>
            ))}
          </div>

          <div style={{ textAlign: "center", marginBottom: 24 }}>
            <div style={{ font: "500 42px/1 var(--font-sans)", letterSpacing: "-0.84px" }}>3,99 €</div>
            <div style={{ font: "400 13px/1 var(--font-sans)", color: "var(--ink-muted)", marginTop: 6 }}>/ Monat · jederzeit kündbar</div>
          </div>

          {error && (
            <div style={{ background: "var(--accent-soft)", border: "1px solid var(--accent)", padding: "10px 16px", marginBottom: 16, font: "500 13px/1.4 var(--font-sans)", color: "var(--ink)" }}>
              {error}
            </div>
          )}

          <button className="btn btn-primary" onClick={handleCheckout} disabled={loading}
            style={{ width: "100%", justifyContent: "center", padding: "18px 0", fontSize: 14 }}>
            {loading ? "Weiterleitung …" : "Jetzt upgraden → Pro"}
          </button>

          <div style={{ textAlign: "center", marginTop: 14, font: "400 12px/1 var(--font-sans)", color: "var(--ink-muted)" }}>
            Sichere Zahlung über Paddle · Steuern und Zahlungsabwicklung durch Merchant of Record
          </div>
        </div>
      </div>
    </>
  );
}

// ----- Profil-Eingabe -----
function ProfileForm({ profile, onChange }) {
  const set = (k, v) => onChange({ ...profile, [k]: v });
  return (
    <div style={{ display: "flex", flexDirection: "column", gap: 28 }}>
      <div>
        <div className="eyebrow" style={{ marginBottom: 18 }}>01 — Fahrer-Profil</div>
        <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 24 }}>
          <SliderField label="Gewicht" min={50} max={130} step={1} unit="kg"
            value={profile.weight} onChange={(v) => set("weight", v)} />
          <SliderField label="Größe" min={150} max={205} step={1} unit="cm"
            value={profile.height} onChange={(v) => set("height", v)} />
        </div>
      </div>

      <hr className="divider" />

      <div>
        <div className="eyebrow" style={{ marginBottom: 14 }}>02 — Antrieb</div>
        <Segmented options={[
          { value: "analog", label: "Bio" },
          { value: "ebike", label: "E-Bike" }
        ]} value={profile.bike_type || "analog"} onChange={(v) => set("bike_type", v)} />
      </div>

      <div>
        <div className="eyebrow" style={{ marginBottom: 14 }}>03 — Skill</div>
        <Segmented options={SKILL_OPTIONS} value={profile.skill_level} onChange={(v) => set("skill_level", v)} />
      </div>

      <div>
        <div className="eyebrow" style={{ marginBottom: 14 }}>04 — Fahrstil</div>
        <Segmented options={STYLE_OPTIONS} value={profile.riding_style} onChange={(v) => set("riding_style", v)} />
      </div>

      <div>
        <div className="eyebrow" style={{ marginBottom: 14 }}>05 — Einsatzbereich</div>
        <Segmented accent options={TERRAIN_OPTIONS} value={profile.terrain} onChange={(v) => set("terrain", v)} />
      </div>

      <hr className="divider" />

      <div>
        <div className="eyebrow" style={{ marginBottom: 18 }}>06 — Budget</div>
        <SliderField label="Budget" min={1200} max={8000} step={100} unit="€"
          value={profile.budget} onChange={(v) => set("budget", v)} large />
      </div>
    </div>
  );
}

function SliderField({ label, min, max, step, unit, value, onChange, large }) {
  return (
    <div className="field">
      <div className="field-label">{label}</div>
      <div className="slider-row">
        <input type="range" min={min} max={max} step={step} value={value}
          onChange={(e) => onChange(Number(e.target.value))} />
        <div className="slider-readout" style={large ? { fontSize: 32 } : null}>
          {unit === "€" ? fmtNum(value) : value}
          <span className="field-suffix" style={{ marginLeft: 6, fontSize: 11 }}>{unit}</span>
        </div>
      </div>
    </div>
  );
}

function Segmented({ options, value, onChange, accent }) {
  return (
    <div className={"seg" + (accent ? " seg-accent" : "")}>
      {options.map(o => (
        <button key={o.value} className={value === o.value ? "is-on" : ""} onClick={() => onChange(o.value)}>
          {o.label}
        </button>
      ))}
    </div>
  );
}

// ----- Component card -----
function ComponentCard({ slot, item, onClick }) {
  if (!item) return null;
  const high = item.level === "high" || item.level === "mid_high";
  const visionConfidence = Math.max(0, Math.min(1, Number(item.vision_confidence || 0)));
  return (
    <div className="comp-card" onClick={onClick}>
      <div className="comp-card-icon"><Icon name={slot} size={28} /></div>
      <div className="comp-card-body">
        <div className="comp-card-cat">{SLOT_LABELS[slot]}</div>
        <div className="comp-card-name">{item.name}</div>
        <div style={{ display: "flex", alignItems: "center", gap: 6, marginTop: 4, flexWrap: "wrap" }}>
          {item.level && <span className={"comp-card-level-pill" + (high ? " is-high" : "")}>{item.level.replace("_", "-")}</span>}
          {item.locked && <span className="comp-card-level-pill is-high">Bestand</span>}
          {item.replacement_for_existing && <span className="comp-card-level-pill">Upgrade</span>}
          {visionConfidence > 0 && <span className="comp-card-level-pill">Vision {Math.round(visionConfidence * 100)}%</span>}
          {item.locked && item.confidence && <span className="comp-card-level-pill">Match {item.confidence}%</span>}
          <BrandBadge name={item.name} size="sm" />
          <MarketplaceButton item={item} compact />
        </div>
      </div>
      <div style={{ display: "flex", flexDirection: "column", alignItems: "flex-end", gap: 4 }}>
        {(item.locked || item.replacement_for_existing) && (
          <div className="eyebrow" style={{ fontSize: 8, color: item.locked ? "var(--ink-muted)" : "var(--accent)" }}>
            {item.locked ? "Wert" : "Kosten"}
          </div>
        )}
        <div className="comp-card-price">{fmtPrice(item.price || 0)}</div>
        <div style={{ color: "var(--ink-muted)" }}><Icon name="chevron" size={14} /></div>
      </div>
    </div>
  );
}

// ----- Spec cell (Ferrari big-number) -----
function SpecCell({ label, value, unit, accent, mega }) {
  return (
    <div className={"spec" + (accent ? " spec-accent" : "") + (mega ? " spec-mega" : "")}>
      <div className="spec-label">{label}</div>
      <div>
        <span className="spec-value">{value}</span>
        {unit && <span className="spec-unit">{unit}</span>}
      </div>
    </div>
  );
}

// ----- Score panel -----
function ScorePanel({ scores }) {
  const items = [
    { key: "fit_score", label: "FIT", v: scores.fit_score, accent: true },
    { key: "performance_score", label: "PERFORMANCE", v: scores.performance_score },
    { key: "value_score", label: "VALUE", v: scores.value_score }
  ];
  return (
    <div className="score-panel">
      {items.map(it => (
        <div key={it.key} className="score-panel-item">
          <div className="spec-label" style={{ marginBottom: 12 }}>{it.label}</div>
          <div style={{ display: "flex", alignItems: "baseline", gap: 4 }}>
            <span className={"score-panel-number" + (it.accent ? " is-accent" : "")}>
              {it.v.toFixed(1)}
            </span>
            <span className="spec-unit" style={{ color: "var(--ink-muted)" }}>/10</span>
          </div>
          <div className="score-bar">
            <div className={"score-bar-fill" + (it.accent ? " is-accent" : "")} style={{ width: `${it.v * 10}%` }} />
          </div>
        </div>
      ))}
    </div>
  );
}

// ----- Warnings -----
function Warnings({ warnings }) {
  if (!warnings || warnings.length === 0) return null;
  return (
    <div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
      {warnings.map((w, i) => (
        <div key={i} style={{
          display: "flex", alignItems: "flex-start", gap: 10,
          padding: "12px 14px",
          background: w.kind === "safety" ? "var(--accent-soft)" : "rgba(255,255,255,0.04)",
          borderLeft: `2px solid ${w.kind === "safety" ? "var(--accent)" : "var(--ink-soft)"}`,
          color: "var(--ink)"
        }}>
          <Icon name={w.kind === "safety" ? "warn" : "info"} size={16} />
          <span style={{ font: "500 13px/1.4 var(--font-sans)" }}>{w.msg}</span>
        </div>
      ))}
    </div>
  );
}

// ----- Drawer für Komponenten-Tausch -----
function ComponentDrawer({ slot, currentItem, pool, frame, profile, onPick, onClose }) {
  if (!slot || !pool) return null;
  // Filter passende Komponenten (gleiche Kompat.-Regeln wie engine)
  let candidates = pool.slice();
  if (slot === "wheels") {
    candidates = candidates.filter(w => (!w.wheel_sizes || w.wheel_sizes.includes(frame.wheel_size)) && (!w.axle_front || w.axle_front === frame.axle_front));
  } else if (slot === "fork") {
    candidates = candidates.filter(f => (!f.wheel_sizes || f.wheel_sizes.includes(frame.wheel_size)) && (!f.axle || f.axle === frame.axle_front));
  } else if (slot === "tires") {
    candidates = candidates.filter(t => !t.terrain || t.terrain === profile.terrain);
  } else if (slot === "dropper") {
    candidates = candidates.filter(d => !d.seatpost_diameters || d.seatpost_diameters.includes(frame.seatpost_diameter));
  } else if (slot === "shock" && frame.shock_size_class) {
    candidates = candidates.filter(s => !s.size_class || s.size_class.includes(frame.shock_size_class));
  }
  candidates.sort((a, b) => (a.price || 0) - (b.price || 0));

  return (
    <>
      <div className="drawer-backdrop" onClick={onClose} />
      <div className="drawer scroll-area">
        <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", padding: "24px 32px", borderBottom: "1px solid var(--hairline)", flex: "0 0 auto" }}>
          <div>
            <div className="eyebrow eyebrow-accent">Komponente wählen</div>
            <h3 style={{ marginTop: 6 }}>{SLOT_LABELS[slot]}</h3>
          </div>
          <button className="btn btn-ghost btn-sm" onClick={onClose}><Icon name="close" size={14} /></button>
        </div>
        <div style={{ padding: 24, display: "flex", flexDirection: "column", gap: 6 }}>
          {candidates.map(c => (
            <div key={c.id}
              className={"comp-card" + (currentItem && currentItem.id === c.id ? " is-active" : "")}
              onClick={() => onPick(c)}>
              <div className="comp-card-icon"><Icon name={slot} size={26} /></div>
              <div className="comp-card-body">
                <div style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: 2 }}>
                  <div className="comp-card-cat">{c.level ? c.level.replace("_", "-") : ""}</div>
                  <BrandBadge name={c.name} size="lg" />
                </div>
                <div className="comp-card-name">{c.name}</div>
                <div style={{ display: "flex", gap: 12, marginTop: 6, flexWrap: "wrap" }}>
                  {c.power != null && <SpecMini label="Power" value={`${c.power}/10`} />}
                  {c.range != null && <SpecMini label="Range" value={`${c.range}/10`} />}
                  {c.stiffness != null && <SpecMini label="Steif." value={`${c.stiffness}/10`} />}
                  {c.performance != null && <SpecMini label="Perf." value={`${c.performance}/10`} />}
                  {c.travel_mm != null && <SpecMini label="Travel" value={`${c.travel_mm}mm`} />}
                  {c.weight_g != null && <SpecMini label="Gewicht" value={`${c.weight_g}g`} />}
                </div>
              </div>
              <div style={{ display: "flex", flexDirection: "column", alignItems: "flex-end", gap: 10 }}>
                <div className="comp-card-price">{fmtPrice(c.price || 0)}</div>
                <MarketplaceButton item={c} />
              </div>
            </div>
          ))}
          {candidates.length === 0 && <div style={{ color: "var(--ink-muted)", padding: 24 }}>Keine kompatiblen Komponenten gefunden.</div>}
        </div>
      </div>
    </>
  );
}

function SpecMini({ label, value }) {
  return (
    <span style={{ font: "600 10px/1.2 var(--font-sans)", letterSpacing: "0.6px", color: "var(--ink-muted)", textTransform: "uppercase" }}>
      {label} <span style={{ color: "var(--ink)", fontWeight: 700, fontVariantNumeric: "tabular-nums" }}>{value}</span>
    </span>
  );
}

function formatSavedDate(value) {
  if (!value) return "unbekannt";
  return new Intl.DateTimeFormat("de-DE", {
    day: "2-digit",
    month: "2-digit",
    year: "numeric",
    hour: "2-digit",
    minute: "2-digit"
  }).format(new Date(value));
}

function savedBuildScore(entry) {
  const scores = entry.scores || {};
  const values = [scores.fit_score, scores.performance_score, scores.value_score].map(Number).filter(Number.isFinite);
  return values.length ? values.reduce((sum, score) => sum + score, 0) / values.length : 0;
}

function savedBuildSearchText(entry) {
  const appliedParts = entry.profile?.custom_plan?.applied_parts || [];
  return [
    entry.title,
    entry.profile?.terrain,
    entry.profile?.riding_style,
    entry.profile?.skill_level,
    entry.profile?.existing_bike?.model_name,
    entry.profile?.custom_plan?.name,
    ...(entry.profile?.custom_plan?.palette || []).map(color => color.name),
    ...appliedParts.map(part => `${part.label} ${part.name}`),
  ].filter(Boolean).join(" ").toLowerCase();
}

// ----- Saved Builds Drawer -----
function GarageDrawer({ saved, onClose, onLoad, onDelete, onCompare, user, onLogout, plan, onUpgrade }) {
  const [query, setQuery] = useState("");
  const [sortMode, setSortMode] = useState("newest");
  const visibleSaved = useMemo(() => {
    const q = query.trim().toLowerCase();
    const filtered = q ? saved.filter(entry => savedBuildSearchText(entry).includes(q)) : saved;
    return [...filtered].sort((a, b) => {
      if (sortMode === "price") return (a.total || 0) - (b.total || 0);
      if (sortMode === "score") return savedBuildScore(b) - savedBuildScore(a);
      return (b.created || 0) - (a.created || 0);
    });
  }, [query, saved, sortMode]);

  return (
    <>
      <div className="drawer-backdrop" onClick={onClose} />
      <div className="drawer scroll-area" style={{ display: "flex", flexDirection: "column" }}>
        {/* Header */}
        <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", padding: "24px 32px", borderBottom: "1px solid var(--hairline)", flex: "0 0 auto" }}>
          <div>
            <div className="eyebrow eyebrow-accent">Meine Builds</div>
            <h3 style={{ marginTop: 6 }}>Garage</h3>
          </div>
          <div style={{ display: "flex", gap: 8 }}>
            {saved.length >= 2 && onCompare && (
              <button className={plan === "pro" ? "btn btn-primary btn-sm" : "btn btn-outline btn-sm"} onClick={plan === "pro" ? onCompare : onUpgrade}>
                <Icon name="compare" size={14} /> {plan === "pro" ? "Vergleichen" : "Vergleich ist Pro"}
              </button>
            )}
            <button className="btn btn-ghost btn-sm" onClick={onClose}><Icon name="close" size={14} /></button>
          </div>
        </div>

        {/* Search + sort */}
        <div className="garage-tools" style={{ padding: "18px 24px 0", display: "grid", gridTemplateColumns: "1fr 170px", gap: 10 }}>
          <label style={{
            display: "flex", alignItems: "center", gap: 10,
            border: "1px solid var(--hairline-strong)", background: "rgba(255,255,255,0.025)",
            padding: "0 12px", minHeight: 42
          }}>
            <Icon name="list" size={14} />
            <input
              value={query}
              onChange={(event) => setQuery(event.target.value)}
              placeholder="Build suchen"
              style={{
                width: "100%", minWidth: 0, border: 0, outline: 0, background: "transparent",
                color: "var(--ink)", font: "500 13px/1 var(--font-sans)"
              }}
            />
          </label>
          <select
            value={sortMode}
            onChange={(event) => setSortMode(event.target.value)}
            style={{
              border: "1px solid var(--hairline-strong)", background: "rgba(255,255,255,0.025)",
              color: "var(--ink)", padding: "0 12px", minHeight: 42,
              font: "700 11px/1 var(--font-sans)", letterSpacing: "0.8px", textTransform: "uppercase"
            }}
          >
            <option value="newest">Neueste</option>
            <option value="score">Score</option>
            <option value="price">Preis</option>
          </select>
        </div>

        {/* Build list */}
        <div style={{ padding: 24, display: "flex", flexDirection: "column", gap: 14, flex: 1 }}>
          {saved.length === 0 && (
            <div style={{ textAlign: "center", color: "var(--ink-muted)", padding: 48 }}>
              <Icon name="frame" size={48} />
              <div className="eyebrow" style={{ marginTop: 14 }}>Noch keine Builds gespeichert</div>
              <p style={{ marginTop: 8, color: "var(--ink-muted)" }}>Konfiguriere ein Bike und speichere deinen Build hier.</p>
            </div>
          )}
          {saved.length > 0 && visibleSaved.length === 0 && (
            <div style={{ textAlign: "center", color: "var(--ink-muted)", padding: 36, border: "1px dashed var(--hairline-strong)" }}>
              <div className="eyebrow">Keine Treffer</div>
              <p style={{ marginTop: 8, color: "var(--ink-muted)" }}>Passe die Suche an oder lösche den Filter.</p>
            </div>
          )}
          {visibleSaved.map(s => {
            const existingBike = s.profile?.existing_bike;
            const savedMeta = s.profile?.saved_state || {};
            const breakdown = savedMeta.price_breakdown;
            const existingSummary = savedMeta.existing_summary;
            const customPlan = s.profile?.custom_plan;
            const appliedCoachParts = Array.isArray(customPlan?.applied_parts) ? customPlan.applied_parts : [];
            const primaryPrice = existingBike && breakdown ? breakdown.upgrade_cost : s.total;
            const retainedCount = existingSummary?.locked_slots?.length || 0;
            const replacedCount = existingSummary?.replaced_slots?.length || 0;
            const score = savedBuildScore(s);
            return (
              <div key={s.id} style={{ background: "var(--canvas-card)", border: "1px solid var(--hairline)", padding: 0 }}>
                {s.thumbnail && <div style={{ height: 140, background: `url(${s.thumbnail}) center/cover`, borderBottom: "1px solid var(--hairline)" }} />}
                <div style={{ padding: 18 }}>
                  <div style={{ display: "flex", justifyContent: "space-between", gap: 12, alignItems: "flex-start" }}>
                    <div style={{ minWidth: 0 }}>
                      <div className="eyebrow eyebrow-accent">{s.profile.terrain.toUpperCase()} · {s.profile.riding_style}</div>
                      <div style={{ font: "500 18px/1.3 var(--font-sans)", marginTop: 4, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{s.title}</div>
                    </div>
                    <div style={{ font: "700 11px/1 var(--font-sans)", color: "var(--accent)", whiteSpace: "nowrap" }}>
                      Ø {score.toFixed(1)}
                    </div>
                  </div>
                  <div style={{ marginTop: 8, color: "var(--ink-muted)", font: "600 10px/1.4 var(--font-sans)", letterSpacing: "0.8px", textTransform: "uppercase" }}>
                    Gespeichert: {formatSavedDate(s.created)}
                  </div>
                  {existingBike && (
                    <div style={{ display: "flex", gap: 6, flexWrap: "wrap", marginTop: 10 }}>
                      <span className="comp-card-level-pill is-high">Bestandsbike</span>
                      {retainedCount > 0 && <span className="comp-card-level-pill">Bestand {retainedCount}</span>}
                      {replacedCount > 0 && <span className="comp-card-level-pill">Upgrade {replacedCount}</span>}
                    </div>
                  )}
                  {customPlan && (
                    <div style={{ marginTop: 10, padding: "10px 12px", border: "1px solid var(--hairline)", background: "rgba(218,41,28,0.07)" }}>
                      <div style={{ display: "flex", justifyContent: "space-between", gap: 10, alignItems: "center" }}>
                        <span style={{ color: "var(--accent)", font: "700 10px/1 var(--font-sans)", letterSpacing: "0.8px", textTransform: "uppercase" }}>
                          {customPlan.name}
                        </span>
                        <span style={{ color: "var(--ink-muted)", font: "700 10px/1 var(--font-sans)", letterSpacing: "0.8px", textTransform: "uppercase" }}>
                          {customPlan.estimated_total}
                        </span>
                      </div>
                      <div style={{ display: "flex", gap: 6, marginTop: 8, alignItems: "center", flexWrap: "wrap" }}>
                        {(customPlan.palette || []).slice(0, 4).map(color => (
                          <span key={`${s.id}-${color.name}`} title={color.name} style={{
                            width: 18,
                            height: 18,
                            border: "1px solid var(--hairline-strong)",
                            background: color.hex || "transparent",
                            boxShadow: color.hex === "#ffffff" ? "inset 0 0 0 1px rgba(0,0,0,0.35)" : "none"
                          }} />
                        ))}
                      </div>
                      {appliedCoachParts.length > 0 && (
                        <div style={{ marginTop: 10, display: "grid", gap: 6 }}>
                          <div style={{ color: "var(--ink-muted)", font: "700 9px/1 var(--font-sans)", letterSpacing: "0.8px", textTransform: "uppercase" }}>
                            Coach-Teile
                          </div>
                          {appliedCoachParts.slice(0, 4).map(part => (
                            <div key={`${s.id}-${part.slot}-${part.id}`} style={{
                              display: "flex",
                              justifyContent: "space-between",
                              gap: 10,
                              color: "var(--ink-soft)",
                              font: "600 11px/1.35 var(--font-sans)"
                            }}>
                              <span>{part.label}: {part.name}</span>
                              <span style={{ color: "var(--ink-muted)", whiteSpace: "nowrap" }}>{fmtPrice(part.price || 0)}</span>
                            </div>
                          ))}
                        </div>
                      )}
                    </div>
                  )}
                  <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr 1fr", gap: 12, marginTop: 14 }}>
                    <SpecMini label={existingBike ? "Upgrade" : "Preis"} value={fmtPrice(primaryPrice)} />
                    <SpecMini label="Fit" value={`${s.scores.fit_score}/10`} />
                    <SpecMini label="Perf" value={`${s.scores.performance_score}/10`} />
                  </div>
                  {existingBike && breakdown && (
                    <div style={{ display: "flex", gap: 10, flexWrap: "wrap", marginTop: 10 }}>
                      <SpecMini label="Bestandswert" value={fmtPrice(breakdown.existing_value || 0)} />
                      <SpecMini label="Gesamtwert" value={fmtPrice(s.total)} />
                    </div>
                  )}
                  <div style={{ display: "flex", gap: 8, marginTop: 16 }}>
                    <button className="btn btn-primary btn-sm" onClick={() => onLoad(s)}>Laden</button>
                    <button className="btn btn-outline btn-sm" onClick={() => onDelete(s.id)}>Löschen</button>
                  </div>
                </div>
              </div>
            );
          })}
        </div>

        {/* User profile footer */}
        {user && (
          <div style={{ flex: "0 0 auto", borderTop: "1px solid var(--hairline)", padding: "20px 32px" }}>
            <div style={{ display: "flex", alignItems: "center", gap: 14 }}>
              <div style={{
                width: 40, height: 40, background: "var(--accent)",
                display: "grid", placeItems: "center",
                font: "700 15px/1 var(--font-sans)", color: "var(--accent-ink)", flexShrink: 0
              }}>
                {user.name.charAt(0).toUpperCase()}
              </div>
              <div style={{ flex: 1, minWidth: 0 }}>
                <div style={{ font: "600 14px/1 var(--font-sans)", color: "var(--ink)", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>
                  {user.name}
                </div>
                <div style={{ font: "400 12px/1 var(--font-sans)", color: "var(--ink-muted)", marginTop: 4, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>
                  {user.email}
                </div>
                {plan === "pro" ? (
                  <span style={{ display: "inline-block", marginTop: 6, background: "var(--accent)", color: "var(--accent-ink)", font: "700 9px/1 var(--font-sans)", letterSpacing: "1px", textTransform: "uppercase", padding: "3px 8px" }}>PRO</span>
                ) : (
                  <button onClick={onUpgrade} style={{ display: "inline-block", marginTop: 6, background: "none", border: "none", padding: 0, cursor: "pointer", font: "600 11px/1 var(--font-sans)", color: "var(--accent)", letterSpacing: "0.4px" }}>
                    → Upgrade auf Pro
                  </button>
                )}
              </div>
              <button className="btn btn-ghost btn-sm" onClick={onLogout} title="Abmelden" style={{ flexShrink: 0 }}>
                <Icon name="close" size={14} /> Abmelden
              </button>
            </div>
          </div>
        )}
      </div>
    </>
  );
}

window.BikeUI = {
  TopNav, ProfileForm, ComponentCard, SpecCell, ScorePanel, Warnings,
  ComponentDrawer, GarageDrawer, UpgradeModal, ExistingBikeModal, CustomCoachDrawer, BikePassDrawer, BodySetupDrawer, SuspensionSetupDrawer, BrandTicker,
  SpecMini, Segmented, SliderField
};
