/* ==========================================================
   NewsNator — Shared components (React, Babel)
   ========================================================== */

const { useState, useEffect, useMemo, useRef, useLayoutEffect } = React;

// ---------- BrandWordmark (the full opc.how lockup) ---------
function BrandWordmark({ height = 22, color = 'currentColor', accent = '#FF6A00' }) {
  // Native ratio 56:23 ≈ 2.43:1
  const width = Math.round(height * (56/23));
  return (
    <svg width={width} height={height} viewBox="0 0 56 23" fill="none" xmlns="http://www.w3.org/2000/svg" aria-label="opc.how">
      <path d="M19.7484 20.0433C19.0524 20.0433 18.4324 19.9033 17.8884 19.6233C17.3444 19.3353 16.9164 18.9393 16.6044 18.4353C16.3004 17.9313 16.1484 17.3593 16.1484 16.7193C16.1484 16.0713 16.3004 15.4993 16.6044 15.0033C16.9164 14.4993 17.3444 14.1073 17.8884 13.8273C18.4324 13.5393 19.0524 13.3953 19.7484 13.3953C20.4284 13.3953 21.0204 13.5393 21.5244 13.8273C22.0284 14.1073 22.4004 14.5113 22.6404 15.0393L21.1884 15.8193C21.0204 15.5153 20.8084 15.2913 20.5524 15.1473C20.3044 15.0033 20.0324 14.9313 19.7364 14.9313C19.4164 14.9313 19.1284 15.0033 18.8724 15.1473C18.6164 15.2913 18.4124 15.4953 18.2604 15.7593C18.1164 16.0233 18.0444 16.3433 18.0444 16.7193C18.0444 17.0953 18.1164 17.4153 18.2604 17.6793C18.4124 17.9433 18.6164 18.1473 18.8724 18.2913C19.1284 18.4353 19.4164 18.5073 19.7364 18.5073C20.0324 18.5073 20.3044 18.4393 20.5524 18.3033C20.8084 18.1593 21.0204 17.9313 21.1884 17.6193L22.6404 18.4113C22.4004 18.9313 22.0284 19.3353 21.5244 19.6233C21.0204 19.9033 20.4284 20.0433 19.7484 20.0433Z" fill={color}></path>
      <path d="M12.1593 20.0433C11.6153 20.0433 11.1393 19.9233 10.7313 19.6833C10.3233 19.4433 10.0033 19.0793 9.77128 18.5913C9.54728 18.0953 9.43528 17.4713 9.43528 16.7193C9.43528 15.9593 9.54328 15.3353 9.75928 14.8473C9.97528 14.3593 10.2873 13.9953 10.6953 13.7553C11.1033 13.5153 11.5913 13.3953 12.1593 13.3953C12.7673 13.3953 13.3113 13.5353 13.7913 13.8153C14.2793 14.0873 14.6633 14.4713 14.9433 14.9673C15.2313 15.4633 15.3753 16.0473 15.3753 16.7193C15.3753 17.3993 15.2313 17.9873 14.9433 18.4833C14.6633 18.9793 14.2793 19.3633 13.7913 19.6353C13.3113 19.9073 12.7673 20.0433 12.1593 20.0433ZM8.34328 22.2753V13.4913H10.1313V14.8113L10.0953 16.7313L10.2153 18.6393V22.2753H8.34328ZM11.8353 18.5073C12.1473 18.5073 12.4233 18.4353 12.6633 18.2913C12.9113 18.1473 13.1073 17.9433 13.2513 17.6793C13.4033 17.4073 13.4793 17.0873 13.4793 16.7193C13.4793 16.3433 13.4033 16.0233 13.2513 15.7593C13.1073 15.4953 12.9113 15.2913 12.6633 15.1473C12.4233 15.0033 12.1473 14.9313 11.8353 14.9313C11.5233 14.9313 11.2433 15.0033 10.9953 15.1473C10.7473 15.2913 10.5513 15.4953 10.4073 15.7593C10.2633 16.0233 10.1913 16.3433 10.1913 16.7193C10.1913 17.0873 10.2633 17.4073 10.4073 17.6793C10.5513 17.9433 10.7473 18.1473 10.9953 18.2913C11.2433 18.4353 11.5233 18.5073 11.8353 18.5073Z" fill={color}></path>
      <path d="M3.552 20.0433C2.864 20.0433 2.252 19.8993 1.716 19.6113C1.188 19.3233 0.768 18.9313 0.456 18.4353C0.152 17.9313 0 17.3593 0 16.7193C0 16.0713 0.152 15.4993 0.456 15.0033C0.768 14.4993 1.188 14.1073 1.716 13.8273C2.252 13.5393 2.864 13.3953 3.552 13.3953C4.232 13.3953 4.84 13.5393 5.376 13.8273C5.912 14.1073 6.332 14.4953 6.636 14.9913C6.94 15.4873 7.092 16.0633 7.092 16.7193C7.092 17.3593 6.94 17.9313 6.636 18.4353C6.332 18.9313 5.912 19.3233 5.376 19.6113C4.84 19.8993 4.232 20.0433 3.552 20.0433ZM3.552 18.5073C3.864 18.5073 4.144 18.4353 4.392 18.2913C4.64 18.1473 4.836 17.9433 4.98 17.6793C5.124 17.4073 5.196 17.0873 5.196 16.7193C5.196 16.3433 5.124 16.0233 4.98 15.7593C4.836 15.4953 4.64 15.2913 4.392 15.1473C4.144 15.0033 3.864 14.9313 3.552 14.9313C3.24 14.9313 2.96 15.0033 2.712 15.1473C2.464 15.2913 2.264 15.4953 2.112 15.7593C1.968 16.0233 1.896 16.3433 1.896 16.7193C1.896 17.0873 1.968 17.4073 2.112 17.6793C2.264 17.9433 2.464 18.1473 2.712 18.2913C2.96 18.4353 3.24 18.5073 3.552 18.5073Z" fill={color}></path>
      <path d="M45.9873 19.9473L43.6592 13.4913H45.4232L47.3552 19.0473H46.5153L48.5312 13.4913H50.1152L52.0713 19.0473H51.2312L53.2233 13.4913H54.8792L52.5392 19.9473H50.7272L49.0112 15.1833H49.5632L47.7872 19.9473H45.9873Z" fill={color}></path>
      <path d="M39.8957 20.0433C39.2077 20.0433 38.5957 19.8993 38.0597 19.6113C37.5317 19.3233 37.1117 18.9313 36.7997 18.4353C36.4957 17.9313 36.3437 17.3593 36.3437 16.7193C36.3437 16.0713 36.4957 15.4993 36.7997 15.0033C37.1117 14.4993 37.5317 14.1073 38.0597 13.8273C38.5957 13.5393 39.2077 13.3953 39.8957 13.3953C40.5757 13.3953 41.1837 13.5393 41.7197 13.8273C42.2557 14.1073 42.6757 14.4953 42.9797 14.9913C43.2837 15.4873 43.4357 16.0633 43.4357 16.7193C43.4357 17.3593 43.2837 17.9313 42.9797 18.4353C42.6757 18.9313 42.2557 19.3233 41.7197 19.6113C41.1837 19.8993 40.5757 20.0433 39.8957 20.0433ZM39.8957 18.5073C40.2077 18.5073 40.4877 18.4353 40.7357 18.2913C40.9837 18.1473 41.1797 17.9433 41.3237 17.6793C41.4677 17.4073 41.5397 17.0873 41.5397 16.7193C41.5397 16.3433 41.4677 16.0233 41.3237 15.7593C41.1797 15.4953 40.9837 15.2913 40.7357 15.1473C40.4877 15.0033 40.2077 14.9313 39.8957 14.9313C39.5837 14.9313 39.3037 15.0033 39.0557 15.1473C38.8077 15.2913 38.6077 15.4953 38.4557 15.7593C38.3117 16.0233 38.2397 16.3433 38.2397 16.7193C38.2397 17.0873 38.3117 17.4073 38.4557 17.6793C38.6077 17.9433 38.8077 18.1473 39.0557 18.2913C39.3037 18.4353 39.5837 18.5073 39.8957 18.5073Z" fill={color}></path>
      <path d="M28.48 19.9473V11.0433H30.352V15.2793L29.932 14.7393C30.164 14.3073 30.496 13.9753 30.928 13.7433C31.36 13.5113 31.852 13.3953 32.404 13.3953C32.916 13.3953 33.372 13.4993 33.772 13.7073C34.18 13.9073 34.5 14.2193 34.732 14.6433C34.964 15.0593 35.08 15.5953 35.08 16.2513V19.9473H33.208V16.5393C33.208 16.0193 33.092 15.6353 32.86 15.3873C32.636 15.1393 32.316 15.0153 31.9 15.0153C31.604 15.0153 31.336 15.0793 31.096 15.2073C30.864 15.3273 30.68 15.5153 30.544 15.7713C30.416 16.0273 30.352 16.3553 30.352 16.7553V19.9473H28.48Z" fill={color}></path>
      <path d="M26.616 18.6973C26.616 19.3876 26.0564 19.9473 25.366 19.9473C24.6756 19.9473 24.116 19.3876 24.116 18.6973C24.116 18.0069 24.6756 17.4473 25.366 17.4473C26.0564 17.4473 26.616 18.0069 26.616 18.6973Z" fill={color}></path>
      <path d="M24.4443 10.2217L27.792 4.00586L29.7207 8.08691H46.4746L49.5742 4.20703L52.207 6.57031L54.708 2.70703L55.2539 3.05957L55.7998 3.41309L52.4727 8.55469L49.7295 6.09375L47.2949 9.14258L47.1006 9.38672H28.8975L27.7158 6.8877L24.0635 13.6719L21.3154 3.89453L18.5273 10.3945L16.3408 7.32617L13.6602 10.6982L9.69434 7.15918L4.80469 12.5127L2.45312 9.52832L0.507812 10.3535L0 9.15625L2.86035 7.94434L4.87793 10.5039L9.16797 5.80859L9.60059 5.33398L13.499 8.81152L16.3975 5.16699L18.2441 7.75684L21.5723 0L24.4443 10.2217Z" fill={accent}></path>
    </svg>
  );
}

