// Shift Onboarding v2 - "6 Steps" sheet - multilingual (pl / en / uk)
// User-facing strings live in i18n.js; components read them via t().

const heroCollageVideos = [
  { src: "assets/video/b-roll/poland/0428.mp4",   poster: "assets/video/b-roll/poland/posters/0428.jpg" },
  { src: "assets/video/b-roll/poland/0503_6.mp4", poster: "assets/video/b-roll/poland/posters/0503_6.jpg" },
  { src: "assets/video/b-roll/poland/0503_2.mp4", poster: "assets/video/b-roll/poland/posters/0503_2.jpg" },
  { src: "assets/video/b-roll/poland/0503_7.mp4", poster: "assets/video/b-roll/poland/posters/0503_7.jpg" },
  { src: "assets/video/b-roll/poland/0503_3.mp4", poster: "assets/video/b-roll/poland/posters/0503_3.jpg" },
  { src: "assets/video/b-roll/poland/0503_8.mp4", poster: "assets/video/b-roll/poland/posters/0503_8.jpg" },
  { src: "assets/video/b-roll/poland/0503_4.mp4", poster: "assets/video/b-roll/poland/posters/0503_4.jpg" },
  { src: "assets/video/b-roll/poland/0503_9.mp4", poster: "assets/video/b-roll/poland/posters/0503_9.jpg" },
  { src: "assets/video/b-roll/poland/0503_10.mp4", poster: "assets/video/b-roll/poland/posters/0503_10.jpg" },
  { src: "assets/video/b-roll/poland/0503_5.mp4", poster: "assets/video/b-roll/poland/posters/0503_5.jpg" },
  { src: "assets/video/b-roll/poland/0503_11.mp4", poster: "assets/video/b-roll/poland/posters/0503_11.jpg" },
  { src: "assets/video/b-roll/poland/0503_12.mp4", poster: "assets/video/b-roll/poland/posters/0503_12.jpg" },
  { src: "assets/video/b-roll/poland/0503_13.mp4", poster: "assets/video/b-roll/poland/posters/0503_13.jpg" },
  { src: "assets/video/b-roll/poland/0503_14.mp4", poster: "assets/video/b-roll/poland/posters/0503_14.jpg" },
  { src: "assets/video/b-roll/poland/0503_15.mp4", poster: "assets/video/b-roll/poland/posters/0503_15.jpg" },
  { src: "assets/video/b-roll/poland/0521.mp4", poster: "assets/video/b-roll/poland/posters/0521.jpg" },
];

/* ---------- i18n plumbing ---------- */
const LangContext = React.createContext({
  lang: 'pl',
  setLang: function () {},
  // Mirrors the real t() in index.html, including its `optional` second
  // argument. Only reached if a component renders outside the Provider.
  t: function (k, optional) {
    const d = (window.I18N && window.I18N.pl) || {};
    return d[k] || (optional ? '' : k);
  },
});
const useI18n = () => React.useContext(LangContext);

// IMPORTANT: keep the declarative `autoPlay` attribute. Desktop Safari refuses a
// scripted v.play() on a muted video (NotAllowedError) unless it came from the
// autoplay attribute or a real click — scrolling is not a user gesture in Safari.
// So autoplay starts playback (works in Chrome/Safari/iOS); the observer only
// pauses clips while off screen (pause is always allowed) and resumes on return.
// preload="metadata" keeps the initial load light (header only, not the file).
const LazyVideo = (props) => {
  const ref = React.useRef(null);
  React.useEffect(() => {
    const el = ref.current;
    if (!el) return;
    let inView = false;
    const observer = new IntersectionObserver(
      (entries) => {
        entries.forEach((entry) => {
          inView = entry.isIntersecting;
          if (entry.isIntersecting) {
            el.play().catch(() => {});
          } else {
            el.pause();
          }
        });
      },
      // Trigger play ~one screen early so it's ready by the time it's visible.
      { rootMargin: '300px 0px', threshold: 0.1 }
    );
    observer.observe(el);
    // Safari fallback: if autoplay is suppressed, the first real user gesture
    // (click/tap/key) unconditionally unlocks play() — start any on-screen clip.
    const unlock = () => { if (inView) el.play().catch(() => {}); };
    document.addEventListener('pointerdown', unlock, { once: true });
    document.addEventListener('keydown', unlock, { once: true });
    document.addEventListener('touchstart', unlock, { once: true, passive: true });
    return () => {
      observer.disconnect();
      document.removeEventListener('pointerdown', unlock);
      document.removeEventListener('keydown', unlock);
      document.removeEventListener('touchstart', unlock);
    };
  }, []);
  return (
    <video
      ref={ref}
      src={props.src}
      poster={props.poster}
      className={props.className}
      autoPlay muted loop playsInline preload="metadata"
    />
  );
};

// Renders a translated string that contains inline markup (<strong>, <em>, <br>).
const RichText = (props) => {
  const tag = props.as || 'span';
  const attrs = { dangerouslySetInnerHTML: { __html: props.html } };
  if (props.className) attrs.className = props.className;
  return React.createElement(tag, attrs);
};

/* Several locale strings end in "ask the assistant on this page" rather than in
   an email address — the chatbot is the one support surface that costs the team
   nothing. Those strings are rendered with dangerouslySetInnerHTML, so a button
   inside one cannot carry a React onClick. One delegated listener instead: any
   [data-shift-chat] element opens the widget by clicking the FAB chatbot.js
   mounts on document.body. A programmatic .click() fires even while the FAB is
   display:none (which is how chatbot.js hides it when the panel is already
   open), so this works in both states.

   If chatbot.js failed to load there is nothing to click and the click is a
   no-op. That is why every string carrying one of these buttons puts the
   self-serve answer FIRST and offers the assistant only as the last resort —
   the reader is never left with the chat as their only route. */
