/* =========================================================
   Sections — Fran Ruiz portfolio
   Pulls from window.T (i18n) + window.PROJECTS + window.CLIENTS
   ========================================================= */

const { useEffect, useRef, useState } = React;

/* PREVIEW vs PRODUCTION url helpers.
   ------------------------------------------------------------------
   In production (Hostinger, LiteSpeed + .htaccess) the clean URLs
     /contact  /legal  /services/<slug>  /projects/<slug>
   are rewritten to their backing static files. In the design tool
   preview there is no Apache, so those clean URLs 404 — which is why
   project / service / contact pages "don't load" when navigating from
   the home page in preview.

   We detect the preview environment by hostname (the design tool
   serves from *.claudeusercontent.com) and route links to the raw
   files (project.html?slug=…) there, while keeping clean URLs in
   production. The target pages already accept ?slug= as fallback, so
   no per-page change is required. */
const IS_PREVIEW = typeof location !== "undefined" &&
  /claudeusercontent\.com$/i.test(location.hostname);

const routeProject = (slug) => IS_PREVIEW
  ? `project.html?slug=${encodeURIComponent(slug)}`
  : `/projects/${slug}`;
const routeService = (slug) => IS_PREVIEW
  ? `service.html?slug=${encodeURIComponent(slug)}`
  : `/services/${slug}`;
const routeContact = () => IS_PREVIEW ? "contact.html" : "/contact";
const routeLegal   = () => IS_PREVIEW ? "legal.html"   : "/legal";

// Expose to other Babel-compiled files (e.g. project-app.jsx) — each
// <script type="text/babel"> gets its own scope, globals must be
// stamped onto window explicitly to be shared.
if (typeof window !== "undefined") {
  window.routeProject = routeProject;
  window.routeService = routeService;
  window.routeContact = routeContact;
  window.routeLegal = routeLegal;
  window.IS_PREVIEW_ENV = IS_PREVIEW;
}

/* HOME — where the logo + section links should point.
   On the homepage it stays "" so links resolve to in-page anchors
   (#work, #about…). On any sub-page (/services/<slug>, /projects/<slug>,
   /contact, /legal) it becomes "/" so the same links jump back to the
   homepage first, then to the section. "/" and "/index.html" count as
   the homepage; anything else is a sub-page.

   In preview the homepage is `index.html`, not "/", so we route there
   explicitly when on a sub-page. */
const _onHomePath = typeof location !== "undefined" &&
  (/^\/(index\.html)?$/i.test(location.pathname) ||
   /\/index\.html$/i.test(location.pathname) ||
   /\/serve\/?$/i.test(location.pathname));
const HOME = _onHomePath ? "" : (IS_PREVIEW ? "index.html" : "/");

/* WebP-optimised assets. When the source PNG is in this set we render a
   <picture> so modern browsers fetch the smaller .webp and legacy ones
   still get the .png fallback. Keeps visual output identical. */
const WEBP_ASSETS = new Set([
  "/assets/fran-portrait.png",
  "/assets/work-influencer-agency.png",
  "/assets/work-cocreate-coaching.png",
  "/assets/work-bodegas-campanar.png",
  "/assets/work-fimed-clinic.png",
  "/assets/work-garden-brasas.png",
  "/assets/work-jp-physiotherapy.png",
  "/assets/work-mediterranean-home.png",
]);
const Img = ({ src, alt, className, style, loading, fetchPriority, width, height }) => {
  const webp = WEBP_ASSETS.has(src) ? src.replace(/\.png$/, ".webp") : null;
  // `decoding="async"` lets the browser decode off the main thread so the
  // image swap doesn't block scroll. Explicit width/height let the layout
  // engine reserve space even before the image has loaded.
  const img = (
    <img
      src={src}
      alt={alt}
      className={className}
      style={style}
      loading={loading}
      fetchpriority={fetchPriority}
      decoding="async"
      width={width}
      height={height}
    />
  );
  if (!webp) return img;
  return (
    <picture>
      <source srcSet={webp} type="image/webp" />
      {img}
    </picture>
  );
};

/* Eagerly preload all featured work images on mount so they're cached
   before the user scrolls into the Work section. We use <link rel="preload"
   as="image"> injected on first render — the browser will fetch in
   parallel with the other resources and reuse the cached copy when the
   <img> mounts. */
const useImagePreload = (srcs) => {
  useEffect(() => {
    const links = srcs.map((src) => {
      const webp = WEBP_ASSETS.has(src) ? src.replace(/\.png$/, ".webp") : src;
      const l = document.createElement("link");
      l.rel = "preload";
      l.as = "image";
      l.href = webp;
      l.type = webp.endsWith(".webp") ? "image/webp" : undefined;
      l.fetchPriority = "low";
      document.head.appendChild(l);
      return l;
    });
    return () => links.forEach((l) => l.remove());
  }, []);
};

const CountUp = ({ value }) => {
  // ─── Safety bail-outs ────────────────────────────────────────────
  // The integer regex below captures only the leading whole number, so
  // values like "4.8/5" or "8 J." would animate through "0.8/5", "1.8/5"
  // (Spanish "4,8/5" likewise). Those intermediate frames look broken
  // — a fresh visitor sees "0.8/5" and bounces. Skip the animation for
  // any value that isn't a clean integer + simple suffix:
  //   ✓ "50+", "8 yrs"           → animated
  //   ✗ "4.8/5", "4,8/5", "8 J." → rendered directly, no animation
  const str = String(value);
  if (/[.,]/.test(str)) return <em>{value}</em>;
  const m = str.match(/^(\d+)(.*)$/);
  if (!m) return <em>{value}</em>;
  const target = parseInt(m[1], 10);
  const suffix = m[2] || "";
  const ref = useRef(null);
  // Start at the final value, not at 0. If the IntersectionObserver
  // fails to fire (off-screen on tall pages, blocked by sticky header,
  // user has reduced-motion preferences, etc.) we still display the
  // correct number instead of a permanent "0+".
  const [n, setN] = useState(target);
  useEffect(() => {
    const el = ref.current;
    if (!el) return;
    let raf, start;
    const dur = 1400;
    const animate = (t) => {
      if (!start) start = t;
      const p = Math.min(1, (t - start) / dur);
      const eased = 1 - Math.pow(1 - p, 3);
      setN(Math.round(eased * target));
      if (p < 1) raf = requestAnimationFrame(animate);
    };
    // Honour the user's reduced-motion preference — no animation.
    const reduced = typeof window !== "undefined" &&
      window.matchMedia && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
    if (reduced) { setN(target); return; }
    // Reset to 0 when the element comes into view, then animate up.
    const io = new IntersectionObserver((entries) => {
      entries.forEach((e) => {
        if (e.isIntersecting) {
          setN(0);
          raf = requestAnimationFrame(animate);
          io.unobserve(el);
        }
      });
    }, { threshold: 0.3 });
    io.observe(el);
    // Fallback: if the IO never fires within 2.5s (e.g. element is
    // permanently outside the threshold), force the final value so
    // we never strand the user on a "0" frame.
    const fallback = setTimeout(() => { setN(target); }, 2500);
    return () => { io.disconnect(); cancelAnimationFrame(raf); clearTimeout(fallback); };
  }, [target]);
  return <em ref={ref}>{n}{suffix}</em>;
};