// ---------- BrandMark (icon-only — just the orange signal pulse) ---------
function BrandMark({ size = 22, color = '#FF6A00' }) {
  // Just the signal-line path from the wordmark, normalized to a 24×24 viewBox
  return (
    <svg width={size} height={size} viewBox="-4 -4 64 18" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
      <path d="M24.4443 10.2217L27.792 4.00586L29.7207 8.08691H46.4746L49.5742 4.20703L52.207 6.57031L54.708 2.70703L55.2539 3.05957L55.7998 3.41309L52.4727 8.55469L49.7295 6.09375L47.2949 9.14258L47.1006 9.38672H28.8975L27.7158 6.8877L24.0635 13.6719L21.3154 3.89453L18.5273 10.3945L16.3408 7.32617L13.6602 10.6982L9.69434 7.15918L4.80469 12.5127L2.45312 9.52832L0.507812 10.3535L0 9.15625L2.86035 7.94434L4.87793 10.5039L9.16797 5.80859L9.60059 5.33398L13.499 8.81152L16.3975 5.16699L18.2441 7.75684L21.5723 0L24.4443 10.2217Z" fill={color}/>
    </svg>
  );
}

// ---------- LogoStageMark (annotated stages of the brand line) ---------
function LogoStageMark({ variant, color = '#FF6A00' }) {
  // Each variant is a small symbolic stroke echoing one part of the brand pulse
  const paths = {
    peak:    'M0 20 L10 18 L18 14 L26 4 L34 14 L42 18 L52 20',
    valley:  'M0 4  L10 8  L18 14 L26 22 L34 14 L42 8  L52 4',
    plateau: 'M0 12 L10 12 L18 12 L26 12 L34 12 L42 12 L52 12',
    restart: 'M0 22 L10 20 L18 18 L24 22 L30 14 L38 8  L52 2',
  };
  return (
    <svg width="100%" height="32" viewBox="0 0 52 24" fill="none" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="none" aria-hidden="true">
      <line x1="0" y1="12" x2="52" y2="12" stroke="currentColor" strokeOpacity="0.12" strokeDasharray="2 3"/>
      <path d={paths[variant] || paths.plateau} stroke={color} strokeWidth="2" strokeLinejoin="round" strokeLinecap="round" fill="none"/>
    </svg>
  );
}

