/* AIMSify Blog — shared React components
   Brand: AIMSify (Artan Consulting Pte Ltd, Singapore)
   Author: Rajesh Laskary (rajesh@artanconsulting.com · +65 8606 2966)
*/

const { useState, useEffect, useMemo, useRef } = React;
const { FRAMEWORKS, TOPICS, ROLES, TYPES, AUTHORS, POSTS } = window.BLOG_DATA;

// ---------- Routing helpers ----------
function parseHash() {
  const h = (location.hash || '#/').slice(1);
  const [path, query = ''] = h.split('?');
  const parts = path.split('/').filter(Boolean);
  const params = Object.fromEntries(new URLSearchParams(query));
  return { parts, params };
}
function navigate(path) { location.hash = path; window.scrollTo({ top: 0, behavior: 'instant' }); }
function useRoute() {
  const [route, setRoute] = useState(parseHash());
  useEffect(() => {
    const onHash = () => setRoute(parseHash());
    window.addEventListener('hashchange', onHash);
    return () => window.removeEventListener('hashchange', onHash);
  }, []);
  return route;
}
function fmtDate(iso) {
  const d = new Date(iso);
  return d.toLocaleDateString('en-GB', { day: 'numeric', month: 'short', year: 'numeric' });
}

// ---------- Brand mark (AIMSify crosshair) ----------
function CrosshairMark({ size = 26, color = '#FFFFFF', dot = '#00E5FF', strokeWidth = 1.6 }) {
  // Faithful to AIMSify icon: outer ring, inner ring, cyan dot, four crosshair ticks
  return (
    <svg width={size} height={size} viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
      <circle cx="16" cy="16" r="9" stroke={color} strokeWidth={strokeWidth} />
      <circle cx="16" cy="16" r="5" stroke={color} strokeWidth={strokeWidth} />
      <circle cx="16" cy="16" r="2.2" fill={dot} />
      <path d="M16 2 V5.4 M16 26.6 V30 M2 16 H5.4 M26.6 16 H30" stroke={color} strokeWidth={strokeWidth} strokeLinecap="square" />
    </svg>
  );
}

// ---------- AIMSify wordmark lockup ----------
function BrandLockup({ onDark = false, withTag = true }) {
  return (
    <a className={`brand-lockup ${onDark ? 'on-dark' : ''}`} href="#/" onClick={(e) => { e.preventDefault(); navigate('/'); }}>
      <span className="brand-icon">
        <CrosshairMark size={24} color={onDark ? '#FFFFFF' : '#FFFFFF'} dot="#00E5FF" />
      </span>
      <span className="wordmark">
        AIMS<span className="ify">ify</span>
      </span>
      {withTag && <span className="journal-tag">Journal</span>}
    </a>
  );
}

// ---------- Atoms ----------
function FwTag({ id, active, onClick, asLink }) {
  const fw = FRAMEWORKS[id];
  if (!fw) return null;
  const cls = `fw-tag ${active ? 'is-active' : ''}`;
  if (asLink) {
    return (
      <a className={cls} href={`#/category/framework/${id}`} onClick={(e) => e.stopPropagation()}>
        <span className="dot" style={{ background: active ? 'currentColor' : fw.color }}></span>
        {fw.label}
      </a>
    );
  }
  return (
    <button className={cls} onClick={onClick}>
      <span className="dot" style={{ background: active ? 'currentColor' : fw.color }}></span>
      {fw.label}
    </button>
  );
}

function TypeBadge({ type }) {
  const t = TYPES[type];
  if (!t) return null;
  return <span className={`type-badge ${type}`}>{t.label}</span>;
}

function Avatar({ author, size = 'sm' }) {
  const a = AUTHORS[author];
  if (!a) return null;
  const cls = size === 'lg' ? 'avatar-lg' : size === 'xl' ? 'avatar-xl' : 'avatar';
  // Prefer real photo if present
  return (
    <span className={cls} style={{ background: a.tint }}>
      {a.photo
        ? <img src={a.photo} alt={a.name} loading="lazy" />
        : a.mono}
    </span>
  );
}

