/* global React, KENNY, GWM_DEALER, GWM_MODELS, KENNY_REVIEWS, KENNY_DELIVERIES, KENNY_BROCHURES, KENNY_VALUES, KENNY_HERO_SLIDES, KENNY_HERO_SLIDE_INTERVAL_MS, gwmWa, useKennyLang, formatReviewWhenI18n */
/* Kenny's personal-site components — sits alongside app.jsx. */

const { useState: useStateK, useEffect: useEffectK, useMemo: useMemoK, useRef: useRefK } = React;

/* ============ Small icons (extends Ico in app.jsx) ============ */
const KIco = {
  wa: (p) => <svg width="28" height="28" viewBox="0 0 32 32" fill="#062b13" {...p}><path d="M16 3a13 13 0 0 0-11 19.7L3 29l6.6-1.8A13 13 0 1 0 16 3Zm0 23.6a10.5 10.5 0 0 1-5.4-1.5l-.4-.2-3.9 1 1-3.8-.3-.4A10.6 10.6 0 1 1 16 26.6Zm5.8-7.9c-.3-.2-1.9-.9-2.2-1s-.5-.2-.7.2-.8 1-1 1.2-.4.2-.7 0a8.7 8.7 0 0 1-4.3-3.7c-.3-.6.3-.5.9-1.7.1-.2 0-.4 0-.6s-.7-1.8-1-2.4-.5-.5-.7-.5h-.6a1.2 1.2 0 0 0-.8.4 3.6 3.6 0 0 0-1.1 2.6 6.2 6.2 0 0 0 1.3 3.3c.2.2 2.2 3.4 5.4 4.7s3.2 1 3.8 1a3.3 3.3 0 0 0 2.2-1.5 2.7 2.7 0 0 0 .2-1.5c-.1-.2-.3-.3-.7-.5Z" /></svg>,
  star: (p) => <svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor" {...p}><path d="m12 2 3 7 7 .5-5.5 4.8L18 22l-6-4-6 4 1.5-7.7L2 9.5 9 9Z" /></svg>,
  quote: (p) => <svg width="32" height="32" viewBox="0 0 32 32" fill="currentColor" {...p}><path d="M10 8c-4 0-7 3-7 8v8h8v-8H6c0-3 2-5 4-5Zm14 0c-4 0-7 3-7 8v8h8v-8h-5c0-3 2-5 4-5Z" /></svg>,
  badge: (p) => <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" {...p}><circle cx="12" cy="9" r="6" /><path d="m7 13-2 8 7-3 7 3-2-8" /></svg>,
  globe: (p) => <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" {...p}><circle cx="12" cy="12" r="9" /><path d="M3 12h18M12 3a14 14 0 0 1 0 18M12 3a14 14 0 0 0 0 18" /></svg>,
  download: (p) => <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" {...p}><path d="M12 3v13m-5-5 5 5 5-5M4 21h16" /></svg>,
  calc: (p) => <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" {...p}><rect x="4" y="3" width="16" height="18" /><path d="M8 7h8M8 11h2m4 0h2M8 15h2m4 0h2M8 19h2m4 0h2" /></svg>,
  cam: (p) => <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" {...p}><path d="M3 7h4l2-3h6l2 3h4v13H3z" /><circle cx="12" cy="13" r="4" /></svg>,
  warn: (p) => <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" {...p}><path d="M12 3 2 21h20Z" /><path d="M12 10v5M12 18v.5" /></svg>
};

/* ============ Disclaimer Strip (top of page) ============ */
function DisclaimerStrip({ prominence = "strong" }) {
  const { t } = useKennyLang();
  if (prominence === "off") return null;
  if (prominence === "soft") return null; // footer-only handles it
  return (
    <div className={`disclaimer-strip disclaimer-${prominence}`}>
      <div className="shell disclaimer-inner">
        <KIco.warn />
        <div className="disclaimer-copy">
          <b>{t("disclaimer.title")}</b>
          {t("disclaimer.body", { dealer: GWM_DEALER.legalName })}
        </div>
      </div>
    </div>);

}

/* ============ Floating Language Dropdown (sits in fab stack) ============ */
function FloatingLang({ lang, setLang }) {
  const { t } = useKennyLang();
  const [open, setOpen] = useStateK(false);
  const ref = useRefK(null);

  useEffectK(() => {
    if (!open) return;
    const onDoc = (e) => {if (ref.current && !ref.current.contains(e.target)) setOpen(false);};
    const onEsc = (e) => {if (e.key === "Escape") setOpen(false);};
    document.addEventListener("mousedown", onDoc);
    window.addEventListener("keydown", onEsc);
    return () => {
      document.removeEventListener("mousedown", onDoc);
      window.removeEventListener("keydown", onEsc);
    };
  }, [open]);

  const opts = [
  { code: "EN", label: "English" },
  { code: "BM", label: "Bahasa Malaysia" },
  { code: "CN", label: "中文 · Chinese" }];

  const current = opts.find((o) => o.code === lang) || opts[0];

  return (
    <div className="fab-lang-wrap" ref={ref}>
      {open &&
      <div className="fab-lang-menu" role="listbox" aria-label={t("fab.langMenu")}>
          {opts.map((o) =>
        <button
          key={o.code}
          role="option"
          aria-selected={lang === o.code}
          className={`fab-lang-opt ${lang === o.code ? "is-active" : ""}`}
          onClick={() => {setLang(o.code);setOpen(false);}}>
          
              <span className="fab-lang-code">{o.code}</span>
              <span className="fab-lang-name">{o.label}</span>
              {lang === o.code && <span className="fab-lang-tick">✓</span>}
            </button>
        )}
        </div>
      }
      <button
        type="button"
        className="fab fab-lang"
        aria-label={t("fab.changeLang")}
        aria-haspopup="listbox"
        aria-expanded={open}
        onClick={() => setOpen((v) => !v)}>
        
        <KIco.globe />
        <span className="fab-lang-current">{current.code}</span>
      </button>
    </div>);

}

