// While² — landing page marketing sections
// Sans-serif (Geist) marketing chrome a la openclaw.ai. In-app screen
// internals stay DotGothic16 monospace.

const _C = '#5cd7ff';
const _INK = '#e8e6df';
const _DIM = 'rgba(232,230,223,0.6)';
const _FAINT = 'rgba(232,230,223,0.32)';
const _HAIR = 'rgba(232,230,223,0.12)';
const _SANS = '"Geist", "Inter", -apple-system, BlinkMacSystemFont, system-ui, sans-serif';
const _MONO = '"Geist Mono", "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, monospace';

// Inject one-time dolphin float keyframes
if (typeof document !== 'undefined' && !document.getElementById('w2-dolphin-anim')) {
  const s = document.createElement('style');
  s.id = 'w2-dolphin-anim';
  s.textContent = `
    @keyframes w2-dolphin-float {
      0%   { transform: translateY(0) rotate(0deg); }
      25%  { transform: translateY(-8px) rotate(-2deg); }
      50%  { transform: translateY(-14px) rotate(0deg); }
      75%  { transform: translateY(-8px) rotate(2deg); }
      100% { transform: translateY(0) rotate(0deg); }
    }
    @keyframes w2-cmd-fade {
      0%   { opacity: 0; transform: translateY(6px); }
      100% { opacity: 1; transform: translateY(0); }
    }
    @keyframes w2-tab-progress {
      0%   { transform: scaleX(0); }
      100% { transform: scaleX(1); }
    }
    @keyframes w2-shout-shake {
      0%, 100% { transform: translateX(0); }
      25% { transform: translateX(-1.5px); }
      75% { transform: translateX(1.5px); }
    }
    .w2-dolphin-float { animation: w2-dolphin-float 4.2s ease-in-out infinite; }
    /* multi-part dolphin — independent loops per limb, kept subtle */
    @keyframes w2-fin-flap {
      0%, 100% { transform: rotate(-0.8deg); }
      50%      { transform: rotate(2.5deg); }
    }
    @keyframes w2-tail-sway {
      0%, 100% { transform: rotate(0.8deg); }
      50%      { transform: rotate(-1.6deg); }
    }
    @keyframes w2-right-wave {
      0%, 100% { transform: translateY(0) rotate(0deg); }
      50%      { transform: translateY(-1px) rotate(0.8deg); }
    }
    .w2-dol-part { position: absolute; inset: 0; width: 100%; height: 100%; }
    /* group node = body float (everyone follows) */
    .w2-dol-group { position: relative; width: 100%; height: 100%; animation: w2-dolphin-float 4.2s ease-in-out infinite; }
    /* per-part overlays animate ON TOP of the group transform */
    .w2-dol-tail { animation: w2-tail-sway 4.6s ease-in-out infinite; transform-origin: 70% 60%; }
    .w2-dol-fin  { animation: w2-fin-flap  3.6s ease-in-out infinite; transform-origin: 30% 80%; }
    .w2-dol-right{ animation: w2-right-wave 5s ease-in-out infinite; transform-origin: 30% 50%; }
    @keyframes w2-phrase-fade {
      0%   { opacity: 0; transform: translateY(4px); }
      12%  { opacity: 1; transform: translateY(0); }
      88%  { opacity: 1; transform: translateY(0); }
      100% { opacity: 0; transform: translateY(-4px); }
    }

    /* ── Responsive (≤ 720px) ───────────────────────────── */
    @media (max-width: 720px) {
      .w2-hero { padding: 64px 20px 28px !important; }
      .w2-section-pad { padding: 16px 32px 56px !important; }
      .w2-section-pad-y { padding: 56px 0 !important; }
      .w2-section-pad-y > div { padding: 0 32px !important; }
      .w2-download { padding: 56px 32px 96px !important; }
      .w2-cmd-body { grid-template-columns: 1fr !important; }
      .w2-cmd-screen { border-left: none !important; border-top: 1px solid rgba(232,230,223,0.12) !important; min-height: 320px !important; }
      .w2-cmd-block .w2-cmd-body > div:first-child { padding: 22px 20px !important; }
      .w2-footer-grid { grid-template-columns: 1fr !important; }
      .w2-footer-socials { justify-content: flex-start !important; }
    }

    @media (max-width: 480px) {
      .w2-hero { padding: 48px 16px 24px !important; }
      .w2-section-pad { padding: 12px 28px 48px !important; }
      .w2-section-pad-y > div { padding: 0 28px !important; }
      .w2-download { padding: 48px 28px 80px !important; }
    }
  `;
  document.head.appendChild(s);
}

// Patched-line pairs now live in copy.heroPatchedPairs (per-language).

// Layered, animated dolphin. 4 PNG parts stacked by suffix order. The whole
// group floats together (so fins/tail follow the body), and each part adds its
// own subtle micro-motion on top of the group transform.
const Dolphin = ({ size = 64, animate = false, style }) =>
<div style={{
  position: 'relative', width: size, height: size, display: 'inline-block',
  filter: 'drop-shadow(0 4px 24px rgba(92,215,255,0.35))',
  ...style
}}>
  <div className={animate ? 'w2-dol-group' : 'w2-dol-part'} style={animate ? null : { position: 'relative' }}>
    <img src="assets/img/dolphine_01_left.png"  alt="" className={`w2-dol-part ${animate ? 'w2-dol-tail'  : ''}`} />
    <img src="assets/img/dolphine_02_fin.png"   alt="" className={`w2-dol-part ${animate ? 'w2-dol-fin'   : ''}`} />
    <img src="assets/img/dolphine_03_body.png"  alt="" className="w2-dol-part" />
    <img src="assets/img/dolphine_04_right.png" alt="" className={`w2-dol-part ${animate ? 'w2-dol-right' : ''}`} />
  </div>
</div>;


function TopBar() {return null;}