// ---------- Header ----------
function Header({ active = '' }) {
  // Auth-aware "Go to Platform" — when logged out, route to aimsify.ai home instead.
  const [isLoggedIn, setIsLoggedIn] = useState(false);
  useEffect(() => {
    try {
      setIsLoggedIn(localStorage.getItem('aimsify_logged_in') === 'true');
    } catch (e) { /* ignore */ }
    const onStorage = (e) => {
      if (e.key === 'aimsify_logged_in') setIsLoggedIn(e.newValue === 'true');
    };
    window.addEventListener('storage', onStorage);
    return () => window.removeEventListener('storage', onStorage);
  }, []);

  return (
    <>
      {/* Cross-site bar — signals "you're in the journal, here's the way back" */}
      <div className="cross-site-bar">
        <div className="cross-site-bar-inner">
          <a href="https://aimsify.ai" className="cross-site-back">
            <svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round"><path d="M19 12H5"/><path d="m12 19-7-7 7-7"/></svg>
            <span>aimsify.ai</span>
          </a>
          <span className="cross-site-sep">/</span>
          <span className="cross-site-here">Journal</span>
          <div className="cross-site-spacer"></div>
          <a href="https://aimsify.ai/platform" className="cross-site-link">Platform</a>
          {isLoggedIn ? (
            <a href="https://aimsify.ai/dashboard" className="cross-site-cta">
              Go to Platform
              <svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg>
            </a>
          ) : (
            <a href="https://aimsify.ai" className="cross-site-cta">
              aimsify.ai home
              <svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg>
            </a>
          )}
        </div>
      </div>

      <header className="site-header">
        <div className="site-header-inner">
          <BrandLockup />
          <nav className="site-nav">
            <a href="#/" className={active === 'home' ? 'active' : ''} onClick={(e) => { e.preventDefault(); navigate('/'); }}>Latest</a>
            <a href="#/category/topic/ai_governance" className={active === 'ai' ? 'active' : ''} onClick={(e) => { e.preventDefault(); navigate('/category/topic/ai_governance'); }}>AI Governance</a>
            <a href="#/category/topic/regulatory" className={active === 'reg' ? 'active' : ''} onClick={(e) => { e.preventDefault(); navigate('/category/topic/regulatory'); }}>Regulatory</a>
            <a href="#/category/type/playbook" className={active === 'play' ? 'active' : ''} onClick={(e) => { e.preventDefault(); navigate('/category/type/playbook'); }}>Playbooks</a>
            <a href="#/category/topic/supplier_risk" className={active === 'sup' ? 'active' : ''} onClick={(e) => { e.preventDefault(); navigate('/category/topic/supplier_risk'); }}>Supplier Risk</a>
            <div className="nav-dropdown">
              <a
                href="#/category/framework/ctm"
                className={active === 'frameworks' ? 'active' : ''}
                aria-haspopup="true"
                aria-expanded="false"
                onClick={(e) => { e.preventDefault(); e.currentTarget.parentElement.classList.toggle('open'); }}
              >
                Frameworks <span aria-hidden="true">▾</span>
              </a>
              <div className="nav-dropdown-menu">
                <a href="#/category/framework/ctm" onClick={(e) => { e.preventDefault(); navigate('/category/framework/ctm'); }}>Cyber Trust Mark</a>
                <a href="#/category/framework/iso27001" onClick={(e) => { e.preventDefault(); navigate('/category/framework/iso27001'); }}>ISO 27001 ISMS</a>
                <a href="#/category/framework/iso42001" onClick={(e) => { e.preventDefault(); navigate('/category/framework/iso42001'); }}>ISO 42001 AIMS</a>
                <a href="#/category/framework/nist_csf" onClick={(e) => { e.preventDefault(); navigate('/category/framework/nist_csf'); }}>NIST CSF</a>
                <a href="#/category/framework/nist_airmf" onClick={(e) => { e.preventDefault(); navigate('/category/framework/nist_airmf'); }}>NIST AI RMF</a>
              </div>
            </div>
          </nav>
          <div className="header-actions">
            <button className="search-trigger" onClick={() => navigate('/search')}>
              <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><circle cx="11" cy="11" r="7"/><path d="M21 21l-4.3-4.3"/></svg>
              <span>Search journal…</span>
              <kbd>⌘K</kbd>
            </button>
            <a className="btn-demo cyan" href="https://aimsify.ai/book-demo">Talk to Rajesh</a>
          </div>
        </div>
      </header>
    </>
  );
}