function Flag({ code }) {
  // Pure unicode emoji flag — small, works without assets
  if (!code) return null;
  const cp = [...code.toUpperCase()].map(c => 0x1F1E6 - 65 + c.charCodeAt(0));
  return <span style={{fontSize:13, lineHeight:1}}>{String.fromCodePoint(...cp)}</span>;
}

// ---------- ToolIcon ----------------------------------------------
function ToolIcon({ domain, size = 20, bordered = true }) {
  const t = toolByDomain(domain);
  const cls = `tool-icon tool-icon-${size} ${bordered ? '' : 'no-border'}`;
  const fontSize = size <= 20 ? 9 : size <= 24 ? 11 : size <= 32 ? 13 : 18;
  return (
    <span className={cls} style={{
      background: t.color,
      color: '#fff',
      borderColor: 'transparent',
      fontSize,
    }} title={t.name}>
      {t.initial}
    </span>
  );
}

// ---------- Avatar ----------------------------------------------
function Avatar({ maker, size = 40 }) {
  const dim = size + 'px';
  return (
    <div style={{
      width: dim, height: dim,
      borderRadius: '50%',
      background: maker.avatarColor,
      color: '#fff',
      display: 'grid',
      placeItems: 'center',
      fontFamily: 'var(--font-mono)',
      fontWeight: 600,
      fontSize: Math.max(10, Math.floor(size * 0.38)) + 'px',
      flexShrink: 0,
      border: '1px solid var(--border-soft)',
    }}>
      {maker.initial}
    </div>
  );
}

// ---------- Chip (Source label) ----------------------------------
function SourceChip({ source }) {
  const label = {
    rss: 'rss',
    twitter: 'x.com',
    github: 'github',
    producthunt: 'product-hunt',
    reddit: 'reddit',
    youtube: 'youtube',
  }[source] || source;
  return <span className="chip"><span style={{width:5,height:5,borderRadius:'50%',background:'var(--fg-faint)'}}/>{label}</span>;
}

// ---------- Event type chip -------------------------------------
function TypeChip({ type }) {
  const tone = {
    launch: 'chip-positive',
    milestone: 'chip-positive',
    pricing: 'chip',
    kill: 'chip-negative',
    exit: 'chip-accent',
    post: 'chip',
    hire: 'chip',
  }[type] || 'chip';
  return <span className={`chip ${tone}`}>{t('type_' + type) || type}</span>;
}