/* ============ Language Switcher ============ */
function LangSwitch({ lang, setLang }) {
  const opts = [
  { code: "EN", label: "EN" },
  { code: "BM", label: "BM" },
  { code: "CN", label: "中" }];

  return (
    <div className="lang-switch" role="tablist" aria-label="Language">
      {opts.map((o) =>
      <button
        key={o.code}
        type="button"
        role="tab"
        aria-selected={lang === o.code}
        className={lang === o.code ? "is-active" : ""}
        onClick={() => setLang(o.code)}>
        
          {o.label}
        </button>
      )}
    </div>);

}

/* ============ Language coming-soon banner ============ */
function LangBanner() {
  return null;
}

/* ============ Kenny Brand Mark (replaces dealer-led BrandMark) ============ */
function KennyBrand() {
  const { t } = useKennyLang();
  return (
    <a href="#top" className="brand kenny-brand">
      <img src="assets/gwm-logo-horizontal.png" alt="GWM" className="kenny-gwm-logo kenny-gwm-logo--h" />
      <img src="assets/gwm-white-vertical.png" alt="" aria-hidden="true" className="kenny-gwm-logo kenny-gwm-logo--v" />
      <span className="brand-divider" />
      <div className="brand-text">
        <div className="a">GWM Kota Damansara</div>
        <div className="b">{t("brand.subtitle")}</div>
      </div>
    </a>);

}

/* ============ Hero Slideshow — auto-rotating image carousel ============
   Slides come from KENNY_HERO_SLIDES in kenny-data.js. Drop new images
   into assets/hero/ and update the array there — no JSX changes needed. */
function HeroSlideshow({ slides, intervalMs }) {
  const [active, setActive] = useStateK(0);
  const [paused, setPaused] = useStateK(false);
  const touchRef = useRefK({ startX: null, currentX: null });
  // Only pause on hover-capable devices (desktop); touch devices don't reliably
  // fire mouseleave after a tap, which would freeze the slideshow.
  const canHover = useMemoK(() => window.matchMedia("(hover: hover)").matches, []);

  useEffectK(() => {
    if (paused || slides.length < 2) return;
    const id = setInterval(
      () => setActive((v) => (v + 1) % slides.length),
      intervalMs
    );
    return () => clearInterval(id);
  }, [slides.length, intervalMs, paused]);

  const goNext = () => setActive((v) => (v + 1) % slides.length);
  const goPrev = () => setActive((v) => (v - 1 + slides.length) % slides.length);

  const onTouchStart = (e) => {
    if (slides.length < 2) return;
    const x = e.touches && e.touches[0] ? e.touches[0].clientX : null;
    touchRef.current = { startX: x, currentX: x };
  };

  const onTouchMove = (e) => {
    if (slides.length < 2) return;
    if (!touchRef.current.startX) return;
    const x = e.touches && e.touches[0] ? e.touches[0].clientX : null;
    touchRef.current.currentX = x;
  };

  const onTouchEnd = () => {
    if (slides.length < 2) return;
    const { startX, currentX } = touchRef.current;
    if (startX == null || currentX == null) return;
    const deltaX = currentX - startX;
    const swipeThreshold = 40;
    if (Math.abs(deltaX) >= swipeThreshold) {
      if (deltaX < 0) {
        goNext();
      } else {
        goPrev();
      }
    }
    touchRef.current = { startX: null, currentX: null };
  };

  return (
    <div
      className="hero-slideshow"
      onMouseEnter={canHover ? () => setPaused(true) : undefined}
      onMouseLeave={canHover ? () => setPaused(false) : undefined}
      onTouchStart={onTouchStart}
      onTouchMove={onTouchMove}
      onTouchEnd={onTouchEnd}>
      {slides.length > 1 &&
      <>
          <button className="hero-nav hero-nav-prev" type="button" aria-label="Previous hero image" onClick={goPrev}>
            {"<"}
          </button>
          <button className="hero-nav hero-nav-next" type="button" aria-label="Next hero image" onClick={goNext}>
            {">"}
          </button>
        </>
      }
      {slides.map((s, i) =>
      <div
        className={`hero-slide${i === active ? " is-active" : ""}`}
        key={`${s.src}-${i}`}
        aria-hidden={i !== active}>
          <img
          src={s.src}
          alt={s.alt || ""}
          loading={i === 0 ? "eager" : "lazy"}
          onError={(e) => { e.currentTarget.style.visibility = "hidden"; }} />
        </div>
      )}
      {slides.length > 1 &&
      <div className="hero-dots" role="tablist" aria-label="Hero slides">
          {slides.map((_, i) =>
        <button
          key={i}
          type="button"
          role="tab"
          className={`hero-dot${i === active ? " is-active" : ""}`}
          aria-selected={i === active}
          aria-label={`Show slide ${i + 1}`}
          onClick={() => setActive(i)} />
        )}
        </div>
      }
    </div>);

}