// ---------- Footer ----------
function Footer() {
  return (
    <footer className="site-footer">
      <div className="site-footer-inner">

        {/* Top strip — cross-site return-to-platform band */}
        <div className="footer-platform-strip">
          <div className="footer-platform-copy">
            <div className="footer-platform-eyebrow">Continue with AIMSify</div>
            <div className="footer-platform-headline">The platform behind these field notes.</div>
            <p className="footer-platform-sub">
              AI-native cybersecurity and AI assurance &mdash; ISO 27001, ISO 42001, NIST CSF, MAS TRM, and more, in a single workspace.
            </p>
          </div>
          <div className="footer-platform-actions">
            <a className="footer-platform-cta primary" href="https://aimsify.ai/dashboard">
              Go to Platform
              <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg>
            </a>
            <a className="footer-platform-cta secondary" href="https://aimsify.ai">
              aimsify.ai home
            </a>
          </div>
        </div>

        <div className="footer-grid">
          <div>
            <BrandLockup onDark withTag />
            <p className="footer-tagline">
              Field notes from the practitioners building Singapore&rsquo;s AI assurance and cybersecurity programmes.
            </p>
            <div className="footer-attribution">
              <strong>AIMSify</strong> is a product of <a href="https://www.ArtanConsulting.com" target="_blank" rel="noreferrer">Artan Consulting Pte Ltd, Singapore</a>.<br />
              For consulting, advisory, or platform demos, write to <a href="mailto:rajesh@artanconsulting.com">rajesh@artanconsulting.com</a> or call <a href="tel:+6586062966">+65 8606 2966</a>.
            </div>
          </div>
          <div>
            <h5>Topics</h5>
            <ul>
              {Object.entries(TOPICS).slice(0, 6).map(([k, v]) => (
                <li key={k}><a href={`#/category/topic/${k}`}>{v}</a></li>
              ))}
            </ul>
          </div>
          <div>
            <h5>Frameworks</h5>
            <ul>
              {['iso27001', 'iso42001', 'nist_csf', 'mas_trm', 'eu_aiact', 'ctm'].map((k) => (
                <li key={k}><a href={`#/category/framework/${k}`}>{FRAMEWORKS[k].label}</a></li>
              ))}
            </ul>
          </div>
          <div>
            <h5>AIMSify</h5>
            <ul>
              <li><a href="https://aimsify.ai">aimsify.ai &mdash; home</a></li>
              <li><a href="https://aimsify.ai/platform">Platform</a></li>
              <li><a href="https://aimsify.ai/dashboard">Dashboard</a></li>
              <li><a href="https://www.linkedin.com/in/rajeshlaskary/" target="_blank" rel="noreferrer">Rajesh on LinkedIn</a></li>
              <li><a href="mailto:rajesh@artanconsulting.com">rajesh@artanconsulting.com</a></li>
            </ul>
          </div>
        </div>
        <div className="footer-bottom">
          <span>© 2026 Artan Consulting Pte Ltd · AIMSify is a registered product mark</span>
          <span>Engineered in Singapore. Built for the world.</span>
        </div>
      </div>
    </footer>
  );
}

