// shared components — Hazel mark, brand mark, arrow

const HazelMark = ({ size = 22, color = 'var(--hazel)' }) => (
  <svg width={size} height={size} viewBox="0 0 28 28" fill="none">
    <circle cx="14" cy="14" r="13" stroke={color} strokeWidth="1.2" />
    <path d="M9 19 L19 9 M16 9 L19 9 L19 12" stroke={color} strokeWidth="1.2" strokeLinecap="round" strokeLinejoin="round" />
    <circle cx="9" cy="19" r="1.6" fill={color} />
  </svg>
);

const BrandMark = ({ size = 28, variant = 'light' }) => (
  <img
    src={variant === 'dark' ? 'brand/evenmonth-icon-dark.svg' : 'brand/evenmonth-icon-light.svg'}
    width={size}
    height={size}
    alt="EvenMonth"
    style={{ display: 'block' }}
  />
);

const ArrowSm = ({ size = 14 }) => (
  <svg width={size * 1.4} height={size} viewBox="0 0 20 12" fill="none" className="arrow">
    <path d="M1 6 L17 6 M13 2 L17 6 L13 10" stroke="currentColor" strokeWidth="1.4" strokeLinecap="round" strokeLinejoin="round" />
  </svg>
);

// Live timestamp — "Hazel posted 12 minutes ago", advances while the page is open
const useTimeAgo = (initialMins = 12) => {
  const [mins, setMins] = React.useState(initialMins);
  React.useEffect(() => {
    const id = setInterval(() => setMins((m) => m + 1), 60000);
    return () => clearInterval(id);
  }, []);
  if (mins < 1) return 'just now';
  if (mins < 60) return `${mins} min ago`;
  const hrs = Math.floor(mins / 60);
  return `${hrs}h ago`;
};

const useScrolled = (threshold = 32) => {
  const [scrolled, setScrolled] = React.useState(false);
  React.useEffect(() => {
    const onScroll = () => setScrolled(window.scrollY > threshold);
    window.addEventListener('scroll', onScroll, { passive: true });
    onScroll();
    return () => window.removeEventListener('scroll', onScroll);
  }, [threshold]);
  return scrolled;
};

// Animated count-up. Triggers on first visibility.
const useCountUp = (target, opts = {}) => {
  const { duration = 1400, decimals = 0, start = 0 } = opts;
  const ref = React.useRef(null);
  const [value, setValue] = React.useState(start);
  const triggered = React.useRef(false);
  React.useEffect(() => {
    if (!ref.current || triggered.current) return;
    const obs = new IntersectionObserver(([entry]) => {
      if (entry.isIntersecting && !triggered.current) {
        triggered.current = true;
        const t0 = performance.now();
        const step = (t) => {
          const p = Math.min(1, (t - t0) / duration);
          const eased = 1 - Math.pow(1 - p, 3);
          setValue(start + (target - start) * eased);
          if (p < 1) requestAnimationFrame(step);
        };
        requestAnimationFrame(step);
        obs.disconnect();
      }
    }, { threshold: 0.4 });
    obs.observe(ref.current);
    return () => obs.disconnect();
  }, [target, duration, decimals, start]);
  return [ref, decimals === 0 ? Math.round(value) : value.toFixed(decimals)];
};

// Edition strip + nav
const TopChrome = () => {
  const scrolled = useScrolled();
  const ago = useTimeAgo();
  return (
    <>
      <div className="edition-strip nosel">
        <span>
          <span>EvenMonth · The Daily</span>
          <span>Vol. I · No. 47</span>
          <span>Tuesday Edition · May 31, 2026</span>
        </span>
        <span>
          <span className="live-dot">Hazel posted {ago}</span>
          <span>$2.84B reconciled to date</span>
        </span>
      </div>
      <nav className={'nav' + (scrolled ? ' scrolled' : '')}>
        <a href="#top" className="nav-logo" style={{ textDecoration: 'none', color: 'inherit' }}>
          <BrandMark size={28} />
          <span>EvenMonth</span>
        </a>
        <div className="nav-links">
          <a className="nav-link" href="#how">How it works</a>
          <a className="nav-link" href="#hazel">Hazel</a>
          <a className="nav-link" href="#daily">The Daily</a>
          <a className="nav-link" href="#doctrine">Doctrine</a>
          <a className="nav-link" href="#pricing">Pricing</a>
          <a className="nav-link" href="#faq">FAQ</a>
          <a className="btn btn-ghost" href="#cta" style={{ padding: '8px 14px' }}>Sign in</a>
          <a className="btn btn-primary" href="#cta">Start free <ArrowSm /></a>
        </div>
      </nav>
    </>
  );
};