const Nav = ({ lang, setLang, theme, toggleTheme, t }) => {
  const [open, setOpen] = useState(false);
  const [drawer, setDrawer] = useState(false);
  const menuRef = useRef(null);
  useEffect(() => {
    const close = (e) => { if (menuRef.current && !menuRef.current.contains(e.target)) setOpen(false); };
    document.addEventListener("click", close);
    return () => document.removeEventListener("click", close);
  }, []);
  useEffect(() => { document.body.style.overflow = drawer ? "hidden" : ""; return () => { document.body.style.overflow = ""; }; }, [drawer]);
  useEffect(() => {
    const onR = () => { if (window.innerWidth > 900) setDrawer(false); };
    window.addEventListener("resize", onR);
    return () => window.removeEventListener("resize", onR);
  }, []);
  const langs = [{ id: "en", flag: "🇬🇧", name: "English" }, { id: "de", flag: "🇨🇭", name: "Deutsch" }, { id: "es", flag: "🇪🇸", name: "Español" }];
  const current = langs.find((l) => l.id === lang);
  const navLinks = [
    { href: HOME + "#work", label: t.nav.work },
    { href: HOME + "#about", label: t.nav.about },
    { href: HOME + "#services", label: t.nav.services },
    { href: HOME + "#pricing", label: t.nav.pricing },
    { href: routeContact(), label: t.nav.contact }];
  return (
    <>
      <nav className="nav">
        <a className="nav-logo" href={HOME || "#top"} aria-label="Fran Ruiz — home"><span className="mark"><img src="/assets/fr-logo-round.png" alt="" width="34" height="34" /></span>Fran Ruiz</a>
        <div className="nav-links">{navLinks.map((l) => <a key={l.href} href={l.href}>{l.label}</a>)}</div>
        <div className="nav-right">
          {/* MODIFIED: aria-label theme button */}
          <button className="theme-pill" onClick={toggleTheme} aria-label={theme === "dark" ? "Switch to light mode" : "Switch to dark mode"}>
            {theme === "dark" ? "☼" : "☾"}<span className="hide-sm">{theme === "dark" ? "Light" : "Dark"}</span>
          </button>
          <div className={`lang-menu hide-sm-flex ${open ? "open" : ""}`} ref={menuRef}>
            {/* MODIFIED: aria-label lang button */}
            <button className="lang-pill" aria-label={`Language: ${current.name}`} onClick={(e) => { e.stopPropagation(); setOpen(!open); }}>
              <span className="flag">{current.flag}</span><span>{current.id.toUpperCase()}</span><span>▾</span>
            </button>
            <div className="lang-dropdown">
              {langs.map((l) => <button key={l.id} className={l.id === lang ? "active" : ""} onClick={() => { setLang(l.id); setOpen(false); }}><span className="flag">{l.flag}</span>{l.name}</button>)}
            </div>
          </div>
          <a className="nav-cta hide-sm" href={routeContact()}>{t.nav.cta}<span className="arr">↗</span></a>
          <button className="nav-burger" onClick={() => setDrawer(true)} aria-label="Open menu"><span /><span /><span /></button>
        </div>
      </nav>
      <div className={`drawer-overlay ${drawer ? "open" : ""}`} onClick={() => setDrawer(false)} />
      <aside className={`drawer ${drawer ? "open" : ""}`} role="dialog" aria-modal="true">
        <div className="drawer-head">
          <a className="nav-logo" href={HOME || "#top"} onClick={() => setDrawer(false)} aria-label="Fran Ruiz — home"><span className="mark"><img src="/assets/fr-logo-round.png" alt="" width="34" height="34" /></span>Fran Ruiz</a>
          <button className="drawer-close" onClick={() => setDrawer(false)} aria-label="Close menu">✕</button>
        </div>
        <div className="drawer-links">
          {navLinks.map((l, i) =>
            <a key={l.href} href={l.href} onClick={() => setDrawer(false)} style={{ animationDelay: `${i * 60}ms` }}>
              <span className="drawer-num">/ {String(i + 1).padStart(2, "0")}</span>
              <span className="drawer-label">{l.label}</span>
              <span className="drawer-arrow">↑</span>
            </a>
          )}
        </div>
        <div className="drawer-foot">
          <div className="drawer-section-label">{t.nav.cta}</div>
          <div className="drawer-lang">
            {langs.map((l) => <button key={l.id} className={l.id === lang ? "active" : ""} onClick={() => setLang(l.id)}><span className="flag">{l.flag}</span>{l.id.toUpperCase()}</button>)}
          </div>
          <a className="drawer-cta" href={routeContact()} onClick={() => setDrawer(false)}>{t.nav.cta}<span className="arr">↗</span></a>
          <div className="drawer-contact">
            <a href="mailto:contact@franruiz.ch">contact@franruiz.ch</a>
            <a href="tel:+41767312517">+41 076 73 125 17</a>
            <a href="https://wa.me/41767312517" target="_blank" rel="noopener noreferrer" aria-label="WhatsApp Switzerland (+41 76 731 25 17)">WhatsApp →</a>
          </div>
        </div>
      </aside>
    </>);
};

/* MODIFIED: Hero con tertiary CTA */
const Hero = ({ t }) =>
  <header className="hero wrap" id="top">
    <div className="hero-shape s1" aria-hidden="true" /><div className="hero-shape s2" aria-hidden="true" /><div className="hero-shape s3" aria-hidden="true" />
    <div className="hero-availability reveal in"><span className="led" />{t.hero.avail}</div>
    <h1 className="reveal in">
      {t.hero.titleA}<br />
      <span className="ink">{t.hero.titleB}</span><br />
      {t.hero.titleC}{t.hero.titleD ? <> <span className="underline">{t.hero.titleD}</span></> : null}
    </h1>
    <p className="hero-sub reveal" data-d="200">{t.hero.sub}</p>
    {/* MODIFIED: service anchor chips — high-intent visitors can jump
        straight to the matching Services card without scrolling past
        Work + About. Three chips, monospace mark + label, all routed
        to #services for now; we can split into per-card anchors later
        if a single landing zone proves too coarse. */}
    {t.hero.chips && (
      <div className="hero-chips reveal" data-d="250">
        {t.hero.chips.map((c) => (
          <a key={c.label} className="hero-chip" href={c.href}>
            <span className="hero-chip-mark" aria-hidden="true">↗</span>
            {c.label}
          </a>
        ))}
      </div>
    )}
    <div className="hero-actions reveal" data-d="300">
      {/* MODIFIED: primary CTA now goes directly to the Calendly booking flow.
          A 20-min discovery call is a faster conversion than a contact-form
          message — fewer fields, instant slot picking, no 24h wait. The
          tertiary link below still gives "prefer to write" visitors a path
          to the same #contact form they had before. */}
      <a className="btn btn-primary" href="https://calendly.com/franruizmeet/free-call" target="_blank" rel="noopener noreferrer">{t.hero.primary}<span className="arr">→</span></a>
      <a className="btn btn-ghost" href="#work">{t.hero.secondary}<span className="arr">→</span></a>
    </div>
    {/* MODIFIED: "prefer to write" tertiary CTA — anchors to the
        in-page contact form for visitors who'd rather email than book. */}
    {t.hero.tertiary && (
      <a className="btn-text-cta" href="#contact" style={{ fontSize: "0.85rem", opacity: 0.7, marginTop: "8px", display: "inline-block" }}>
        {t.hero.tertiary}
      </a>
    )}
    <div className="hero-meta">
      <div className="stat reveal" data-d="100"><div className="num"><CountUp value={t.hero.stat1.num} /></div><div className="lbl">{t.hero.stat1.lbl}</div></div>
      <div className="stat reveal" data-d="200"><div className="num"><CountUp value={t.hero.stat2.num} /></div><div className="lbl">{t.hero.stat2.lbl}</div></div>
      <div className="stat reveal" data-d="300"><div className="num"><CountUp value={t.hero.stat3.num} /></div><div className="lbl">{t.hero.stat3.lbl}</div></div>
    </div>
  </header>;