// ---------- Post Card ----------
function PostCard({ post, variant = 'default' }) {
  const author = AUTHORS[post.author];
  const onClick = () => navigate(`/post/${post.slug}`);

  if (variant === 'feature') {
    return (
      <article className="post-feature" onClick={onClick}>
        <div className="feature-art">
          <div className="grid-overlay"></div>
          <FeatureArt post={post} />
        </div>
        <div>
          <div className="post-card-meta">
            <TypeBadge type={post.type} />
            <span className="dot-sep">·</span>
            <span>{fmtDate(post.date)}</span>
            <span className="dot-sep">·</span>
            <span className="read-mins">{post.readMins} min read</span>
          </div>
          <h2 className="post-card-title" style={{ marginTop: 12 }}>{post.title}</h2>
          <p className="post-card-dek" style={{ marginTop: 12, fontSize: 16 }}>{post.dek}</p>
          <div className="post-card-foot" style={{ marginTop: 16 }}>
            <span className="author">
              <Avatar author={post.author} />
              <span>{author.name}</span>
            </span>
            {post.frameworks.slice(0, 3).map((f) => <FwTag key={f} id={f} asLink />)}
          </div>
        </div>
      </article>
    );
  }

  if (variant === 'secondary') {
    return (
      <article className="feature-secondary" onClick={onClick}>
        <div className="post-card-meta">
          <TypeBadge type={post.type} />
          <span className="dot-sep">·</span>
          <span className="read-mins">{post.readMins} min</span>
        </div>
        <h3 className="post-card-title">{post.title}</h3>
        <div className="post-card-foot" style={{ marginTop: 10 }}>
          <span className="author">
            <Avatar author={post.author} />
            <span>{author.name}</span>
          </span>
        </div>
      </article>
    );
  }

  return (
    <article className="post-card" onClick={onClick}>
      <div className="post-card-meta">
        <TypeBadge type={post.type} />
        {post.frameworks.slice(0, 2).map((f) => <FwTag key={f} id={f} asLink />)}
      </div>
      <h3 className="post-card-title">{post.title}</h3>
      <p className="post-card-dek">{post.dek}</p>
      <div className="post-card-foot">
        <span className="author">
          <Avatar author={post.author} />
          <span>{author.name}</span>
        </span>
        <span className="post-card-meta" style={{ marginLeft: 'auto' }}>
          <span>{fmtDate(post.date)}</span>
          <span className="dot-sep">·</span>
          <span className="read-mins">{post.readMins} min</span>
        </span>
      </div>
    </article>
  );
}

// Feature art — AIMSify-branded crosshair grid (no fake images)
function FeatureArt({ post }) {
  const colors = post.frameworks.slice(0, 2).map((f) => FRAMEWORKS[f]?.color).filter(Boolean);
  const primary = colors[0] || '#00E5FF';
  return (
    <svg width="100%" height="100%" viewBox="0 0 400 225" preserveAspectRatio="xMidYMid slice"
         style={{ position: 'absolute', inset: 0 }}>
      <defs>
        <radialGradient id={`glow-${post.id}`} cx="0.7" cy="0.3" r="0.7">
          <stop offset="0%" stopColor={primary} stopOpacity="0.25"/>
          <stop offset="100%" stopColor="transparent"/>
        </radialGradient>
      </defs>
      <rect width="400" height="225" fill="#0D1B3E"/>
      <rect width="400" height="225" fill={`url(#glow-${post.id})`}/>
      {/* Background crosshairs (decorative pattern) */}
      <g opacity="0.18" stroke="#FFFFFF" strokeWidth="0.8" fill="none">
        <circle cx="320" cy="60" r="14"/>
        <circle cx="320" cy="60" r="22"/>
        <circle cx="320" cy="60" r="2" fill="#00E5FF" stroke="none"/>
        <path d="M320 42 v6 M320 78 v-6 M302 60 h6 M338 60 h-6" strokeLinecap="square"/>
        <circle cx="80" cy="170" r="9"/>
        <circle cx="80" cy="170" r="14"/>
        <circle cx="80" cy="170" r="1.4" fill="#00E5FF" stroke="none"/>
      </g>
      {/* Faint grid */}
      <g opacity="0.08" stroke="#00E5FF" strokeWidth="0.5">
        {Array.from({length:13}).map((_,i) => <line key={`v${i}`} x1={i*32} y1="0" x2={i*32} y2="225"/>)}
        {Array.from({length:8}).map((_,i) => <line key={`h${i}`} x1="0" y1={i*32} x2="400" y2={i*32}/>)}
      </g>
      <text x="32" y="56" fill="#00E5FF" opacity="0.95"
            style={{ font: '700 11px/1 "Inter", "Inter Tight", sans-serif', letterSpacing: '0.16em', textTransform: 'uppercase' }}>
        {(TYPES[post.type]?.label || '').toUpperCase()}
      </text>
      <text x="32" y="200" fill="#FFFFFF" opacity="0.55"
            style={{ font: '500 10px/1 "JetBrains Mono", monospace', letterSpacing: '0.08em' }}>
        {post.frameworks.map((f) => FRAMEWORKS[f]?.label).join(' · ')}
      </text>
    </svg>
  );
}