/* ============ Hero (Car-led — GWM is the visual hero) ============ */
function KennyHero() {
  const { t } = useKennyLang();
  const hero = GWM_MODELS && GWM_MODELS[0] || null; // TANK 300

  // Tiny inline VehiclePhoto/Silhouette using the global ones from app.jsx
  const VP = window.VehiclePhoto;

  const slides = Array.isArray(window.KENNY_HERO_SLIDES) ? window.KENNY_HERO_SLIDES : [];
  const slideInterval = Number(window.KENNY_HERO_SLIDE_INTERVAL_MS) || 4500;
  const useSlideshow = slides.length > 0;

  return (
    <section className="hero hero-car" id="top">
      <div className="hero-bg" />
      <div className="hero-grid" />

      {useSlideshow ?
      <HeroSlideshow slides={slides} intervalMs={slideInterval} /> :
      hero && VP &&
      <div className="hero-img-wrap">
          <VP src={hero.hero} alt={hero.heroAlt || hero.name} type="suv" cardPhoto={hero.cardPhoto} />
        </div>
      }

      <div className="hero-vignette" />

      <div className="shell">
        <div className="hero-eyebrow hero-slideshow-watermark">
          {t("hero.eyebrow")}
        </div>
      </div>

      {/* Stats strip — about the FEATURED CAR, not about Kenny */}
      {hero && hero.stats &&
      <div className="hero-meta">
          <div className="stat">
            <div className="l">{t("hero.startsFrom")}</div>
            <div className="v">{hero.price}</div>
          </div>
          {hero.stats.slice(0, 2).map((s, i) =>
        <div className="stat" key={i}>
              <div className="l">{s.label}</div>
              <div className="v">{s.value}{s.unit ? <span style={{ fontSize: "0.5em", marginLeft: 4, fontWeight: 400, color: "var(--muted)" }}> {s.unit}</span> : null}</div>
            </div>
        )}
        </div>
      }

    </section>);

}

/* ============ Sticky Disclaimer Banner (alt to strip) ============ */

/* ============ GWM Kota Damansara — location SEO block ============ */
function GwmLocationSeo() {
  const { t } = useKennyLang();
  return (
    <section className="gwm-location-seo hairline" id="gwm-kota-damansara" aria-labelledby="gwm-location-seo-title">
      <div className="shell">
        <h2 id="gwm-location-seo-title" className="gwm-location-seo__title">{t("location.seo.title")}</h2>
        <p className="gwm-location-seo__body body">
          {t("location.seo.body", { dealer: GWM_DEALER.legalName })}
        </p>
        <p className="gwm-location-seo__address caption">{GWM_DEALER.address}</p>
        <div className="gwm-location-seo__actions">
          <a className="btn btn--sm" href={GWM_DEALER.gmaps} target="_blank" rel="noreferrer">{t("showroom.directions")}</a>
          <a className="btn btn--sm btn--wa" href={gwmWa(t("wa.showroom"))} target="_blank" rel="noreferrer">
            <KIco.wa width="16" height="16" /> {t("showroom.tellKenny")}
          </a>
        </div>
      </div>
    </section>);

}

/* ============ About Kenny ============ */
function AboutKenny({ onJumpModels }) {
  const { t } = useKennyLang();
  return (
    <section className="section about-kenny" id="about-kenny">
      <div className="shell">
        <div className="section-head">
          <div>
            <span className="label-muted">{t("about.label")}</span>
            <h2 className="display-lg">{t("about.title")}</h2>
          </div>
          <p className="body" style={{ maxWidth: 420 }}>
            {t("about.intro")}
          </p>
        </div>

        <div className="about-kenny-grid">
          <div className="ak-portrait">
            <img
              src={KENNY.portrait}
              alt={KENNY.name}
              onLoad={(e) => { e.currentTarget.nextElementSibling.style.display = "none"; }}
              onError={(e) => { e.currentTarget.style.display = "none"; e.currentTarget.nextElementSibling.style.display = "grid"; }} />
            
            <div className="ak-portrait-fallback">
              <div>K</div>
              <div className="cap">{t("about.portraitHint")}<br /><code>assets/kenny.jpg</code></div>
            </div>

            <div className="ak-portrait-tag">
              <div className="ak-portrait-tag-title">{t("about.tagline")}</div>
              <div className="ak-portrait-tag-tel">+60 19-231 6065</div>
            </div>
          </div>

          <div className="ak-body">
            <div className="ak-bio">
              <p>{t("about.bio1", { years: KENNY.yearsExp })}</p>
              <p>{t("about.bio2")}</p>
            </div>

            <div className="ak-fact ak-based-at">
              <div className="l">{t("about.basedAt")}</div>
              <div className="v">{KENNY.basedAt} · {GWM_DEALER.shortAddress}</div>
            </div>

            <div className="hero-ctas">
              <button className="btn btn--primary" onClick={onJumpModels}>
                {t("about.explore")}
              </button>
              <a className="btn btn--wa" href={gwmWa(t("wa.about"))} target="_blank" rel="noreferrer">
                <KIco.wa width="16" height="16" /> {t("about.whatsapp")}
              </a>
            </div>
          </div>
        </div>
      </div>
    </section>);

}