// ---------- AI summary ------------------------------------------
function AISummary({ summary, caption, sourceUrl }) {
  return (
    <div className="ai-block">
      <div className="ai-strip mb-3">
        <span className="ai-strip-dot"/>
        <span>{t('ai_summary')}</span>
      </div>
      <p style={{margin:0, fontSize:15, lineHeight:1.6, color:'var(--fg)'}}>{summary}</p>
      {caption && (
        <p style={{margin:'12px 0 0', fontFamily:'var(--font-mono)', fontSize:13, color:'var(--fg-muted)', borderLeft:'2px solid var(--accent)', paddingLeft:10}}>
          {caption}
        </p>
      )}
      <div className="flex items-center gap-4 mt-4" style={{fontSize:11, fontFamily:'var(--font-mono)', color:'var(--fg-faint)', flexWrap:'wrap'}}>
        <span style={{flex:'1 1 220px', minWidth:0}}>{t('ai_disclaimer')}</span>
        <span style={{marginLeft:'auto', whiteSpace:'nowrap'}}>
          <a href={sourceUrl} target="_blank" rel="noopener" style={{color:'var(--fg)', textDecoration:'underline', textUnderlineOffset:'2px'}}>
            {t('view_source')} →
          </a>
        </span>
      </div>
    </div>
  );
}

// ---------- Sparkline ------------------------------------------
function Sparkline({ data, width = 80, height = 24, color = 'var(--accent)' }) {
  if (!data || data.length < 2) return null;
  const min = Math.min(...data), max = Math.max(...data);
  const range = max - min || 1;
  const step = width / (data.length - 1);
  const points = data.map((v, i) => `${i * step},${height - ((v - min) / range) * (height - 2) - 1}`).join(' ');
  return (
    <svg className="spark" width={width} height={height} viewBox={`0 0 ${width} ${height}`} aria-hidden="true">
      <polyline fill="none" stroke={color} strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" points={points}/>
    </svg>
  );
}

// ---------- Line chart ------------------------------------------
function LineChart({ data, width = 720, height = 280, label = 'MRR' }) {
  if (!data || data.length === 0) return null;
  const padding = { top: 28, right: 24, bottom: 32, left: 56 };
  const w = width - padding.left - padding.right;
  const h = height - padding.top - padding.bottom;
  const values = data.map(d => d.v);
  const min = 0;
  const max = Math.max(...values) * 1.05;
  const step = w / (data.length - 1);
  const yFor = v => h - ((v - min) / (max - min)) * h;
  const points = data.map((d, i) => [i * step, yFor(d.v)]);
  const pathD = points.map((p, i) => (i === 0 ? 'M' : 'L') + p[0] + ',' + p[1]).join(' ');
  const areaD = pathD + ` L ${w},${h} L 0,${h} Z`;
  const ticks = 4;
  const tickValues = Array.from({length: ticks + 1}, (_, i) => Math.round(min + (max - min) * i / ticks / 1000) * 1000);
  const fmt = v => v >= 1000 ? `$${(v/1000).toFixed(0)}k` : `$${v}`;

  return (
    <svg width="100%" viewBox={`0 0 ${width} ${height}`} preserveAspectRatio="none" style={{display:'block'}}>
      <g transform={`translate(${padding.left},${padding.top})`}>
        {/* gridlines */}
        {tickValues.map((tv, i) => (
          <g key={i}>
            <line x1="0" y1={yFor(tv)} x2={w} y2={yFor(tv)} stroke="var(--border-soft)" strokeDasharray="2 4"/>
            <text x="-10" y={yFor(tv) + 4} fill="var(--fg-faint)" fontSize="10" textAnchor="end" fontFamily="var(--font-mono)">{fmt(tv)}</text>
          </g>
        ))}
        {/* area gradient */}
        <defs>
          <linearGradient id="area-gradient" x1="0" y1="0" x2="0" y2="1">
            <stop offset="0%" stopColor="var(--accent)" stopOpacity="0.18"/>
            <stop offset="100%" stopColor="var(--accent)" stopOpacity="0"/>
          </linearGradient>
        </defs>
        <path d={areaD} fill="url(#area-gradient)"/>
        <path d={pathD} fill="none" stroke="var(--accent)" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
        {/* end dot */}
        {points.length > 0 && (
          <g>
            <circle cx={points[points.length-1][0]} cy={points[points.length-1][1]} r="4" fill="var(--accent)"/>
            <circle cx={points[points.length-1][0]} cy={points[points.length-1][1]} r="8" fill="var(--accent)" opacity="0.2"/>
          </g>
        )}
        {/* x axis labels — first, middle, last */}
        {[0, Math.floor(data.length/2), data.length-1].map(i => (
          <text key={i} x={i * step} y={h + 18} fill="var(--fg-faint)" fontSize="10" textAnchor="middle" fontFamily="var(--font-mono)">{data[i].d}</text>
        ))}
      </g>
    </svg>
  );
}

// ---------- Bar chart ----------------------------------------------
function BarRow({ label, value, max, accent }) {
  const pct = Math.round((value / max) * 100);
  return (
    <div style={{display:'grid', gridTemplateColumns:'160px 1fr 64px', gap:12, alignItems:'center', padding:'7px 0'}}>
      <div className="mono" style={{fontSize:12, color:'var(--fg-soft)'}}>{label}</div>
      <div style={{height:8, background:'var(--bg-sunken)', borderRadius:4, overflow:'hidden'}}>
        <div style={{height:'100%', width: pct + '%', background: accent ? 'var(--accent)' : 'var(--fg)'}}/>
      </div>
      <div className="mono" style={{fontSize:12, textAlign:'right', color:'var(--fg)'}}>{value}</div>
    </div>
  );
}

