// Karrie 29 Interiors — single-page site.
// Receives a locked cream palette plus an i18n bundle (`t`) and renders the
// entire site against the current language. A three-way language toggle in
// the nav (EN / 繁中 / 简中) replaces the previous color-scheme toggle.

// Transformations carousel source. Pills map to catalog sections (window
// .PHOTO_CATALOG); the default "All" tour plays a curated set of reps drawn
// from those sections. TF_FEATURED entries are [section, indexIntoPairs] —
// indices come from the 2-column catalog grid the picks were chosen from.
const TF_SECTIONS = [
  'Bedroom', 'Closet', 'Kids Room', 'Kitchen', 'Pantry',
  'Office', 'Living Room', 'Basement', 'Entryway',
];
const TF_FEATURED = [
  ['Bedroom', 0],
  ['Closet', 0],
  ['Kids Room', 0],
  ['Kitchen', 1],
  ['Pantry', 3], ['Pantry', 1],
  ['Office', 0], ['Office', 4],
  ['Living Room', 0],
  ['Basement', 3], ['Basement', 2],
];

// FLOW section geometry. Four cubics of 250 units each: the midpoint of a
// cubic is (P0 + 3P1 + 3P2 + P3) / 8, so segment one peaks at (125, 47.5) and
// segment two bottoms at (375, 152.5) — the centres of columns 1 and 2 in the
// four-column grid. Alternating crest/trough repeats for columns 3 and 4.
const FLOW_PATH =
  'M 0 100 C 60 30, 190 30, 250 100 C 310 170, 440 170, 500 100 ' +
  'C 560 30, 690 30, 750 100 C 810 170, 940 170, 1000 100';
const FLOW_WAVE_TOP = 12;   // px the wave sits below the top of the band
const FLOW_LETTER_H = 110;  // px tall letter zone, centred on the wave
const flowStepTop = (i) =>
  Math.round((i % 2 === 0 ? 47.5 : 152.5) + FLOW_WAVE_TOP - FLOW_LETTER_H / 2);

// Karrie's inbox. The consultation form opens the visitor's mail app addressed
// here with all their answers pre-filled. Kept in sync with the address shown
// in the contact card and footer (contact.email in i18n.jsx).
const CONTACT_EMAIL = 'linkarrie@gmail.com';

// Defined at module scope (not inside KSite) so its frequent state changes —
// hover-pause, the 4s auto-advance, pill/arrow/dot clicks — re-render only the
// carousel. Section components in this file are inline in KSite and rendered
// as elements, so any carousel state kept in KSite would remount every section
// on each tick and flash the whole page.
const BeforeAfterCarousel = ({ p, ui, Placeholder }) => {
  const [mode, setMode] = React.useState('all'); // 'all' tour, or a catalog section name
  const [slideIdx, setSlideIdx] = React.useState(0);
  const [paused, setPaused] = React.useState(false);
  const [open, setOpen] = React.useState(false); // fullscreen side-by-side viewer

  const catalog = (typeof window !== 'undefined' && window.PHOTO_CATALOG) || [];
  const sectionPairs = (name) => {
    const s = catalog.find((x) => x.section === name);
    return s ? s.pairs : [];
  };

  // 'all' plays the curated cross-room tour; a section name plays every pair
  // in that room.
  const playlist =
    mode === 'all'
      ? TF_FEATURED
          .map(([name, i]) => {
            const pr = sectionPairs(name)[i];
            return pr ? { ...pr, section: name } : null;
          })
          .filter(Boolean)
      : sectionPairs(mode).map((pr) => ({ ...pr, section: mode }));

  const count = playlist.length;
  const idx = count ? slideIdx % count : 0;
  const slide = count ? playlist[idx] : null;

  // Auto-advance every 5s. slideIdx is a dependency so any manual move (arrows
  // or dots) restarts the countdown — the slider only auto-scrolls when the
  // user isn't driving it. Pauses on hover and for reduced-motion users.
  React.useEffect(() => {
    if (count <= 1 || paused || open) return;
    const reduced = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
    if (reduced) return;
    const timer = setInterval(() => setSlideIdx((s) => (s + 1) % count), 5000);
    return () => clearInterval(timer);
  }, [count, paused, mode, slideIdx, open]);

  // While the fullscreen viewer is open, lock body scroll and close on Escape.
  React.useEffect(() => {
    if (!open) return;
    const onKey = (e) => {
      if (e.key === 'Escape') setOpen(false);
      else if (e.key === 'ArrowLeft') go(-1);
      else if (e.key === 'ArrowRight') go(1);
    };
    window.addEventListener('keydown', onKey);
    const prevOverflow = document.body.style.overflow;
    document.body.style.overflow = 'hidden';
    return () => {
      window.removeEventListener('keydown', onKey);
      document.body.style.overflow = prevOverflow;
    };
  }, [open]);

  const pickMode = (m) => {
    setMode(m);
    setSlideIdx(0);
  };
  const go = (delta) => count && setSlideIdx((s) => (((s + delta) % count) + count) % count);

  const arrow = (side) => ({
    position: 'absolute',
    top: '50%',
    [side]: -22,
    transform: 'translateY(-50%)',
    width: 52,
    height: 52,
    borderRadius: 999,
    background: p.accent,
    border: `2px solid ${p.onAccent}`,
    color: p.onAccent,
    cursor: 'pointer',
    display: 'flex',
    alignItems: 'center',
    justifyContent: 'center',
    fontSize: 26,
    fontWeight: 600,
    lineHeight: 1,
    boxShadow: '0 8px 22px -6px rgba(60, 50, 40, 0.55)',
    transition: 'all .2s ease',
    zIndex: 2,
  });
  const imgStyle = {
    width: '100%', height: 520, objectFit: 'contain', background: p.bgAlt,
    display: 'block', borderRadius: 18, border: `1px solid ${p.border}`,
  };
  const fsImg = {
    maxWidth: '100%', maxHeight: '88vh', width: 'auto', height: 'auto',
    display: 'block', margin: '0 auto', borderRadius: 12, background: p.bgAlt,
  };
  const fsArrow = (side) => ({
    position: 'absolute',
    top: '50%',
    [side]: 'clamp(8px, 2.5vw, 28px)',
    transform: 'translateY(-50%)',
    width: 52,
    height: 52,
    borderRadius: 999,
    background: p.accent,
    border: `2px solid ${p.onAccent}`,
    color: p.onAccent,
    cursor: 'pointer',
    display: 'flex',
    alignItems: 'center',
    justifyContent: 'center',
    fontSize: 26,
    fontWeight: 600,
    lineHeight: 1,
    boxShadow: '0 8px 22px -6px rgba(0, 0, 0, 0.6)',
    zIndex: 2,
  });
  const badge = (bg, color) => ({
    position: 'absolute', top: 16, left: 16, background: bg, color,
    padding: '5px 12px', borderRadius: 999, fontFamily: "'JetBrains Mono', monospace",
    fontSize: 10, letterSpacing: 1.5, textTransform: 'uppercase',
  });

  return (
    <React.Fragment>
      <div className="reveal r-fade" style={{ display: 'flex', flexWrap: 'wrap', gap: 8, marginBottom: 36 }}>
        {['all', ...TF_SECTIONS].map((m) => {
          const active = mode === m;
          // During the tour, the room the current slide belongs to gets a soft
          // ring so the pills "follow" the carousel.
          const current = mode === 'all' && m !== 'all' && slide && slide.section === m;
          return (
            <button
              key={m}
              onClick={() => pickMode(m)}
              style={{
                padding: '10px 18px',
                borderRadius: 999,
                background: active ? p.accent : 'transparent',
                color: active ? p.onAccent : current ? p.accentDark : p.textMuted,
                border: `1px solid ${active || current ? p.accent : p.border}`,
                cursor: 'pointer',
                fontSize: 13,
                fontFamily: "'Inter', 'Noto Sans TC', 'Noto Sans SC', sans-serif",
                fontWeight: 500,
                letterSpacing: 0.3,
                transition: 'all .2s ease',
              }}
            >
              {m === 'all' ? 'All' : m}
            </button>
          );
        })}
      </div>

      <div
        className="reveal r-scale"
        style={{
          position: 'relative',
          background: p.surface,
          border: `1px solid ${p.border}`,
          borderRadius: 28,
          padding: 14,
        }}
        onMouseEnter={() => setPaused(true)}
        onMouseLeave={() => setPaused(false)}
      >
        {count > 1 && (
          <React.Fragment>
            <button type="button" className="tf-arrow tf-arrow-left" aria-label="Previous transformation" onClick={() => go(-1)} style={arrow('left')}>&#8249;</button>
            <button type="button" className="tf-arrow tf-arrow-right" aria-label="Next transformation" onClick={() => go(1)} style={arrow('right')}>&#8250;</button>
          </React.Fragment>
        )}
        <div
          key={mode + ':' + idx}
          className="ba-grid tf-slide"
          onClick={() => slide && setOpen(true)}
          role={slide ? 'button' : undefined}
          aria-label={slide ? 'View this transformation full screen' : undefined}
          style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 16, cursor: slide ? 'zoom-in' : 'default' }}
        >
          <div style={{ position: 'relative' }}>
            {slide && slide.before ? (
              <img src={slide.before} alt={ui.beforeLabel + ' · ' + slide.section} style={imgStyle} />
            ) : (
              <Placeholder mode="messy" palette={p} label={ui.beforeLabel} h={360} radius={18} />
            )}
            <span style={badge(p.text, p.bg)}>{ui.beforeLabel}</span>
          </div>
          <div style={{ position: 'relative' }}>
            {slide && slide.after ? (
              <img src={slide.after} alt={ui.afterLabel + ' · ' + slide.section} style={imgStyle} />
            ) : (
              <Placeholder mode="calm" palette={p} label={ui.afterLabel} h={360} radius={18} />
            )}
            <span style={badge(p.accent, p.onAccent)}>{ui.afterLabel}</span>
          </div>
        </div>

        {count > 1 && (
          <div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', gap: 8, marginTop: 24 }}>
            {playlist.map((_, i) => (
              <button
                key={i}
                onClick={() => setSlideIdx(i)}
                aria-label={'Show transformation ' + (i + 1)}
                style={{
                  width: i === idx ? 22 : 8,
                  height: 8,
                  padding: 0,
                  borderRadius: 999,
                  border: 'none',
                  cursor: 'pointer',
                  background: i === idx ? p.accent : p.border,
                  transition: 'all .3s ease',
                }}
              />
            ))}
          </div>
        )}
      </div>

      {open && slide && ReactDOM.createPortal(
        <div
          role="dialog"
          aria-modal="true"
          aria-label={'Transformation · ' + slide.section}
          onClick={() => setOpen(false)}
          style={{
            position: 'fixed',
            inset: 0,
            zIndex: 1000,
            background: 'rgba(20, 16, 12, 0.97)',
            display: 'flex',
            alignItems: 'center',
            justifyContent: 'center',
            padding: 'clamp(16px, 4vw, 48px)',
          }}
        >
          <button
            type="button"
            aria-label="Close full screen view"
            onClick={() => setOpen(false)}
            style={{
              position: 'absolute',
              top: 18,
              right: 18,
              width: 48,
              height: 48,
              borderRadius: 999,
              background: p.surface,
              border: `1px solid ${p.border}`,
              color: p.text,
              fontSize: 22,
              lineHeight: 1,
              cursor: 'pointer',
              display: 'flex',
              alignItems: 'center',
              justifyContent: 'center',
              zIndex: 2,
            }}
          >&#10005;</button>
          {count > 1 && (
            <React.Fragment>
              <button
                type="button"
                aria-label="Previous transformation"
                onClick={(e) => { e.stopPropagation(); go(-1); }}
                style={fsArrow('left')}
              >&#8249;</button>
              <button
                type="button"
                aria-label="Next transformation"
                onClick={(e) => { e.stopPropagation(); go(1); }}
                style={fsArrow('right')}
              >&#8250;</button>
            </React.Fragment>
          )}
          <div
            onClick={(e) => e.stopPropagation()}
            style={{
              display: 'grid',
              gridTemplateColumns: '1fr 1fr',
              gap: 'clamp(8px, 2vw, 22px)',
              width: '100%',
              maxWidth: 1500,
              alignItems: 'center',
            }}
          >
            <div style={{ position: 'relative' }}>
              {slide.before ? (
                <img src={slide.before} alt={ui.beforeLabel + ' · ' + slide.section} style={fsImg} />
              ) : (
                <Placeholder mode="messy" palette={p} label={ui.beforeLabel} h={520} radius={12} />
              )}
              <span style={badge(p.text, p.bg)}>{ui.beforeLabel}</span>
            </div>
            <div style={{ position: 'relative' }}>
              {slide.after ? (
                <img src={slide.after} alt={ui.afterLabel + ' · ' + slide.section} style={fsImg} />
              ) : (
                <Placeholder mode="calm" palette={p} label={ui.afterLabel} h={520} radius={12} />
              )}
              <span style={badge(p.accent, p.onAccent)}>{ui.afterLabel}</span>
            </div>
          </div>
        </div>,
        document.body
      )}
    </React.Fragment>
  );
};