const Marquee = ({ t }) => {
  const row = <div className="marquee-track">{t.marquee.concat(t.marquee).map((it, i) => <span key={i}>{it}<span className="dot" /></span>)}</div>;
  return <div className="marquee">{row}</div>;
};

/* MODIFIED: About con negrita de apertura en párrafos */
const About = ({ t }) =>
  <section className="section wrap" id="about" style={{ padding: "119.5px 24px" }}>
    <div className="eyebrow reveal" style={{ margin: "0px 0px 28px" }}>{t.about.eyebrow}</div>
    <div className="about-grid" style={{ margin: "0px" }}>
      <div className="about-photo reveal">
        <Img src="/assets/fran-portrait.png" alt="Fran Ruiz at his desk" fetchPriority="high" style={{ objectFit: "cover", padding: "0px", margin: "0px" }} />
        <div className="badge"><span className="moto">🏍</span>{t.about.badge}</div>
      </div>
      <div className="about-copy">
        <h2 className="reveal">{t.about.title}</h2>
        {/* MODIFIED: negrita apertura p1 */}
        <p className="reveal" data-d="100">
          <strong>{t.about.p1.split('—')[0].trim()} —</strong>{t.about.p1.split('—').slice(1).join('—')}
        </p>
        {/* MODIFIED: condensed About — p2 used to be a second "what I do"
            paragraph that overlapped with p1, and p3+p4 split the closing
            beat across two sentences. Now p2 carries the personal+CTA
            closer, and p3/p4 are kept in the schema but left empty so
            translations can be tightened without code changes. */}
        {t.about.p2 && (
          <p className="reveal" data-d="200">
            {t.about.p2.includes('—')
              ? <><strong>{t.about.p2.split('—')[0].trim()} —</strong>{t.about.p2.split('—').slice(1).join('—')}</>
              : t.about.p2}
          </p>
        )}
        {t.about.p3 && (
          <p className="reveal" data-d="300">
            {t.about.p3.includes('—')
              ? <><strong>{t.about.p3.split('—')[0].trim()} —</strong>{t.about.p3.split('—').slice(1).join('—')}</>
              : t.about.p3}
          </p>
        )}
        {t.about.p4 && <p className="reveal" data-d="400"><strong>{t.about.p4}</strong></p>}
        <div className="about-cards">
          <div className="card reveal" data-d="200"><div className="ico">📍</div><h3>{t.about.cardA.t}</h3><p>{t.about.cardA.s}</p></div>
          <div className="card reveal" data-d="300"><div className="ico">✦</div><h3>{t.about.cardB.t}</h3><p>{t.about.cardB.s}</p></div>
        </div>
      </div>
    </div>
  </section>;

const FeaturedCard = ({ p, i, t, lang }) => {
  const ref = useRef(null);
  const evenRight = i % 2 === 1;
  // First card loads eagerly + high priority so the hero of the Work
  // section paints instantly. The rest load eagerly too (we already
  // preloaded them in <Work>) so they're decoded before the user reaches
  // them — preventing scroll jank from late image decode.
  const isFirst = i === 0;
  return (
    <a ref={ref} className={`feat-card reveal-stagger ${evenRight ? "from-right" : "from-left"}`} href={routeProject(p.slug)} style={{ "--card-bg": p.bg, "--card-fg": p.fg }}>
      <div className="feat-meta-top"><span className="feat-num">/ {String(i + 1).padStart(2, "0")}</span><span className="feat-year">{p.year}</span></div>
      <div className="feat-canvas">
        {p.img && <Img src={p.img} alt={p.title[lang]} loading="eager" fetchPriority={isFirst ? "high" : "low"} width="1600" height="1100" />}
        {!p.img && <div className="canvas-empty"><span className="canvas-cross">+</span><span className="canvas-label">{p.title[lang]}</span><span className="canvas-hint">Drop image here</span></div>}
        <div className="feat-canvas-overlay"><div className="feat-hover"><span>{t.work.view}</span><span className="arr">↗</span></div></div>
      </div>
      <div className="feat-foot">
        <div className="feat-foot-left">
          <h3 className="feat-title">{p.title[lang]}</h3>
          {/* MODIFIED: result line — surfaces the single most legible
              metric for each case directly on the card. Visitors stop
              hunting through testimonials to find the "+40%" number;
              if `p.result[lang]` is missing on a project, the line
              silently degrades to nothing. */}
          {p.result && p.result[lang] && (
            <div className="feat-result" style={{ fontSize: "0.82rem", fontFamily: "var(--font-mono, ui-monospace, monospace)", opacity: 0.7, marginTop: "2px", marginBottom: "4px", letterSpacing: "-0.01em" }}>
              {p.result[lang]}
            </div>
          )}
          <div className="feat-tags">{p.tags[lang].map((tag) => <span key={tag}>{tag}</span>)}</div>
        </div>
        <div className="feat-arrow"><span>↗</span></div>
      </div>
    </a>);
};

const CTAFeaturedCard = ({ t }) =>
  <a className="feat-card feat-card-cta reveal-stagger from-right" href="#contact">
    <div className="feat-meta-top"><span className="feat-num">/ 04</span><span className="feat-year">{t.work.ctaEyebrow}</span></div>
    <div className="feat-canvas feat-canvas-cta">
      <div className="cta-pattern" aria-hidden="true"><span /><span /><span /><span /><span /><span /></div>
      <div className="cta-content"><span className="cta-eyebrow">— {t.work.ctaEyebrow}</span><h3 className="cta-big">{t.work.ctaTitle}</h3><div className="cta-circle"><span>↗</span></div></div>
    </div>
    <div className="feat-foot">
      <div className="feat-foot-left"><h3 className="feat-title">{t.work.ctaBtn}</h3><div className="feat-tags"><span style={{ padding: "5px 0px" }}>contact@franruiz.ch</span></div></div>
      <div className="feat-arrow"><span>↗</span></div>
    </div>
  </a>;