if (typeof document !== 'undefined' && !window.__shiftChatLinks) {
  window.__shiftChatLinks = true;
  document.addEventListener('click', (e) => {
    const trigger = e.target.closest && e.target.closest('[data-shift-chat]');
    if (!trigger) return;
    e.preventDefault();
    const fab = document.querySelector('.shift-chat-fab');
    if (fab) fab.click();
  });
}

const Hero = () => {
  const { t } = useI18n();
  const sectionRef = React.useRef(null);
  const videoRefs = React.useRef([]);
  const [progress, setProgress] = React.useState(0);

  React.useEffect(() => {
    const onScroll = () => {
      const el = sectionRef.current;
      if (!el) return;
      const total = el.offsetHeight - window.innerHeight;
      const scrolled = Math.max(0, -el.getBoundingClientRect().top);
      const p = total > 0 ? Math.max(0, Math.min(1, scrolled / total)) : 0;
      setProgress(p);
    };
    onScroll();
    window.addEventListener('scroll', onScroll, { passive: true });
    window.addEventListener('resize', onScroll);
    return () => {
      window.removeEventListener('scroll', onScroll);
      window.removeEventListener('resize', onScroll);
    };
  }, []);

  // Play a clip only while its cell is on screen, so all 16 don't try to decode at once.
  React.useEffect(() => {
    const observer = new IntersectionObserver(
      (entries) => {
        entries.forEach((entry) => {
          if (entry.isIntersecting) {
            entry.target.play().catch(() => {});
          } else {
            entry.target.pause();
          }
        });
      },
      { threshold: 0.1 }
    );
    videoRefs.current.forEach((v) => v && observer.observe(v));
    return () => observer.disconnect();
  }, []);

  // Grid starts at scale 2 (zoomed in) and ends at scale 1 (all cells in view)
  const scale = 2 - progress;
  // Text overlay fades out across the first half of the scroll-zoom
  const textOpacity = Math.max(0, 1 - progress * 2);

  return (
    <section ref={sectionRef} id="hero" className="hero hero-collage" data-screen-label="Hero">
      <div className="hero-collage-sticky">
        <div className="hero-collage-grid" style={{ transform: `scale(${scale})` }}>
          {heroCollageVideos.map((v, i) => (
            <div key={i} className="hero-collage-cell">
              <video
                ref={(el) => { videoRefs.current[i] = el; }}
                src={v.src}
                poster={v.poster}
                muted loop playsInline preload="metadata"
              />
            </div>
          ))}
        </div>
        <div className="hero-scrim" />
        <div className="hero-content" style={{ opacity: textOpacity, pointerEvents: textOpacity > 0.1 ? 'auto' : 'none' }}>
          <div className="hero-kicker">
            <span className="tag-dot" />
            <span>{t('hero.kicker')}</span>
          </div>
          <RichText as="h1" className="hero-title" html={t('hero.title')} />
          <p className="hero-sub">{t('hero.sub')}</p>
        </div>
        <div className="hero-scroll-cue" style={{ opacity: textOpacity }}>{t('hero.scroll')}</div>
      </div>
    </section>
  );
};

const Intro = () => {
  const { t } = useI18n();
  return (
    <section id="intro" className="intro" data-screen-label="Intro">
      <div className="intro-tag">{t('intro.tag')}</div>
      <RichText as="h2" html={t('intro.heading')} />
      <p>{t('intro.body')}</p>
    </section>
  );
};

/* The list of handsets the app actually accepts, shown under the store button for
   the platform it belongs to. Data lives in phones.js, generated by
   `npm run phones` from src/devices.ts — the same list the funnel's device check
   uses, so the two can never tell a visitor different things.

   Collapsed by default and searchable, because the Android list is 80 models
   long: printed in full it would bury the download button this step exists for,
   and scanning 80 names for your own is exactly the job a filter does better than
   a human eye.

   Apple is a RULE, not a roster — "iPhone 12 and newer" — so the iOS panel leads
   with the rule and treats its names as a way to double-check rather than as the
   criterion. That matters for a phone released after this list was written: the
   rule still covers it, and .phones-note under the list says as much.

   Renders nothing when there is no list for a platform. */