// ── Hero ──────────────────────────────────────────────────────
function Hero({ copy }) {
  const [shout, setShout] = React.useState(false);
  return (
    <section id="top" className="w2-hero" style={{
      position: 'relative', minHeight: 'auto',
      padding: '88px 32px 0',
      display: 'flex', flexDirection: 'column', alignItems: 'center',
      justifyContent: 'center', textAlign: 'center', overflow: 'hidden',
      fontFamily: _SANS
    }}>

      <div
        onMouseEnter={() => setShout(true)}
        onMouseLeave={() => setShout(false)}
        onTouchStart={() => setShout(s => !s)}
        style={{ cursor: 'pointer', marginBottom: 22, lineHeight: 0 }}>
        <Dolphin size={96} animate style={{
          transform: shout ? 'scale(1.18)' : 'scale(1)',
          transition: 'transform 0.25s cubic-bezier(0.2, 0.7, 0.2, 1.4), filter 0.25s ease',
          filter: shout
            ? 'drop-shadow(0 0 18px rgba(255,58,58,0.85)) drop-shadow(0 0 36px rgba(255,58,58,0.5)) drop-shadow(0 4px 20px rgba(255,58,58,0.35))'
            : 'drop-shadow(0 4px 24px rgba(92,215,255,0.35))'
        }} />
      </div>

      {/* OpenClaw-style wordmark — heavy sans, horizontal cyan gradient */}
      <h1 style={{
        fontFamily: _SANS, fontWeight: 900,
        fontSize: 'clamp(72px, 9.5vw, 128px)',
        letterSpacing: '-0.04em', lineHeight: 1.05,
        margin: 0, paddingRight: '0.12em',
        background: 'linear-gradient(90deg, #b9f0ff 0%, #5cd7ff 38%, #2196d6 78%, #1f4a6b 100%)',
        WebkitBackgroundClip: 'text', backgroundClip: 'text',
        WebkitTextFillColor: 'transparent', color: 'transparent'
      }}>
        while<span style={{
          fontSize: '0.55em', fontWeight: 900,
          verticalAlign: 'super', marginLeft: '0.06em',
          marginRight: '0.05em',
          lineHeight: 1
        }}>2</span>
      </h1>

      {/* FACT line — cyan gradient, OpenClaw-tagline style */}
      {shout ? (
        <div style={{
          fontFamily: _SANS, fontWeight: 700,
          fontSize: 'clamp(20px, 2.2vw, 30px)',
          letterSpacing: '0.02em',
          marginTop: 8, lineHeight: 1.35,
          color: '#ff3a3a',
          textShadow: '0 0 12px rgba(255,58,58,0.45), 0 0 24px rgba(255,58,58,0.3)',
          animation: 'w2-shout-shake 0.18s ease-in-out infinite'
        }}>
          ALLOW! ALLOW!
        </div>
      ) : (
        <div style={{
          fontFamily: _SANS, fontWeight: 700,
          fontSize: 'clamp(20px, 2.2vw, 30px)',
          letterSpacing: '0.02em',
          marginTop: 8, lineHeight: 1.35,
          maxWidth: 'min(94vw, 720px)',
          background: 'linear-gradient(90deg, #5cd7ff 0%, #2196d6 100%)',
          WebkitBackgroundClip: 'text', backgroundClip: 'text',
          WebkitTextFillColor: 'transparent', color: 'transparent'
        }}>
          {copy.heroFact}
        </div>
      )}

      {/* TARGET line — who it's for */}
      <div style={{
        fontFamily: _SANS, fontWeight: 400,
        fontSize: 'clamp(15px, 1.7vw, 19px)',
        color: _DIM, letterSpacing: '-0.005em',
        marginTop: 40, lineHeight: 1.55,
        maxWidth: 'min(94vw, 960px)'
      }}>
        {copy.heroTarget}
      </div>

    </section>);

}

// ScrambledText: per-char wave-jitter reveal a la ScrambledText.swift
const W2_GLYPH_POOL = '!<>-_\\/[]{}—=+*^?#·░▒▓█';
function useScramble(target, duration = 520) {
  const [out, setOut] = React.useState(target);
  const startRef = React.useRef(0);
  const rafRef = React.useRef(0);
  const targetRef = React.useRef(target);
  const revealsRef = React.useRef([]);

  React.useEffect(() => {
    targetRef.current = target;
    startRef.current = performance.now();
    // per-char reveal time: (i/len*0.55 + rand*0.45) * duration
    const len = target.length;
    revealsRef.current = Array.from({ length: len }, (_, i) =>
    (i / Math.max(1, len) * 0.55 + Math.random() * 0.45) * duration
    );

    const tick = (now) => {
      const t = now - startRef.current;
      let next = '';
      let allDone = true;
      for (let i = 0; i < target.length; i++) {
        const reveal = revealsRef.current[i];
        const ch = target[i];
        if (t >= reveal || ch === ' ') {
          next += ch;
        } else {
          allDone = false;
          // pick a random glyph; refresh ~every frame
          next += W2_GLYPH_POOL[Math.floor(Math.random() * W2_GLYPH_POOL.length)];
        }
      }
      setOut(next);
      if (!allDone) {
        rafRef.current = setTimeout(() => tick(performance.now()), 16);
      } else {
        setOut(target);
      }
    };
    rafRef.current = setTimeout(() => tick(performance.now()), 16);
    return () => clearTimeout(rafRef.current);
  }, [target, duration]);

  return out;
}

function ScrambledSpan({ value, style }) {
  const out = useScramble(value);
  return (
    <span style={{ ...style, fontVariantNumeric: 'tabular-nums' }}>
      {out}
    </span>);

}