// ---------- Contact CTA — primary "Talk to Rajesh" block ----------
function ContactCTA({ compact = false, eyebrow = 'Let\u2019s talk', headline, body }) {
  const a = AUTHORS.rajesh;
  const defaultHeadline = compact
    ? 'Need a sounding board on this?'
    : 'Working through this in your own programme?';
  const defaultBody = compact
    ? <>Talk it through with <strong>Rajesh Laskary</strong> &mdash; founder of Artan Consulting and creator of AIMSify. No pitch, just a conversation. <em>(AIMSify is a product of Artan Consulting Pte Ltd, Singapore &mdash; <a href="https://www.ArtanConsulting.com" target="_blank" rel="noreferrer" style={{color:'var(--aim-cyan)'}}>www.ArtanConsulting.com</a>)</em></>
    : <>If you are operationalising any of this in a real programme &mdash; ISMS, AI governance, supplier risk, or a regulator response &mdash; I am happy to share what is working in the field. <strong>Drop me a note, send a WhatsApp, or pick up the phone.</strong> Most useful conversations start informally. <em>(AIMSify is a product of Artan Consulting Pte Ltd, Singapore &mdash; visit <a href="https://www.ArtanConsulting.com" target="_blank" rel="noreferrer" style={{color:'var(--aim-cyan)'}}>www.ArtanConsulting.com</a> to learn more.)</em></>;

  return (
    <section className={`contact-cta ${compact ? 'compact' : ''}`}>
      <div className="cta-avatar">
        <img src={a.photo} alt={a.name} loading="lazy" />
      </div>
      <div>
        <div className="cta-eyebrow">{eyebrow}</div>
        <h3>{headline || defaultHeadline}</h3>
        <p>{body || defaultBody}</p>
        <div className="contact-channels">
          <a className="contact-chip primary" href={`mailto:${a.email}?subject=AIMSify%20-%20Talk%20to%20Rajesh`}>
            <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><rect x="2" y="4" width="20" height="16" rx="2"/><path d="m22 7-10 5L2 7"/></svg>
            <span>{a.email}</span>
          </a>
          <a className="contact-chip" href={`https://wa.me/${a.whatsapp}`} target="_blank" rel="noreferrer">
            <svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor"><path d="M17.472 14.382c-.297-.149-1.758-.867-2.03-.967-.273-.099-.471-.148-.67.15-.197.296-.767.966-.94 1.164-.173.199-.347.223-.644.075-.297-.15-1.255-.463-2.39-1.475-.883-.788-1.48-1.761-1.653-2.059-.173-.297-.018-.458.13-.606.134-.133.298-.347.446-.52.149-.174.198-.298.298-.497.099-.198.05-.371-.025-.52-.075-.149-.669-1.612-.916-2.207-.242-.579-.487-.5-.669-.51-.173-.008-.371-.01-.57-.01-.198 0-.52.074-.792.372-.272.297-1.04 1.016-1.04 2.479 0 1.462 1.065 2.875 1.213 3.074.149.198 2.096 3.2 5.077 4.487.709.306 1.262.489 1.694.625.712.227 1.36.195 1.871.118.571-.085 1.758-.719 2.006-1.413.248-.694.248-1.289.173-1.413-.074-.124-.272-.198-.57-.347m-5.421 7.403h-.004a9.87 9.87 0 0 1-5.031-1.378l-.361-.214-3.741.982.998-3.648-.235-.374a9.86 9.86 0 0 1-1.51-5.26c.001-5.45 4.436-9.884 9.888-9.884 2.64 0 5.122 1.03 6.988 2.898a9.825 9.825 0 0 1 2.893 6.994c-.003 5.45-4.437 9.884-9.885 9.884m8.413-18.297A11.815 11.815 0 0 0 12.05 0C5.495 0 .16 5.335.157 11.892c0 2.096.547 4.142 1.588 5.945L.057 24l6.305-1.654a11.882 11.882 0 0 0 5.683 1.448h.005c6.554 0 11.89-5.335 11.893-11.893a11.821 11.821 0 0 0-3.48-8.413Z"/></svg>
            <span>WhatsApp +65 8606 2966</span>
          </a>
          <a className="contact-chip" href={`tel:${a.phone.replace(/\s/g,'')}`}>
            <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path d="M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72 12.84 12.84 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7A2 2 0 0 1 22 16.92z"/></svg>
            <span>Call +65 8606 2966</span>
          </a>
          <a className="contact-chip" href={a.linkedin} target="_blank" rel="noreferrer">
            <svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor"><path d="M20.45 20.45h-3.55v-5.57c0-1.33-.02-3.04-1.85-3.04-1.85 0-2.13 1.45-2.13 2.95v5.66H9.36V9h3.41v1.56h.05c.47-.9 1.64-1.85 3.37-1.85 3.6 0 4.27 2.37 4.27 5.46v6.28zM5.34 7.43a2.06 2.06 0 1 1 0-4.12 2.06 2.06 0 0 1 0 4.12zM7.12 20.45H3.56V9h3.56v11.45zM22.22 0H1.77C.79 0 0 .77 0 1.72v20.56C0 23.23.79 24 1.77 24h20.45c.98 0 1.78-.77 1.78-1.72V1.72C24 .77 23.2 0 22.22 0z"/></svg>
            <span>LinkedIn</span>
          </a>
        </div>
        {!compact && <div className="cta-meta">Singapore time, GMT+8 · Replies within 24 hrs · No sales pipeline, just direct lines.</div>}
      </div>
    </section>
  );
}