const EligiblePhones = ({ platform }) => {
  const { t } = useI18n();
  const data = (typeof window !== 'undefined' && window.SHIFT_PHONES) || {};
  const groups = data[platform];
  const [open, setOpen] = React.useState(false);
  const [q, setQ] = React.useState('');
  if (!groups || !Object.keys(groups).length) return null;

  /* Per-platform copy overrides. A key no dictionary defines is simply absent
     for this platform, and t() returns '' for it (index.html deliberately
     returns blank rather than showing a visitor an internal key).

     This used to test `t(key) === key`, from back when t() echoed the key back.
     That test has not matched since t() started returning '', which is why the
     Android search box silently lost its "e.g. S23 or Pixel 8" placeholder:
     the fallback branch was never taken. Test for emptiness instead. */
  const tOr = (key, fallbackKey) => t(key, true) || t(fallbackKey);

  // Android has no rule to state — it is a plain roster — so this is blank
  // there and the paragraph below is not rendered at all.
  const ruleText = t(`phones.rule.${platform}`, true);

  // "e.g. S23 or Pixel 8" is useless advice on the Apple panel, so iOS
  // overrides it; Android falls through to the generic placeholder.
  const placeholder = tOr(`phones.search.${platform}`, 'phones.search');

  /* The empty state and the footnote are platform-specific for the same reason
     the rule is. For Android, "not on the list" means "not supported", full
     stop — the roster IS the criterion. For Apple it does not: the rule above
     covers any iPhone 12 or newer, including one released after this list was
     written, so telling that reader their phone is unsupported would be wrong.
     iOS therefore overrides both strings and points back at the rule. */
  const noneText = tOr(`phones.none.${platform}`, 'phones.none');
  const noteText = tOr(`phones.note.${platform}`, 'phones.note');

  const total = Object.values(groups).reduce((n, list) => n + list.length, 0);
  const needle = q.trim().toLowerCase();
  const shown = Object.entries(groups)
    .map(([brand, list]) => [brand, needle ? list.filter((m) => m.toLowerCase().includes(needle)) : list])
    .filter(([, list]) => list.length);
  const hits = shown.reduce((n, [, list]) => n + list.length, 0);

  return (
    <div className="phones">
      <button
        type="button"
        className={`phones-toggle ${open ? 'open' : ''}`}
        onClick={() => setOpen((v) => !v)}
        aria-expanded={open}
      >
        <span>{t('phones.title')}</span>
        <span className="phones-count">{total}</span>
      </button>

      {/* Outside the collapsed panel on purpose. For Apple this one line IS the
          answer, and most people will never open the list to find it. */}
      {ruleText && <p className="phones-rule">{ruleText}</p>}

      {open && (
        <div className="phones-panel">
          <input
            className="phones-search"
            type="search"
            value={q}
            onChange={(e) => setQ(e.target.value)}
            placeholder={placeholder}
            aria-label={t('phones.title')}
          />
          {/* Plain numerals, so a filtered view says how much it is hiding
              without needing a translated string. Only while filtering — "80/80"
              on an untouched panel is noise. */}
          {needle && hits > 0 && <p className="phones-hits">{hits} / {total}</p>}

          {/* The list scrolls inside its own box. Without the cap this panel was
              2326px tall on a 390px screen — one column of 80 names that buried
              the download button this whole step exists for. The note stays
              OUTSIDE the scroller so it is readable without scrolling to the
              bottom of the list. */}
          <div className="phones-scroll">
            {hits === 0 ? (
              <RichText as="p" className="phones-empty" html={noneText} />
            ) : (
              shown.map(([brand, list]) => (
                <div className="phones-group" key={brand}>
                  <div className="phones-brand">{brand}</div>
                  <ul className="phones-list">
                    {list.map((m) => <li key={m}>{m}</li>)}
                  </ul>
                </div>
              ))
            )}
          </div>
          <RichText as="p" className="phones-note" html={noteText} />
        </div>
      )}
    </div>
  );
};

const Step1 = () => {
  const { t } = useI18n();
  return (
    <div id="step-1" className="step" data-screen-label="Step 1">
      <div className="step-num">1</div>
      <div className="step-body">
        <h3 className="step-verb">{t('step1.title')}</h3>

        <div className="platform-blocks">
          <div className="platform-block">
            <div className="platform-label">
              <svg className="platform-icon" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
                <path d="M17.05 20.28c-.98.95-2.05.8-3.08.35-1.09-.46-2.09-.48-3.24 0-1.44.62-2.2.44-3.06-.35C2.79 15.25 3.51 7.59 9.05 7.31c1.35.07 2.29.74 3.08.8 1.18-.24 2.31-.93 3.57-.84 1.51.12 2.65.72 3.4 1.8-3.12 1.87-2.38 5.98.48 7.13-.57 1.5-1.31 2.99-2.54 4.09zM12 7.25c-.15-2.23 1.66-4.07 3.74-4.25.29 2.58-2.34 4.5-3.74 4.25z"/>
              </svg>
              <span>iOS</span>
            </div>
            <RichText as="p" className="step-desc" html={t('step1.iosDesc')} />
            <div className="download-row">
              <a className="download-btn" href="https://apps.apple.com/pl/app/shift-microagi/id6760602482" target="_blank" rel="noopener noreferrer">
                <svg className="store-icon" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
                  <path d="M17.05 20.28c-.98.95-2.05.8-3.08.35-1.09-.46-2.09-.48-3.24 0-1.44.62-2.2.44-3.06-.35C2.79 15.25 3.51 7.59 9.05 7.31c1.35.07 2.29.74 3.08.8 1.18-.24 2.31-.93 3.57-.84 1.51.12 2.65.72 3.4 1.8-3.12 1.87-2.38 5.98.48 7.13-.57 1.5-1.31 2.99-2.54 4.09zM12 7.25c-.15-2.23 1.66-4.07 3.74-4.25.29 2.58-2.34 4.5-3.74 4.25z"/>
                </svg>
                <span>
                  <span className="store-small">{t('step1.iosBtn1Small')}</span>
                  <span className="store-big">{t('step1.iosBtn1Big')}</span>
                </span>
              </a>
              {/* This guide is read on a laptop and the app is installed on a
                  phone, so the button beside this is unreachable from the device
                  it is being read on. The QR is that bridge, and nothing more:
                  it goes exactly where the button goes, it is hidden on phones
                  (where the reader is already holding the thing they would scan
                  with), and it is aria-hidden because a QR is of no use to
                  anyone who cannot see it — for them the link is the answer.
                  Both images are generated by scripts/gen-qr.mjs. */}
              <div className="download-qr" aria-hidden="true">
                <img src="/images/qr/app-store-pl.svg" alt="" width="132" height="132" loading="lazy" />
                <span>{t('step1.qrHint')}</span>
              </div>
            </div>
            <EligiblePhones platform="ios" />
          </div>

          <div className="platform-block">
            <div className="platform-label">
              <svg className="platform-icon" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
                <path d="M17.523 15.341c-.5 0-.91-.41-.91-.91 0-.5.41-.91.91-.91s.91.41.91.91-.41.91-.91.91zm-11.046 0c-.5 0-.91-.41-.91-.91 0-.5.41-.91.91-.91s.91.41.91.91-.41.91-.91.91zm11.372-6.034l1.815-3.143a.376.376 0 0 0-.137-.513.375.375 0 0 0-.513.137l-1.838 3.183a11.43 11.43 0 0 0-9.554 0L5.784 5.788a.375.375 0 0 0-.513-.137.375.375 0 0 0-.137.513L6.95 9.307C3.831 11.012 1.7 14.193 1.395 17.852h21.21c-.305-3.659-2.435-6.84-5.556-8.545z"/>
              </svg>
              <span>Android</span>
            </div>
            <RichText as="p" className="step-desc" html={t('step1.androidDesc')} />
            <div className="download-row">
              <a className="download-btn" href="https://play.google.com/store/apps/details?id=com.microagi.android" target="_blank" rel="noopener noreferrer">
                <svg className="store-icon" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
                  <path d="M17.523 15.341c-.5 0-.91-.41-.91-.91 0-.5.41-.91.91-.91s.91.41.91.91-.41.91-.91.91zm-11.046 0c-.5 0-.91-.41-.91-.91 0-.5.41-.91.91-.91s.91.41.91.91-.41.91-.91.91zm11.372-6.034l1.815-3.143a.376.376 0 0 0-.137-.513.375.375 0 0 0-.513.137l-1.838 3.183a11.43 11.43 0 0 0-9.554 0L5.784 5.788a.375.375 0 0 0-.513-.137.375.375 0 0 0-.137.513L6.95 9.307C3.831 11.012 1.7 14.193 1.395 17.852h21.21c-.305-3.659-2.435-6.84-5.556-8.545z"/>
                </svg>
                <span>
                  <span className="store-small">{t('step1.androidBtnSmall')}</span>
                  <span className="store-big">{t('step1.androidBtnBig')}</span>
                </span>
              </a>
              <div className="download-qr" aria-hidden="true">
                <img src="/images/qr/google-play.svg" alt="" width="132" height="132" loading="lazy" />
                <span>{t('step1.qrHint')}</span>
              </div>
            </div>
            <EligiblePhones platform="android" />
          </div>
        </div>
      </div>
    </div>
  );
};