const Work = ({ t, lang }) => {
  // Preload all 3 featured project images on mount — small webp variants
  // (~50–100KB each) so they're in the browser cache long before the user
  // scrolls down to the Work section. No more pop-in / decode jank.
  useImagePreload(FEATURED.map((p) => p.img).filter(Boolean));
  return (
    <section className="section wrap" id="work" style={{ padding: "100px 24px" }}>
      <div className="eyebrow reveal" style={{ margin: "0px 0px 28px" }}>{t.work.eyebrow}</div>
      <div className="work-head">
        <h2 className="reveal" style={{ margin: "0px" }}>
          {t.work.title.includes(",") ? <>{t.work.title.split(",")[0]},<br /><span className="ink">{t.work.title.split(",")[1]}</span></> : t.work.title}
        </h2>
      </div>
      <div className="featured-grid">
        {FEATURED.map((p, i) => <FeaturedCard key={p.id} p={p} i={i} t={t} lang={lang} />)}
        <CTAFeaturedCard t={t} />
      </div>
    </section>
  );
};

/* MODIFIED: Services con micro-CTA */
const Services = ({ t }) => {
  /* MODIFIED: SEO (idx 3) recoloured from blue → terracotta orange and
     AI workflow (idx 5) recoloured from black → blue, so the two
     "info-tech" services read distinctly from the rest of the grid. */
  const svcBgs = ["#e8624a", "#a3b59a", "#f4b65c", "#4a7a6a", "#c97a64", "#319AFF"];
  return (
    <section className="section wrap" id="services" style={{ padding: "100px 24px" }}>
      <style>{`
        .svc-cta-link { display:inline-block; margin-top:12px; font-size:0.8rem; font-weight:600; color:var(--ink); text-decoration:none; opacity:0.7; transition:opacity 0.2s; }
        .svc-cta-link:hover { opacity:1; }
        /* MODIFIED: faint pre-qualification price line under each service CTA
           — same family as the CTA but smaller / quieter so users can
           gauge tier without jumping to the Pricing section. */
        .svc-from { display:block; margin-top:4px; font-family:var(--font-mono, ui-monospace, monospace); font-size:0.72rem; letter-spacing:0.04em; opacity:0.45; font-weight:500; }
      `}</style>
      <div className="eyebrow reveal">{t.services.eyebrow}</div>
      <div className="services-head">
        <h2 className="reveal" style={{ margin: "0px", width: "700px" }}>{t.services.title.split(",").map((part, i) =>
          <React.Fragment key={i}>{i > 0 && <>,<br /></>}{i === 1 ? <span className="ink">{part.trim()}</span> : part}</React.Fragment>
        )}</h2>
      </div>
      <div className="services-grid" style={{ margin: "0px" }}>
        {t.services.items.map((s, i) =>
          <div className="svc-card reveal" key={s.t} data-d={(i % 3 * 100).toString()} style={{ "--svc-bg": svcBgs[i % svcBgs.length] }}>
            <div className="num">/ {String(i + 1).padStart(2, "0")}</div>
            <div className="ico">{s.icon}</div>
            <h3>{s.t}</h3>
            <p>{s.d}</p>
            {/* MODIFIED: micro-CTA por servicio.
                If the service has its own `slug`, the CTA deep-links to
                the service detail page (/services/…). Otherwise
                we keep the historical #contact jump — those services
                are presented in full on the home and a dedicated page
                would be near-empty filler. */}
            {s.cta && (
              <a href={s.slug ? routeService(s.slug) : "#contact"} className="svc-cta-link">
                {s.cta}
              </a>
            )}
            {/* MODIFIED: pre-qualification price hint */}
            {s.from && <span className="svc-from">{s.from}</span>}
          </div>
        )}
      </div>
    </section>);
};

const Tech = ({ t }) =>
  <section className="section wrap" style={{ padding: "119.5px 24px" }}>
    <div className="tech-head"><h2 className="reveal">{t.tech.title}</h2><p className="reveal" data-d="100">{t.tech.sub}</p></div>
    <div className="tech-grid">
      {t.tech.items.map((it, i) => <div className="tech-pill reveal" key={it.n} data-d={(i % 6 * 80).toString()}><div className="ico">{it.i}</div><div className="name">{it.n}</div></div>)}
    </div>
  </section>;

const PriceCard = ({ p, i, hoveredIndex, setHoveredIndex }) => {
  const cardRef = useRef(null);
  const onMove = (e) => {
    const el = cardRef.current; if (!el) return;
    const r = el.getBoundingClientRect();
    const x = e.clientX - r.left - r.width / 2;
    const y = e.clientY - r.top - r.height / 2;
    el.style.transform = `perspective(800px) rotateX(${-(y / r.height) * 8}deg) rotateY(${(x / r.width) * 8}deg)`;
    el.style.setProperty("--mx", `${((e.clientX - r.left) / r.width) * 100}%`);
    el.style.setProperty("--my", `${((e.clientY - r.top) / r.height) * 100}%`);
  };
  const onLeave = () => { const el = cardRef.current; if (!el) return; el.style.transform = "perspective(800px) rotateX(0deg) rotateY(0deg)"; setHoveredIndex(null); };
  const isFeatured = i === 1 || p.featured;
  const isDimmed = hoveredIndex !== null && hoveredIndex !== i;
  return (
    <div ref={cardRef} className={`price-card price-card-3d${isFeatured ? " featured" : ""}${isDimmed ? " is-dimmed" : ""}`}
      data-d={(i * 100).toString()} onMouseEnter={() => setHoveredIndex(i)} onMouseMove={onMove} onMouseLeave={onLeave}>
      <div className="price-card-inner">
        <div className="price-card-glare" />
        <div className="tier">{p.tier}</div>
        <h3>{p.name}</h3>
        <div className="price-tag">{p.price} <em>{p.per}</em></div>
        <p className="desc">{p.desc}</p>
        <ul role="list">{p.features.map((f) => <li key={f}>{f}</li>)}</ul>
        <a className="pc-btn" href="#contact">{p.cta} →</a>
      </div>
    </div>);
};

/* MODIFIED: Pricing — 3 main packages + a 4-tier add-on grid (Care
   Basic / Pro / Studio + Automation Retainer). The add-ons used to
   render as a single row labelled "Add-on" at CHF 90; we now treat
   them as a first-class second section with a real heading + grid
   so recurring revenue stops feeling like an afterthought. The
   `recommended` flag on Care Pro gets a subtle highlight, mirroring
   the `featured` flag on the Standard main plan. */