function ScrambledPatchedLine({ copy }) {
  const pairs = copy.heroPatchedPairs;
  const tmpl = copy.heroPatchedTemplate;
  const label = copy.heroPatchedLabel;

  const [i, setI] = React.useState(0);
  const measureRef = React.useRef(null);
  const [shift, setShift] = React.useState(0);

  const [isNarrow, setIsNarrow] = React.useState(
    typeof window !== 'undefined' ? window.innerWidth <= 720 : false
  );
  React.useEffect(() => {
    const onResize = () => setIsNarrow(window.innerWidth <= 720);
    window.addEventListener('resize', onResize);
    return () => window.removeEventListener('resize', onResize);
  }, []);

  // reset to a fresh random index whenever language (pairs) changes
  React.useEffect(() => {setI(0);}, [pairs]);

  React.useEffect(() => {
    const id = setInterval(() => {
      setI((prev) => {
        // never repeat the same pair
        let next = prev;
        while (next === prev && pairs.length > 1) {
          next = Math.floor(Math.random() * pairs.length);
        }
        return next;
      });
    }, 5000);
    return () => clearInterval(id);
  }, [pairs]);

  React.useLayoutEffect(() => {
    if (!measureRef.current) return;
    if (isNarrow) { setShift(0); return; }
    const ro = new ResizeObserver(() => {
      const w = measureRef.current.offsetWidth;
      setShift(-w / 2);
    });
    ro.observe(measureRef.current);
    const w = measureRef.current.offsetWidth;
    setShift(-w / 2);
    return () => ro.disconnect();
  }, [i, tmpl, isNarrow]);

  const [v1, v2] = pairs[i % pairs.length];

  // Split the template at __A__ and __B__ so we render scrambled spans inline
  // in the right position regardless of language word order.
  // tmpl examples:
  //   "Remembering to __A__ when __B__."
  //   "__B__ときに __A__ ことを覚えていること。"
  // We tokenize into a list of { kind: 'text'|'A'|'B', val? }.
  const tokens = React.useMemo(() => {
    const out = [];
    const re = /__A__|__B__/g;
    let last = 0,m;
    while ((m = re.exec(tmpl)) !== null) {
      if (m.index > last) out.push({ kind: 'text', val: tmpl.slice(last, m.index) });
      out.push({ kind: m[0] === '__A__' ? 'A' : 'B' });
      last = m.index + m[0].length;
    }
    if (last < tmpl.length) out.push({ kind: 'text', val: tmpl.slice(last) });
    return out;
  }, [tmpl]);

  return (
    <div style={isNarrow ? {
      position: 'static',
      whiteSpace: 'normal',
      textAlign: 'center',
      display: 'block',
      lineHeight: 1.5,
      wordBreak: 'normal',
      overflowWrap: 'break-word'
    } : {
      position: 'absolute', left: '50%', top: 0,
      transform: `translateX(${shift}px)`,
      transition: 'transform 700ms cubic-bezier(0.22, 1, 0.36, 1)',
      whiteSpace: 'nowrap',
      display: 'flex', alignItems: 'baseline', gap: '0.35em'
    }} ref={measureRef}>
      <span style={{ color: _DIM, flex: 'none', fontWeight: 700, marginRight: isNarrow ? '0.35em' : 0 }}>{label}</span>
      <span style={{ color: _DIM, whiteSpace: isNarrow ? 'normal' : 'nowrap' }}>
        {tokens.map((t, ti) => {
          if (t.kind === 'text') {
            // On narrow screens, encourage a break before " when " so the line
            // doesn't overflow horizontally.
            if (isNarrow) {
              const parts = t.val.split(/(\s+when\s+)/);
              return (
                <React.Fragment key={ti}>
                  {parts.map((p, pi) => {
                    if (/^\s+when\s+$/.test(p)) {
                      return <React.Fragment key={pi}>{' '}<wbr />when{' '}</React.Fragment>;
                    }
                    return <span key={pi}>{p}</span>;
                  })}
                </React.Fragment>
              );
            }
            return <span key={ti}>{t.val}</span>;
          }
          const val = t.kind === 'A' ? v1 : v2;
          return (
            <ScrambledSpan key={`${t.kind}-${ti}-${i}`} value={val} style={{ color: _DIM }} />);

        })}
      </span>
    </div>);

}

function RotatingPatchedLine() {
  const [i, setI] = React.useState(0);
  const measureRef = React.useRef(null);
  const [shift, setShift] = React.useState(0);

  React.useEffect(() => {
    const id = setInterval(() => {
      setI((p) => (p + 1) % W2_PHRASES.length);
    }, 3000);
    return () => clearInterval(id);
  }, []);

  // Center the whole line by measuring its width and shifting half-of-difference.
  React.useLayoutEffect(() => {
    if (!measureRef.current) return;
    const ro = new ResizeObserver(() => {
      const w = measureRef.current.offsetWidth;
      setShift(-w / 2);
    });
    ro.observe(measureRef.current);
    const w = measureRef.current.offsetWidth;
    setShift(-w / 2);
    return () => ro.disconnect();
  }, [i]);

  return (
    <div style={{
      position: 'absolute', left: '50%', top: 0,
      transform: `translateX(${shift}px)`,
      transition: 'transform 700ms cubic-bezier(0.22, 1, 0.36, 1)',
      whiteSpace: 'nowrap',
      display: 'flex', alignItems: 'baseline', gap: '0.35em'
    }} ref={measureRef}>
      <span style={{ color: _FAINT, flex: 'none' }}>Patched:</span>
      <span style={{
        position: 'relative', display: 'inline-block',
        whiteSpace: 'nowrap'
      }}>
        <span
          key={i}
          style={{
            display: 'inline-block', color: _INK,
            animation: 'w2-phrase-fade 3000ms ease-in-out'
          }}>
          
          “{W2_PHRASES[i]}”
        </span>
      </span>
    </div>);

}

function RotatingPhrase() {
  const [i, setI] = React.useState(0);
  React.useEffect(() => {
    const id = setInterval(() => {
      setI((p) => (p + 1) % W2_PHRASES.length);
    }, 3000);
    return () => clearInterval(id);
  }, []);
  return (
    <span
      key={i}
      style={{
        display: 'inline-block',
        animation: 'w2-phrase-fade 3000ms ease-in-out',
        color: _INK
      }}>
      
      “{W2_PHRASES[i]}”
    </span>);

}