/* The guide's step 2 opens with "unpack the headstrap", which quietly assumes the
   reader already owns one. Anyone who pressed "Rozpocznij" on the site without the
   hardware reached that line and had nowhere to go — substep.a.body mentions the
   shop but never linked to it. This is that missing branch, and it splits the same
   way the shop section on the site does: a private individual buys, a business
   does not.

   The cart permalink is duplicated from src/store.ts on purpose: this guide is a
   standalone static app with no bundler and no import path into src/. If the
   variant id ever changes, both files have to move together — hence the note in
   both. utm_medium=onboarding so orders from here can be told apart. */
const STORE_URL = 'https://shiftpl.myshopify.com/cart/58636147229056:1?utm_source=joinshift.pl&utm_medium=onboarding&utm_campaign=opaska';
const BIZ_URL = 'https://joinshift.pl/partner/partnerships';

// Where the final CTA sends the reader. Same address src/ApplyWizard.tsx and
// api/sign-up.js use; it fans out to the right store from the device itself.
const APP_URL = 'https://go-shift.app/';

const NoStrap = () => {
  const { t } = useI18n();
  return (
    <aside className="nostrap">
      <div className="nostrap-body">
        <h4 className="nostrap-title">{t('nostrap.title')}</h4>
        <RichText as="p" className="nostrap-line" html={t('nostrap.solo')} />
        <RichText as="p" className="nostrap-line" html={t('nostrap.biz')} />
      </div>
      <div className="nostrap-actions">
        <a
          className="nostrap-btn nostrap-btn-buy"
          href={STORE_URL}
          target="_blank"
          rel="noopener noreferrer"
          aria-label={t('nostrap.buyAria')}
        >{t('nostrap.buy')}</a>
        <a className="nostrap-btn" href={BIZ_URL}>{t('nostrap.contact')}</a>
        <p className="nostrap-have">{t('nostrap.have')}</p>
      </div>
    </aside>
  );
};

const substeps = [
  { n: 'a', video: "assets/video/tutorial/unboxing-headstrap.mp4" },
  { n: 'b', video: "assets/video/tutorial/mount-phone.mp4" },
  { n: 'c', video: "assets/video/tutorial/mount-on-head.mp4" },
  { n: 'd', video: "assets/video/tutorial/calibrate-hands.mp4" },
];

const Step2 = () => {
  const { t } = useI18n();
  return (
    <div id="step-2" className="step step-record" data-screen-label="Step 2">
      <div className="step-record-head">
        <div className="step-num">2</div>
        <div>
          <h3 className="step-verb">{t('step2.title')}</h3>
          <p className="step-desc">{t('step2.desc')}</p>
        </div>
      </div>

      <NoStrap />

      <div className="substeps-grid substeps-4">
        {substeps.map((s) => (
          <div key={s.n} className={`substep substep-${s.n}`}>
            <div className="substep-media">
              <LazyVideo src={s.video} />
            </div>
            <div className="substep-letter">2{s.n}</div>
            <h5 className="substep-title">{t(`substep.${s.n}.title`)}</h5>
            <RichText as="p" className="substep-body" html={t(`substep.${s.n}.body`)} />
          </div>
        ))}
      </div>
    </div>
  );
};

// The partner code every Poland recruit types into the app. One constant, read
// by both the code card and the caption below the second phone, so the code can
// never be right in one place and stale in the other. It is deliberately NOT in
// i18n.js: it is not translatable copy, and four locale copies of it would be
// four chances to update three of them.
const PARTNER_CODE = 'DTEWS2';