const Pricing = ({ t }) => {
  const [hoveredIndex, setHoveredIndex] = useState(null);
  const mainPlans = t.pricing.plans.filter(p => !p.addon);
  const addons = t.pricing.plans.filter(p => p.addon);
  return (
    <section className="section wrap" id="pricing" style={{ padding: "100px 24px" }}>
      <style>{`
        /* Add-on heading — mirrors the main pricing-head styling but
           tighter, so the second section feels related, not a new page. */
        .addons-head { text-align:center; max-width:760px; margin:80px auto 32px; }
        .addons-head h3 { font-family:var(--font-display, inherit); font-size:clamp(1.5rem, 3.5vw, 2.2rem); font-weight:700; margin:8px 0 12px; letter-spacing:-0.02em; line-height:1.15; }
        .addons-head p { font-size:0.95rem; opacity:0.65; margin:0; }
        /* Compact grid of add-on cards — same visual family as PriceCard
           but smaller, no 3D tilt, no glare. Auto-fit so 4 cards become
           2×2 on tablets and a single column on mobile. */
        .addons-grid { display:grid; grid-template-columns:repeat(auto-fit, minmax(240px, 1fr)); gap:16px; }
        .addon-card { position:relative; background:var(--surface,var(--bg-2)); border:1px solid var(--line); border-radius:16px; padding:24px 22px; display:flex; flex-direction:column; gap:8px; transition:border-color .2s, transform .2s; }
        .addon-card:hover { border-color:color-mix(in srgb, var(--accent,var(--ink)) 35%, var(--line)); transform:translateY(-2px); }
        .addon-card.recommended { border-color:color-mix(in srgb, var(--accent,var(--ink)) 55%, var(--line)); background:color-mix(in srgb, var(--accent,var(--ink)) 5%, var(--surface,var(--bg-2))); }
        .addon-card .reco-badge { position:absolute; top:-10px; right:16px; background:var(--accent,var(--ink)); color:var(--bg,#fff); font-size:0.66rem; font-weight:700; letter-spacing:0.1em; text-transform:uppercase; padding:4px 10px; border-radius:999px; }
        .addon-card .tier { font-size:0.7rem; font-weight:600; letter-spacing:0.12em; text-transform:uppercase; opacity:0.5; }
        .addon-card h3 { font-size:1.05rem; font-weight:700; margin:0; }
        .addon-card .price-line { font-family:var(--font-mono, ui-monospace, monospace); font-size:0.95rem; margin:2px 0 6px; }
        .addon-card .price-line em { font-style:normal; font-size:0.78em; opacity:0.55; margin-left:3px; }
        .addon-card .desc { font-size:0.85rem; opacity:0.72; margin:0 0 6px; line-height:1.45; }
        .addon-card ul { list-style:none; padding:0; margin:0 0 12px; font-size:0.8rem; display:flex; flex-direction:column; gap:4px; }
        .addon-card ul li { padding-left:16px; position:relative; opacity:0.78; }
        .addon-card ul li::before { content:"✓"; position:absolute; left:0; color:var(--accent,var(--ink)); font-weight:700; font-size:0.78rem; }
        .addon-card .pc-btn { margin-top:auto; font-size:0.85rem; font-weight:600; text-decoration:none; color:var(--text); opacity:0.85; transition:opacity .2s; }
        .addon-card .pc-btn:hover { opacity:1; }
        @media (max-width:600px){ .addons-grid { grid-template-columns:1fr; } .addons-head { margin-top:60px; } }
      `}</style>
      <div className="pricing-head">
        <div className="eyebrow reveal" style={{ justifyContent: "center" }}>{t.pricing.eyebrow}</div>
        <h2 className="reveal">{t.pricing.title.split(",").map((p, i) =>
          <React.Fragment key={i}>{i > 0 && <>,<br /><span className="ink">{p.trim()}</span></>}{i === 0 && p}</React.Fragment>
        )}</h2>
        <p className="reveal" data-d="200">{t.pricing.sub}</p>
      </div>
      <div className="pricing-grid" onMouseLeave={() => setHoveredIndex(null)}>
        {mainPlans.map((p, i) => <PriceCard key={p.name} p={p} i={i} hoveredIndex={hoveredIndex} setHoveredIndex={setHoveredIndex} />)}
      </div>
      {/* MODIFIED: Add-on grid — Care plans + Automation retainer */}
      {addons.length > 0 && (
        <>
          <div className="addons-head reveal" data-d="100">
            {t.pricing.addonsTitle && <h3>{t.pricing.addonsTitle}</h3>}
            {t.pricing.addonsSub && <p>{t.pricing.addonsSub}</p>}
          </div>
          <div className="addons-grid">
            {addons.map((addon, i) => (
              <div key={addon.name} className={`addon-card reveal${addon.recommended ? " recommended" : ""}`} data-d={(i * 80).toString()}>
                {addon.recommended && <span className="reco-badge">Recommended</span>}
                <span className="tier">{addon.tier}</span>
                <h3>{addon.name}</h3>
                <div className="price-line">{addon.price}<em>{addon.per}</em></div>
                <p className="desc">{addon.desc}</p>
                <ul role="list">{addon.features.map(f => <li key={f}>{f}</li>)}</ul>
                <a className="pc-btn" href="#contact">{addon.cta} →</a>
              </div>
            ))}
          </div>
        </>
      )}
    </section>);
};

/* ---------- TESTIMONIALS ---------- */
const Testimonials = ({ t }) => {
  const [current, setCurrent] = React.useState(0);
  const [dragging, setDragging] = React.useState(false);
  const [startX, setStartX] = React.useState(0);
  const [dragDelta, setDragDelta] = React.useState(0);
  const trackRef = React.useRef(null);
  const items = t.tests.items;
  const total = items.length;

  const goTo = (idx) => {
    setCurrent(Math.max(0, Math.min(idx, total - 1)));
    setDragDelta(0);
  };

  const onPointerDown = (e) => {
    setDragging(true);
    setStartX(e.clientX ?? e.touches?.[0]?.clientX ?? 0);
    setDragDelta(0);
  };
  const onPointerMove = (e) => {
    if (!dragging) return;
    const x = e.clientX ?? e.touches?.[0]?.clientX ?? startX;
    setDragDelta(x - startX);
  };
  const onPointerUp = () => {
    if (!dragging) return;
    setDragging(false);
    if (dragDelta < -50) goTo(current + 1);
    else if (dragDelta > 50) goTo(current - 1);
    else setDragDelta(0);
  };

  return (
    <section className="section tests-section" style={{ padding: "119.5px 0" }}>
      <div className="wrap" style={{ padding: "0 24px" }}>
        <div className="tests-head">
          <div className="eyebrow reveal" style={{ justifyContent: "center" }}>{t.tests.eyebrow}</div>
          <h2 className="reveal">{t.tests.title}</h2>
        </div>
      </div>

      {/* Desktop grid */}
      <div className="wrap" style={{ padding: "0 24px" }}>
        <div className="tests-grid tests-grid--desktop">
          {items.map((tt, i) =>
            <div className="test-card reveal" key={i} data-d={(i * 100).toString()}>
              <div className="stars">★★★★★</div>
              <blockquote>"{tt.q}"</blockquote>
              <div className="who">
                <div className="av">{tt.name.split(" ").map((s) => s[0]).slice(0, 2).join("")}</div>
                <div><div className="name">{tt.name}</div><div className="role">{tt.role}</div></div>
              </div>
            </div>
          )}
        </div>
      </div>

      {/* Mobile carousel */}
      <div className="tests-carousel-wrap">
        <div
          className="tests-carousel-track"
          ref={trackRef}
          onMouseDown={onPointerDown}
          onMouseMove={onPointerMove}
          onMouseUp={onPointerUp}
          onMouseLeave={onPointerUp}
          onTouchStart={(e) => onPointerDown(e.touches[0])}
          onTouchMove={(e) => onPointerMove(e.touches[0])}
          onTouchEnd={onPointerUp}
          style={{
            transform: `translateX(calc(${-current * 100}% + ${dragDelta}px))`,
            transition: dragging ? "none" : "transform 0.45s cubic-bezier(0.25,1,0.5,1)",
          }}
        >
          {items.map((tt, i) =>
            <div className="tests-carousel-slide" key={i}>
              <div className="test-card">
                <div className="stars">★★★★★ <span className="stars-num">5</span></div>
                <blockquote>"{tt.q}"</blockquote>
                <div className="who">
                  <div className="av">{tt.name.split(" ").map((s) => s[0]).slice(0, 2).join("")}</div>
                  <div><div className="name">{tt.name}</div><div className="role">{tt.role}</div></div>
                </div>
              </div>
            </div>
          )}
        </div>
        <div className="tests-fade tests-fade--left" />
        <div className="tests-fade tests-fade--right" />
      </div>

      <div className="tests-dots">
        {items.map((_, i) =>
          <button
            key={i}
            className={"tests-dot" + (i === current ? " tests-dot--active" : "")}
            onClick={() => goTo(i)}
            aria-label={`Testimonial ${i + 1}`}
          />
        )}
      </div>
    </section>
  );
};