// ---------- Bubble wall ----------------------------------------------
function BubbleWall({ items, onSelect }) {
  // pack via simple grid-based layout sized by mention count
  if (!items || !items.length) return null;
  const max = Math.max(...items.map(i => i.count));
  return (
    <div style={{display:'flex', flexWrap:'wrap', gap:10, alignItems:'center'}}>
      {items.map(item => {
        const tool = toolByDomain(item.domain);
        const size = 44 + Math.round((item.count / max) * 64);
        const dim = size + 'px';
        const deltaCol = item.delta > 0 ? 'var(--positive)' : item.delta < 0 ? 'var(--negative)' : 'var(--fg-faint)';
        return (
          <button
            key={item.domain}
            onClick={() => onSelect && onSelect(item.domain)}
            style={{
              width: dim, height: dim,
              borderRadius: '50%',
              background: tool.color,
              color: '#fff',
              display:'grid', placeItems:'center',
              border:'2px solid var(--bg-elev)',
              boxShadow: 'var(--shadow-sm)',
              position:'relative',
              cursor:'pointer',
              transition:'transform 160ms var(--ease-out)',
              fontFamily:'var(--font-mono)',
              fontWeight:600,
              fontSize: Math.max(10, Math.floor(size * 0.22)) + 'px',
            }}
            title={`${tool.name} — ${item.count} mentions, ${item.delta > 0 ? '+' : ''}${item.delta} WoW`}
            onMouseEnter={e => e.currentTarget.style.transform = 'translateY(-2px) scale(1.04)'}
            onMouseLeave={e => e.currentTarget.style.transform = ''}>
            {tool.initial}
            <span style={{
              position:'absolute',
              bottom:-6, right:-4,
              fontSize: 10,
              fontFamily:'var(--font-mono)',
              fontWeight:500,
              background:'var(--bg-elev)',
              color:'var(--fg)',
              padding:'1px 5px',
              borderRadius: 999,
              border:'1px solid var(--border)',
              whiteSpace:'nowrap',
              lineHeight: 1.4,
            }}>
              {item.count}
              <span style={{color: deltaCol, marginLeft:3, fontWeight:500}}>
                {item.delta > 0 ? '↑' : item.delta < 0 ? '↓' : '·'}
              </span>
            </span>
          </button>
        );
      })}
    </div>
  );
}

// ---------- Top nav --------------------------------------------
function TopNav({ route, lang, setLang, theme, setTheme, openSubscribe }) {
  const links = [
    { id: 'builders',  href: '#/',           label: t('nav_makers') },
    { id: 'newsletter',href: '#/newsletter', label: t('nav_thisWeek') },
    { id: 'tools',     href: '#/tools',      label: t('nav_tools') },
    { id: 'trends',    href: '#/trends',     label: t('nav_trends') },
    { id: 'archive',   href: '#/archive',    label: t('nav_archive') },
    { id: 'about',     href: '#/about',      label: t('nav_about') },
  ];
  const isActive = (id) => {
    if (id === 'builders') return route.page === 'builders' || route.page === 'maker';
    if (id === 'newsletter') return route.page === 'newsletter';
    if (id === 'tools') return route.page === 'tools' || route.page === 'tool';
    if (id === 'trends') return route.page === 'trends' || route.page === 'trend';
    if (id === 'archive') return route.page === 'archive';
    if (id === 'about') return route.page === 'about';
    return false;
  };
  return (
    <header className="topnav">
      <div className="topnav-inner">
        <a href="/" className="brand" style={{color:'var(--fg)'}}>
          <BrandWordmark height={40}/>
        </a>
        <nav className="topnav-links">
          {links.map(l => (
            <a key={l.id} href={l.href} className={isActive(l.id) ? 'active' : ''}>{l.label}</a>
          ))}
        </nav>
        <div className="topnav-spacer"/>
        <div className="topnav-right">
          <span className="search-inline" onClick={(e)=>e.preventDefault()}>
            <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><circle cx="11" cy="11" r="7"/><path d="m21 21-4.3-4.3"/></svg>
            <span>{t('nav_search_placeholder')}</span>
            <kbd>⌘K</kbd>
          </span>
          <button onClick={openSubscribe} className="btn btn-primary" style={{padding:'7px 14px', gap:6}}>
            <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"/><polyline points="22,6 12,13 2,6"/></svg>
            {window.__lang === 'zh' ? '订阅' : 'Subscribe'}
          </button>
          <LocaleSwitcher lang={lang} setLang={setLang}/>
          <ThemeToggle theme={theme} setTheme={setTheme}/>
        </div>
      </div>
    </header>
  );
}