// Locale strings write `{code}` where the code belongs; this swaps in the real
// one at render time. Translators never retype the code, so no locale can drift.
const withCode = (html) => String(html || '').replace(/\{code\}/g, `<em>${PARTNER_CODE}</em>`);

// Shows the code big enough to read at arm's length, with a copy button — the
// visitor is on a phone and has to retype it into the Shift app, so copying
// beats transcribing. navigator.clipboard is absent on http:// and older
// in-app browsers, hence the execCommand fallback and the silent give-up: a
// failed copy still leaves the code on screen to read, which is the real point.
const PartnerCodeCard = () => {
  const { t } = useI18n();
  const [copied, setCopied] = React.useState(false);

  React.useEffect(() => {
    if (!copied) return;
    const id = window.setTimeout(() => setCopied(false), 2000);
    return () => window.clearTimeout(id);
  }, [copied]);

  const copy = () => {
    const done = () => setCopied(true);
    if (navigator.clipboard && navigator.clipboard.writeText) {
      navigator.clipboard.writeText(PARTNER_CODE).then(done, fallback);
    } else {
      fallback();
    }
    function fallback() {
      try {
        const ta = document.createElement('textarea');
        ta.value = PARTNER_CODE;
        ta.setAttribute('readonly', '');
        ta.style.position = 'fixed';
        ta.style.opacity = '0';
        document.body.appendChild(ta);
        ta.select();
        document.execCommand('copy');
        document.body.removeChild(ta);
        done();
      } catch (e) { /* code stays readable on screen */ }
    }
  };

  return (
    <div className="partner-code">
      <div className="partner-code-main">
        <span className="partner-code-label">{t('step3.code.label')}</span>
        <code className="partner-code-value">{PARTNER_CODE}</code>
      </div>
      <button
        type="button"
        className={`partner-code-copy ${copied ? 'copied' : ''}`}
        onClick={copy}
        aria-live="polite"
      >
        {copied ? t('step3.code.copied') : t('step3.code.copy')}
      </button>
      <p className="partner-code-hint">{t('step3.code.hint')}</p>
    </div>
  );
};

const Step3 = () => {
  const { t } = useI18n();
  return (
    <div id="step-3" className="step step-signin" data-screen-label="Step 3">
      <div className="step-record-head">
        <div className="step-num">3</div>
        <div>
          <h3 className="step-verb">{t('step3.title')}</h3>
          <RichText as="p" className="step-desc" html={withCode(t('step3.desc'))} />
        </div>
      </div>

      <PartnerCodeCard />

      <div className="signin-flow">
        <div className="signin-card">
          <div className="signin-card-num">1</div>
          <div className="signin-phone">
            <img src="assets/img/app/partner-code-empty.png" alt={t('step3.card1.alt')} />
          </div>
          <div className="signin-caption">
            <strong>{t('step3.card1.title')}</strong>
            <span>{t('step3.card1.text')}</span>
          </div>
        </div>

        <div className="signin-arrow" aria-hidden="true">
          <svg viewBox="0 0 40 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
            <path d="M4 12h32M28 4l8 8-8 8"/>
          </svg>
        </div>

        <div className="signin-card">
          <div className="signin-card-num">2</div>
          <div className="signin-phone">
            <img src="assets/img/app/partner-code-empty.png" alt={t('step3.card2.alt')} />
          </div>
          <div className="signin-caption">
            <strong>{t('step3.card2.title')}</strong>
            <RichText as="span" html={withCode(t('step3.card2.text'))} />
          </div>
        </div>

        <div className="signin-arrow" aria-hidden="true">
          <svg viewBox="0 0 40 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
            <path d="M4 12h32M28 4l8 8-8 8"/>
          </svg>
        </div>

        <div className="signin-card">
          <div className="signin-card-num">3</div>
          <div className="signin-phone">
            <img src="assets/img/app/recording-tasks.png" alt={t('step3.card3.alt')} />
          </div>
          <div className="signin-caption">
            <strong>{t('step3.card3.title')}</strong>
            <RichText as="span" html={t('step3.card3.text')} />
          </div>
        </div>
      </div>

      <div className="signin-note">
        <svg className="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><circle cx="12" cy="12" r="10"/><path d="M12 16v-4M12 8h.01"/></svg>
        <RichText as="span" html={withCode(t('step3.note'))} />
      </div>
    </div>
  );
};