// At module scope (like BeforeAfterCarousel) so opening a question re-renders
// only the accordion. Kept inside KSite, its setState would re-render KSite and
// remount every section — flashing the page and snapping scroll back to the top.
const FAQAccordion = ({ p, ui, c, wrap, SectionHeader, Btn }) => {
  const [openFaq, setOpenFaq] = React.useState(0);
  return (
    <section id="faq" style={{ background: p.bgAlt, borderTop: `1px solid ${p.border}`, borderBottom: `1px solid ${p.border}`, padding: '120px 0' }}>
      <div style={wrap}>
        <div style={{ display: 'grid', gridTemplateColumns: '0.7fr 1fr', gap: 80, alignItems: 'flex-start' }} className="faq-grid">
          <div className="reveal r-left" style={{ position: 'sticky', top: 100 }}>
            <SectionHeader
              eyebrow={ui.sections.faq.eyebrow}
              title={ui.sections.faq.title}
              lede={ui.sections.faq.lede}
            />
            <div style={{ marginTop: 36 }}>
              <Btn variant="ghost" icon="mail">{c.contact.email}</Btn>
            </div>
          </div>
          <div className="reveal r-right" style={{ background: p.surface, borderRadius: 24, border: `1px solid ${p.border}`, overflow: 'hidden' }}>
            {c.faq.map((f, i) => (
              <div key={i} style={{ borderTop: i ? `1px solid ${p.border}` : 'none' }}>
                <button
                  type="button"
                  aria-expanded={openFaq === i}
                  onClick={() => setOpenFaq(openFaq === i ? -1 : i)}
                  style={{
                    width: '100%',
                    textAlign: 'left',
                    background: 'transparent',
                    border: 'none',
                    padding: '26px 30px',
                    cursor: 'pointer',
                    display: 'flex',
                    alignItems: 'center',
                    justifyContent: 'space-between',
                    fontFamily: "'Cormorant Garamond', 'Noto Serif TC', 'Noto Serif SC', serif",
                    fontSize: 21,
                    color: p.text,
                    fontWeight: 500,
                    letterSpacing: -0.1,
                    gap: 16,
                  }}
                >
                  <span style={{ textAlign: 'left' }}>{f.q}</span>
                  <Icon name={openFaq === i ? 'minus' : 'plus'} size={20} stroke={1.4} style={{ color: p.accentDark, flex: '0 0 auto' }} />
                </button>
                {openFaq === i && (
                  <div
                    style={{
                      padding: '0 30px 28px',
                      fontSize: 15.5,
                      lineHeight: 1.75,
                      color: p.textMuted,
                      fontWeight: 300,
                      maxWidth: 640,
                      fontFamily: "'Inter', 'Noto Sans TC', 'Noto Sans SC', sans-serif",
                    }}
                  >
                    {f.a}
                  </div>
                )}
              </div>
            ))}
          </div>
        </div>
      </div>
    </section>
  );
};

// Mobile-only nav. The hamburger drops a full-width panel with the section
// links and the booking CTA. Module-scope (like FAQAccordion) so its open
// state survives KSite re-renders — e.g. a language switch. The dropdown and
// backdrop use position:absolute against the sticky <nav>: the nav's
// backdrop-filter would otherwise capture position:fixed descendants.
const MobileMenu = ({ p, ui, links, PAD_X }) => {
  const [open, setOpen] = React.useState(false);
  const close = () => setOpen(false);
  const linkStyle = {
    display: 'block',
    padding: '16px 2px',
    color: p.text,
    textDecoration: 'none',
    fontFamily: "'Cormorant Garamond', 'Noto Serif TC', 'Noto Serif SC', serif",
    fontSize: 23,
    fontWeight: 500,
    letterSpacing: -0.2,
    borderBottom: `1px solid ${p.border}`,
  };
  return (
    <div className="nav-mobile" style={{ alignItems: 'center' }}>
      <button
        type="button"
        aria-label="Menu"
        aria-expanded={open}
        onClick={() => setOpen((v) => !v)}
        style={{
          background: 'transparent',
          border: 'none',
          color: p.text,
          cursor: 'pointer',
          padding: 6,
          display: 'flex',
          alignItems: 'center',
        }}
      >
        <Icon name={open ? 'close' : 'menu'} size={26} stroke={1.5} />
      </button>
      {open && (
        <React.Fragment>
          <div
            onClick={close}
            style={{ position: 'absolute', top: '100%', left: 0, right: 0, height: '100vh', background: 'rgba(40,34,26,0.28)', zIndex: 40 }}
          />
          <div
            style={{
              position: 'absolute',
              top: '100%',
              left: 0,
              right: 0,
              background: p.bg,
              borderBottom: `1px solid ${p.border}`,
              boxShadow: '0 24px 48px -28px rgba(60,50,40,0.5)',
              padding: `4px ${PAD_X} 28px`,
              zIndex: 45,
            }}
          >
            {links.map(([l, h]) => (
              <a key={h} href={h} onClick={close} style={linkStyle}>{l}</a>
            ))}
            <a
              href="#contact"
              onClick={close}
              style={{
                marginTop: 24,
                background: p.accent,
                color: p.onAccent,
                border: `1px solid ${p.accent}`,
                fontFamily: "'Inter', 'Noto Sans TC', 'Noto Sans SC', sans-serif",
                fontWeight: 500,
                fontSize: 15,
                letterSpacing: 0.3,
                padding: '15px 22px',
                borderRadius: 999,
                cursor: 'pointer',
                textDecoration: 'none',
                display: 'flex',
                alignItems: 'center',
                justifyContent: 'center',
                gap: 8,
              }}
            >
              {ui.bookConsultation}
              <Icon name="arrow" size={15} />
            </a>
          </div>
        </React.Fragment>
      )}
    </div>
  );
};