/* ============ Deliveries Wall ============ */
function Deliveries() {
  const { t } = useKennyLang();
  const items = KENNY_DELIVERIES || [];
  const [isPaused, setIsPaused] = useStateK(false);
  const [topOffsetPx, setTopOffsetPx] = useStateK(0);
  const [bottomOffsetPx, setBottomOffsetPx] = useStateK(0);
  const [topStepPx, setTopStepPx] = useStateK(0);
  const [bottomStepPx, setBottomStepPx] = useStateK(0);
  const [topLoopWidthPx, setTopLoopWidthPx] = useStateK(0);
  const [bottomLoopWidthPx, setBottomLoopWidthPx] = useStateK(0);
  const topTrackRef = useRefK(null);
  const bottomTrackRef = useRefK(null);

  // Future-ready: first 10 photos = row 1, next 10 photos = row 2.
  // Fallback for smaller sets: split roughly half/half.
  const row1Raw = items.length >= 20 ? items.slice(0, 10) : items.slice(0, Math.ceil(items.length / 2));
  const row2Raw = items.length >= 20 ? items.slice(10, 20) : items.slice(Math.ceil(items.length / 2));
  const row1 = row1Raw.length ? row1Raw : items;
  const row2 = row2Raw.length ? row2Raw : row1;
  const canLoopTop = row1.length > 1;
  const canLoopBottom = row2.length > 1;
  const useLoop = canLoopTop || canLoopBottom;
  const row1Rendered = canLoopTop ? row1.concat(row1) : row1;
  const row2Rendered = canLoopBottom ? row2.concat(row2) : row2;

  const normalizeOffset = (v, loopWidth) => {
    if (!loopWidth) return 0;
    return ((v % loopWidth) + loopWidth) % loopWidth;
  };

  useEffectK(() => {
    if (!useLoop) return;
    const recompute = (trackEl, setStep, setLoopWidth) => {
      if (!trackEl) return;
      const cards = trackEl.querySelectorAll(".delivery-card");
      if (!cards.length) return;
      const gap = parseFloat(getComputedStyle(trackEl).gap || "0") || 0;
      const cardW = cards[0].getBoundingClientRect().width;
      setStep(cardW + gap);
      setLoopWidth(trackEl.scrollWidth / 2);
    };

    const topTrack = topTrackRef.current;
    const bottomTrack = bottomTrackRef.current;
    recompute(topTrack, setTopStepPx, setTopLoopWidthPx);
    recompute(bottomTrack, setBottomStepPx, setBottomLoopWidthPx);

    const roTop = new ResizeObserver(() => recompute(topTrackRef.current, setTopStepPx, setTopLoopWidthPx));
    const roBottom = new ResizeObserver(() => recompute(bottomTrackRef.current, setBottomStepPx, setBottomLoopWidthPx));
    if (topTrack) roTop.observe(topTrack);
    if (bottomTrack) roBottom.observe(bottomTrack);
    const onResize = () => {
      recompute(topTrackRef.current, setTopStepPx, setTopLoopWidthPx);
      recompute(bottomTrackRef.current, setBottomStepPx, setBottomLoopWidthPx);
    };
    window.addEventListener("resize", onResize);
    return () => {
      roTop.disconnect();
      roBottom.disconnect();
      window.removeEventListener("resize", onResize);
    };
  }, [useLoop, row1Rendered.length, row2Rendered.length]);

  useEffectK(() => {
    if (!useLoop || isPaused || (!topLoopWidthPx && !bottomLoopWidthPx)) return;
    let raf = 0;
    let lastTs = 0;
    const speedPxPerSec = 22;
    const tick = (ts) => {
      if (!lastTs) lastTs = ts;
      const dt = (ts - lastTs) / 1000;
      lastTs = ts;
      // Row 1: left -> right
      if (canLoopTop) setTopOffsetPx((v) => normalizeOffset(v + speedPxPerSec * dt, topLoopWidthPx));
      // Row 2: right -> left
      if (canLoopBottom) setBottomOffsetPx((v) => normalizeOffset(v + speedPxPerSec * dt, bottomLoopWidthPx));
      raf = requestAnimationFrame(tick);
    };
    raf = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(raf);
  }, [useLoop, isPaused, topLoopWidthPx, bottomLoopWidthPx, canLoopTop, canLoopBottom]);

  useEffectK(() => {
    if (useLoop) return;
    setTopOffsetPx(0);
    setBottomOffsetPx(0);
  }, [useLoop]);

  const goTopNext = () => {
    if (!canLoopTop) return;
    setTopOffsetPx((v) => normalizeOffset(v + topStepPx, topLoopWidthPx));
  };

  const goTopPrev = () => {
    if (!canLoopTop) return;
    setTopOffsetPx((v) => normalizeOffset(v - topStepPx, topLoopWidthPx));
  };

  const goBottomNext = () => {
    if (!canLoopBottom) return;
    setBottomOffsetPx((v) => normalizeOffset(v + bottomStepPx, bottomLoopWidthPx));
  };

  const goBottomPrev = () => {
    if (!canLoopBottom) return;
    setBottomOffsetPx((v) => normalizeOffset(v - bottomStepPx, bottomLoopWidthPx));
  };

  return (
    <section className="section deliveries hairline" id="deliveries" style={{ background: "var(--soft)" }}>
      <div className="shell">
        <div className="section-head">
          <div>
            <span className="label-muted">{t("deliveries.label")}</span>
            <h2 className="display-lg">{t("deliveries.title")}</h2>
          </div>
          <p className="body" style={{ maxWidth: 420 }}>
            {t("deliveries.intro")}
          </p>
        </div>

        <div
          className="delivery-carousel-wrap"
          onMouseEnter={() => setIsPaused(true)}
          onMouseLeave={() => setIsPaused(false)}>
          <div className="delivery-rows">
            <div className="delivery-row">
              <button className="delivery-nav delivery-nav-prev" onClick={goTopPrev} aria-label="Previous first-row gallery items">
                {"<"}
              </button>
              <div className="delivery-viewport">
                <div
                  ref={topTrackRef}
                  className="delivery-track"
                  style={{
                    transform: canLoopTop ? `translateX(${-(topLoopWidthPx - topOffsetPx)}px)` : "none"
                  }}>
                  {row1Rendered.map((d, i) =>
                  <figure className="delivery-card" key={`top-${i}-${d.src || d.caption || "item"}`}>
                      <div className="delivery-img">
                        <img
                        src={d.src}
                        alt={d.caption}
                        onLoad={(e) => { e.currentTarget.nextElementSibling.style.display = "none"; }}
                        onError={(e) => { e.currentTarget.style.display = "none"; e.currentTarget.nextElementSibling.style.display = "grid"; }} />
                      
                        <div className="delivery-placeholder">
                          <KIco.cam />
                          <div className="dp-cap">{t("deliveries.placeholder", { n: String((i % row1.length) + 1).padStart(2, "0") })}</div>
                          <div className="dp-hint">{t("deliveries.dropHint")}<br /><code>{d.src}</code></div>
                        </div>
                      </div>
                    </figure>
                  )}
                </div>
              </div>
              <button className="delivery-nav delivery-nav-next" onClick={goTopNext} aria-label="Next first-row gallery items">
                {">"}
              </button>
            </div>

            <div className="delivery-row">
              <button className="delivery-nav delivery-nav-prev" onClick={goBottomPrev} aria-label="Previous second-row gallery items">
                {"<"}
              </button>
              <div className="delivery-viewport">
                <div
                  ref={bottomTrackRef}
                  className="delivery-track"
                  style={{
                    transform: canLoopBottom ? `translateX(-${bottomOffsetPx}px)` : "none"
                  }}>
                  {row2Rendered.map((d, i) =>
                  <figure className="delivery-card" key={`bottom-${i}-${d.src || d.caption || "item"}`}>
                      <div className="delivery-img">
                        <img
                        src={d.src}
                        alt={d.caption}
                        onLoad={(e) => { e.currentTarget.nextElementSibling.style.display = "none"; }}
                        onError={(e) => { e.currentTarget.style.display = "none"; e.currentTarget.nextElementSibling.style.display = "grid"; }} />
                      
                        <div className="delivery-placeholder">
                          <KIco.cam />
                          <div className="dp-cap">{t("deliveries.placeholder", { n: String((i % row2.length) + 1).padStart(2, "0") })}</div>
                          <div className="dp-hint">{t("deliveries.dropHint")}<br /><code>{d.src}</code></div>
                        </div>
                      </div>
                    </figure>
                  )}
                </div>
              </div>
              <button className="delivery-nav delivery-nav-next" onClick={goBottomNext} aria-label="Next second-row gallery items">
                {">"}
              </button>
            </div>
          </div>
        </div>

        <p className="caption" style={{ marginTop: 24, textAlign: "center", maxWidth: 640, marginInline: "auto" }}>
          {t("deliveries.caption")}
        </p>
      </div>
    </section>);

}