const Step4 = () => {
  const { t } = useI18n();
  return (
    <div id="step-4" className="step step-record" data-screen-label="Step 4">
      <div className="step-record-head">
        <div className="step-num">4</div>
        <div>
          <h3 className="step-verb">{t('step4.title')}</h3>
          <RichText as="p" className="step-desc" html={t('step4.desc')} />
        </div>
      </div>

      <div className="rules-row">
        <div className="rule rule-illustrated">
          <div className="rule-media">
            <img src="assets/img/tutorial/hands-in-picture.png" alt={t('step4.ruleA.alt')} />
          </div>
          <div className="rule-head">
            <div className="rule-num">A</div>
            <div className="rule-text">
              <h5>{t('step4.ruleA.title')}</h5>
              <p>{t('step4.ruleA.body')}</p>
            </div>
          </div>
        </div>
        <div className="rule rule-illustrated">
          <div className="rule-media rule-media-diagram">
            <img src="assets/img/tutorial/phone-angle-20deg.png" alt={t('step4.ruleB.alt')} />
          </div>
          <div className="rule-head">
            <div className="rule-num">B</div>
            <div className="rule-text">
              <h5>{t('step4.ruleB.title')}</h5>
              <RichText as="p" html={t('step4.ruleB.body')} />
            </div>
          </div>
        </div>
      </div>

      <div className="reel-heading good">
        <span>{t('step4.goodHeading')}</span>
        <span className="reel-pill">
          <svg className="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="3" strokeLinecap="round" strokeLinejoin="round"><path d="M20 6L9 17l-5-5"/></svg>
          {t('step4.goodPill')}
        </span>
      </div>
      <div className="reel-grid">
        <div className="reel-clip">
          <LazyVideo src="assets/video/annotated/vegetable-prep.webm" />
          <span className="clip-label good">{t('step4.labelGood')}</span>
          <div className="clip-caption">{t('step4.good1')}</div>
        </div>
        <div className="reel-clip">
          <LazyVideo src="assets/video/annotated/metallic-assembly.webm" />
          <span className="clip-label good">{t('step4.labelGood')}</span>
          <div className="clip-caption">{t('step4.good2')}</div>
        </div>
        <div className="reel-clip">
          <LazyVideo src="assets/video/annotated/folding-clothing.webm" />
          <span className="clip-label good">{t('step4.labelGood')}</span>
          <div className="clip-caption">{t('step4.good3')}</div>
        </div>
      </div>

      <div className="reel-heading bad">
        <span>{t('step4.badHeading')}</span>
        <span className="reel-pill">
          <svg className="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="3" strokeLinecap="round" strokeLinejoin="round"><path d="M18 6L6 18M6 6l12 12"/></svg>
          {t('step4.badPill')}
        </span>
      </div>
      <div className="reel-grid">
        <div className="reel-clip">
          <LazyVideo src="assets/video/tutorial/bad-hands-not-visible.mp4" />
          <span className="clip-label bad">{t('step4.labelBad')}</span>
          <div className="clip-caption">{t('step4.bad1')}</div>
        </div>
        <div className="reel-clip">
          <LazyVideo src="assets/video/tutorial/bad-staged-task.mp4" />
          <span className="clip-label bad">{t('step4.labelBad')}</span>
          <div className="clip-caption">{t('step4.bad2')}</div>
        </div>
        <div className="reel-clip">
          <LazyVideo src="assets/video/tutorial/bad-irrelevant-task.mp4" />
          <span className="clip-label bad">{t('step4.labelBad')}</span>
          <div className="clip-caption">{t('step4.bad3')}</div>
        </div>
      </div>
    </div>
  );
};

const ScoreExplainer = () => {
  const { t } = useI18n();
  return (
    <section id="score-explainer" className="score-explainer" data-screen-label="About your score">
      <div className="score-kicker">
        <span className="score-kicker-dot" />
        {t('score.kicker')}
      </div>
      <h2 className="score-title">
        {t('score.title')}
      </h2>
      <p className="score-lede">
        {t('score.lede')}
      </p>

      <div className="score-preview">
        <div className="score-preview-phone">
          <img src="assets/img/app/recording-tasks.png" alt={t('score.previewAlt')} />
        </div>
        <div className="score-preview-copy">
          <div className="score-preview-kicker">{t('score.previewKicker')}</div>
          <h3>{t('score.previewTitle')}</h3>
          <ul className="score-preview-list">
            <li>
              <span className="score-chip score-chip-good">96%</span>
              <span>{t('score.item1')}</span>
            </li>
            <li>
              <span className="score-chip score-chip-neutral">-</span>
              <span>{t('score.item2')}</span>
            </li>
            <li>
              <span className="score-chip score-chip-good">{t('score.chipAvg')}</span>
              <span>{t('score.item3')}</span>
            </li>
          </ul>
        </div>
      </div>

      <div className="score-factors-head">
        <h3>{t('score.factorsTitle')}</h3>
        <p>{t('score.factorsSub')}</p>
      </div>

      <div className="score-columns">
        <div className="score-col score-col-good">
          <div className="score-col-head">
            <svg className="score-col-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
              <path d="M7 13l3 3 7-7"/>
            </svg>
            <span>{t('score.goodHead')}</span>
          </div>
          <ul className="score-tiles">
            <li className="score-tile">
              <div className="score-tile-icon score-tile-icon-good">
                <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
                  <circle cx="12" cy="12" r="4"/>
                  <path d="M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M6.34 17.66l-1.41 1.41M19.07 4.93l-1.41 1.41"/>
                </svg>
              </div>
              <div>
                <h5>{t('score.good1.title')}</h5>
                <p>{t('score.good1.body')}</p>
              </div>
            </li>
            <li className="score-tile">
              <div className="score-tile-icon score-tile-icon-good">
                <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
                  <path d="M9 11V6a2 2 0 1 1 4 0v5M13 11V4a2 2 0 1 1 4 0v7M17 11V6a2 2 0 1 1 4 0v8a7 7 0 0 1-7 7h-2a6 6 0 0 1-6-6v-4l-1.5-1.5a2 2 0 0 1 2.83-2.83L9 11"/>
                </svg>
              </div>
              <div>
                <h5>{t('score.good2.title')}</h5>
                <p>{t('score.good2.body')}</p>
              </div>
            </li>
            <li className="score-tile">
              <div className="score-tile-icon score-tile-icon-good">
                <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
                  <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/>
                  <path d="M14 2v6h6M9 13l2 2 4-4"/>
                </svg>
              </div>
              <div>
                <h5>{t('score.good3.title')}</h5>
                <p>{t('score.good3.body')}</p>
              </div>
            </li>
          </ul>
        </div>

        <div className="score-col score-col-bad">
          <div className="score-col-head">
            <svg className="score-col-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
              <path d="M18 6L6 18M6 6l12 12"/>
            </svg>
            <span>{t('score.badHead')}</span>
          </div>
          <ul className="score-tiles">
            <li className="score-tile">
              <div className="score-tile-icon score-tile-icon-bad">
                <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
                  <path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/>
                </svg>
              </div>
              <div>
                <h5>{t('score.bad1.title')}</h5>
                <p>{t('score.bad1.body')}</p>
              </div>
            </li>
            <li className="score-tile">
              <div className="score-tile-icon score-tile-icon-bad">
                <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
                  <path d="M1 1l22 22"/>
                  <path d="M9.53 9.53A3 3 0 0 0 12 15a3 3 0 0 0 2.47-1.47M17 17a10 10 0 0 1-5 1c-6 0-10-6-10-6a15 15 0 0 1 3.5-4.5M9 4.55A10 10 0 0 1 12 4c6 0 10 6 10 6a15 15 0 0 1-1.5 2.3"/>
                </svg>
              </div>
              <div>
                <h5>{t('score.bad2.title')}</h5>
                <p>{t('score.bad2.body')}</p>
              </div>
            </li>
            <li className="score-tile">
              <div className="score-tile-icon score-tile-icon-bad">
                <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
                  <circle cx="12" cy="12" r="10"/>
                  <path d="M12 6v6l4 2"/>
                </svg>
              </div>
              <div>
                <h5>{t('score.bad3.title')}</h5>
                <p>{t('score.bad3.body')}</p>
              </div>
            </li>
          </ul>
        </div>
      </div>
    </section>
  );
};