function AppStoreBtn({ store, copy, primary }) {
  // Coming-soon state — not yet released. Render as a non-interactive div.
  return (
    <div
      aria-disabled="true"
      style={{
        display: 'inline-flex', alignItems: 'center', gap: 12,
        padding: '10px 22px',
        background: '#000',
        border: '1px solid rgba(255,255,255,0.12)',
        borderRadius: 12,
        fontFamily: '-apple-system, BlinkMacSystemFont, "SF Pro Display", "Helvetica Neue", sans-serif',
        color: 'rgba(255,255,255,0.55)',
        boxShadow: '0 4px 14px rgba(0,0,0,0.4)',
        cursor: 'not-allowed',
        opacity: 0.85,
        userSelect: 'none'
      }}>
      {/* Apple logo */}
      <svg viewBox="0 0 24 24" width="28" height="28" fill="rgba(255,255,255,0.55)" 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.03 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>
      <div style={{ textAlign: 'left', lineHeight: 1.05 }}>
        <div style={{
          fontSize: 11,
          fontWeight: 400,
          letterSpacing: '0.02em'
        }}>
          {copy ? copy.comingSoonTo : 'Coming soon to'}
        </div>
        <div style={{
          fontSize: 22, marginTop: 2,
          fontWeight: 600,
          letterSpacing: '-0.02em'
        }}>
          {store === 'ios' ?
          copy ? copy.downloadIos : 'App Store' :
          copy ? copy.downloadMac : 'Mac App Store'}
        </div>
      </div>
    </div>);

}

function SectionHeader({ marker, title, sub }) {
  return (
    <div>
      <div style={{
        fontFamily: _MONO, fontSize: 11, color: _C,
        letterSpacing: '0.04em', marginBottom: 14
      }}>{marker}</div>
      <h2 style={{
        fontFamily: _SANS, fontSize: 'clamp(24px, 3vw, 36px)',
        fontWeight: 600, letterSpacing: '-0.025em', lineHeight: 1.05,
        margin: 0, color: _INK
      }}>{title}</h2>
      {sub ?
      <p style={{
        fontFamily: _SANS, fontSize: 13, color: _DIM,
        margin: '14px 0 0', lineHeight: 1.6, maxWidth: 640
      }}>{sub}</p> :
      null}
    </div>);

}

// ── Use Cases (instant relatability) ──────────────────────────
function UseCasesSection({ copy }) {
  return (
    <section id="use-cases" className="w2-section-pad" style={{
      padding: '40px 32px 24px',
      position: 'relative', fontFamily: _SANS
    }}>
      <div style={{ maxWidth: 1080, margin: '0 auto' }}>
        <SectionHeader title={copy.useCasesTitle} sub={copy.useCasesSub} />

        <div style={{
          marginTop: 36,
          display: 'grid',
          gridTemplateColumns: 'repeat(auto-fit, minmax(160px, 1fr))',
          gap: 12
        }}>
          {copy.useCases.map((item, i) =>
            <div key={i} style={{
              padding: '18px 20px',
              border: `1px solid ${_HAIR}`,
              borderRadius: 10,
              background: 'rgba(4,12,22,0.4)',
              fontFamily: _SANS, fontSize: 15, fontWeight: 500,
              color: _INK, letterSpacing: '-0.005em',
              textAlign: 'center'
            }}>{item}</div>
          )}
        </div>
      </div>
    </section>);

}

// ── Hidden Mode (terminal easter egg via shake) ──────────────
function HiddenModeSection({ copy }) {
  return (
    <section id="hidden-mode" className="w2-section-pad" style={{
      padding: '64px 32px',
      position: 'relative', fontFamily: _SANS
    }}>
      <div style={{ maxWidth: 1080, margin: '0 auto' }}>
        <SectionHeader title={copy.hiddenModeTitle} sub={copy.hiddenModeSub} />

        {/* visual hint — phone with shake motion arrows */}
        <div style={{
          marginTop: 48,
          display: 'flex', justifyContent: 'center',
          alignItems: 'center', gap: 18,
          fontFamily: _MONO, color: _C, fontSize: 14
        }}>
          <span style={{ fontSize: 22, opacity: 0.6 }}>‹</span>
          <PhoneFrame><ScreenshotPair name="list" /></PhoneFrame>
          <span style={{ fontSize: 22, opacity: 0.6 }}>›</span>
        </div>
      </div>
    </section>);

}

// ── Dev Note (personal letter) ───────────────────────────────
function DevNoteSection({ copy }) {
  return (
    <section id="dev-note" className="w2-section-pad" style={{
      padding: '20px 32px 32px',
      position: 'relative', fontFamily: _SANS
    }}>
      <div style={{ maxWidth: 920, margin: '0 auto' }}>
        <h2 style={{
          fontFamily: _SANS, fontSize: 'clamp(24px, 3vw, 36px)',
          fontWeight: 600, letterSpacing: '-0.025em', lineHeight: 1.05,
          margin: 0, color: _INK,
          textAlign: 'center'
        }}>{copy.devNoteTitle}</h2>

        <div style={{
          display: 'flex',
          gap: 40,
          alignItems: 'center',
          flexWrap: 'wrap',
          justifyContent: 'center',
          marginTop: 56
        }}>
          <img
            src="assets/img/daisandenki.jpg"
            alt="daisandenki"
            style={{
              width: 'clamp(160px, 22vw, 220px)',
              height: 'clamp(160px, 22vw, 220px)',
              borderRadius: '50%',
              objectFit: 'cover',
              flexShrink: 0,
              boxShadow: '0 8px 28px rgba(0, 0, 0, 0.45)',
              alignSelf: 'center',
            }}
          />
          <div style={{ flex: '1 1 360px', minWidth: 0, maxWidth: 640 }}>
            {copy.devNote.map((p, i) =>
              <p key={i} style={{
                fontFamily: _SANS, fontSize: 'clamp(13px, 1.2vw, 16px)', color: _DIM,
                margin: i === 0 ? '0' : '10px 0 0',
                lineHeight: 1.7
              }}>{p}</p>
            )}
          </div>
        </div>
      </div>
    </section>);

}