/* ============ Reviews ============ */
function Reviews() {
  const { t, lang } = useKennyLang();
  const reviews = KENNY_REVIEWS || [];
  const hasMany = reviews.length > 1;
  const useLoop = hasMany;
  const rendered = useLoop ? reviews.concat(reviews) : reviews;
  const [isPaused, setIsPaused] = useStateK(false);
  // Only pause on hover-capable devices (desktop/mouse); touch devices don't reliably
  // fire mouseleave after a tap, which would freeze the animation permanently.
  const canHover = useMemoK(() => window.matchMedia("(hover: hover)").matches, []);
  const [offsetPx, setOffsetPx] = useStateK(0);
  const [stepPx, setStepPx] = useStateK(0);
  const [loopWidthPx, setLoopWidthPx] = useStateK(0);
  const trackRef = useRefK(null);

  const normalizeOffset = (v) => {
    if (!loopWidthPx) return 0;
    return ((v % loopWidthPx) + loopWidthPx) % loopWidthPx;
  };

  useEffectK(() => {
    const track = trackRef.current;
    if (!track || !useLoop) return;
    const recompute = () => {
      const cards = track.querySelectorAll(".review-card");
      if (!cards.length) return;
      const gap = parseFloat(getComputedStyle(track).gap || "0") || 0;
      const cardW = cards[0].getBoundingClientRect().width;
      setStepPx(cardW + gap);
      setLoopWidthPx(track.scrollWidth / 2);
    };
    recompute();
    const ro = new ResizeObserver(recompute);
    ro.observe(track);
    window.addEventListener("resize", recompute);
    return () => {
      ro.disconnect();
      window.removeEventListener("resize", recompute);
    };
  }, [useLoop, rendered.length]);

  useEffectK(() => {
    if (!useLoop || isPaused || !loopWidthPx) return;
    let raf = 0;
    let lastTs = 0;
    const speedPxPerSec = 24;
    const tick = (ts) => {
      if (!lastTs) lastTs = ts;
      const dt = (ts - lastTs) / 1000;
      lastTs = ts;
      setOffsetPx((v) => normalizeOffset(v + speedPxPerSec * dt));
      raf = requestAnimationFrame(tick);
    };
    raf = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(raf);
  }, [useLoop, isPaused, loopWidthPx]);

  useEffectK(() => {
    if (useLoop) return;
    setOffsetPx(0);
  }, [useLoop]);

  const goNext = () => {
    if (!useLoop) return;
    setOffsetPx((v) => normalizeOffset(v + stepPx));
  };

  const goPrev = () => {
    if (!useLoop) return;
    setOffsetPx((v) => normalizeOffset(v - stepPx));
  };

  return (
    <section className="section reviews" id="reviews">
      <div className="shell">
        <div className="section-head">
          <div>
            <span className="label-muted">{t("reviews.label")}</span>
            <h2 className="display-lg">{t("reviews.title")}</h2>
          </div>
          <a className="btn btn--sm" href={GWM_DEALER.googleReviews} target="_blank" rel="noreferrer">
            <img
              src="assets/google.svg"
              alt="Google"
              width="16"
              height="16"
              style={{ display: "block" }}
            />
            {t("reviews.seeAll")}
          </a>
        </div>

        <div
          className="reviews-carousel"
          onMouseEnter={canHover ? () => setIsPaused(true) : undefined}
          onMouseLeave={canHover ? () => setIsPaused(false) : undefined}>
          <button className="review-nav review-nav-prev" onClick={goPrev} aria-label="Previous review">
            {"<"}
          </button>

          <div className="reviews-viewport">
            <div
              ref={trackRef}
              className="reviews-track"
              style={{
                transform: useLoop ? `translateX(-${offsetPx}px)` : "none"
              }}>
              {rendered.map((r, i) =>
              <article className="review-card" key={`${i}-${r.name}-${r.when || r.date || "review"}`}>
                  <div className="review-stars" aria-label={`${r.rating} stars`}>
                    {Array.from({ length: r.rating }).map((_, k) => <KIco.star key={k} />)}
                  </div>
                  <KIco.quote className="review-quote" />
                  <p className="review-body">{r.body}</p>
                  <div className="review-meta">
                    <div>
                      <div className="rm-name">{r.name}</div>
                      <div className="rm-when">{formatReviewWhenI18n(lang, r)}</div>
                    </div>
                    {r.placeholder && <span className="review-placeholder-tag">PLACEHOLDER</span>}
                  </div>
                </article>
              )}
            </div>
          </div>

          <button className="review-nav review-nav-next" onClick={goNext} aria-label="Next review">
            {">"}
          </button>
        </div>

        <p className="caption review-source-caption" style={{ marginTop: 24 }}>
          {t("reviews.caption", { dealer: GWM_DEALER.legalName })}{" "}
          <a href={GWM_DEALER.googleReviews} target="_blank" rel="noreferrer" style={{ color: "var(--accent)" }}>
            {t("reviews.captionLink")}
          </a>.
        </p>
      </div>
    </section>);

}