// ---------- Locale switcher --------------------------------------------
function LocaleSwitcher({ lang, setLang }) {
  const [open, setOpen] = useState(false);
  const locales = [
    { code: 'en',    label: 'English',    short: 'EN' },
    { code: 'zh',    label: '简体中文',    short: 'ZH' },
    { code: 'ja',    label: '日本語',      short: 'JA', disabled: true },
    { code: 'de',    label: 'Deutsch',    short: 'DE', disabled: true },
    { code: 'pt-BR', label: 'Português',  short: 'PT', disabled: true },
  ];
  const cur = locales.find(l => l.code === lang) || locales[0];
  return (
    <div style={{position:'relative'}}>
      <button className="icon-btn" style={{width:'auto', padding:'0 10px', gap:6, fontFamily:'var(--font-mono)', fontSize:11, color:'var(--fg)'}}
              onClick={()=>setOpen(o=>!o)}>
        <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><circle cx="12" cy="12" r="9"/><path d="M3 12h18M12 3a14 14 0 0 1 0 18M12 3a14 14 0 0 0 0 18"/></svg>
        {cur.short}
      </button>
      {open && (
        <div onMouseLeave={()=>setOpen(false)} style={{
          position:'absolute', top:'100%', right:0, marginTop:6, minWidth:160,
          background:'var(--bg-elev)', border:'1px solid var(--border)', borderRadius:'var(--r-md)',
          boxShadow:'var(--shadow-md)', padding:6, zIndex:60,
        }}>
          {locales.map(l => (
            <button key={l.code} disabled={l.disabled}
                    onClick={()=>{ if(!l.disabled){ setLang(l.code); setOpen(false); } }}
                    style={{
                      display:'flex', alignItems:'center', gap:10, width:'100%', padding:'7px 10px',
                      background: l.code === lang ? 'var(--bg-sunken)' : 'transparent',
                      color: l.disabled ? 'var(--fg-faint)' : 'var(--fg)',
                      cursor: l.disabled ? 'not-allowed' : 'pointer',
                      fontFamily:'var(--font-sans)', fontSize:13, borderRadius:'var(--r-sm)',
                      textAlign:'left',
                    }}>
              <span style={{fontFamily:'var(--font-mono)', fontSize:10, color:'var(--fg-muted)', minWidth:22}}>{l.short}</span>
              <span style={{flex:1}}>{l.label}</span>
              {l.disabled && <span className="mono" style={{fontSize:9, color:'var(--fg-faint)'}}>soon</span>}
              {l.code === lang && <span style={{color:'var(--accent)'}}>●</span>}
            </button>
          ))}
        </div>
      )}
    </div>
  );
}

// ---------- Theme toggle --------------------------------------------
function ThemeToggle({ theme, setTheme }) {
  const next = theme === 'dark' ? 'light' : 'dark';
  return (
    <button className="icon-btn" onClick={()=>setTheme(next)} aria-label={`Switch to ${next} mode`} title={`Switch to ${next} mode`}>
      {theme === 'dark' ? (
        <svg width="14" height="14" 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>
      ) : (
        <svg width="14" height="14" 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 3A7 7 0 0 0 21 12.79z"/>
        </svg>
      )}
    </button>
  );
}

// ---------- Footer --------------------------------------------
function Footer() {
  return (
    <footer className="footer">
      <div className="footer-grid">
        <div>
          <a href="/" className="brand" style={{marginBottom:12}}>
            <BrandWordmark height={44}/>
          </a>
          <p className="soft" style={{maxWidth:340, marginTop:14}}>{t('brand_tagline')}</p>
          <div className="ai-strip mt-4">
            <span className="ai-strip-dot"/>
            <span>{t('footer_built_by')}</span>
          </div>
        </div>
        <div>
          <h4>{t('footer_product')}</h4>
          <a className="footer-link" href="/">{t('nav_makers')}</a>
          <a className="footer-link" href="/newsletter">{t('nav_thisWeek')}</a>
          <a className="footer-link" href="/tools">{t('nav_tools')}</a>
          <a className="footer-link" href="/trends">{t('nav_trends')}</a>
          <a className="footer-link" href="/archive">{t('nav_archive')}</a>
        </div>
        <div>
          <h4>{t('footer_data')}</h4>
          <a className="footer-link" href="/about">{t('footer_methodology')}</a>
          <a className="footer-link" href="/about">{t('footer_sources')}</a>
          <a className="footer-link" href="/archive">{t('footer_archive')}</a>
          <a className="footer-link" href="#">{t('footer_rss')}</a>
        </div>
        <div>
          <h4>{t('footer_legal')}</h4>
          <a className="footer-link" href="/opt-out">{t('footer_remove')}</a>
          <a className="footer-link" href="#">{t('footer_privacy')}</a>
          <a className="footer-link" href="#">{t('footer_terms')}</a>
          <a className="footer-link" href="#">{t('footer_contact')}</a>
        </div>
      </div>
      <div className="footer-bottom">
        <span>© 2026 opc.how</span>
        <span>·</span>
        <span>Built with Next.js · Vercel · Supabase</span>
        <span className="grow"/>
        <span>v0.4.2-beta</span>
      </div>
    </footer>
  );
}