const BRANDS = [
  { id: "fimed",         name: "Clínica Fimed",       style: { fontFamily: "'Hanken Grotesk', sans-serif", fontWeight: 300, letterSpacing: "0.22em", textTransform: "lowercase", fontSize: "20px" } },
  { id: "garden",        name: "Garden Brasas",       style: { fontFamily: "Georgia, 'Times New Roman', serif", fontStyle: "italic", fontWeight: 700, fontSize: "24px", letterSpacing: "-0.01em" } },
  { id: "brilla",        name: "Brilla Influencers",  style: { fontFamily: "'Bricolage Grotesque', sans-serif", fontWeight: 800, letterSpacing: "0.16em", fontSize: "18px" } },
  { id: "evaporalia",    name: "Evaporalia",          style: { fontFamily: "'Bricolage Grotesque', sans-serif", fontWeight: 600, fontSize: "23px", letterSpacing: "-0.01em" } },
  { id: "hicool",        name: "HiCool",              style: { fontFamily: "'Bricolage Grotesque', sans-serif", fontWeight: 800, fontSize: "24px", letterSpacing: "-0.04em" } },
  { id: "lfans",         name: "LFans",               style: { fontFamily: "Georgia, serif", fontWeight: 400, fontSize: "24px", letterSpacing: "0.1em" } },
  { id: "mediterranean", name: "Mediterranean",       style: { fontFamily: "Georgia, 'Times New Roman', serif", fontStyle: "italic", fontWeight: 400, fontSize: "22px", letterSpacing: "0.02em" } },
  { id: "bodegas",       name: "Bodegas Campanar",    style: { fontFamily: "Georgia, serif", fontWeight: 700, fontSize: "17px", letterSpacing: "0.02em", textTransform: "uppercase", lineHeight: 1.2 } },
  { id: "jp",            name: "ADRÍAN DEVÉS",       style: { fontFamily: "'Bricolage Grotesque', sans-serif", fontWeight: 800, fontSize: "22px", letterSpacing: "-0.03em" } },
  { id: "calma",         name: "CoCreate Succes",     style: { fontFamily: "Georgia, serif", fontWeight: 400, fontStyle: "italic", fontSize: "20px", letterSpacing: "0.04em" } },
];

const Clients = ({ t }) =>
  <section className="section wrap" style={{ padding: "100px 24px" }}>
    <div className="brands-head" style={{ margin: "0px 0px 28px" }}>
      <div className="eyebrow reveal" style={{ justifyContent: "center" }}>{t.clients?.title || "Trusted by"}</div>
    </div>
    <div className="brands-grid">
      {BRANDS.map((b, i) =>
        <div key={b.id} className="brand-cell reveal" data-d={(i % 5 * 60).toString()}>
          <span className="brand-name" style={b.style}>{b.name}</span>
        </div>
      )}
    </div>
  </section>;

const FAQ = ({ t }) =>
  <section className="section wrap">
    <div className="faq-grid" style={{ margin: "0px 24px" }}>
      <div className="faq-intro">
        <div className="eyebrow reveal">{t.faq.eyebrow}</div>
        <h2 className="reveal">
          {t.faq.title.split(" ").length > 2
            ? <>{t.faq.title.split(" ").slice(0, -1).join(" ")} <span className="ink">{t.faq.title.split(" ").slice(-1)[0]}</span></>
            : t.faq.title}
        </h2>
        <p className="reveal" data-d="100">{t.faq.sub}</p>
      </div>
      <div className="faq-list">
        {t.faq.items.map((it, i) =>
          <details className="faq-item reveal" key={i} data-d={(i % 4 * 80).toString()}>
            <summary className="q">{it.q}<span className="ico" aria-hidden="true">+</span></summary>
            <div className="a">{it.a}</div>
          </details>
        )}
      </div>
    </div>
  </section>;

/* MODIFIED: Contact — hardened form (a11y labels, POST, states, honeypot)

   ─── Bot / spam protection ──────────────────────────────────────────
   This form is the primary conversion point. Three layers of defence:

   1. **Honeypot field** (`#contact-website`, name="website").
      Visually hidden via inline styles AND .hp-field class, tabIndex
      -1, aria-hidden, autocomplete off. Real users never see it; bots
      that flood every input will fill it. If the field arrives with
      any value, we silent-drop the submission (show the success UI so
      the bot doesn't retry) and never POST to the backend.

   2. **Client-side guard** in onSubmit: if `website` is non-empty,
      we set status="success" and return WITHOUT calling fetch.
      Keep the success cosmetic — DON'T leak any signal that the
      honeypot was tripped (no console.warn, no different UI).

   3. **Server-side rate limiting — REQUIRED ON THE BACKEND.**
      The /api/contact endpoint MUST enforce, at minimum:
        • 1 submission per IP per 60 seconds (soft cap, return 429)
        • 5 submissions per IP per hour    (hard cap, return 429)
        • Re-check the `website` honeypot field server-side and silent
          200-OK any payload where it is non-empty (do NOT 4xx — that
          tells the bot the trap exists).
        • Validate `email` format and reject anything > 5KB total.
      When we wire a real backend (Formspree / Netlify Forms / a
      Worker), document the limits it enforces here and update the
      CSP `connect-src` + `form-action` in index.html to include the
      backend origin.
   ────────────────────────────────────────────────────────────────── */