const KSite = ({ palette: p, t, lang, languages, onChangeLang }) => {
  const ui = t.ui;
  const c = t.content;

  const [bookingPulse, setBookingPulse] = React.useState(false);
  // Lives in KSite (stable) rather than in the inline Contact component, which
  // remounts on every KSite re-render (e.g. nav booking-button hover) and would
  // otherwise reset the success view back to the empty form.
  const [contactSent, setContactSent] = React.useState(false);

  // ─────────── reusable primitives ───────────
  const SectionHeader = ({ eyebrow, title, lede, align = 'left', maxLede = 640 }) => (
    <div style={{ textAlign: align, maxWidth: align === 'center' ? 720 : 760, marginLeft: align === 'center' ? 'auto' : 0, marginRight: align === 'center' ? 'auto' : 0 }}>
      <div
        style={{
          fontFamily: "'JetBrains Mono', ui-monospace, monospace",
          fontSize: 11,
          letterSpacing: 2.4,
          textTransform: 'uppercase',
          color: p.accentDark,
          marginBottom: 18,
          display: 'flex',
          alignItems: 'center',
          gap: 10,
          justifyContent: align === 'center' ? 'center' : 'flex-start',
        }}
      >
        <span style={{ width: 24, height: 1, background: p.accent, display: 'inline-block' }} />
        {eyebrow}
      </div>
      <h2
        style={{
          fontFamily: "'Cormorant Garamond', 'Noto Serif TC', 'Noto Serif SC', serif",
          fontWeight: 400,
          fontSize: 'clamp(36px, 5vw, 56px)',
          lineHeight: 1.15,
          letterSpacing: -0.4,
          color: p.text,
          margin: '0 0 18px',
        }}
      >
        {title}
      </h2>
      {lede && (
        <p
          style={{
            fontFamily: "'Inter', 'Noto Sans TC', 'Noto Sans SC', sans-serif",
            fontSize: 17,
            lineHeight: 1.65,
            color: p.textMuted,
            margin: 0,
            fontWeight: 300,
            maxWidth: maxLede,
            marginLeft: align === 'center' ? 'auto' : 0,
            marginRight: align === 'center' ? 'auto' : 0,
          }}
        >
          {lede}
        </p>
      )}
    </div>
  );

  const Btn = ({ variant = 'primary', children, icon = 'arrow', onClick, type }) => {
    const styles =
      variant === 'primary'
        ? { background: p.accent, color: p.onAccent, border: `1px solid ${p.accent}` }
        : variant === 'inverse'
        ? { background: p.text, color: p.bg, border: `1px solid ${p.text}` }
        : { background: 'transparent', color: p.text, border: `1px solid ${p.borderStrong}` };
    return (
      <button
        type={type}
        onClick={onClick}
        style={{
          ...styles,
          fontFamily: "'Inter', 'Noto Sans TC', 'Noto Sans SC', sans-serif",
          fontWeight: 500,
          fontSize: 14,
          letterSpacing: 0.3,
          padding: '14px 26px',
          borderRadius: 999,
          cursor: 'pointer',
          display: 'inline-flex',
          alignItems: 'center',
          gap: 10,
          transition: 'all .2s ease',
        }}
      >
        {children}
        {icon && <Icon name={icon} size={16} />}
      </button>
    );
  };

  const PAD_X = 'clamp(20px, 5vw, 80px)';
  const MAX_W = 1240;
  const wrap = { maxWidth: MAX_W, margin: '0 auto', padding: `0 ${PAD_X}` };

  // ─────────── language toggle (EN / 繁中 / 简中) ───────────
  const LanguageToggle = ({ compact = false }) => (
    <div
      role="radiogroup"
      aria-label="Language"
      style={{
        display: 'inline-flex',
        alignItems: 'center',
        gap: 2,
        padding: 4,
        borderRadius: 999,
        background: p.surface,
        border: `1px solid ${p.border}`,
        flexShrink: 0,
      }}
    >
      {Object.entries(languages).map(([key, opt]) => {
        const active = key === lang;
        return (
          <button
            key={key}
            role="radio"
            aria-checked={active}
            aria-label={opt.fullLabel}
            onClick={() => onChangeLang(key)}
            style={{
              display: 'inline-flex',
              alignItems: 'center',
              gap: 6,
              padding: compact ? '6px 10px' : '8px 14px',
              borderRadius: 999,
              background: active ? p.accent : 'transparent',
              color: active ? p.onAccent : p.textMuted,
              border: 'none',
              cursor: 'pointer',
              fontFamily: "'Inter', 'Noto Sans TC', 'Noto Sans SC', sans-serif",
              fontSize: 12,
              fontWeight: 600,
              letterSpacing: 0.4,
              transition: 'all .2s ease',
              minWidth: compact ? 0 : 44,
              justifyContent: 'center',
              whiteSpace: 'nowrap',
            }}
            title={opt.fullLabel}
          >
            {opt.label}
          </button>
        );
      })}
    </div>
  );

  // ─────────── NAV ───────────
  const Nav = () => {
    const navLinks = [
      [ui.nav.about, '#about'],
      [ui.nav.flow, '#flow'],
      [ui.nav.services, '#services'],
      [ui.nav.transformations, '#transformations'],
      [ui.nav.faq, '#faq'],
    ];
    return (
    <nav
      className="k-nav"
      style={{
        position: 'sticky',
        top: 0,
        zIndex: 50,
        backdropFilter: 'blur(14px)',
        background: 'rgba(243, 234, 215, 0.82)',
        borderBottom: `1px solid ${p.border}`,
      }}
    >
      <div
        style={{
          ...wrap,
          display: 'flex',
          alignItems: 'center',
          justifyContent: 'space-between',
          padding: `18px ${PAD_X}`,
          gap: 24,
        }}
      >
        <a href="#top" aria-label="Karrie 29 Interiors" style={{ display: 'flex', alignItems: 'center', gap: 11, flex: '0 0 auto', textDecoration: 'none', color: p.text }}>
          <img src="logo-k.png" alt="" style={{ height: 50, width: 'auto', display: 'block' }} />
          <span className="nav-wordmark" style={{ display: 'flex', flexDirection: 'column', justifyContent: 'center', gap: 3, lineHeight: 1 }}>
            <span style={{ fontFamily: "'Cormorant Garamond', serif", fontWeight: 500, fontSize: 16, letterSpacing: 1.6, lineHeight: 1 }}>KARRIE 29</span>
            <span style={{ height: 0.8, background: p.text, opacity: 0.45 }} />
            <span style={{ fontFamily: "'Cormorant Garamond', serif", fontWeight: 400, fontSize: 10.5, letterSpacing: 3.6, lineHeight: 1 }}>INTERIORS</span>
          </span>
        </a>
        <div
          style={{
            display: 'flex',
            gap: 32,
            fontFamily: "'Inter', 'Noto Sans TC', 'Noto Sans SC', sans-serif",
            fontSize: 13.5,
            color: p.textMuted,
            letterSpacing: 0.2,
          }}
          className="nav-links"
        >
          {navLinks.map(([l, h]) => (
            <a key={h} href={h} style={{ color: 'inherit', textDecoration: 'none', cursor: 'pointer' }}>
              {l}
            </a>
          ))}
        </div>
        <div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
          <LanguageToggle />
          <a
            href="#contact"
            className="nav-cta"
            onMouseEnter={() => setBookingPulse(true)}
            onMouseLeave={() => setBookingPulse(false)}
            style={{
              background: p.accent,
              color: p.onAccent,
              border: `1px solid ${p.accent}`,
              fontFamily: "'Inter', 'Noto Sans TC', 'Noto Sans SC', sans-serif",
              fontWeight: 500,
              fontSize: 13.5,
              letterSpacing: 0.3,
              padding: '12px 22px',
              borderRadius: 999,
              cursor: 'pointer',
              textDecoration: 'none',
              display: 'inline-flex',
              alignItems: 'center',
              gap: 8,
              transition: 'all .2s ease',
              boxShadow: bookingPulse ? `0 8px 28px -10px ${p.accent}` : 'none',
              transform: bookingPulse ? 'translateY(-1px)' : 'none',
              whiteSpace: 'nowrap',
            }}
          >
            {ui.bookConsultation}
            <Icon name="arrow" size={14} />
          </a>
          <MobileMenu p={p} ui={ui} links={navLinks} PAD_X={PAD_X} />
        </div>
      </div>
    </nav>
    );
  };

  // ─────────── HERO ───────────
  const Hero = () => {
    const h = c.hero.headline;
    return (
      <section style={{ ...wrap, padding: `40px ${PAD_X} 88px`, position: 'relative' }}>
        <div style={{ display: 'grid', gridTemplateColumns: '1.1fr 1fr', gap: 80, alignItems: 'center' }} className="hero-grid">
          <div className="reveal r-up">
            <div
              style={{
                fontFamily: "'JetBrains Mono', monospace",
                fontSize: 11,
                letterSpacing: 2.4,
                textTransform: 'uppercase',
                color: p.accentDark,
                marginBottom: 28,
                display: 'flex',
                alignItems: 'center',
                gap: 12,
              }}
            >
              <span style={{ width: 24, height: 1, background: p.accent, display: 'inline-block' }} />
              {c.hero.eyebrow}
            </div>
            <h1
              style={{
                fontFamily: "'Cormorant Garamond', 'Noto Serif TC', 'Noto Serif SC', serif",
                fontSize: lang === 'en' ? 'clamp(44px, 8vw, 104px)' : 'clamp(36px, 5.4vw, 80px)',
                lineHeight: lang === 'en' ? 1.0 : 1.2,
                letterSpacing: lang === 'en' ? -1.8 : -0.4,
                fontWeight: 400,
                color: p.text,
                margin: '0 0 28px',
              }}
            >
              {h.l1Pre}<em style={{ fontWeight: 400, color: p.accentDark, fontStyle: 'italic' }}>{h.l1Em}</em>{h.l1Post}<br />
              {h.l2Pre}<em style={{ fontWeight: 400, color: p.accentDark, fontStyle: 'italic' }}>{h.l2Em}</em>{h.l2Post}
            </h1>
            <p
              style={{
                fontSize: 19,
                lineHeight: 1.65,
                color: p.textMuted,
                margin: '0 0 40px',
                fontWeight: 300,
                maxWidth: 520,
                fontFamily: "'Inter', 'Noto Sans TC', 'Noto Sans SC', sans-serif",
              }}
            >
              {c.hero.sub}
            </p>
            <div style={{ display: 'flex', gap: 14, alignItems: 'center', flexWrap: 'wrap' }}>
              <Btn variant="primary" icon="arrow">{c.hero.cta}</Btn>
              <Btn variant="ghost" icon="arrow">{c.hero.cta2}</Btn>
            </div>
            <div
              style={{
                marginTop: 64,
                display: 'flex',
                gap: 48,
                paddingTop: 36,
                borderTop: `1px solid ${p.border}`,
                flexWrap: 'wrap',
              }}
            >
              {c.trustStats.map((s, i) => (
                <div key={i}>
                  <div
                    style={{
                      fontFamily: "'Noto Serif TC', 'Cormorant Garamond', serif",
                      fontSize: 36,
                      color: p.text,
                      lineHeight: 1,
                      fontWeight: 400,
                    }}
                  >
                    {s.k}
                  </div>
                  <div
                    style={{
                      fontSize: 12,
                      color: p.textSubtle,
                      letterSpacing: 0.4,
                      marginTop: 8,
                      fontFamily: "'JetBrains Mono', monospace",
                      textTransform: 'uppercase',
                    }}
                  >
                    {s.v}
                  </div>
                </div>
              ))}
            </div>
          </div>

          <div className="k-hero-art reveal r-fade" style={{ position: 'relative', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
            <img
              src="hero-living-room.jpg"
              alt={ui.heroPlaceholderLabel}
              style={{
                width: '100%',
                maxWidth: 506,
                aspectRatio: '3 / 4',
                objectFit: 'cover',
                display: 'block',
                margin: '0 auto',
                borderRadius: 28,
                border: `1px solid ${p.border}`,
                boxShadow: '0 30px 70px -32px rgba(60,50,40,0.4)',
              }}
            />
          </div>
        </div>
      </section>
    );
  };

  // ─────────── WHY 29 (full-width band beneath the hero) ───────────
  const Why29 = () => (
    <section style={{ ...wrap, padding: `96px ${PAD_X}`, borderTop: `1px solid ${p.border}` }}>
      {/* Copy and logomark share one row and are centred against each other, so
          the medallion's height is absorbed by the paragraphs rather than
          leaving a well of empty space beside the heading. */}
      <div
        className="why29-body"
        style={{
          display: 'grid',
          gridTemplateColumns: 'minmax(0, 1fr) minmax(0, 0.5fr)',
          gap: 'clamp(32px, 5vw, 72px)',
          alignItems: 'center',
        }}
      >
        <div>
          <div className="reveal r-up" style={{ marginBottom: 32 }}>
            <SectionHeader
              eyebrow={c.why29.eyebrow}
              title={
                <span>
                  {c.why29.title.pre}
                  <em style={{ fontStyle: 'italic', color: p.accentDark }}>{c.why29.title.em}</em>
                </span>
              }
            />
          </div>
          <div className="reveal r-fade" style={{ display: 'grid', gap: 22 }}>
            <p style={{ fontSize: 17.5, lineHeight: 1.85, color: p.textMuted, margin: 0, fontFamily: "'Inter', 'Noto Sans TC', 'Noto Sans SC', sans-serif", fontWeight: 300 }}>
              {c.why29.body}
            </p>
            <p style={{ fontSize: 17.5, lineHeight: 1.85, color: p.textMuted, margin: 0, fontFamily: "'Inter', 'Noto Sans TC', 'Noto Sans SC', sans-serif", fontWeight: 300 }}>
              {c.why29.body2}
            </p>
          </div>
        </div>

        {/* The medallion scales with its column, so every inner measurement is
            a percentage of the circle rather than a fixed pixel size. */}
        <div
          className="reveal r-scale"
          style={{
            width: '100%',
            aspectRatio: '1',
            borderRadius: '50%',
            background: `radial-gradient(circle at 30% 30%, ${p.surface} 0%, ${p.bgAlt} 100%)`,
            border: `1px solid ${p.border}`,
            display: 'flex',
            alignItems: 'center',
            justifyContent: 'center',
            position: 'relative',
            margin: '0 auto',
            padding: '11%',
            boxSizing: 'border-box',
            boxShadow: '0 30px 70px -32px rgba(60,50,40,0.4)',
          }}
        >
          <img
            src="logo-lockup.png"
            alt={c.brand}
            style={{ width: '65%', height: 'auto', display: 'block', position: 'relative', zIndex: 1 }}
          />
          <div
            style={{
              position: 'absolute',
              inset: '3.2%',
              borderRadius: '50%',
              border: `1px dashed ${p.borderStrong}`,
              opacity: 0.5,
            }}
          />
          <div
            style={{
              position: 'absolute',
              top: '5.5%',
              right: '5.5%',
              width: '2.8%',
              height: '2.8%',
              borderRadius: '50%',
              background: p.accent,
            }}
          />
        </div>
      </div>

      <div
        className="reveal r-fade"
        style={{
          display: 'grid',
          gridTemplateColumns: 'repeat(3, 1fr)',
          gap: 24,
          borderTop: `1px solid ${p.border}`,
          paddingTop: 36,
          marginTop: 48,
        }}
      >
        {c.why29.bullets.map((b, i) => (
          <div key={i} style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
            <div
              style={{
                fontFamily: "'Noto Serif TC', 'Noto Serif SC', serif",
                fontSize: 40,
                color: p.accent,
                lineHeight: 1,
              }}
            >
              {b.k}
            </div>
            <div
              style={{
                fontFamily: "'JetBrains Mono', monospace",
                fontSize: 11,
                letterSpacing: 1.4,
                color: p.textSubtle,
                textTransform: 'uppercase',
                lineHeight: 1.5,
              }}
            >
              {b.v}
            </div>
          </div>
        ))}
      </div>
    </section>
  );

  // ─────────── SERVICES ───────────
  const Services = () => (
    <section id="services" style={{ background: p.bgAlt, borderTop: `1px solid ${p.border}`, borderBottom: `1px solid ${p.border}`, padding: `120px 0` }}>
      <div style={wrap}>
        <div className="reveal r-up" style={{ marginBottom: 64, display: 'flex', justifyContent: 'space-between', alignItems: 'flex-end', flexWrap: 'wrap', gap: 24 }}>
          <SectionHeader
            eyebrow={ui.sections.services.eyebrow}
            title={ui.sections.services.title}
            lede={ui.sections.services.lede}
          />
          <div
            style={{
              fontFamily: "'JetBrains Mono', monospace",
              fontSize: 11,
              letterSpacing: 1.6,
              color: p.textSubtle,
              textTransform: 'uppercase',
            }}
          >
            {ui.offeringsCount}
          </div>
        </div>

        <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 22 }} className="services-grid">
          {c.services.map((s, i) => (
            <article
              key={i}
              className="reveal r-rise"
              style={{
                transitionDelay: (i * 70) + 'ms',
                background: p.surface,
                border: `1px solid ${p.border}`,
                borderRadius: 24,
                padding: 32,
                display: 'flex',
                flexDirection: 'column',
                gap: 18,
                position: 'relative',
                overflow: 'hidden',
              }}
            >
              <div style={{ display: 'flex', alignItems: 'flex-start', justifyContent: 'space-between' }}>
                <div
                  style={{
                    width: 48,
                    height: 48,
                    borderRadius: 14,
                    background: p.accentSoft,
                    display: 'flex',
                    alignItems: 'center',
                    justifyContent: 'center',
                    color: p.accentDark,
                  }}
                >
                  <Icon name={s.icon} size={22} stroke={1.4} />
                </div>
                <span
                  style={{
                    fontFamily: "'JetBrains Mono', monospace",
                    fontSize: 11,
                    color: p.textSubtle,
                    letterSpacing: 1,
                  }}
                >
                  0{i + 1}
                </span>
              </div>

              <h3
                style={{
                  fontFamily: "'Cormorant Garamond', 'Noto Serif TC', 'Noto Serif SC', serif",
                  fontWeight: 500,
                  fontSize: 26,
                  margin: 0,
                  color: p.text,
                  letterSpacing: -0.2,
                  lineHeight: 1.2,
                }}
              >
                {s.title}
              </h3>

              <p
                style={{
                  fontSize: 14.5,
                  lineHeight: 1.7,
                  color: p.textMuted,
                  margin: 0,
                  fontWeight: 300,
                  fontFamily: "'Inter', 'Noto Sans TC', 'Noto Sans SC', sans-serif",
                }}
              >
                {s.body}
              </p>
            </article>
          ))}

          {/* Seven cards leave a gap in the 3-column grid; the bespoke-work
              note fills it as a wider tile rather than orphaning card seven. */}
          <div
            className="services-note reveal r-rise"
            style={{
              gridColumn: 'span 2',
              transitionDelay: (c.services.length * 70) + 'ms',
              background: p.accentSoft,
              border: '1px solid transparent',
              borderRadius: 24,
              padding: 32,
              display: 'flex',
              alignItems: 'center',
              gap: 20,
            }}
          >
            <Icon name="leaf" size={26} stroke={1.3} style={{ color: p.accentDark }} />
            <p
              style={{
                margin: 0,
                fontFamily: "'Cormorant Garamond', 'Noto Serif TC', 'Noto Serif SC', serif",
                fontSize: 21,
                lineHeight: 1.5,
                color: p.text,
                fontWeight: 400,
              }}
            >
              {ui.sections.services.customNote}
            </p>
          </div>
        </div>
      </div>
    </section>
  );

  // ─────────── BEFORE / AFTER ───────────
  const Transformations = () => (
    <section id="transformations" style={{ ...wrap, maxWidth: 1400, padding: `120px ${PAD_X}` }}>
      <div className="reveal r-up" style={{ marginBottom: 56 }}>
        <SectionHeader
          eyebrow={ui.sections.transformations.eyebrow}
          title={ui.sections.transformations.title}
          lede={ui.sections.transformations.lede}
        />
      </div>

      <BeforeAfterCarousel p={p} ui={ui} Placeholder={RoomPlaceholder} />

      <div className="reveal r-fade" style={{ marginTop: 36, display: 'flex', justifyContent: 'center' }}>
        <Btn
          variant="ghost"
          onClick={() => {
            const el = document.getElementById('catalog');
            if (el) el.scrollIntoView({ behavior: 'smooth' });
          }}
        >
          {ui.browseAll}
        </Btn>
      </div>
    </section>
  );

  // ─────────── FLOW · Family · Living · Organize · Warmth ───────────
  // The four letters ride a wave rather than sit on a straight rail. Each
  // cubic in FLOW_PATH spans 250 viewBox units, so its midpoint — where the
  // curve reaches its extreme — lands exactly on a column centre (125, 375,
  // 625, 875). flowStepTop() drops each column so the line threads straight
  // through its letter: crest for the even steps, trough for the odd ones.
  const Flow = () => (
    <section
      id="flow"
      style={{
        background: `radial-gradient(130% 90% at 50% 0%, ${p.surface} 0%, ${p.bg} 64%)`,
        borderTop: `1px solid ${p.border}`,
        borderBottom: `1px solid ${p.border}`,
        padding: '120px 0 96px',
      }}
    >
      <div style={wrap}>
        <div className="reveal r-up" style={{ marginBottom: 60, textAlign: 'center' }}>
          <SectionHeader
            eyebrow={ui.sections.flow.eyebrow}
            title={ui.sections.flow.title}
            lede={ui.sections.flow.lede}
            align="center"
          />
        </div>

        <div style={{ position: 'relative' }}>
          <svg
            className="flow-wave reveal r-fade"
            viewBox="0 0 1000 200"
            preserveAspectRatio="none"
            aria-hidden="true"
            style={{ position: 'absolute', top: FLOW_WAVE_TOP, left: 0, width: '100%', height: 200, zIndex: 0 }}
          >
            <path className="flow-line" pathLength="1" d={FLOW_PATH} fill="none" stroke={p.accent} strokeWidth="1.4" strokeLinecap="round" opacity="0.55" />
            <path className="flow-echo" pathLength="1" d={FLOW_PATH} fill="none" stroke={p.accentDark} strokeWidth="3" strokeLinecap="round" opacity="0.4" />
          </svg>

          <div
            className="flow-grid"
            style={{ display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', alignItems: 'start', position: 'relative', zIndex: 1 }}
          >
            {c.howItWorks.map((step, i) => (
              <div
                key={i}
                className="flow-step reveal r-rise"
                style={{ padding: `${flowStepTop(i)}px 16px 0`, textAlign: 'center', transitionDelay: (i * 150) + 'ms' }}
              >
                <div style={{ position: 'relative', height: FLOW_LETTER_H, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
                  <span
                    aria-hidden="true"
                    style={{
                      position: 'absolute',
                      width: 158,
                      height: 158,
                      borderRadius: '50%',
                      background: `radial-gradient(circle, ${p.accentSoft} 0%, ${p.accentSoft}00 70%)`,
                      opacity: 0.7,
                    }}
                  />
                  <span
                    className="flow-letter"
                    style={{
                      position: 'relative',
                      fontFamily: "'Cormorant Garamond', serif",
                      fontStyle: 'italic',
                      fontWeight: 300,
                      fontSize: 'clamp(60px, 7.2vw, 98px)',
                      lineHeight: 1,
                      letterSpacing: -2,
                      color: p.accentDark,
                    }}
                  >
                    {step.letter}
                  </span>
                </div>

                <div
                  style={{
                    fontFamily: "'JetBrains Mono', monospace",
                    fontSize: 12,
                    letterSpacing: 2.8,
                    textTransform: 'uppercase',
                    color: p.accentDark,
                    margin: '20px 0 0',
                  }}
                >
                  {step.word}
                </div>
              </div>
            ))}
          </div>
        </div>
      </div>
    </section>
  );

  // ─────────── NO-JUDGMENT VALUES ───────────
  const Values = () => (
    <section style={{ ...wrap, padding: `120px ${PAD_X}` }}>
      <div style={{ display: 'grid', gridTemplateColumns: '0.85fr 1.15fr', gap: 80, alignItems: 'flex-start' }} className="values-grid">
        <div className="reveal r-left" style={{ position: 'sticky', top: 100 }}>
          <SectionHeader
            eyebrow={ui.sections.values.eyebrow}
            title={
              <span>
                {ui.sections.values.title.line1}
                <br />
                <em style={{ fontStyle: 'italic', color: p.accentDark }}>{ui.sections.values.title.em}</em>
              </span>
            }
            lede={ui.sections.values.lede}
          />
        </div>
        <div style={{ display: 'grid', gridTemplateColumns: 'repeat(2, 1fr)', gap: 16 }} className="values-cards">
          {c.values.map((v, i) => (
            <div
              key={i}
              className="reveal r-rise"
              style={{
                transitionDelay: (i * 70) + 'ms',
                background: i % 3 === 0 ? p.accentSoft : p.surface,
                border: `1px solid ${i % 3 === 0 ? 'transparent' : p.border}`,
                borderRadius: 20,
                padding: 26,
                display: 'flex',
                flexDirection: 'column',
                gap: 12,
              }}
            >
              <Icon name="check" size={20} stroke={1.6} style={{ color: p.accent }} />
              <h4
                style={{
                  fontFamily: "'Cormorant Garamond', 'Noto Serif TC', 'Noto Serif SC', serif",
                  fontWeight: 500,
                  fontSize: 22,
                  margin: 0,
                  color: p.text,
                  letterSpacing: -0.1,
                  lineHeight: 1.25,
                }}
              >
                {v.title}
              </h4>
              <p
                style={{
                  fontSize: 14.5,
                  lineHeight: 1.6,
                  color: p.textMuted,
                  margin: 0,
                  fontWeight: 300,
                  fontFamily: "'Inter', 'Noto Sans TC', 'Noto Sans SC', sans-serif",
                }}
              >
                {v.body}
              </p>
            </div>
          ))}
        </div>
      </div>
    </section>
  );

  // ─────────── ABOUT KARRIE ───────────
  const About = () => (
    <section id="about" style={{ background: p.bgAlt, borderTop: `1px solid ${p.border}`, borderBottom: `1px solid ${p.border}`, padding: '120px 0' }}>
      <div style={wrap}>
        <div style={{ display: 'grid', gridTemplateColumns: '0.6fr 1.4fr', gap: 80, alignItems: 'center' }} className="about-grid">
          <div className="reveal r-left about-portrait">
            <img
              src="photos/thumbs/066_Karrie_s_portrait.jpg"
              alt={ui.portraitCaption}
              style={{
                width: '100%',
                height: 392,
                objectFit: 'cover',
                objectPosition: 'center',
                display: 'block',
                borderRadius: 28,
                border: `1px solid ${p.border}`,
              }}
            />
          </div>
          <div className="reveal r-right">
            <SectionHeader
              eyebrow={ui.sections.about.eyebrow}
              title={
                <span>
                  {ui.sections.about.title.line1}
                  <br />
                  {ui.sections.about.title.line2Pre}
                  <em style={{ fontStyle: 'italic', color: p.accentDark }}>{ui.sections.about.title.line2Em}</em>
                  {ui.sections.about.title.line2Post}
                </span>
              }
            />
            <p style={{ fontSize: 17.5, lineHeight: 1.85, color: p.textMuted, marginTop: 24, fontWeight: 300, fontFamily: "'Inter', 'Noto Sans TC', 'Noto Sans SC', sans-serif" }}>
              {c.about.bio}
            </p>
            <p style={{ fontSize: 17.5, lineHeight: 1.85, color: p.textMuted, marginTop: 18, fontWeight: 300, fontFamily: "'Inter', 'Noto Sans TC', 'Noto Sans SC', sans-serif" }}>
              {c.about.bio2}
            </p>
            <div style={{ display: 'flex', flexWrap: 'wrap', gap: 10, marginTop: 32 }}>
              {c.about.creds.map((cr, i) => (
                <div
                  key={i}
                  style={{
                    border: `1px solid ${p.border}`,
                    background: p.surface,
                    padding: '8px 14px',
                    borderRadius: 999,
                    fontSize: 12.5,
                    color: p.textMuted,
                    letterSpacing: 0.2,
                    display: 'inline-flex',
                    alignItems: 'center',
                    gap: 8,
                    fontFamily: "'Inter', 'Noto Sans TC', 'Noto Sans SC', sans-serif",
                  }}
                >
                  <Icon name="check" size={12} style={{ color: p.accent }} />
                  {cr}
                </div>
              ))}
            </div>
          </div>
        </div>
      </div>
    </section>
  );

  // ─────────── TESTIMONIALS ───────────
  const Testimonials = () => (
    <section style={{ ...wrap, padding: `80px ${PAD_X}` }}>
      <div className="reveal r-up" style={{ marginBottom: 20 }}>
        <SectionHeader
          eyebrow={ui.sections.testimonials.eyebrow}
          title={ui.sections.testimonials.title}
          lede={ui.sections.testimonials.lede}
        />
      </div>
      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(2, 1fr)', gap: 24 }} className="testimonial-grid">
        {c.testimonials.slice(0, 2).map((tm, i) => (
          <div
            key={i}
            className="reveal r-rise"
            style={{
              transitionDelay: (i * 90) + 'ms',
              background: i === 0 ? p.accentSoft : p.surface,
              border: `1px solid ${i === 0 ? 'transparent' : p.border}`,
              borderRadius: 24,
              padding: 36,
              display: 'flex',
              flexDirection: 'column',
              gap: 22,
            }}
          >
            <Icon name="quote" size={30} stroke={1.2} style={{ color: p.accent }} />
            <p
              style={{
                fontFamily: "'Cormorant Garamond', 'Noto Serif TC', 'Noto Serif SC', serif",
                fontSize: 23,
                lineHeight: 1.5,
                color: p.text,
                margin: 0,
                fontWeight: 400,
                letterSpacing: -0.1,
              }}
            >
              "{tm.quote}"
            </p>
            <div style={{ display: 'flex', alignItems: 'center', gap: 14, marginTop: 'auto' }}>
              <div
                style={{
                  width: 40,
                  height: 40,
                  borderRadius: '50%',
                  background: p.accent,
                  color: p.onAccent,
                  fontFamily: "'Cormorant Garamond', 'Noto Serif TC', 'Noto Serif SC', serif",
                  fontSize: 18,
                  display: 'flex',
                  alignItems: 'center',
                  justifyContent: 'center',
                  fontWeight: 500,
                }}
              >
                {Array.from(tm.who)[0]}
              </div>
              <div>
                <div style={{ fontSize: 14, color: p.text, fontWeight: 500, fontFamily: "'Inter', 'Noto Sans TC', 'Noto Sans SC', sans-serif" }}>{tm.who}</div>
                <div style={{ fontSize: 12, color: p.textSubtle, letterSpacing: 0.3, fontFamily: "'Inter', 'Noto Sans TC', 'Noto Sans SC', sans-serif" }}>{tm.where}</div>
              </div>
            </div>
          </div>
        ))}
      </div>
    </section>
  );

  // ─────────── CONTACT FORM ───────────
  const Contact = () => {
    const fieldStyle = {
      background: p.bg,
      border: `1px solid ${p.border}`,
      borderRadius: 14,
      padding: '14px 18px',
      fontFamily: "'Inter', 'Noto Sans TC', 'Noto Sans SC', sans-serif",
      fontSize: 15,
      color: p.text,
      outline: 'none',
      width: '100%',
      boxSizing: 'border-box',
    };
    const labelStyle = { display: 'flex', flexDirection: 'column', gap: 8 };
    const labelText = {
      fontSize: 11,
      color: p.textMuted,
      letterSpacing: 1.4,
      textTransform: 'uppercase',
      fontFamily: "'JetBrains Mono', monospace",
    };

    const infoRows = [
      { icon: 'mail', label: c.contact.email, k: ui.sections.contact.infoLabels.Email },
      { icon: 'phone', label: c.contact.phone, k: ui.sections.contact.infoLabels.Phone },
      { icon: 'cal', label: c.contact.hours, k: ui.sections.contact.infoLabels.Hours },
      { icon: 'pin', label: c.contact.area, k: ui.sections.contact.infoLabels['Service area'] },
    ];

    // No backend on this static site: gather the answers, open the visitor's
    // mail app with everything pre-filled, then show the confirmation view.
    const handleSubmit = (e) => {
      e.preventDefault();
      const fd = new FormData(e.currentTarget);
      const get = (k) => (fd.get(k) || '').toString().trim();
      const L = ui.form.labels;
      const service = get('service') === ui.form.placeholders.service ? '' : get('service');
      const space = get('space') === ui.form.placeholders.space ? '' : get('space');
      const rows = [
        [L.name, get('name')],
        [L.email, get('email')],
        [L.phone, get('phone')],
        [L.city, get('city')],
        [L.service, service],
        [L.space, space],
        [L.format, get('format')],
        [L.challenge, get('challenge')],
      ].filter(([, v]) => v);
      const body = rows.map(([k, v]) => k + ': ' + v).join('\n') + '\n\n' + L.message + ':\n' + get('message');
      const subject = ui.form.consultationRequest + (get('name') ? ' · ' + get('name') : '');
      window.location.href =
        'mailto:' + CONTACT_EMAIL + '?subject=' + encodeURIComponent(subject) + '&body=' + encodeURIComponent(body);
      setContactSent(true);
    };

    return (
      <section id="contact" style={{ ...wrap, padding: `120px ${PAD_X}` }}>
        <div style={{ display: 'grid', gridTemplateColumns: '0.85fr 1.15fr', gap: 72 }} className="contact-grid">
          <div className="reveal r-left">
            <SectionHeader
              eyebrow={ui.sections.contact.eyebrow}
              title={
                <span>
                  {ui.sections.contact.title.line1}
                  <br />
                  <em style={{ fontStyle: 'italic', color: p.accentDark }}>{ui.sections.contact.title.line2Em}</em>
                </span>
              }
              lede={c.contact.sub}
            />
            <div style={{ marginTop: 48, display: 'flex', flexDirection: 'column', gap: 22 }}>
              {infoRows.map((row, idx) => (
                <div key={idx} style={{ display: 'flex', alignItems: 'center', gap: 16 }}>
                  <div
                    style={{
                      width: 42,
                      height: 42,
                      borderRadius: 12,
                      background: p.accentSoft,
                      color: p.accentDark,
                      display: 'flex',
                      alignItems: 'center',
                      justifyContent: 'center',
                      flex: '0 0 auto',
                    }}
                  >
                    <Icon name={row.icon} size={18} stroke={1.4} />
                  </div>
                  <div>
                    <div
                      style={{
                        fontSize: 10.5,
                        color: p.textSubtle,
                        letterSpacing: 1.5,
                        textTransform: 'uppercase',
                        fontFamily: "'JetBrains Mono', monospace",
                      }}
                    >
                      {row.k}
                    </div>
                    <div style={{ fontSize: 15.5, color: p.text, marginTop: 3, fontFamily: "'Inter', 'Noto Sans TC', 'Noto Sans SC', sans-serif" }}>{row.label}</div>
                  </div>
                </div>
              ))}
            </div>

            <div
              style={{
                marginTop: 48,
                padding: 22,
                borderRadius: 18,
                background: p.surface,
                border: `1px solid ${p.border}`,
                display: 'flex',
                gap: 14,
                alignItems: 'flex-start',
              }}
            >
              <Icon name="leaf" size={22} stroke={1.3} style={{ color: p.accent, marginTop: 2 }} />
              <div style={{ fontSize: 13.5, lineHeight: 1.7, color: p.textMuted, fontFamily: "'Inter', 'Noto Sans TC', 'Noto Sans SC', sans-serif" }}>
                {ui.sections.contact.bilingualNote}
              </div>
            </div>
          </div>

          {contactSent ? (
            <div
              className="reveal r-right"
              style={{
                background: p.surface,
                border: `1px solid ${p.border}`,
                borderRadius: 28,
                padding: 36,
                display: 'flex',
                flexDirection: 'column',
                gap: 16,
                justifyContent: 'center',
                minHeight: 380,
              }}
            >
              <Icon name="leaf" size={30} stroke={1.3} style={{ color: p.accent }} />
              <h3 style={{ margin: 0, fontFamily: "'Cormorant Garamond', 'Noto Serif TC', 'Noto Serif SC', serif", fontWeight: 500, fontSize: 30, color: p.text, letterSpacing: -0.3, lineHeight: 1.2 }}>
                {ui.form.sentTitle}
              </h3>
              <p style={{ margin: 0, fontSize: 15, lineHeight: 1.75, color: p.textMuted, fontWeight: 300, maxWidth: 460, fontFamily: "'Inter', 'Noto Sans TC', 'Noto Sans SC', sans-serif" }}>
                {ui.form.sentBody}
              </p>
              <button
                type="button"
                onClick={() => setContactSent(false)}
                style={{ alignSelf: 'flex-start', marginTop: 8, background: 'transparent', border: 'none', padding: 0, cursor: 'pointer', color: p.accentDark, fontSize: 14, fontFamily: "'Inter', 'Noto Sans TC', 'Noto Sans SC', sans-serif", textDecoration: 'underline' }}
              >
                {ui.form.sendAnother}
              </button>
            </div>
          ) : (
          <form
            onSubmit={handleSubmit}
            className="reveal r-right"
            style={{
              background: p.surface,
              border: `1px solid ${p.border}`,
              borderRadius: 28,
              padding: 36,
              display: 'flex',
              flexDirection: 'column',
              gap: 18,
            }}
          >
            <div
              style={{
                fontFamily: "'JetBrains Mono', monospace",
                fontSize: 11,
                letterSpacing: 2,
                color: p.accentDark,
                textTransform: 'uppercase',
              }}
            >
              {ui.form.consultationRequest}
            </div>

            <div className="form-row" style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 14 }}>
              <label style={labelStyle}>
                <span style={labelText}>{ui.form.labels.name}</span>
                <input name="name" required style={fieldStyle} placeholder={ui.form.placeholders.name} />
              </label>
              <label style={labelStyle}>
                <span style={labelText}>{ui.form.labels.email}</span>
                <input name="email" required style={fieldStyle} type="email" placeholder={ui.form.placeholders.email} />
              </label>
              <label style={labelStyle}>
                <span style={labelText}>{ui.form.labels.phone}</span>
                <input name="phone" style={fieldStyle} placeholder={ui.form.placeholders.phone} />
              </label>
              <label style={labelStyle}>
                <span style={labelText}>{ui.form.labels.city}</span>
                <input name="city" style={fieldStyle} placeholder={ui.form.placeholders.city} />
              </label>
            </div>

            <div className="form-row" style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 14 }}>
              <label style={labelStyle}>
                <span style={labelText}>{ui.form.labels.service}</span>
                <select
                  name="service"
                  style={{
                    ...fieldStyle,
                    appearance: 'none',
                    backgroundImage: `url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='${encodeURIComponent(p.textMuted)}' stroke-width='1.5'><path d='M6 9l6 6 6-6'/></svg>")`,
                    backgroundRepeat: 'no-repeat',
                    backgroundPosition: 'right 14px center',
                    backgroundSize: '18px',
                  }}
                >
                  <option>{ui.form.placeholders.service}</option>
                  {c.services.map((s, i) => (
                    <option key={i}>{s.title}</option>
                  ))}
                </select>
              </label>
              <label style={labelStyle}>
                <span style={labelText}>{ui.form.labels.space}</span>
                <select
                  name="space"
                  style={{
                    ...fieldStyle,
                    appearance: 'none',
                    backgroundImage: `url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='${encodeURIComponent(p.textMuted)}' stroke-width='1.5'><path d='M6 9l6 6 6-6'/></svg>")`,
                    backgroundRepeat: 'no-repeat',
                    backgroundPosition: 'right 14px center',
                    backgroundSize: '18px',
                  }}
                >
                  <option>{ui.form.placeholders.space}</option>
                  {ui.form.spaceOptions.map((s, i) => (
                    <option key={i}>{s}</option>
                  ))}
                </select>
              </label>
            </div>

            <div>
              <div style={{ ...labelText, marginBottom: 10 }}>{ui.form.labels.format}</div>
              <div style={{ display: 'flex', gap: 10, flexWrap: 'wrap' }}>
                {ui.form.formatOptions.map((opt, i) => (
                  <label
                    key={i}
                    style={{
                      flex: '1 1 160px',
                      display: 'flex',
                      alignItems: 'center',
                      gap: 10,
                      padding: '12px 14px',
                      borderRadius: 12,
                      border: `1px solid ${p.border}`,
                      background: p.bg,
                      cursor: 'pointer',
                      fontSize: 13.5,
                      color: p.textMuted,
                      fontFamily: "'Inter', 'Noto Sans TC', 'Noto Sans SC', sans-serif",
                    }}
                  >
                    <input type="radio" name="format" value={opt} defaultChecked={i === 0} style={{ accentColor: p.accent }} />
                    {opt}
                  </label>
                ))}
              </div>
            </div>

            <label style={labelStyle}>
              <span style={labelText}>{ui.form.labels.challenge}</span>
              <input name="challenge" style={fieldStyle} placeholder={ui.form.placeholders.challenge} />
            </label>

            <label style={labelStyle}>
              <span style={labelText}>{ui.form.labels.message}</span>
              <textarea
                name="message"
                required
                rows={4}
                style={{ ...fieldStyle, resize: 'vertical', fontFamily: "'Inter', 'Noto Sans TC', 'Noto Sans SC', sans-serif" }}
                placeholder={ui.form.placeholders.message}
              />
            </label>

            <label
              style={{
                ...labelStyle,
                border: `1px dashed ${p.borderStrong}`,
                borderRadius: 14,
                padding: '16px 18px',
                cursor: 'pointer',
                background: p.bg,
              }}
            >
              <div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
                <div
                  style={{
                    width: 36,
                    height: 36,
                    borderRadius: 10,
                    background: p.accentSoft,
                    color: p.accentDark,
                    display: 'flex',
                    alignItems: 'center',
                    justifyContent: 'center',
                  }}
                >
                  <Icon name="upload" size={18} stroke={1.5} />
                </div>
                <div>
                  <div style={{ fontSize: 14, color: p.text, fontFamily: "'Inter', 'Noto Sans TC', 'Noto Sans SC', sans-serif", fontWeight: 500 }}>
                    {ui.form.uploadTitle}
                  </div>
                  <div style={{ fontSize: 12, color: p.textSubtle, marginTop: 2, fontFamily: "'Inter', 'Noto Sans TC', 'Noto Sans SC', sans-serif" }}>
                    {ui.form.uploadHint}
                  </div>
                </div>
              </div>
            </label>

            <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginTop: 6, gap: 18, flexWrap: 'wrap' }}>
              <div
                style={{
                  fontSize: 13,
                  color: p.textSubtle,
                  fontStyle: 'italic',
                  fontFamily: "'Cormorant Garamond', 'Noto Serif TC', 'Noto Serif SC', serif",
                  maxWidth: 280,
                  lineHeight: 1.5,
                }}
              >
                {ui.form.replyPersonally}
              </div>
              <Btn variant="primary" icon="arrow" type="submit">{ui.form.sendRequest}</Btn>
            </div>
          </form>
          )}
        </div>
      </section>
    );
  };

  // ─────────── FOOTER ───────────
  const Footer = () => (
    <footer
      style={{
        background: p.text,
        color: 'rgba(255,255,255,0.65)',
        padding: `72px 0 32px`,
      }}
    >
      <div style={wrap}>
        <div style={{ display: 'grid', gridTemplateColumns: '1.4fr 1fr 1fr 1fr', gap: 60, alignItems: 'flex-start' }} className="footer-grid">
          <div>
            <div style={{ marginBottom: 20 }}>
              <img src="logo-lockup-light.png" alt="Karrie 29 Interiors" style={{ width: 190, maxWidth: '100%', height: 'auto', display: 'block' }} />
            </div>
            <p
              style={{
                fontSize: 14,
                lineHeight: 1.75,
                color: 'rgba(255,255,255,0.55)',
                maxWidth: 300,
                margin: 0,
                fontWeight: 300,
                fontFamily: "'Inter', 'Noto Sans TC', 'Noto Sans SC', sans-serif",
              }}
            >
              {ui.footer.description}
            </p>
          </div>
          {[
            // studioItems order matches these section anchors: Services,
            // Transformations, About. Keep them aligned if the list changes.
            { title: ui.footer.columns.studio, items: ui.footer.studioItems, hrefs: ['#services', '#transformations', '#about'] },
            { title: ui.footer.columns.visit, items: ui.footer.visitItems },
            { title: ui.footer.columns.contact, items: [c.contact.email, c.contact.phone, c.contact.hours] },
          ].map((col, idx) => (
            <div key={idx}>
              <div
                style={{
                  fontSize: 11,
                  letterSpacing: 2,
                  textTransform: 'uppercase',
                  fontFamily: "'JetBrains Mono', monospace",
                  color: 'rgba(255,255,255,0.5)',
                  marginBottom: 18,
                }}
              >
                {col.title}
              </div>
              {col.items.map((it, k) => {
                const linkStyle = {
                  fontSize: 14,
                  color: '#fff',
                  marginBottom: 10,
                  fontWeight: 300,
                  fontFamily: "'Inter', 'Noto Sans TC', 'Noto Sans SC', sans-serif",
                };
                const href = col.hrefs && col.hrefs[k];
                return href ? (
                  <a key={k} href={href} className="footer-link" style={{ ...linkStyle, display: 'block', color: '#fff', textDecoration: 'none' }}>
                    {it}
                  </a>
                ) : (
                  <div key={k} style={linkStyle}>
                    {it}
                  </div>
                );
              })}
            </div>
          ))}
        </div>
        <div
          style={{
            marginTop: 56,
            paddingTop: 24,
            borderTop: `1px solid rgba(255,255,255,0.1)`,
            display: 'flex',
            justifyContent: 'space-between',
            fontSize: 12,
            color: 'rgba(255,255,255,0.5)',
            flexWrap: 'wrap',
            gap: 12,
            fontFamily: "'Inter', 'Noto Sans TC', 'Noto Sans SC', sans-serif",
          }}
        >
          <div>{ui.footer.copyright}</div>
          <div style={{ fontFamily: "'JetBrains Mono', monospace", letterSpacing: 1 }}>
            {ui.footer.watermark}
          </div>
        </div>
        <div
          style={{
            marginTop: 20,
            textAlign: 'center',
            fontSize: 11,
            color: 'rgba(255,255,255,0.35)',
            fontFamily: "'Inter', 'Noto Sans TC', 'Noto Sans SC', sans-serif",
          }}
        >
          Website Created &amp; Maintained by{' '}
          <a href="mailto:ethanchiouu@gmail.com" style={{ color: 'inherit', textDecoration: 'underline' }}>
            @ethanchiouu@gmail.com
          </a>
        </div>
      </div>
    </footer>
  );

  // ─────────── CATALOG (full before/after archive) ───────────
  const Catalog = () => {
    const data = (typeof window !== 'undefined' && window.PHOTO_CATALOG) || [];
    if (!data.length) return null;
    const badge = (bg, color) => ({
      position: 'absolute', top: 10, left: 10, background: bg, color,
      padding: '4px 10px', borderRadius: 999, fontFamily: "'JetBrains Mono', monospace",
      fontSize: 9, letterSpacing: 1.2, textTransform: 'uppercase',
    });
    return (
      <section id="catalog" style={{ background: p.bgAlt, borderTop: `1px solid ${p.border}`, borderBottom: `1px solid ${p.border}`, padding: '120px 0' }}>
        <div style={wrap}>
          <div className="reveal r-up" style={{ marginBottom: 56 }}>
            <SectionHeader
              eyebrow={ui.sections.catalog.eyebrow}
              title={ui.sections.catalog.title}
              lede={ui.sections.catalog.lede}
            />
          </div>
          {data.map((sec, si) => (
            <div key={si} style={{ marginBottom: 60 }}>
              <div
                className="reveal r-fade"
                style={{ display: 'flex', alignItems: 'baseline', gap: 12, marginBottom: 24, paddingBottom: 12, borderBottom: `1px solid ${p.border}` }}
              >
                <h3 style={{ margin: 0, fontFamily: "'Cormorant Garamond', 'Noto Serif TC', 'Noto Serif SC', serif", fontWeight: 500, fontSize: 28, color: p.text, letterSpacing: -0.2 }}>
                  {sec.section}
                </h3>
                <span style={{ fontFamily: "'JetBrains Mono', monospace", fontSize: 11, color: p.textSubtle, letterSpacing: 0.5 }}>
                  {String(sec.pairs.length).padStart(2, '0')}
                </span>
              </div>
              <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(min(420px, 100%), 1fr))', gap: 24 }} className="catalog-grid">
                {sec.pairs.map((pair, pi) => (
                  <div key={pi} className="reveal r-scale" style={{ background: p.surface, border: `1px solid ${p.border}`, borderRadius: 18, padding: 10 }}>
                    <div className="catalog-pair" style={{ display: 'flex', gap: 6, borderRadius: 12, overflow: 'hidden' }}>
                      {pair.before && (
                        <div style={{ position: 'relative', flex: 1 }}>
                          <img src={pair.before} alt={pair.label + ' ' + ui.beforeLabel} loading="lazy" style={{ width: '100%', height: 210, objectFit: 'contain', background: p.bgAlt, display: 'block' }} />
                          <span style={badge(p.text, p.bg)}>{ui.beforeLabel}</span>
                        </div>
                      )}
                      {pair.after && (
                        <div style={{ position: 'relative', flex: 1 }}>
                          <img src={pair.after} alt={pair.label + ' ' + ui.afterLabel} loading="lazy" style={{ width: '100%', height: 210, objectFit: 'contain', background: p.bgAlt, display: 'block' }} />
                          <span style={badge(p.accent, p.onAccent)}>{ui.afterLabel}</span>
                        </div>
                      )}
                    </div>
                  </div>
                ))}
              </div>
            </div>
          ))}
        </div>
      </section>
    );
  };

  return (
    <div
      style={{
        background: p.bg,
        color: p.text,
        fontFamily: "'Inter', 'Noto Sans TC', 'Noto Sans SC', sans-serif",
        fontWeight: 300,
        minHeight: '100vh',
      }}
    >
      <Nav />
      <Hero />
      <Why29 />
      <About />
      <Flow />
      <Services />
      <Transformations />
      <Testimonials />
      <Values />
      <FAQAccordion key={lang} p={p} ui={ui} c={c} wrap={wrap} SectionHeader={SectionHeader} Btn={Btn} />
      <Contact />
      <Catalog />
      <Footer />
    </div>
  );
};

Object.assign(window, { KSite });