/* ============ Loan Calculator ============ */
function LoanCalculator() {
  const { t, lang } = useKennyLang();
  // Parse "RM 250,000" → 250000
  const parsePrice = (s) => {
    if (!s) return 0;
    const n = String(s).replace(/[^\d.]/g, "");
    return parseFloat(n) || 0;
  };

  const modelOptions = useMemoK(() => {
    const opts = [{ id: "custom", name: t("loan.manualEntry"), price: 0 }];
    (GWM_MODELS || []).forEach((m) => {
      opts.push({ id: m.id, name: m.name, price: parsePrice(m.price) });
    });
    return opts;
  }, [lang]);

  const clamp = (n, min, max) => Math.max(min, Math.min(max, n));
  const fmtPct = (n) => {
    if (!isFinite(n) || n < 0) return "0";
    if (Number.isInteger(n)) return String(n);
    return n.toFixed(2).replace(/0+$/, "").replace(/\.$/, "");
  };

  const [modelId, setModelId] = useStateK(modelOptions[1]?.id || "custom");
  const [customPrice, setCustomPrice] = useStateK("");
  const [downPct, setDownPct] = useStateK(10);
  const [dpMode, setDpMode] = useStateK("pct"); // 'pct' or 'amt'
  const [tenure, setTenure] = useStateK(9);
  const [rate, setRate] = useStateK(2.25);

  const selectedModel = modelOptions.find((o) => o.id === modelId);
  const price = modelId === "custom" ? parseFloat(customPrice) || 0 : selectedModel?.price || 0;
  const dp = price * (downPct / 100);
  const loan = price - dp;
  const interest = loan * (rate / 100) * tenure;
  const total = loan + interest;
  const monthly = tenure > 0 ? total / (tenure * 12) : 0;

  // Draft strings for manual entry — let users type freely (decimals, empties)
  // without the controlled-input round-trip stripping their keystrokes.
  const [dpDraft, setDpDraft] = useStateK(() => String(10));
  const [tenureDraft, setTenureDraft] = useStateK(() => "9");
  const [rateDraft, setRateDraft] = useStateK(() => "2.25");

  const dpAmountStr = (pct, p) => String(Math.max(0, Math.round(p * pct / 100)));
  const syncDpDraft = (pct, mode, p) => {
    setDpDraft(mode === "pct" ? fmtPct(pct) : dpAmountStr(pct, p));
  };

  // Re-sync the DP amount draft when the vehicle price changes (model switch
  // or custom price edit) and the user is currently viewing/entering an amount.
  useEffectK(() => {
    if (dpMode === "amt") setDpDraft(dpAmountStr(downPct, price));
    // intentionally not depending on downPct so typing isn't interrupted
  }, [price, dpMode]);

  const onDpSlider = (v) => {
    setDownPct(v);
    syncDpDraft(v, dpMode, price);
  };
  const onDpInput = (s) => {
    setDpDraft(s);
    if (s === "") return;
    const n = parseFloat(s);
    if (isNaN(n)) return;
    if (dpMode === "pct") {
      setDownPct(clamp(n, 0, 95));
    } else {
      if (!price) return;
      setDownPct(clamp(n / price * 100, 0, 95));
    }
  };
  const onDpModeToggle = (m) => {
    if (m === dpMode) return;
    setDpMode(m);
    syncDpDraft(downPct, m, price);
  };

  const onTenureSlider = (v) => {
    setTenure(v);
    setTenureDraft(String(v));
  };
  const onTenureInput = (s) => {
    setTenureDraft(s);
    if (s === "") return;
    const n = parseInt(s, 10);
    if (isNaN(n)) return;
    setTenure(clamp(n, 1, 9));
  };

  const onRateSlider = (v) => {
    setRate(v);
    setRateDraft(v.toFixed(2));
  };
  const onRateInput = (s) => {
    setRateDraft(s);
    if (s === "") return;
    const n = parseFloat(s);
    if (isNaN(n)) return;
    setRate(clamp(n, 0, 15));
  };

  const fmt = (n) => {
    if (!isFinite(n) || n <= 0) return "—";
    return "RM " + Math.round(n).toLocaleString("en-MY");
  };

  const waMsg = t("wa.loan", {
    model: selectedModel?.name || "—",
    price: fmt(price),
    downPct: fmtPct(downPct),
    downAmt: fmt(dp),
    tenure,
    rate: rate.toFixed(2),
    monthly: fmt(monthly)
  });

  return (
    <section className="section loan-calc hairline" id="loan" style={{ background: "var(--soft)" }}>
      <div className="shell">
        <div className="section-head">
          <div>
            <span className="label-muted">{t("loan.label")}</span>
            <h2 className="display-lg">{t("loan.title")}</h2>
          </div>
          <p className="body" style={{ maxWidth: 420 }}>
            {t("loan.intro")}
          </p>
        </div>

        <div className="loan-grid">
          {/* Inputs */}
          <div className="loan-inputs">
            <div className="loan-field">
              <label>{t("loan.model")}</label>
              <select value={modelId} onChange={(e) => setModelId(e.target.value)}>
                {modelOptions.map((o) =>
                <option key={o.id} value={o.id}>
                    {o.name}{o.price ? ` — RM ${o.price.toLocaleString("en-MY")}` : ""}
                  </option>
                )}
              </select>
            </div>

            {modelId === "custom" &&
            <div className="loan-field">
                <label>{t("loan.vehiclePrice")}</label>
                <input
                type="number"
                min="0"
                step="1000"
                inputMode="decimal"
                value={customPrice}
                onChange={(e) => setCustomPrice(e.target.value)}
                placeholder={t("loan.pricePlaceholder")} />
              
              </div>
            }

            {modelId !== "custom" &&
            <div className="loan-field">
                <label>{t("loan.vehiclePriceDefault")}</label>
                <div className="loan-readout">{fmt(price)}</div>
              </div>
            }

            <div className="loan-field">
              <label>
                {t("loan.downPayment")}{" "}
                <span className="loan-val">{fmtPct(downPct)}% · {fmt(dp)}</span>
              </label>
              <input
                type="range"
                min="5"
                max="90"
                step="1"
                value={downPct}
                onChange={(e) => onDpSlider(parseFloat(e.target.value))} />
              
              <div className="loan-manual">
                <input
                  type="number"
                  inputMode="decimal"
                  min="0"
                  max={dpMode === "pct" ? 95 : price || undefined}
                  step={dpMode === "pct" ? 0.5 : 100}
                  value={dpDraft}
                  onChange={(e) => onDpInput(e.target.value)}
                  aria-label={dpMode === "pct" ? "Down payment percentage" : "Down payment amount in Ringgit"} />
                
                <div className="loan-manual-toggle" role="group" aria-label="Down payment unit">
                  <button
                    type="button"
                    className={dpMode === "pct" ? "is-active" : ""}
                    aria-pressed={dpMode === "pct"}
                    onClick={() => onDpModeToggle("pct")}>%</button>
                  <button
                    type="button"
                    className={dpMode === "amt" ? "is-active" : ""}
                    aria-pressed={dpMode === "amt"}
                    onClick={() => onDpModeToggle("amt")}>RM</button>
                </div>
              </div>
            </div>

            <div className="loan-field">
              <label>
                {t("loan.tenure")} <span className="loan-val">{tenure} {tenure === 1 ? t("loan.year") : t("loan.years")}</span>
              </label>
              <input
                type="range"
                min="1"
                max="9"
                step="1"
                value={tenure}
                onChange={(e) => onTenureSlider(parseInt(e.target.value))} />
              
              <div className="loan-manual">
                <input
                  type="number"
                  inputMode="numeric"
                  min="1"
                  max="9"
                  step="1"
                  value={tenureDraft}
                  onChange={(e) => onTenureInput(e.target.value)}
                  aria-label="Loan tenure in years" />
                
                <div className="loan-manual-suffix">{t("loan.years")}</div>
              </div>
            </div>

            <div className="loan-field">
              <label>{t("loan.interestRate")} <span className="loan-val">{rate.toFixed(2)}%</span></label>
              <input
                type="range"
                min="0.5"
                max="10"
                step="0.05"
                value={rate}
                onChange={(e) => onRateSlider(parseFloat(e.target.value))} />
              
              <div className="loan-manual">
                <input
                  type="number"
                  inputMode="decimal"
                  min="0"
                  max="15"
                  step="0.05"
                  value={rateDraft}
                  onChange={(e) => onRateInput(e.target.value)}
                  aria-label="Interest rate per annum" />
                
                <div className="loan-manual-suffix">% p.a.</div>
              </div>
              <div className="loan-sub">{t("loan.interestHint")}</div>
            </div>
          </div>

          {/* Result */}
          <div className="loan-result">
            <div className="lr-mono">{selectedModel?.name || t("loan.yourCar")}</div>
            <div className="lr-mainLabel">{t("loan.estMonthly")}</div>
            <div className="lr-main">{fmt(monthly)}<span className="lr-suffix">{t("loan.perMonth")}</span></div>

            <div className="lr-row"><span>{t("loan.rowPrice")}</span><b>{fmt(price)}</b></div>
            <div className="lr-row"><span>{t("loan.rowDown", { pct: fmtPct(downPct) })}</span><b>{fmt(dp)}</b></div>
            <div className="lr-row"><span>{t("loan.rowLoan")}</span><b>{fmt(loan)}</b></div>
            <div className="lr-row"><span>{t("loan.rowInterest", { rate: rate.toFixed(2), years: tenure })}</span><b>{fmt(interest)}</b></div>
            <div className="lr-row lr-total"><span>{t("loan.rowTotal")}</span><b>{fmt(total)}</b></div>

            <a
              className="btn btn--wa lr-cta"
              href={gwmWa(waMsg)}
              target="_blank"
              rel="noreferrer">
              
              <KIco.wa width="16" height="16" /> {t("loan.sendWa")}
            </a>
            <p className="caption" style={{ marginTop: 12 }}>
              {t("loan.disclaimer")}
            </p>
          </div>
        </div>
      </div>
    </section>);

}