// ── Reference (compact command docs) ─────────────────────────
function ReferenceSection({ copy }) {
  const [active, setActive] = React.useState(0);
  const cmds = copy.refCmds;

  // Below this width we keep the tab+single-phone UX; above it we lay out all
  // three phones in a row so the section doesn't feel sparse on wide screens.
  const [isNarrow, setIsNarrow] = React.useState(
    typeof window !== 'undefined' ? window.innerWidth <= 900 : false
  );
  React.useEffect(() => {
    const onResize = () => setIsNarrow(window.innerWidth <= 900);
    window.addEventListener('resize', onResize);
    return () => window.removeEventListener('resize', onResize);
  }, []);


  return (
    <section id="reference" className="w2-section-pad" style={{
      padding: '0 32px 88px',
      position: 'relative',
      fontFamily: _SANS
    }}>
      <div style={{ maxWidth: 1080, margin: '0 auto' }}>

        {isNarrow ?
        <div style={{ marginTop: 0, position: 'relative' }}>
          {/* prev / next arrows beside the phone */}
          <button onClick={() => setActive((active - 1 + cmds.length) % cmds.length)}
            aria-label="previous"
            style={{
              position: 'absolute', left: -16, top: 180,
              width: 40, height: 40, borderRadius: 999,
              background: 'rgba(0,0,0,0.4)', border: `1px solid ${_HAIR}`,
              color: _INK, fontFamily: _MONO, fontSize: 18, lineHeight: 1,
              cursor: 'pointer', zIndex: 5,
              backdropFilter: 'blur(8px)',
              transition: 'all 0.18s'
            }}>‹</button>
          <button onClick={() => setActive((active + 1) % cmds.length)}
            aria-label="next"
            style={{
              position: 'absolute', right: -16, top: 180,
              width: 40, height: 40, borderRadius: 999,
              background: 'rgba(0,0,0,0.4)', border: `1px solid ${_HAIR}`,
              color: _INK, fontFamily: _MONO, fontSize: 18, lineHeight: 1,
              cursor: 'pointer', zIndex: 5,
              backdropFilter: 'blur(8px)',
              transition: 'all 0.18s'
            }}>›</button>

          {cmds.map((c, i) =>
            <div key={i} style={{
              display: i === active ? 'block' : 'none',
              animation: i === active ? 'w2-cmd-fade 480ms cubic-bezier(0.2, 0.7, 0.2, 1)' : 'none'
            }}>
              <CommandBlock c={c} termPrefix={copy.refTermPrefix} compact />
            </div>
          )}
        </div> :
        // Desktop: all three side by side, no tabs needed.
        <div style={{
          marginTop: 64,
          display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)',
          gap: 32, alignItems: 'start'
        }}>
          {cmds.map((c, i) =>
            <CommandBlock key={i} c={c} termPrefix={copy.refTermPrefix} compact />
          )}
        </div>
        }

        {/* CTA below the showcase */}
        <div style={{
          marginTop: 48,
          display: 'flex', justifyContent: 'center'
        }}>
          <AppStoreBtn store="ios" copy={copy} primary />
        </div>
      </div>
    </section>);

}

function CommandBlock({ c, termPrefix, compact }) {
  // Map the legacy `screen` value to the screenshot name used in /assets/img/screens.
  const screenName =
  c.screen === 'home' ? 'list' :
  c.screen === 'new' ? 'add' :
  'config';
  return (
    <div className="w2-cmd-block" style={{
      display: 'flex', flexDirection: 'column', alignItems: 'center',
      gap: compact ? 32 : 40,
      padding: compact ? '0 8px 16px' : '48px 32px 56px'
    }}>
      {/* phone frame with the matching screenshot */}
      <PhoneFrame><ScreenshotPair name={screenName} /></PhoneFrame>

      {/* copy */}
      <div style={{ maxWidth: 520, textAlign: 'center' }}>
        <div style={{
          fontFamily: _SANS, fontSize: compact ? 18 : 22, color: _INK, fontWeight: 700,
          letterSpacing: '-0.015em', lineHeight: 1.25
        }}>{c.summary}</div>

        <p style={{
          marginTop: 12, fontFamily: _SANS, fontSize: compact ? 12 : 13, color: _DIM,
          lineHeight: 1.65
        }}>{c.detail}</p>
      </div>
    </div>);

}


// Real-device screenshot pair, alternating to mimic the cursor blink.
// Renders directly into the phone-frame's viewport at full image height,
// anchored to the top so the relevant top of the screen stays visible when the
// frame is cropped.
function ScreenshotPair({ name }) {
  const [frame, setFrame] = React.useState(0);
  React.useEffect(() => {
    const id = setInterval(() => setFrame((f) => 1 - f), 530);
    return () => clearInterval(id);
  }, []);
  return (
    <img
      src={`assets/img/screens/${name}-${frame + 1}.png`}
      alt=""
      style={{
        position: 'absolute', top: 0, left: 0,
        width: '100%',
        objectFit: 'cover', objectPosition: 'top',
        display: 'block'
      }} />);

}

function PhoneFrame({ label, children }) {
  // If children is a screenshot, skip the scale-down canvas; img fills the
  // frame directly anchored to the top. Otherwise use the iPhone-resolution
  // canvas (390×844 scaled) that the original JSX screen components expect.
  const isScreenshot = children?.type === ScreenshotPair;
  // Crop the bottom: phone is only ~2/3 visible. Bottom edge fades.
  const PHONE_W = 260;
  const VISIBLE_H = 380;
  const fadeMask = 'linear-gradient(180deg, #000 0, #000 80%, transparent 100%)';
  return (
    <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 16 }}>
      <div style={{
        // drop-shadow follows the masked silhouette so it fades with the phone.
        filter: 'drop-shadow(0 6px 18px rgba(0,0,0,0.55))'
      }}>
        <div style={{
          // outer bezel: silver "metal" frame around the screen.
          width: PHONE_W, height: VISIBLE_H,
          borderRadius: '38px 38px 0 0',
          background: 'linear-gradient(135deg, #d8d8de 0%, #b8b8c0 50%, #9c9ca4 100%)',
          padding: '8px 7px 0 7px',
          position: 'relative',
          boxSizing: 'border-box',
          WebkitMaskImage: fadeMask,
          maskImage: fadeMask
        }}>
          {/* inner screen — black, holds the image */}
          <div style={{
            width: '100%', height: '100%',
            borderRadius: '30px 30px 0 0',
            overflow: 'hidden',
            background: '#000',
            position: 'relative'
          }}>
            {!isScreenshot &&
            <div style={{ position: 'absolute', top: 8, left: '50%', transform: 'translateX(-50%)',
              width: 82, height: 24, background: '#000', borderRadius: 16, zIndex: 50 }} />
            }
            {isScreenshot ?
            children :
            <div style={{ width: '100%', height: 562, transform: 'scale(0.667)',
              transformOrigin: 'top left' }}>
              <div style={{ width: 390, height: 844 }}>{children}</div>
            </div>
            }
          </div>
        </div>
      </div>
      {label ?
      <div style={{ fontFamily: _SANS, fontSize: 12, color: _DIM, fontWeight: 500 }}>{label}</div> :
      null}
    </div>);

}