// ---------- NewsletterStrip (compact inline form) ----------
function NewsletterStrip({ variant = 'default' }) {
  const [email, setEmail] = useState('');
  const [done, setDone] = useState(false);
  const isZh = window.__lang === 'zh';

  const copy = {
    makers: {
      eyebrow: isZh ? '每周日 · digest' : 'Every Sunday',
      title: isZh ? '追踪 100 位创业者太累了。让我们替你整理。' : 'Following 100 builders is a lot. Let us digest it for you.',
      sub: isZh
        ? '每周日把 50+ 条新动态压缩成一封 5 分钟可读完的摘要。'
        : 'Every Sunday: 50+ events distilled into a 5-minute read.',
    },
    default: {
      eyebrow: isZh ? '每周日 · digest' : 'Every Sunday',
      title: isZh ? '100 位创业者 · 一封信 · 每周日' : '100 builders. One inbox. Every Sunday.',
      sub: isZh
        ? '5 分钟读完本周所有动态。永久免费。'
        : '5 minutes to read this week. Free forever.',
    },
  };
  const c = copy[variant] || copy.default;

  const proof = isZh ? '4,217 位订阅 · 一键退订' : '4,217 subscribers · one-click unsubscribe';
  const cta = isZh ? '订阅' : 'Subscribe';
  const placeholder = isZh ? '你的邮箱' : 'you@yourdomain.com';

  const submit = (e) => { e.preventDefault(); if (email && email.includes('@')) setDone(true); };

  return (
    <div className="card" style={{
      padding:'24px 28px',
      marginBottom:32,
      borderColor:'var(--fg)',
      display:'grid',
      gridTemplateColumns:'1fr auto',
      gap:24,
      alignItems:'center',
    }}>
      <div style={{display:'flex', alignItems:'center', gap:20, minWidth:0}}>
        <svg width="64" height="22" viewBox="0 0 56 16" fill="none" preserveAspectRatio="xMidYMid meet" style={{flexShrink:0}}>
          <path d="M24.4443 10.2217L27.792 4.00586L29.7207 8.08691H46.4746L49.5742 4.20703L52.207 6.57031L54.708 2.70703L55.2539 3.05957L55.7998 3.41309L52.4727 8.55469L49.7295 6.09375L47.2949 9.14258L47.1006 9.38672H28.8975L27.7158 6.8877L24.0635 13.6719L21.3154 3.89453L18.5273 10.3945L16.3408 7.32617L13.6602 10.6982L9.69434 7.15918L4.80469 12.5127L2.45312 9.52832L0.507812 10.3535L0 9.15625L2.86035 7.94434L4.87793 10.5039L9.16797 5.80859L9.60059 5.33398L13.499 8.81152L16.3975 5.16699L18.2441 7.75684L21.5723 0L24.4443 10.2217Z" fill="#FF6A00"/>
        </svg>
        <div style={{minWidth:0, flex:1}}>
          <div className="eyebrow mb-1">{c.eyebrow}</div>
          <div style={{fontFamily:'var(--font-mono)', fontSize:16, fontWeight:600, letterSpacing:'-0.01em', lineHeight:1.3, marginBottom:4}}>{c.title}</div>
          <div className="mono faint" style={{fontSize:11}}>{c.sub} · {proof}</div>
        </div>
      </div>
      {done ? (
        <div style={{padding:'10px 16px', border:'1px solid var(--positive)', background:'var(--positive-soft)', color:'var(--positive)', borderRadius:'var(--r-sm)', fontFamily:'var(--font-mono)', fontSize:12, whiteSpace:'nowrap'}}>
          ✓ {isZh ? '已订阅' : 'You\'re in'}
        </div>
      ) : (
        <form onSubmit={submit} style={{display:'flex', gap:8, alignItems:'center'}}>
          <input
            type="email"
            required
            value={email}
            onChange={e => setEmail(e.target.value)}
            placeholder={placeholder}
            style={{
              minWidth: 240,
              padding:'10px 12px',
              fontFamily:'var(--font-mono)',
              fontSize:12,
              color:'var(--fg)',
              background:'var(--bg)',
              border:'1px solid var(--border)',
              borderRadius:'var(--r-sm)',
              outline:'none',
            }}
          />
          <button type="submit" className="btn btn-primary" style={{padding:'9px 16px'}}>{cta} →</button>
        </form>
      )}
    </div>
  );
}