/* ============ Brochures ============ */
function Brochures() {
  return (
    <section className="section brochures" id="brochures">
      <div className="shell">
        <div className="section-head">
          <div>
            <span className="label-muted">Brochures &amp; Price List</span>
            <h2 className="display-lg">Take it offline.</h2>
          </div>
          <p className="body" style={{ maxWidth: 420 }}>
            Download the latest spec sheets and price list to read at your own pace. Need a specific variant breakdown? Just ping me on WhatsApp.
          </p>
        </div>

        <div className="brochure-grid">
          {KENNY_BROCHURES.map((b, i) =>
          <a className="brochure-card" key={i} href={b.file} target="_blank" rel="noreferrer">
              <div className="brochure-icon"><KIco.download /></div>
              <div className="brochure-body">
                <div className="brochure-title">{b.title}</div>
                <div className="brochure-sub">{b.size} · Tap to download</div>
              </div>
              <div className="brochure-arrow">↓</div>
            </a>
          )}
        </div>

        <p className="caption" style={{ marginTop: 20, maxWidth: 720 }}>
          Brochure PDFs are placeholders — drop the real files into <code>assets/brochures/</code> with the filenames listed above and they'll start downloading.
        </p>
      </div>
    </section>);

}

/* ============ Showroom Info (small block — was the big About) ============ */
function ShowroomInfo() {
  const { t } = useKennyLang();
  return (
    <section className="section showroom-info" id="showroom">
      <div className="shell">
        <div className="showroom-grid">
          <div>
            <span className="label-muted">{t("showroom.label")}</span>
            <h2 className="display-md" style={{ marginTop: 8 }}>{t("showroom.title")}</h2>
            <p className="body" style={{ marginTop: 12, maxWidth: 480 }}>
              {t("showroom.intro", { dealer: GWM_DEALER.legalName })}
            </p>
            <div className="info-list" style={{ marginTop: 16 }}>
              <div className="info-row">
                <div className="ico">📍</div>
                <div>
                  <div className="l">{t("showroom.address")}</div>
                  <div className="v">{GWM_DEALER.address}</div>
                </div>
              </div>
              <div className="info-row">
                <div className="ico">🕒</div>
                <div>
                  <div className="l">{t("showroom.hours")}</div>
                  <div className="v">{t("showroom.hoursValue")}</div>
                </div>
              </div>
            </div>
            <div style={{ marginTop: 16, display: "flex", gap: 12, flexWrap: "wrap" }}>
              <a className="btn" href={GWM_DEALER.gmaps} target="_blank" rel="noreferrer">{t("showroom.directions")}</a>
              <a className="btn btn--wa" href={gwmWa(t("wa.showroom"))} target="_blank" rel="noreferrer">
                <KIco.wa width="16" height="16" /> {t("showroom.tellKenny")}
              </a>
            </div>
          </div>
          <div className="showroom-map">
            <iframe
              title="GWM Kota Damansara"
              width="100%" height="100%"
              style={{ border: 0, filter: "invert(0.92) hue-rotate(180deg) saturate(0.6) brightness(0.95)" }}
              src="https://www.google.com/maps?q=GWM%20Kota%20Damansara&output=embed"
              loading="lazy" />
            
          </div>
        </div>
      </div>
    </section>);

}

/* Expose components on window so app.jsx can use them */
Object.assign(window, {
  KIco,
  DisclaimerStrip,
  KennyBrand,
  KennyHero,
  GwmLocationSeo,
  AboutKenny,
  Deliveries,
  Reviews,
  LoanCalculator,
  Brochures,
  ShowroomInfo,
  LangSwitch,
  LangBanner,
  FloatingLang
});