// ── Mobile (3 phone screens) ──────────────────────────────
function MobileSection({ copy }) {
  // Track viewport — only true mobile width gets the carousel treatment.
  const [isNarrow, setIsNarrow] = React.useState(
    typeof window !== 'undefined' ? window.innerWidth <= 720 : false
  );
  React.useEffect(() => {
    const onResize = () => setIsNarrow(window.innerWidth <= 720);
    window.addEventListener('resize', onResize);
    return () => window.removeEventListener('resize', onResize);
  }, []);

  const screens = [
    <ScreenshotPair name="list" />,
    <ScreenshotPair name="add" />,
    <ScreenshotPair name="config" />,
  ];
  const [idx, setIdx] = React.useState(0);

  // Swipe state — finger drag offset in px, null when not dragging.
  const PHONE_W = 260;
  const SWIPE_THRESHOLD = 50;
  const startX = React.useRef(null);
  const [drag, setDrag] = React.useState(0);
  const [dragging, setDragging] = React.useState(false);

  const onTouchStart = (e) => {
    startX.current = e.touches[0].clientX;
    setDragging(true);
    setDrag(0);
  };
  const onTouchMove = (e) => {
    if (startX.current == null) return;
    setDrag(e.touches[0].clientX - startX.current);
  };
  const onTouchEnd = () => {
    if (Math.abs(drag) > SWIPE_THRESHOLD) {
      const n = screens.length;
      if (drag < 0) setIdx((idx + 1) % n);
      else if (drag > 0) setIdx((idx - 1 + n) % n);
    }
    startX.current = null;
    setDragging(false);
    setDrag(0);
  };

  return (
    <section id="mobile" className="w2-section-pad" style={{
      padding: '88px 32px',
      fontFamily: _SANS
    }}>
      <div style={{ maxWidth: 1080, margin: '0 auto' }}>
        <SectionHeader
          title={copy.mobileTitle}
          sub={copy.mobileSub} />

        {isNarrow ? (
          // Single phone, swipeable carousel
          <div style={{
            marginTop: 48,
            display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 44
          }}>
            <div
              onTouchStart={onTouchStart}
              onTouchMove={onTouchMove}
              onTouchEnd={onTouchEnd}
              onTouchCancel={onTouchEnd}
              style={{
                width: PHONE_W, height: 562,
                position: 'relative',
                // clip horizontally so off-screen phones don't extend the page;
                // keep vertical visible so the phone's drop-shadow can render.
                overflowX: 'clip',
                overflowY: 'visible',
                touchAction: 'pan-y',
                userSelect: 'none'
              }}>
              {screens.map((s, i) => {
                // Wrap-around: place each screen at its shortest signed offset
                // from the active one so the next/prev neighbour visually
                // appears even at the ends of the array.
                const n = screens.length;
                let offset = i - idx;
                if (offset > n / 2) offset -= n;
                else if (offset < -n / 2) offset += n;
                return (
                  <div key={i} style={{
                    position: 'absolute', top: 0, left: 0,
                    width: PHONE_W,
                    transform: `translateX(${offset * PHONE_W + drag}px)`,
                    transition: dragging ? 'none' : 'transform 420ms cubic-bezier(0.2, 0.7, 0.2, 1)',
                    willChange: 'transform'
                  }}>
                    <PhoneFrame>{s}</PhoneFrame>
                  </div>);
              })}
            </div>
            {/* dots */}
            <div style={{ display: 'flex', gap: 8 }}>
              {screens.map((_, i) => (
                <button key={i} onClick={() => setIdx(i)} aria-label={`screen ${i+1}`}
                  style={{
                    width: 7, height: 7, borderRadius: '50%', border: 'none',
                    padding: 0, cursor: 'pointer',
                    background: i === idx ? _C : 'rgba(232,230,223,0.25)',
                    boxShadow: i === idx ? `0 0 8px ${_C}` : 'none',
                    transition: 'background 0.2s, box-shadow 0.2s'
                  }} />
              ))}
            </div>
          </div>
        ) : (
          // Side-by-side (desktop)
          <div className="w2-mobile-row" style={{
            marginTop: 64, display: 'flex', gap: 64,
            justifyContent: 'center', flexWrap: 'wrap'
          }}>
            <PhoneFrame><ScreenshotPair name="list" /></PhoneFrame>
            <PhoneFrame><ScreenshotPair name="add" /></PhoneFrame>
            <PhoneFrame><ScreenshotPair name="config" /></PhoneFrame>
          </div>
        )}
      </div>
    </section>);

}