// ---------- NewsletterModal (global subscribe popup) ----------
function NewsletterModal({ open, onClose }) {
  const [email, setEmail] = useState('');
  const [done, setDone] = useState(false);
  const isZh = window.__lang === 'zh';

  // close on Escape + lock body scroll while open
  useEffect(() => {
    if (!open) return;
    const onKey = (e) => { if (e.key === 'Escape') onClose(); };
    window.addEventListener('keydown', onKey);
    const prevOverflow = document.body.style.overflow;
    document.body.style.overflow = 'hidden';
    return () => {
      window.removeEventListener('keydown', onKey);
      document.body.style.overflow = prevOverflow;
    };
  }, [open, onClose]);

  if (!open) return null;

  const heading = isZh ? '每周日，100 位创业者，一封信。' : 'Every Sunday. 100 builders. One inbox.';
  const sub = isZh
    ? '把 50+ 条独立创业动态压成一封 5 分钟的 digest。永久免费 · 一键退订。'
    : 'A weekly digest of 50+ indie events in a 5-minute read. Free forever · one-click unsubscribe.';
  const cta = isZh ? '订阅' : 'Subscribe';
  const placeholder = isZh ? '你的邮箱' : 'you@yourdomain.com';

  const submit = (e) => { e.preventDefault(); if (email && email.includes('@')) setDone(true); };

  return (
    <div onClick={onClose} style={{
      position:'fixed', inset:0, zIndex:100,
      background:'rgba(10,10,9,0.55)',
      backdropFilter:'blur(6px)',
      display:'flex', alignItems:'center', justifyContent:'center',
      padding:'24px',
      animation:'fadeIn 200ms ease-out',
    }}>
      <div onClick={e => e.stopPropagation()} style={{
        background:'var(--bg-elev)',
        border:'1px solid var(--border)',
        borderRadius:'var(--r-lg)',
        maxWidth: 520, width:'100%',
        padding:'40px 40px 32px',
        position:'relative',
        boxShadow:'var(--shadow-lg)',
      }}>
        <button onClick={onClose} aria-label="Close" style={{
          position:'absolute', top:16, right:16,
          width:28, height:28, borderRadius:6,
          display:'grid', placeItems:'center',
          color:'var(--fg-muted)',
        }}>
          <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path d="M18 6L6 18M6 6l12 12"/></svg>
        </button>

        <svg width="90" height="32" viewBox="0 0 56 16" fill="none" preserveAspectRatio="xMidYMid meet" style={{marginBottom:20}}>
          <path d="M24.4443 10.2217L27.792 4.00586L29.7207 8.08691H46.4746L49.5742 4.20703L52.207 6.57031L54.708 2.70703L55.2539 3.05957L55.7998 3.41309L52.4727 8.55469L49.7295 6.09375L47.2949 9.14258L47.1006 9.38672H28.8975L27.7158 6.8877L24.0635 13.6719L21.3154 3.89453L18.5273 10.3945L16.3408 7.32617L13.6602 10.6982L9.69434 7.15918L4.80469 12.5127L2.45312 9.52832L0.507812 10.3535L0 9.15625L2.86035 7.94434L4.87793 10.5039L9.16797 5.80859L9.60059 5.33398L13.499 8.81152L16.3975 5.16699L18.2441 7.75684L21.5723 0L24.4443 10.2217Z" fill="#FF6A00"/>
        </svg>

        <h2 style={{margin:'0 0 14px', fontFamily:'var(--font-mono)', fontSize:26, fontWeight:500, letterSpacing:'-0.02em', lineHeight:1.15, color:'var(--fg)'}}>
          {heading}
        </h2>
        <p style={{margin:'0 0 24px', fontSize:14, lineHeight:1.6, color:'var(--fg-soft)'}}>{sub}</p>

        {done ? (
          <div style={{padding:'14px 16px', border:'1px solid var(--positive)', background:'var(--positive-soft)', color:'var(--positive)', borderRadius:'var(--r-sm)', fontFamily:'var(--font-mono)', fontSize:13}}>
            ✓ {isZh ? '已订阅。下周日见。' : 'You\'re in. See you Sunday.'}
          </div>
        ) : (
          <form onSubmit={submit} style={{display:'flex', gap:8, marginBottom:14}}>
            <input
              autoFocus
              type="email"
              required
              value={email}
              onChange={e => setEmail(e.target.value)}
              placeholder={placeholder}
              style={{
                flex:1,
                padding:'12px 14px',
                fontFamily:'var(--font-mono)',
                fontSize:13,
                color:'var(--fg)',
                background:'var(--bg)',
                border:'1px solid var(--border)',
                borderRadius:'var(--r-sm)',
                outline:'none',
              }}
            />
            <button type="submit" className="btn btn-primary" style={{padding:'10px 18px'}}>{cta} →</button>
          </form>
        )}

        <div className="flex items-center gap-6 mt-4" style={{fontFamily:'var(--font-mono)', fontSize:11, color:'var(--fg-muted)', flexWrap:'wrap'}}>
          <span className="flex items-center gap-2">
            <span style={{width:5, height:5, borderRadius:3, background:'var(--positive)'}}/>
            {isZh ? '4,217 位独立创业者已订阅' : '4,217 indie builders subscribed'}
          </span>
          <span style={{color:'var(--fg-faint)'}}>·</span>
          <span>{isZh ? '永久免费 · 一键退订' : 'Free forever · one-click unsubscribe'}</span>
        </div>
      </div>
    </div>
  );
}

Object.assign(window, { NewsletterStrip, NewsletterModal });
function fmtMRR(v) {
  if (v == null) return '—';
  if (v >= 1000000) return '$' + (v/1000000).toFixed(2) + 'M';
  if (v >= 1000) return '$' + Math.round(v/1000) + 'k';
  return '$' + v;
}
function fmtMRRFull(v) {
  if (v == null) return '—';
  return '$' + v.toLocaleString('en-US');
}
function fmtRelative(iso) {
  const then = new Date(iso).getTime();
  const now = Date.now();
  const h = Math.floor((now - then) / (1000 * 60 * 60));
  if (h < 24) return h + t('ago_h');
  return Math.floor(h/24) + t('ago_d');
}

Object.assign(window, {
  BrandMark, BrandWordmark, LogoStageMark, Flag, ToolIcon, Avatar, SourceChip, TypeChip,
  AISummary, Sparkline, LineChart, BarRow, BubbleWall,
  TopNav, LocaleSwitcher, ThemeToggle, Footer,
  fmtMRR, fmtMRRFull, fmtRelative,
});