const Contact = ({ t, lang }) => {
  // status: 'idle' | 'sending' | 'success' | 'error'
  const [status, setStatus] = useState("idle");
  const [errors, setErrors] = useState({});
  const [name, setName] = useState("");
  const [email, setEmail] = useState("");
  const [message, setMessage] = useState("");
  const [budget, setBudget] = useState("");
  // Honeypot — bots fill every field. Real users never see this; if it
  // arrives non-empty on the backend, reject silently.
  const [website, setWebsite] = useState("");

  const fc = t.contact.form;

  const validate = () => {
    const e = {};
    if (!name.trim()) e.name = fc.errName;
    if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email.trim())) e.email = fc.errEmail;
    // "About your project" is optional — no validation.
    return e;
  };

  const onBlur = (field) => () => {
    const e = validate();
    setErrors((cur) => ({ ...cur, [field]: e[field] }));
  };

  const onSubmit = async (ev) => {
    ev.preventDefault();
    // Silent drop if honeypot is filled.
    if (website) { setStatus("success"); return; }

    const e = validate();
    setErrors(e);
    if (Object.keys(e).length) return;

    setStatus("sending");
    try {
      // POST to the Hostinger PHP handler at /contact.php. Same-origin,
      // so the CSP `connect-src 'self'` already covers it. Both the
      // homepage #contact section and the dedicated /contact page hit
      // the same endpoint — one PHP, one rate-limit store, one inbox.
      const res = await fetch(ev.currentTarget.action || "/contact.php", {
        method: "POST",
        headers: { "Accept": "application/json", "Content-Type": "application/json" },
        body: JSON.stringify({ name, email, message, budget, website, lang }),
      });
      if (!res.ok) throw new Error("Bad response");
      setStatus("success");
    } catch {
      setStatus("error");
    }
  };

  return (
    <section className="section wrap" id="contact">
      <div className="contact-wrap">
        <div className="blob" />
        <div className="contact-grid">
          <div className="contact-left">
            <div className="eyebrow reveal" style={{ color: "color-mix(in srgb, var(--bg) 60%, transparent)" }}>{t.contact.eyebrow}</div>
            <h2 className="reveal">{t.contact.title.split(" ").map((w, i, arr) =>
              <React.Fragment key={i}>{i === arr.length - 1 ? <span className="ink">{w}</span> : w}{i < arr.length - 1 ? " " : ""}</React.Fragment>
            )}</h2>
            <p className="reveal" data-d="100">{t.contact.sub}</p>
            <div className="contact-info reveal" data-d="200">
              <div className="row"><div className="ico" aria-hidden="true">✉</div><div><div className="k">{t.contact.info.email}</div><div className="v"><a href="mailto:contact@franruiz.ch">contact@franruiz.ch</a></div></div></div>
              <div className="row"><div className="ico" aria-hidden="true">☎</div><div><div className="k">{t.contact.info.phoneCh}</div><div className="v"><a href="tel:+41767312517">+41 076 73 125 17</a></div></div></div>
              <div className="row"><div className="ico" aria-hidden="true">📍</div><div><div className="k">{t.contact.info.location}</div><div className="v">Bern, Switzerland</div></div></div>
              <div className="row"><div className="ico" aria-hidden="true">in</div><div><div className="k">{t.contact.info.linkedin}</div><div className="v"><a href="https://www.linkedin.com/in/fran-ruiz-studio" target="_blank" rel="noopener noreferrer" aria-label="Fran Ruiz on LinkedIn">/fran-ruiz-studio</a></div></div></div>
            </div>
          </div>

          {status === "success" ? (
            <div className="contact-form contact-success reveal" data-d="200" role="status" aria-live="polite">
              <div className="success-mark" aria-hidden="true">✓</div>
              <h3>{fc.successTitle}</h3>
              <p>{fc.successBody.replace("{name}", name || "")}</p>
            </div>
          ) : (
            <form
              className="contact-form reveal"
              data-d="200"
              method="post"
              action="/contact.php"
              onSubmit={onSubmit}
              noValidate
            >
              {/* Honeypot — visually hidden, inert to keyboard, ignored by AT.
                  Real users skip it; bots filling all fields trip the trap.
                  Belt-and-braces positioning: inline styles guarantee the
                  field stays off-screen even if styles.css fails to load
                  (CSP rejection, network hiccup, cache mismatch). Keep
                  the .hp-field class too — it's the canonical hook. */}
              <div
                className="hp-field"
                aria-hidden="true"
                style={{ position: "absolute", left: "-9999px", width: "1px", height: "1px", overflow: "hidden", opacity: 0, pointerEvents: "none" }}
              >
                <label htmlFor="contact-website">Website (leave blank)</label>
                <input
                  id="contact-website"
                  type="text"
                  name="website"
                  tabIndex="-1"
                  autoComplete="off"
                  value={website}
                  onChange={(e) => setWebsite(e.target.value)}
                />
              </div>

              <div className={"field" + (errors.name ? " has-error" : "")}>
                <label htmlFor="contact-name">{fc.name}</label>
                <input
                  id="contact-name"
                  name="name"
                  type="text"
                  placeholder={fc.namep}
                  value={name}
                  onChange={(e) => setName(e.target.value)}
                  onBlur={onBlur("name")}
                  aria-invalid={errors.name ? "true" : undefined}
                  aria-describedby={errors.name ? "contact-name-err" : undefined}
                  required
                />
                {errors.name && <span className="field-error" id="contact-name-err">{errors.name}</span>}
              </div>

              <div className={"field" + (errors.email ? " has-error" : "")}>
                <label htmlFor="contact-email">{fc.email}</label>
                <input
                  id="contact-email"
                  name="email"
                  type="email"
                  placeholder={fc.emailp}
                  value={email}
                  onChange={(e) => setEmail(e.target.value)}
                  onBlur={onBlur("email")}
                  aria-invalid={errors.email ? "true" : undefined}
                  aria-describedby={errors.email ? "contact-email-err" : undefined}
                  required
                />
                {errors.email && <span className="field-error" id="contact-email-err">{errors.email}</span>}
              </div>

              <div className={"field" + (errors.message ? " has-error" : "")}>
                <label htmlFor="contact-message">{fc.message}</label>
                <textarea
                  id="contact-message"
                  name="message"
                  placeholder={fc.messagep}
                  value={message}
                  onChange={(e) => setMessage(e.target.value)}
                  onBlur={onBlur("message")}
                  aria-invalid={errors.message ? "true" : undefined}
                  aria-describedby={errors.message ? "contact-message-err" : undefined}
                  required
                  style={{ minHeight: "140px" }}
                />
                {errors.message && <span className="field-error" id="contact-message-err">{errors.message}</span>}
              </div>

              <div className="field">
                <label htmlFor="contact-budget">{fc.budget}</label>
                {/* MODIFIED: budget bands now start at the Starter price
                    (CHF 1,400) instead of "< CHF 2,000". Showing a band
                    below the cheapest package invited tire-kickers and
                    framed every conversation around discounting; bands
                    now mirror the published packages so visitors self-
                    qualify into the tier they actually need. "Not sure
                    yet" stays as an explicit option so prospects don't
                    abandon the form when they don't have a number. */}
                <select
                  id="contact-budget"
                  name="budget"
                  value={budget}
                  onChange={(e) => setBudget(e.target.value)}
                >
                  <option value="" disabled>—</option>
                  <option>CHF 1,400 – 2,800 (Starter / Landing)</option>
                  <option>CHF 2,800 – 5,000 (Standard / Business)</option>
                  <option>CHF 5,000 – 10,000 (Studio)</option>
                  <option>{"> CHF 10,000 (Studio + ongoing)"}</option>
                  <option>Not sure yet</option>
                </select>
              </div>

              {status === "error" && (
                <div className="form-banner form-banner-error" role="alert">
                  {fc.errorMsg}
                </div>
              )}

              <button type="submit" disabled={status === "sending"} aria-busy={status === "sending"}>
                {status === "sending" ? fc.sending : fc.send}
                <span aria-hidden="true">→</span>
              </button>
            </form>
          )}
        </div>
      </div>
    </section>);
};