// ── Download ────────────────────────────────────────────────
function DownloadSection({ copy }) {
  return (
    <section id="download" className="w2-download" style={{
      padding: '140px 32px 160px',
      textAlign: 'center', position: 'relative', overflow: 'hidden',
      fontFamily: _SANS
    }}>
      {/* radial halo behind the section */}
      <div aria-hidden="true" style={{
        position: 'absolute', inset: 0, pointerEvents: 'none',
        background: 'radial-gradient(ellipse 60% 70% at 50% 55%, rgba(92,215,255,0.18) 0%, rgba(33,150,214,0.08) 35%, transparent 70%)'
      }} />
      {/* concentric pulse rings behind the dolphin */}

      <div style={{ maxWidth: 720, margin: '0 auto', position: 'relative' }}>
        {/* price callout — price tag + catchphrase */}
        <div style={{
          display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 14
        }}>
          {/* price */}
          <div style={{
            display: 'flex', alignItems: 'baseline', gap: 8,
            fontFamily: _SANS
          }}>
            <span style={{
              fontSize: 'clamp(36px, 5vw, 56px)',
              fontWeight: 700, letterSpacing: '-0.03em',
              background: 'linear-gradient(135deg, #fce58a 0%, #f0c053 50%, #c98c34 100%)',
              WebkitBackgroundClip: 'text', backgroundClip: 'text',
              WebkitTextFillColor: 'transparent', color: 'transparent'
            }}>{copy.priceTag}</span>
            <span style={{
              fontFamily: _MONO, fontSize: 13,
              color: _FAINT, letterSpacing: '0.04em',
              fontWeight: 400
            }}>{copy.priceTagSuffix}</span>
          </div>
          {/* catchphrase */}
          <div style={{
            fontFamily: _SANS, fontSize: 'clamp(14px, 1.4vw, 17px)',
            color: _INK, fontWeight: 500
          }}>{copy.priceLine}</div>
          {copy.priceMicro &&
          <div style={{
            fontFamily: _MONO, fontSize: 11,
            color: _FAINT, letterSpacing: '0.04em'
          }}>
            {copy.priceMicro.split('@daisandenki').map((seg, i, arr) =>
              <React.Fragment key={i}>
                {seg}
                {i < arr.length - 1 &&
                <a href="https://www.instagram.com/daisandenki" target="_blank" rel="noopener noreferrer"
                  style={{ color: _C, textDecoration: 'none', borderBottom: `1px solid ${_C}55` }}>
                  @daisandenki
                </a>}
              </React.Fragment>
            )}
          </div>
          }
        </div>

        {/* CTA button */}
        <div style={{
          marginTop: 36, display: 'flex', gap: 16,
          justifyContent: 'center', flexWrap: 'wrap',
          position: 'relative'
        }}>
          <AppStoreBtn store="ios" copy={copy} primary />
        </div>
      </div>
    </section>);

}

// ── Footer ────────────────────────────────────────────────────
function Footer({ copy }) {
  const linkStyle = {
    color: 'inherit', textDecoration: 'none',
    borderBottom: `1px solid ${_HAIR}`, paddingBottom: 1,
    transition: 'color 0.15s, border-color 0.15s'
  };
  // copy.footerCopyright looks like "while² · 2026 · daisandenki" — turn the
  // trailing "daisandenki" segment into an X link.
  const parts = copy.footerCopyright.split('daisandenki');
  return (
    <footer style={{
      padding: '48px 32px 48px',
      fontFamily: _MONO, fontSize: 11, color: _FAINT,
      letterSpacing: '0.04em', textAlign: 'center'
    }}>
      <div style={{ maxWidth: 640, margin: '0 auto', lineHeight: 1.7 }}>
        {copy.footerManifesto &&
        <div style={{ fontSize: 10, color: _FAINT }}>
          {copy.footerManifesto}
        </div>
        }
        <div style={{ marginTop: 22, display: 'flex', gap: 10, justifyContent: 'center', alignItems: 'center' }}>
          <a href="support/" className="w2-foot-link" style={linkStyle}>{copy.footerSupport}</a>
          <span style={{ color: _FAINT, paddingBottom: 1 }}>·</span>
          <a href="policy/" className="w2-foot-link" style={linkStyle}>{copy.footerPrivacy}</a>
        </div>
        <div style={{ marginTop: 18 }}>
          {parts[0]}
          <a href="https://www.instagram.com/daisandenki" target="_blank" rel="noopener noreferrer"
          className="w2-foot-link" style={linkStyle}>daisandenki</a>
          {parts[1] || ''}
        </div>
      </div>
    </footer>);

}

// ── Mac (also-on-mac) ──────────────────────────────────────────
function MacSection({ copy }) {
  return (
    <section id="mac" className="w2-section-pad" style={{
      padding: '88px 32px 96px',
      position: 'relative', fontFamily: _SANS
    }}>
      <div style={{ maxWidth: 1080, margin: '0 auto' }}>
        <SectionHeader title={copy.macTitle} sub={copy.macSub} />

        <div style={{ marginTop: 56, position: 'relative' }}>
          <img
            src="assets/img/screens/mac-list.png"
            alt=""
            style={{
              display: 'block', width: '100%', height: 'auto',
              borderRadius: 12,
              boxShadow:
                '0 30px 80px rgba(0,0,0,0.55), ' +
                '0 0 60px rgba(92,215,255,0.08)'
            }} />
        </div>
      </div>
    </section>);

}

// ── AI Says (centered slideshow) ─────────────────────────────
function AISaysSection({ copy }) {
  const cards = copy.aiSays;
  const N = cards.length;
  const [idx, setIdx] = React.useState(0);
  const GAP = 24;

  // Track viewport so we can keep the card narrow enough on phones for the
  // adjacent cards to peek in from both sides.
  const [cardW, setCardW] = React.useState(360);
  React.useEffect(() => {
    const update = () =>
      setCardW(Math.min(360, Math.floor(window.innerWidth * 0.68)));
    update();
    window.addEventListener('resize', update);
    return () => window.removeEventListener('resize', update);
  }, []);
  const STEP = cardW + GAP;

  // Auto-advance: every 6s, move to the next card.
  React.useEffect(() => {
    const id = setInterval(() => setIdx((i) => (i + 1) % N), 6000);
    return () => clearInterval(id);
  }, [N]);

  return (
    <section id="ai-says" className="w2-section-pad-y" style={{
      padding: '48px 0 56px',
      position: 'relative', fontFamily: _SANS,
      overflow: 'hidden'
    }}>
      <div style={{ padding: '0 32px' }}>
        <div style={{ maxWidth: 1080, margin: '0 auto' }}>
          <div style={{ textAlign: 'center' }}>
            <SectionHeader
              title={copy.aiSaysTitle}
              sub={copy.aiSaysSub} />
          </div>

          <div style={{
            marginTop: 32,
            position: 'relative',
            height: 280,
            overflow: 'hidden'
          }}>
            {cards.map((r, i) => {
              // Shortest-path wrap so the active card stays in view and the
              // transition slides seamlessly when wrapping past the ends.
              let offset = i - idx;
              if (offset > N / 2) offset -= N;
              else if (offset < -N / 2) offset += N;
              const isActive = offset === 0;
              const dist = Math.abs(offset);
              return (
                <div key={i} style={{
                  position: 'absolute',
                  left: '50%', top: '50%',
                  width: cardW,
                  transform: `translate(-50%, -50%) translateX(${offset * STEP}px)`,
                  opacity: dist === 0 ? 1 : dist === 1 ? 0.4 : 0,
                  transition: 'transform 1000ms cubic-bezier(0.22, 1, 0.36, 1), opacity 600ms ease',
                  willChange: 'transform'
                }}>
                  <AIReviewCard r={r} active={isActive} />
                </div>);
            })}
          </div>

        </div>
      </div>
    </section>);

}