// ---------- Newsletter ----------
function Newsletter({ compact = false }) {
  const [email, setEmail] = useState('');
  const [submitted, setSubmitted] = useState(false);
  const submit = (e) => { e.preventDefault(); if (email) setSubmitted(true); };
  return (
    <section className={`newsletter ${compact ? 'compact' : ''}`}>
      <div>
        <div className="eyebrow" style={{ color: 'var(--aim-cyan)', marginBottom: 10 }}>The Weekly Briefing</div>
        <h3>Audit-ready intelligence, <em>delivered Tuesday morning.</em></h3>
        <p>One CISO-grade briefing per week &mdash; regulatory shifts, framework deltas, and field notes from active engagements. No sales emails, no fluff. Curated by Rajesh Laskary and the AIMSify team.</p>
      </div>
      <div>
        {submitted ? (
          <div style={{ color: 'var(--aim-cyan)', fontFamily: 'var(--serif)', fontStyle: 'italic', fontSize: 18 }}>
            ✓ You&rsquo;re in. Look for the briefing on Tuesday.
          </div>
        ) : (
          <>
            <form className="newsletter-form" onSubmit={submit}>
              <input type="email" required placeholder="you@yourcompany.com" value={email} onChange={(e) => setEmail(e.target.value)} />
              <button type="submit">Subscribe</button>
            </form>
            <div className="newsletter-meta">No spam. Unsubscribe anytime. Read our privacy notice.</div>
          </>
        )}
      </div>
    </section>
  );
}