/* MODIFIED: Footer con nav secundaria + legal + selector de idioma.
   R-P1-1 follow-up — el selector de idioma del nav se oculta entre
   901–1099px (tablet landscape). Aquí vive una copia siempre accesible,
   visible en todos los viewports, para que el usuario nunca pierda el
   control de idioma sin importar el ancho de pantalla. */
const FOOTER_LANGS = [
  { id: "en", flag: "🇬🇧" },
  { id: "de", flag: "🇨🇭" },
  { id: "es", flag: "🇪🇸" },
];
const Footer = ({ t, lang, setLang }) =>
  <footer className="wrap footer-wrap">
    <style>{`
      .footer-nav { display:flex; gap:20px; align-items:center; flex-wrap:wrap; margin-top:16px; }
      .footer-nav a { font-size:0.85rem; color:var(--fg,var(--text)); opacity:0.6; text-decoration:none; transition:opacity 0.2s; }
      .footer-nav a:hover { opacity:1; }
      .footer-lang { display:inline-flex; gap:6px; align-items:center; }
      .footer-lang button {
        background:transparent; border:1px solid var(--line);
        color:var(--text); opacity:0.55;
        font:inherit; font-size:0.8rem; letter-spacing:0.04em;
        padding:6px 10px; border-radius:999px;
        display:inline-flex; align-items:center; gap:6px;
        cursor:pointer; transition:opacity .18s ease, border-color .18s ease, color .18s ease, background .18s ease;
        min-height:32px;
      }
      .footer-lang button:hover { opacity:1; border-color:color-mix(in srgb, var(--accent) 40%, var(--line)); }
      .footer-lang button.active {
        opacity:1; color:var(--accent);
        border-color:color-mix(in srgb, var(--accent) 50%, var(--line));
        background:color-mix(in srgb, var(--accent) 8%, transparent);
      }
      .footer-lang .flag { font-size:12px; line-height:1; }
      @media (max-width: 480px) {
        .footer-lang button { padding:8px 12px; min-height:36px; }
      }
    `}</style>
    <div className="footer">
      <div className="footer-left">
        <div className="big" style={{ color: "var(--text-2)" }}>
          <span style={{ color: "var(--text)" }}>{t.footer.tagline}</span><br />
          <span className="ink">{t.footer.taglineInk}</span>
        </div>
      </div>
      {/* MODIFIED: nav secundaria */}
      <nav className="footer-nav">
        <a href={HOME + "#work"}>{t.nav.work}</a>
        <a href={HOME + "#about"}>{t.nav.about}</a>
        <a href={HOME + "#services"}>{t.nav.services}</a>
        <a href={HOME + "#pricing"}>{t.nav.pricing}</a>
        <a href={routeContact()}>{t.nav.contact}</a>
      </nav>
    </div>
    <div className="footer-bottom" style={{ padding: "20px 0px 80px" }}>
      <div>
        {t.footer.copyright}{" · "}
        {/* MODIFIED: legal link */}
        <a href={routeLegal()} style={{ opacity: 0.5, fontSize: "0.8em" }}>{t.footer.legal}</a>
      </div>
      {/* MODIFIED: selector de idioma siempre accesible — cubre el caso
          tablet landscape (901–1099px) donde .lang-menu del nav se oculta. */}
      {setLang && (
        <div className="footer-lang" role="group" aria-label="Language">
          {FOOTER_LANGS.map((l) => (
            <button
              key={l.id}
              className={l.id === lang ? "active" : ""}
              onClick={() => setLang(l.id)}
              aria-label={`Switch language to ${l.id.toUpperCase()}`}
              aria-pressed={l.id === lang}
            >
              <span className="flag">{l.flag}</span>{l.id.toUpperCase()}
            </button>
          ))}
        </div>
      )}
      <div className="socials">
        <a href="https://www.linkedin.com/in/fran-ruiz-studio" target="_blank" rel="noopener noreferrer">LinkedIn</a>
        <a href="mailto:contact@franruiz.ch">Email</a>
        <a
          className="socials-wa"
          href="https://web.whatsapp.com/send?phone=41767312517"
          target="_blank"
          rel="noopener noreferrer"
          aria-label="WhatsApp Web"
          title="WhatsApp Web"
        >
          <svg viewBox="0 0 24 24" width="18" height="18" aria-hidden="true" fill="currentColor">
            <path d="M19.11 4.84A9.86 9.86 0 0 0 12.04 2c-5.47 0-9.91 4.44-9.91 9.91 0 1.74.46 3.44 1.32 4.94L2 22l5.27-1.38a9.9 9.9 0 0 0 4.76 1.21h.01c5.46 0 9.91-4.44 9.91-9.91a9.86 9.86 0 0 0-2.84-7.08zM12.04 20.15h-.01a8.22 8.22 0 0 1-4.19-1.15l-.3-.18-3.13.82.83-3.05-.2-.31a8.22 8.22 0 0 1-1.26-4.37c0-4.55 3.7-8.25 8.26-8.25 2.2 0 4.27.86 5.83 2.42a8.2 8.2 0 0 1 2.42 5.84c0 4.55-3.7 8.25-8.25 8.25zm4.53-6.18c-.25-.12-1.47-.72-1.7-.8-.23-.09-.39-.12-.56.12-.16.25-.64.8-.78.97-.14.16-.29.18-.54.06-.25-.12-1.05-.39-2-1.23-.74-.66-1.24-1.47-1.38-1.72-.14-.25-.02-.39.11-.51.11-.11.25-.29.37-.43.12-.14.16-.25.25-.41.08-.16.04-.31-.02-.43-.06-.12-.56-1.35-.77-1.85-.2-.48-.4-.41-.56-.42l-.48-.01a.92.92 0 0 0-.67.31c-.23.25-.88.86-.88 2.1 0 1.24.9 2.44 1.03 2.6.13.17 1.78 2.72 4.32 3.81.6.26 1.08.41 1.45.53.61.19 1.16.17 1.6.1.49-.07 1.47-.6 1.68-1.18.21-.58.21-1.08.14-1.18-.06-.11-.22-.17-.47-.29z"/>
          </svg>
          <span>WhatsApp</span>
        </a>
      </div>
    </div>
  </footer>;

Object.assign(window, { Nav, Hero, Marquee, About, Work, Services, Tech, Pricing, Testimonials, Clients, FAQ, Contact, Footer });