const Step5 = () => {
  const { t } = useI18n();
  return (
    <div id="step-5" className="step" data-screen-label="Step 5">
      <div className="step-num">5</div>
      <div className="step-body">
        <h3 className="step-verb">{t('step5.title')}</h3>
        <RichText as="p" className="step-desc" html={t('step5.desc')} />
        <div className="step-video-wrap">
          <LazyVideo src="assets/video/tutorial/end-video.mp4" />
        </div>
      </div>
    </div>
  );
};

/* How the money actually reaches you — the step the guide never had.
   The guide takes someone from installing the app to ending a session and
   getting a score, then stops. "You get paid per accepted hour" is on the
   marketing site; what a person without a registered business wants to know is
   whether they can take the money at all, and that answer has a name here.
   Sits after the score section because that is the order it happens in:
   record, get scored, get paid.

   The logo file is shared with the main site (/images/partners/) rather than
   copied into assets/ — same origin, one file to replace. It is not in the repo
   yet; on a failed load this falls back to the word in the page's own type
   rather than showing a broken image. See docs/partner-logos.md. */
const PayoutSection = () => {
  const { t } = useI18n();
  const [markFailed, setMarkFailed] = React.useState(false);
  const rows = ['row1', 'row2', 'row3'];
  return (
    <section id="payout" className="payout" data-screen-label="Payout">
      <div className="payout-kicker">
        <span className="payout-kicker-dot" />
        {t('payout.kicker')}
      </div>
      <h2 className="payout-title">{t('payout.title')}</h2>
      <RichText as="p" className="payout-lede" html={t('payout.lede')} />

      <div className="payout-card">
        <div className="payout-card-head">
          <span className="payout-card-cap">{t('payout.cardCap')}</span>
          {markFailed ? (
            <span className="payout-mark-text">Useme</span>
          ) : (
            <img
              className="payout-mark"
              src="/images/partners/useme-logo.svg"
              alt="Useme"
              loading="lazy"
              decoding="async"
              onError={() => setMarkFailed(true)}
            />
          )}
        </div>
        <ul className="payout-list">
          {rows.map((r) => (
            <li key={r}>
              <svg className="payout-tick" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round"><path d="M7 13l3 3 7-7"/></svg>
              <RichText as="span" html={t(`payout.${r}`)} />
            </li>
          ))}
        </ul>
        {/* Inside the card, not in the note below it: this is the line that
            changes what the payout figure means, and the 29 zl floor bites
            hardest on the small first payouts a new operator sees. Fine print
            is exactly what it must not look like. */}
        <RichText as="p" className="payout-fee" html={t('payout.fee')} />
        {/* Rows are PER SETTLEMENT, and that is a correctness fix rather than a
            preference. The previous version labelled them monthly earnings and
            read the fee straight off the monthly total — which only holds while
            every single settlement clears 372 zł. At the old 10-hour threshold
            it did. At 5 hours it does not: 1 000 zł earned as four weekly
            settlements of 250 zł meets the 29 zł floor four times, so 116 zł
            and 11,6% — not the 78 zł and 7,8% a monthly reading would show.
            Per settlement the arithmetic is exact: max(29, 7,8%), capped at 349.
            The monthly cap is stated in the note instead of being folded into
            rows it would misprice.
            Amounts and not hours: the hourly rate is an "up to" figure tied to
            the quality coefficient, so hours-to-zloty would overpromise. */}
        <div className="payout-scale">
          <div className="payout-scale-head">{t('payout.scaleTitle')}</div>
          <table className="payout-scale-table">
            <thead>
              <tr>
                <th>{t('payout.colAmount')}</th>
                <th>{t('payout.colFee')}</th>
                <th>{t('payout.colShare')}</th>
              </tr>
            </thead>
            <tbody>
              {[['250 zł', '29 zł', '11,6%'],
                ['372 zł', '29 zł', '7,8%'],
                ['500 zł', '39 zł', '7,8%'],
                ['1 000 zł', '78 zł', '7,8%'],
                ['4 474 zł', '349 zł', '7,8%']].map((r) => (
                <tr key={r[0]}>
                  <td>{r[0]}</td><td>{r[1]}</td><td>{r[2]}</td>
                </tr>
              ))}
            </tbody>
          </table>
          <RichText as="p" className="payout-scale-note" html={t('payout.scaleNote')} />
        </div>
      </div>

      <RichText as="p" className="payout-note" html={t('payout.note')} />
    </section>
  );
};