function AIReviewCard({ r }) {
  return (
    <div className="w2-ai-card" style={{
      width: '100%',
      border: `1px solid ${_HAIR}`,
      borderRadius: 10,
      padding: '24px 22px',
      background: 'rgba(4,12,22,0.4)',
      display: 'flex', flexDirection: 'column', gap: 18,
      boxSizing: 'border-box'
    }}>
      <p style={{
        margin: 0, fontFamily: _SANS, fontSize: 13, color: _INK,
        lineHeight: 1.55, letterSpacing: '-0.005em'
      }}>“{r.review.split(/\*\*([^*]+)\*\*/).map((part, i) =>
        i % 2 === 1 ?
        <span key={i} style={{
          background: 'linear-gradient(135deg, #fce58a 0%, #f0c053 50%, #c98c34 100%)',
          WebkitBackgroundClip: 'text', backgroundClip: 'text',
          WebkitTextFillColor: 'transparent', color: 'transparent',
          fontWeight: 600
        }}>{part}</span> :
        <React.Fragment key={i}>{part}</React.Fragment>
      )}”</p>

      <div style={{
        marginTop: 'auto', paddingTop: 14,
        borderTop: `1px solid ${_HAIR}`
      }}>
        <div style={{
          fontFamily: _SANS, fontSize: 12, fontWeight: 600, color: _INK,
          letterSpacing: '-0.01em'
        }}>{r.model}</div>
        <div style={{
          fontFamily: _MONO, fontSize: 11, color: _FAINT,
          letterSpacing: '0.04em', marginTop: 2
        }}>{r.vendor}</div>
      </div>
    </div>);

}

const W2_LANGS = [
  { code: 'en',      label: 'EN',  name: 'English' },
  { code: 'ja',      label: 'JA',  name: '日本語' },
  { code: 'zh-Hans', label: '中',  name: '简体中文' },
  { code: 'zh-Hant', label: '繁',  name: '繁體中文' },
  { code: 'ko',      label: 'KO',  name: '한국어' },
  { code: 'de',      label: 'DE',  name: 'Deutsch' },
  { code: 'fr',      label: 'FR',  name: 'Français' },
  { code: 'es',      label: 'ES',  name: 'Español' },
  { code: 'pt-BR',   label: 'PT',  name: 'Português' },
  { code: 'it',      label: 'IT',  name: 'Italiano' },
  { code: 'tr',      label: 'TR',  name: 'Türkçe' },
];

function LangSwitch({ lang, setLang }) {
  const [open, setOpen] = React.useState(false);
  const wrapRef = React.useRef(null);

  // Close dropdown on outside click.
  React.useEffect(() => {
    if (!open) return;
    const onDoc = (e) => {
      if (wrapRef.current && !wrapRef.current.contains(e.target)) setOpen(false);
    };
    document.addEventListener('mousedown', onDoc);
    return () => document.removeEventListener('mousedown', onDoc);
  }, [open]);

  const current = W2_LANGS.find((l) => l.code === lang) || W2_LANGS[0];

  return (
    <div ref={wrapRef} style={{
      position: 'fixed', top: 16, right: 16, zIndex: 100,
      fontFamily: _MONO
    }}>
      <button
        onClick={() => setOpen((o) => !o)}
        style={{
          fontFamily: _MONO, fontSize: 11,
          letterSpacing: '0.06em', textTransform: 'uppercase',
          padding: '7px 14px', cursor: 'pointer',
          background: 'rgba(4,12,22,0.7)',
          color: _C,
          border: `1px solid ${_HAIR}`, borderRadius: 8,
          backdropFilter: 'blur(8px)',
          display: 'flex', alignItems: 'center', gap: 6
        }}>
        {current.label}
        <span style={{ fontSize: 8, opacity: 0.7 }}>▾</span>
      </button>
      {open && (
        <div style={{
          position: 'absolute', top: 'calc(100% + 6px)', right: 0,
          minWidth: 160,
          background: 'rgba(4,12,22,0.92)',
          border: `1px solid ${_HAIR}`, borderRadius: 8,
          backdropFilter: 'blur(8px)',
          padding: 4,
          maxHeight: '70vh', overflowY: 'auto'
        }}>
          {W2_LANGS.map((l) =>
            <button key={l.code}
              onClick={() => { setLang(l.code); setOpen(false); }}
              style={{
                width: '100%', textAlign: 'left',
                fontFamily: _MONO, fontSize: 12,
                padding: '8px 12px', cursor: 'pointer',
                background: lang === l.code ? 'rgba(92,215,255,0.12)' : 'transparent',
                color: lang === l.code ? _C : _INK,
                border: 'none', borderRadius: 6,
                display: 'flex', justifyContent: 'space-between', alignItems: 'baseline',
                gap: 12,
                transition: 'background 0.12s'
              }}>
              <span>{l.name}</span>
              <span style={{ opacity: 0.5, fontSize: 10 }}>{l.label}</span>
            </button>
          )}
        </div>
      )}
    </div>);

}

Object.assign(window, {
  TopBar, Hero, UseCasesSection, HiddenModeSection,
  DevNoteSection, ReferenceSection, AISaysSection,
  MobileSection, MacSection, DownloadSection, Footer, LangSwitch
});