// Masthead hero
const Masthead = () => (
  <section className="masthead" id="top">
    <div className="masthead-eyebrow reveal">
      <span>An editorial for people who bounced off YNAB</span>
      <span className="dot-sep">·</span>
      <span>Established Tuesday</span>
      <span className="dot-sep">·</span>
      <span>For active money managers</span>
    </div>
    <h1 className="masthead-title reveal">
      The high<br />
      without the <em>grind.</em>
    </h1>
    <p className="masthead-deck reveal">
      <strong>EvenMonth</strong> is a budget for the busy month, the missed week, and the Sunday morning
      you finally sit down. <em>Hazel</em> does the bookkeeping. You make the calls.
    </p>
    <div className="masthead-cta reveal">
      <a className="btn btn-primary" href="#cta">Start 30-day trial <ArrowSm /></a>
      <a className="btn btn-ghost" href="#daily">Read today’s Daily</a>
    </div>
    <p className="reveal" style={{
      marginTop: 22, textAlign: 'center',
      fontFamily: 'IBM Plex Mono, monospace', fontSize: 10.5,
      letterSpacing: '0.16em', textTransform: 'uppercase', color: 'var(--ink-soft)',
    }}>
      No card required for trial · Cancel any Sunday · iOS · Android · Web
    </p>
  </section>
);

// Ticker tape — running stats
const Ticker = () => {
  const items = [
    '$2.84B reconciled across reader ledgers',
    '47,219 subscribers',
    '$18.6M of card debt killed this quarter',
    'Median runway: 2.7 months',
    '0 silent moves — ever',
    'Hazel writes ~6,300 Dailies a morning',
    'Three things today. Nothing tomorrow.',
  ];
  const full = [...items, ...items];
  return (
    <div className="ticker" aria-hidden>
      <div className="ticker-track">
        {full.map((it, i) => (
          <React.Fragment key={i}>
            <span>{it}</span>
            <span className="sep">◆</span>
          </React.Fragment>
        ))}
      </div>
    </div>
  );
};

// Lede strip — three numbers, animated count-up
const LedeCell = ({ label, target, unit, prefix = '', decimals = 0, body }) => {
  const [ref, val] = useCountUp(target, { decimals });
  return (
    <div className="lede-cell reveal">
      <div className="lede-label">{label}</div>
      <div className="lede-value counting" ref={ref}>
        {prefix}{typeof val === 'number' ? val.toLocaleString() : val}
        <span className="unit">{unit}</span>
      </div>
      <p className="lede-explain">{body}</p>
    </div>
  );
};

const Lede = () => (
  <section className="lede">
    <LedeCell
      label="Days ahead"
      target={12}
      unit="days"
      body="Money assigned to a future month. The dollar-to-next-month feeling, reissued every Sunday."
    />
    <LedeCell
      label="Job-loss runway"
      target={3.4}
      decimals={1}
      unit="months"
      body="Essentials covered if work goes sideways. Hazel keeps the number honest as bills shift."
    />
    <LedeCell
      label="Card debt killed"
      target={4180}
      prefix="$"
      unit="this year"
      body="Off the cards on purpose. A weekly contract with future-you, not a chart that shames present-you."
    />
  </section>
);

// Trust strip — partners + press
const Trust = () => (
  <section className="trust reveal">
    <div className="trust-label">
      Connected to 12,000+ banks via Plaid &nbsp;·&nbsp; Secured at rest and in transit
    </div>
    <div className="trust-logos">
      <span className="trust-logo">Wired</span>
      <span className="trust-logo">The Verge</span>
      <span className="trust-logo">Bloomberg</span>
      <span className="trust-logo">The&nbsp;Plain&nbsp;Dealer</span>
      <span className="trust-logo mono">SOC 2 Type II</span>
      <span className="trust-logo mono">256-bit</span>
    </div>
  </section>
);

// Anti-pitch
const Bounced = () => (
  <section className="section" id="how">
    <div className="section-eyebrow reveal"><span>I · The diagnosis</span></div>
    <h2 className="section-title reveal">You bounced off YNAB.<br/><em>We know why.</em></h2>
    <p className="section-dek reveal">
      You loved assigning the first dollar to next month. Then a month got busy, the queue grew
      to three hundred uncategorized transactions, and the app started feeling like homework. None of
      that was a character flaw. It was a design flaw.
    </p>
    <div className="opcols reveal">
      <div className="opcol">
        <div className="opcol-num">i.</div>
        <h3 className="opcol-h">A <em>busy</em> month.</h3>
        <p className="opcol-body">
          Three hundred receipts, four cards, a week of travel. The categorize-every-transaction
          contract assumes a quiet life. Most lives are not quiet.
        </p>
      </div>
      <div className="opcol">
        <div className="opcol-num">ii.</div>
        <h3 className="opcol-h">Catch-up <em>shame.</em></h3>
        <p className="opcol-body">
          “Behind” is a UI state in most budget apps. Open after a missed week and the screen
          tells you what you already know: you fell off. Then asks you to fix it.
        </p>
      </div>
      <div className="opcol">
        <div className="opcol-num">iii.</div>
        <h3 className="opcol-h">Visibility ≠ <em>action.</em></h3>
        <p className="opcol-body">
          Mint told you where it went. You wanted to be told what to decide. A pie chart at the end
          of the month is not the same as a hand on Tuesday morning.
        </p>
      </div>
    </div>
  </section>
);

Object.assign(window, {
  HazelMark, BrandMark, ArrowSm, TopChrome, Masthead, Ticker, Lede, Trust, Bounced,
  useScrolled, useCountUp, useTimeAgo,
});