// ---------- Author card ----------
function AuthorCard({ authorId }) {
  const a = AUTHORS[authorId];
  if (!a) return null;
  return (
    <div className="author-card">
      <Avatar author={authorId} size="lg" />
      <div>
        <h4>Written by</h4>
        <a className="name" href={`#/author/${a.id}`} style={{ display: 'block' }}>{a.name}</a>
        <div className="title">{a.title}</div>
        <p className="bio">{a.bio}</p>
        <div className="certs">
          {a.certs.map((c) => <span className="cert-pill" key={c}>{c}</span>)}
        </div>
        <div style={{ display: 'flex', flexWrap: 'wrap', gap: 14, marginTop: 14, fontSize: 13, color: 'var(--ink-3)', fontFamily: 'var(--sans)' }}>
          <a href={a.linkedin} target="_blank" rel="noreferrer" style={{ color: 'var(--aim-cyan-deep)', display:'inline-flex', alignItems:'center', gap:6 }}>
            <svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor"><path d="M20.45 20.45h-3.55v-5.57c0-1.33-.02-3.04-1.85-3.04-1.85 0-2.13 1.45-2.13 2.95v5.66H9.36V9h3.41v1.56h.05c.47-.9 1.64-1.85 3.37-1.85 3.6 0 4.27 2.37 4.27 5.46v6.28zM5.34 7.43a2.06 2.06 0 1 1 0-4.12 2.06 2.06 0 0 1 0 4.12zM7.12 20.45H3.56V9h3.56v11.45zM22.22 0H1.77C.79 0 0 .77 0 1.72v20.56C0 23.23.79 24 1.77 24h20.45c.98 0 1.78-.77 1.78-1.72V1.72C24 .77 23.2 0 22.22 0z"/></svg>
            LinkedIn
          </a>
          <a href={`mailto:${a.email}`} style={{ color: 'var(--aim-cyan-deep)' }}>{a.email}</a>
          <a href={`https://wa.me/${a.whatsapp}`} target="_blank" rel="noreferrer" style={{ color: 'var(--aim-cyan-deep)' }}>WhatsApp {a.phone}</a>
        </div>
      </div>
    </div>
  );
}

// ---------- Related ----------
function Related({ post, n = 3 }) {
  const related = useMemo(() => {
    const others = POSTS.filter((p) => p.id !== post.id);
    const scored = others.map((p) => {
      let s = 0;
      p.frameworks.forEach((f) => post.frameworks.includes(f) && (s += 2));
      p.topics.forEach((t) => post.topics.includes(t) && (s += 2));
      if (p.type === post.type) s += 1;
      return { p, s };
    });
    return scored.sort((a, b) => b.s - a.s).slice(0, n).map((x) => x.p);
  }, [post]);
  return (
    <section className="related-section">
      <span className="eyebrow eyebrow-emerald">Continue reading</span>
      <div className="related-grid">
        {related.map((p) => <PostCard key={p.id} post={p} />)}
      </div>
    </section>
  );
}

Object.assign(window, {
  React, useState, useEffect, useMemo, useRef,
  FRAMEWORKS, TOPICS, ROLES, TYPES, AUTHORS, POSTS,
  parseHash, navigate, useRoute, fmtDate,
  CrosshairMark, BrandLockup,
  FwTag, TypeBadge, Avatar, Header, Footer, PostCard, FeatureArt,
  Newsletter, AuthorCard, Related, ContactCTA,
});