const Collage = () => {
  const { t } = useI18n();
  const reelRef = React.useRef(null);
  const items = [
    { video: "assets/video/b-roll/poland/0428.mp4",   poster: "assets/video/b-roll/poland/posters/0428.jpg" },
    { video: "assets/video/b-roll/poland/0503_6.mp4", poster: "assets/video/b-roll/poland/posters/0503_6.jpg" },
    { video: "assets/video/b-roll/poland/0503_2.mp4", poster: "assets/video/b-roll/poland/posters/0503_2.jpg" },
    { video: "assets/video/b-roll/poland/0503_3.mp4", poster: "assets/video/b-roll/poland/posters/0503_3.jpg" },
    { video: "assets/video/b-roll/poland/0503_4.mp4", poster: "assets/video/b-roll/poland/posters/0503_4.jpg" },
    { video: "assets/video/b-roll/poland/0503_5.mp4", poster: "assets/video/b-roll/poland/posters/0503_5.jpg" },
  ];

  const [activeIndex, setActiveIndex] = React.useState(0);

  const goTo = (newIndex) => {
    const el = reelRef.current;
    if (!el) return;
    const card = el.children[newIndex];
    if (!card) return;
    el.scrollTo({ left: card.offsetLeft, behavior: 'smooth' });
    setActiveIndex(newIndex);
  };

  const next = () => goTo((activeIndex + 1) % items.length);
  const prev = () => goTo((activeIndex - 1 + items.length) % items.length);

  return (
    <section id="people" className="reel-section" data-screen-label="People using Shift">
      <div className="reel-section-head">
        <div className="tag">{t('collage.tag')}</div>
        <h3>{t('collage.title')}</h3>
        <p>{t('collage.sub')}</p>
      </div>

      <div className="people-reel-wrap">
        <button className="reel-nav prev" onClick={prev} aria-label={t('collage.prev')}>
          <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M15 18l-6-6 6-6"/></svg>
        </button>
        <div className="people-reel" ref={reelRef}>
          {items.map((it, i) => (
            <figure
              key={i}
              className={`people-reel-card ${activeIndex === i ? 'active' : ''}`}
              onClick={() => goTo(i)}
              role="button"
              tabIndex={0}
              onKeyDown={(e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); goTo(i); } }}
            >
              <LazyVideo src={it.video} poster={it.poster} />
              <figcaption>
                <div className="task">{t(`collage.${i + 1}.task`)}</div>
                <div className="loc">{t(`collage.${i + 1}.loc`)}</div>
              </figcaption>
            </figure>
          ))}
        </div>
        <button className="reel-nav next" onClick={next} aria-label={t('collage.next')}>
          <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M9 18l6-6-6-6"/></svg>
        </button>
      </div>
    </section>
  );
};

const privacyItemKeys = ['privacy.item1', 'privacy.item2', 'privacy.item3', 'privacy.item4', 'privacy.item5', 'privacy.item6'];
const faqKeys = ['faq.1', 'faq.2', 'faq.3', 'faq.4', 'faq.5'];

const Privacy = () => {
  const { t } = useI18n();
  const [open, setOpen] = React.useState(0);
  return (
    <section id="privacy" className="privacy-section" data-screen-label="Privacy and FAQ">
      <div className="privacy-inner">
        <div>
          <div className="privacy-tag">{t('privacy.tag')}</div>
          <RichText as="h3" className="privacy-claim" html={t('privacy.claim')} />
          <div className="privacy-video">
            <LazyVideo src="assets/video/annotated/washing-jar.webm" />
          </div>
          <ul className="privacy-list">
            {privacyItemKeys.map((k) => (
              <li key={k}><div><strong>{t(`${k}.title`)}</strong>{t(`${k}.body`)}</div></li>
            ))}
          </ul>
        </div>

        <div>
          <div className="privacy-tag">{t('privacy.faqTag')}</div>
          <div className="faq-head">{t('privacy.faqHead')}</div>
          <div className="faq">
            {faqKeys.map((k, i) => (
              <div key={k} className={`faq-item ${open === i ? 'open' : ''}`} onClick={() => setOpen(open === i ? -1 : i)}>
                <div className="faq-q">
                  <span>{t(`${k}.q`)}</span>
                  <span className="faq-plus" />
                </div>
                <div className="faq-a">{t(`${k}.a`)}</div>
              </div>
            ))}
          </div>
        </div>
      </div>

      <div className="final-cta" data-screen-label="Final CTA">
        <div style={{ display: 'inline-flex', alignItems: 'center', gap: 10, fontSize: 13, letterSpacing: '0.18em', textTransform: 'uppercase', opacity: 0.9 }}>
          <span className="tag-dot" style={{ background: '#fff' }} />
          <span>{t('cta.kicker')}</span>
        </div>
        <h2>{t('cta.title')}</h2>
        <p>{t('cta.body')}</p>
        {/* Was a bare <button> with no onClick and no href — the guide's last
            instruction ("open the app") did nothing at all when clicked. Same
            destination as ApplyWizard.tsx and api/sign-up.js use. */}
        <a className="pill-cta" href={APP_URL} target="_blank" rel="noopener noreferrer">
          {t('cta.button')}
          <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path d="M5 12h14M13 5l7 7-7 7"/></svg>
        </a>
      </div>
    </section>
  );
};

window.LangContext = LangContext;
Object.assign(window, { Hero, Intro, Step1, Step2, Step3, Step4, ScoreExplainer, Step5, PayoutSection, Collage, Privacy });
