// Domstack — parody newsletter platform
const { useState, useEffect, useMemo } = React;

// ───────────────────────── Icons ─────────────────────────
const Ico = {
  flag: (p) => <svg viewBox="0 0 24 24" fill="currentColor" {...p}><path d="M5 3h14v18l-7-4-7 4V3z"/></svg>,
  chain: (p) => <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" {...p}>
    <path d="M10 14a4 4 0 0 1 0-5.66l2.83-2.83a4 4 0 1 1 5.66 5.66l-1.41 1.41"/>
    <path d="M14 10a4 4 0 0 1 0 5.66l-2.83 2.83a4 4 0 1 1-5.66-5.66l1.41-1.41"/>
  </svg>,
  home: (p) => <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" {...p}><path d="M3 11l9-8 9 8v10a1 1 0 0 1-1 1h-5v-7h-6v7H4a1 1 0 0 1-1-1V11z"/></svg>,
  inbox: (p) => <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" {...p}><path d="M3 13l3-9h12l3 9M3 13v6a1 1 0 0 0 1 1h16a1 1 0 0 0 1-1v-6M3 13h5l1 3h6l1-3h5"/></svg>,
  chat: (p) => <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" {...p}><path d="M21 12a8 8 0 1 1-3-6.24L21 4l-1 4.5A8 8 0 0 1 21 12z"/></svg>,
  bell: (p) => <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" {...p}><path d="M6 8a6 6 0 0 1 12 0c0 7 3 8 3 8H3s3-1 3-8M10 21a2 2 0 0 0 4 0"/></svg>,
  compass: (p) => <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" {...p}><circle cx="12" cy="12" r="9"/><path d="m16 8-2.5 5.5L8 16l2.5-5.5L16 8z"/></svg>,
  user: (p) => <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" {...p}><circle cx="12" cy="8" r="4"/><path d="M4 21c1-4 5-6 8-6s7 2 8 6"/></svg>,
  plus: (p) => <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.4" strokeLinecap="round" {...p}><path d="M12 5v14M5 12h14"/></svg>,
  search: (p) => <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" {...p}><circle cx="11" cy="11" r="7"/><path d="m20 20-3.5-3.5"/></svg>,
  heart: (p) => <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" {...p}><path d="M12 21s-7-4.5-9.5-9A5.5 5.5 0 0 1 12 6a5.5 5.5 0 0 1 9.5 6C19 16.5 12 21 12 21z"/></svg>,
  heartFill: (p) => <svg viewBox="0 0 24 24" fill="currentColor" {...p}><path d="M12 21s-7-4.5-9.5-9A5.5 5.5 0 0 1 12 6a5.5 5.5 0 0 1 9.5 6C19 16.5 12 21 12 21z"/></svg>,
  bubble: (p) => <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" {...p}><path d="M21 11.5a8 8 0 0 1-11.7 7.1L4 20l1.4-4.3A8 8 0 1 1 21 11.5z"/></svg>,
  restack: (p) => <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" {...p}><path d="M17 1l4 4-4 4M3 11V9a4 4 0 0 1 4-4h14M7 23l-4-4 4-4M21 13v2a4 4 0 0 1-4 4H3"/></svg>,
  share: (p) => <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" {...p}><path d="M12 3v13M7 8l5-5 5 5M5 21h14"/></svg>,
  whip: (p) => <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" {...p}><path d="M4 4l6 6"/><path d="M10 10c2 0 4 2 4 4s-2 4-4 4-4-2-4-4"/><path d="M14 13l6 6"/><circle cx="5" cy="5" r="1.5" fill="currentColor"/></svg>,
  collar: (p) => <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" {...p}><path d="M3 9c3 4 6 6 9 6s6-2 9-6"/><circle cx="12" cy="16" r="2"/><path d="M12 18v2"/></svg>,
  candle: (p) => <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" {...p}><rect x="9" y="9" width="6" height="12" rx="1"/><path d="M12 9V6"/><path d="M12 3c1 1 1 2 0 3s-1 2 0 3" strokeLinecap="round"/></svg>,
  mask: (p) => <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" {...p}><path d="M3 9c0-1 1-2 2-2h14c1 0 2 1 2 2v3c0 4-4 7-9 7s-9-3-9-7V9z"/><circle cx="8.5" cy="11" r="1.2" fill="currentColor"/><circle cx="15.5" cy="11" r="1.2" fill="currentColor"/></svg>,
  rope: (p) => <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" {...p}><path d="M4 6c4 4 12 4 16 0M4 12c4 4 12 4 16 0M4 18c4 4 12 4 16 0"/></svg>,
  lock: (p) => <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" {...p}><rect x="4" y="11" width="16" height="10" rx="2"/><path d="M8 11V8a4 4 0 0 1 8 0v3"/></svg>,
  check: (p) => <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.4" strokeLinecap="round" {...p}><path d="m5 12 5 5L20 7"/></svg>,
  bookmark: (p) => <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" {...p}><path d="M6 3h12v18l-6-4-6 4V3z"/></svg>,
  trend: (p) => <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" {...p}><path d="M3 17l6-6 4 4 8-8M14 7h7v7"/></svg>,
  paddle: (p) => <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" {...p}><ellipse cx="12" cy="8" rx="6" ry="7"/><path d="M12 15v6"/></svg>,
  trio: (p) => <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" {...p}><circle cx="6" cy="12" r="3"/><circle cx="12" cy="9" r="3"/><circle cx="18" cy="12" r="3"/><path d="M3 21c1-3 6-3 9-3s8 0 9 3"/></svg>,
  swap: (p) => <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" {...p}><path d="M7 7h12l-3-3"/><path d="M17 17H5l3 3"/></svg>,
};

// ───────────────────────── Data ─────────────────────────
// DOM mode — you, the publisher, take competitors apart.
const DOM_SERVICES = [
  { id:'spank', icon:'paddle', name:'Substack',  italic:'Spanking',
    desc:"Discipline your competitor's growth with consensual, search-optimized correction. We hit where it counts: their open rates.",
    price:'$49', unit:'/ session', tag:'Most popular' },
  { id:'cuck', icon:'mask', name:'Cuck Your',  italic:'Substack',
    desc:"Watch a more virile newsletter take your audience to brunch. You sit. You read. You learn.",
    price:'$79', unit:'/ month', tag:'Premium' },
  { id:'spit', icon:'trio', name:'Spitroasting a', italic:'Substack',
    desc:"Two creators, one feed, zero shame. Coordinated cross-posting from both ends until they tap out.",
    price:'$129', unit:'/ campaign', tag:null },
  { id:'edge', icon:'candle', name:'Edging Your', italic:'Open Rate',
    desc:"Almost. Almost. Almost. We hold your audience right at the brink for 14 emails before letting them click.",
    price:'$39', unit:'/ week', tag:null },
  { id:'collar', icon:'collar', name:'Subscribers on a', italic:'Leash',
    desc:"Retention-focused obedience training. They will sit, stay, and forward to a friend on command.",
    price:'$59', unit:'/ month', tag:'New' },
  { id:'rope', icon:'rope', name:'Tied & True', italic:'Threads',
    desc:"Shibari for your CTAs. Beautifully bound buttons your readers physically cannot scroll past.",
    price:'$29', unit:'/ post', tag:null },
  { id:'whip', icon:'whip', name:'Crack the', italic:'Whip',
    desc:"Aggressive churn intervention. We send your lapsed readers a calendar invite they will not decline.",
    price:'$99', unit:'/ month', tag:null },
  { id:'lock', icon:'lock', name:'Chastity for', italic:'Free Tiers',
    desc:"They want to read. They cannot read. The paywall is the point. Conversion goes up 340%.",
    price:'$69', unit:'/ month', tag:'Hot' },
  { id:'swap', icon:'swap', name:'Switch', italic:'Mode',
    desc:"For the versatile. We top your competitors on Mondays and bottom for your subscribers on Fridays. Boundaries respected.",
    price:'$149', unit:'/ month', tag:null },
];

// SUB mode — you, the publisher, are the one who needs discipline.
const SUB_SERVICES = [
  { id:'paddle', icon:'paddle', name:'The Editor\'s', italic:'Paddle',
    desc:"You write run-ons. You know you write run-ons. We will correct that, publicly, in front of your most engaged readers.",
    price:'$39', unit:'/ session', tag:'Most popular' },
  { id:'humil', icon:'mask', name:'Open-Rate', italic:'Humiliation',
    desc:"Your numbers, posted nightly in a public Slack of 12,000 better writers. You watch them react in real time. Growth.",
    price:'$59', unit:'/ month', tag:'Premium' },
  { id:'bond',  icon:'rope', name:'Deadline', italic:'Bondage',
    desc:"Tied to your editorial calendar with elegant Japanese rope. Cannot get up. Cannot get coffee. Will finish Thursday's draft.",
    price:'$49', unit:'/ week', tag:null },
  { id:'chast', icon:'lock', name:'Dashboard', italic:'Chastity',
    desc:"Locked out of your own analytics until you publish. You may not refresh. You may not look. You may only write.",
    price:'$29', unit:'/ month', tag:'New' },
  { id:'gag',   icon:'collar', name:'The Promotional', italic:'Gag',
    desc:"We revoke your right to self-promote on X for 30 days. Your work must speak for itself. If it cannot, that is information.",
    price:'$19', unit:'/ month', tag:null },
  { id:'force', icon:'whip', name:'Forced', italic:'Engagement',
    desc:"You reply to every comment within four minutes. We monitor. We escalate. Your community has never felt so seen.",
    price:'$45', unit:'/ month', tag:null },
  { id:'draft', icon:'candle', name:'Public', italic:'Drafts',
    desc:"Your worst, ugliest, most exposed first drafts published unedited at 3am. Ego death is the only true editor.",
    price:'$79', unit:'/ month', tag:'Hot' },
  { id:'stool', icon:'trio', name:'Footstool', italic:'Tier',
    desc:"Permanently ranked below all other newsletters in your category. You exist to elevate them. There is peace in this.",
    price:'$25', unit:'/ month', tag:null },
  { id:'sit',   icon:'chain', name:'Sit. Stay.', italic:'Subscribe.',
    desc:"A small electric collar (figurative) buzzes whenever your retention dips below 40%. You will learn what hooks are.",
    price:'$99', unit:'/ month', tag:null },
];

const DOM_POSTS = [
  {
    id:1, author:'Mistress Margolis', initials:'MM', pub:'The Velvet Newsletter',
    verified:true, time:'2h', tone:'paddle',
    title: { pre:'I made my competitor ', em:'unsubscribe from themselves', post:'. Here is the exact email.' },
    snip:"I have been doing this for 8 years. The trick is not to threaten — that is gauche. The trick is to make them volunteer. Here is the template I have used to extract 14,000 subscribers from a single rival's list, with no platform violations. Bookmark this.",
    imgTag:'Confession #04',
    likes: 2418, comments: 312, restacks: 891,
  },
  {
    id:2, author:'Dommie Long-Form', initials:'DL', pub:'The Discipline',
    verified:true, time:'5h', tone:'rope',
    title: { pre:'Every newsletter platform is ', em:'soft', post:'. Mine is not.' },
    snip:"Beehiiv? Mild. Ghost? Sweet. ConvertKit? Frankly adorable. I have been writing here for 11 months and the bounce rate is what it is because I told it to be that. Power is choosing your metrics. Today I want to teach you to choose yours.",
    imgPlaceholder:'[ portrait — author in leather jacket, soft window light ]',
    likes: 1042, comments: 88, restacks: 240,
  },
  {
    id:3, author:'The Crop', initials:'TC', pub:'Punishingly Good Prose',
    verified:false, time:'9h', tone:'collar',
    title: { pre:'On the ', em:'erotic potential', post:' of a 73% open rate' },
    snip:"There is no number in publishing more obscene than 73. It is the number you whisper. The number you do not deserve. I hit it last Tuesday on a 4,000-word essay about my divorce. Here is what I learned about subject lines, longing, and the comma.",
    likes: 5523, comments: 612, restacks: 1820,
  },
];

const SUB_POSTS = [
  {
    id:11, author:'Henry Goodboy', initials:'HG', pub:'Please Punish My Prose',
    verified:true, time:'1h', tone:'paddle',
    title: { pre:'I let my editor read ', em:'everything', post:' for 30 days. I am a better man.' },
    snip:"She read my drafts. She read my DMs. She read the unsent letter to my father. The open rate doubled. The therapy bill tripled. I have never been more productive. I have never been more seen. Here is the contract I signed, redacted only where strictly necessary.",
    imgTag:'Submission #07',
    likes: 1841, comments: 412, restacks: 622,
  },
  {
    id:12, author:'A Penitent Drafter', initials:'PD', pub:'Below The Fold',
    verified:false, time:'4h', tone:'rope',
    title: { pre:'On being ', em:'collared', post:' by my publishing schedule' },
    snip:"Tuesday. Friday. Sunday. There is no negotiation. I have not chosen what to write about in 11 weeks; the calendar chooses for me, and I am, perhaps for the first time, finally free. Today's prompt was: 'apologise to your audience.' Reader, I did.",
    imgPlaceholder:'[ portrait — author kneeling at a vintage typewriter, hands bound, posture excellent ]',
    likes: 988, comments: 142, restacks: 318,
  },
  {
    id:13, author:'The Footnote', initials:'TF', pub:'Worthless Words',
    verified:false, time:'11h', tone:'collar',
    title: { pre:'Why I ', em:'deserve', post:' to be unsubscribed from' },
    snip:"I am, frankly, not very good. My metaphors are tired. My subject lines are vain. I would unsubscribe from me too. And yet — and yet — every Monday, 4,200 of you remain. I do not understand it and I will not insult you by pretending I do. This is a thank-you note. Please be cruel in the comments.",
    likes: 3240, comments: 891, restacks: 1102,
  },
];

const DOM_LEADERBOARD = [
  { rank:1, init:'PD', name:'Paddle Daddy',     pub:"Discipline Quarterly",      sub:'48.2k subs',  arr:'▲ 12', up:true },
  { rank:2, init:'VV', name:'Velvet Volkov',    pub:"The Whip Hand",             sub:'31.7k subs',  arr:'▲ 4',  up:true },
  { rank:3, init:'CB', name:'Cordelia Bind',    pub:"Knots & Notes",             sub:'24.0k subs',  arr:'▼ 1',  up:false },
  { rank:4, init:'OS', name:'Otis Switch',      pub:"Both Ways Bulletin",        sub:'19.3k subs',  arr:'▲ 7',  up:true },
  { rank:5, init:'MS', name:'Madame Substrate', pub:"Confessions of a Topper",   sub:'14.8k subs',  arr:'▲ 2',  up:true },
];

const SUB_LEADERBOARD = [
  { rank:1, init:'HG', name:'Henry Goodboy',    pub:"Please Punish My Prose",    sub:'kneeling for 12.4k',  arr:'▼ 4',  up:false },
  { rank:2, init:'TF', name:'The Footnote',     pub:"Worthless Words",           sub:'4.2k pity-subs',      arr:'▼ 2',  up:false },
  { rank:3, init:'PD', name:'Penitent Drafter', pub:"Below The Fold",            sub:'2.8k devoted',        arr:'▼ 9',  up:false },
  { rank:4, init:'YN', name:'Yes-And Nelson',   pub:"Apology Weekly",            sub:'1.9k forgivers',      arr:'▼ 1',  up:false },
  { rank:5, init:'GB', name:'Good Boy Quarterly',pub:"Threadbare Notes",          sub:'1.1k kind souls',     arr:'▼ 6',  up:false },
];

// ───────────────────────── Components ─────────────────────────
function BrandMark() {
  return (
    <span className="brand-mark" aria-label="Domstack logo">
      {/* chain-link "D" mark */}
      <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round">
        <path d="M9 5h3a7 7 0 0 1 0 14H9V5z"/>
        <circle cx="9" cy="12" r="2" fill="currentColor" stroke="none"/>
      </svg>
    </span>
  );
}

function Sidebar({ route, setRoute, onCreate }) {
  const items = [
    { id:'home',  label:'Home',          icon:'home',    badge:null },
    { id:'inbox', label:'Subscriptions', icon:'inbox',   badge:'12'  },
    { id:'chat',  label:'Chat',          icon:'chat',    badge:null  },
    { id:'bell',  label:'Activity',      icon:'bell',    badge:'9'   },
    { id:'comp',  label:'Explore',       icon:'compass', badge:null  },
    { id:'me',    label:'Profile',       icon:'user',    badge:null  },
  ];
  return (
    <aside className="side">
      <div className="brand">
        <BrandMark />
        <span className="brand-name">Dom<em>stack</em></span>
      </div>
      {items.map(i => {
        const I = Ico[i.icon];
        return (
          <div key={i.id}
               className={'nav-i' + (route===i.id?' active':'')}
               onClick={()=>setRoute(i.id)}>
            <span className="ico"><I/></span>
            <span className="lbl">{i.label}</span>
            {i.badge && <span className="badge">{i.badge}</span>}
          </div>
        );
      })}
      <button className="cta-create" onClick={onCreate}>
        <Ico.plus style={{width:16,height:16}}/>
        <span>Start a Domstack</span>
      </button>
      <div className="nav-spacer"></div>
      <div className="side-foot">
        <b>Safeword:</b> "unsubscribe"<br/>
        Used freely. No questions asked.
      </div>
    </aside>
  );
}

const HERO_COPY = {
  dom: {
    tag: '★ Now accepting consensual takedowns',
    h1a: 'Where every ', h1em: 'Substack', h1b: <><br/>comes to obey.</>,
    p: "Domstack is the newsletter platform for writers who finish on top. Acquire your rivals' subscribers through structured, ethical, profoundly humiliating cross-promotion. Safe, sane, and search-engine-optimized.",
    cta: 'Dominate your niche →',
    cta2: 'Read the rules',
  },
  sub: {
    tag: '★ Currently accepting punishment',
    h1a: 'Be the writer you ', h1em: 'deserve', h1b: <><br/>to punish.</>,
    p: "You miss deadlines. You bury the lede. You wrote 'utilize' again. Domstack's submissive tier puts you in the firm hands of a senior editor who has read your work and is, frankly, very disappointed in you.",
    cta: 'Present yourself →',
    cta2: 'Read the contract',
  },
};

function Hero({ mode, onPrimary }) {
  const c = HERO_COPY[mode] || HERO_COPY.dom;
  return (
    <section className="hero">
      <div className="hero-tag">{c.tag}</div>
      <h1>
        {c.h1a}<em>{c.h1em}</em>{c.h1b}
      </h1>
      <p>{c.p}</p>
      <div className="hero-actions">
        <button className="btn btn-primary" onClick={onPrimary}>{c.cta}</button>
        <button className="btn btn-ghost">{c.cta2}</button>
      </div>

      {/* Decorative illustration: stylized chain + paddle */}
      <svg className="hero-illus" viewBox="0 0 320 320" fill="none">
        <defs>
          <linearGradient id="g1" x1="0" x2="1" y1="0" y2="1">
            <stop offset="0" stopColor="#c8163b" stopOpacity=".9"/>
            <stop offset="1" stopColor="#6b0a1d" stopOpacity=".4"/>
          </linearGradient>
        </defs>
        <g stroke="url(#g1)" strokeWidth="3" fill="none" opacity=".9">
          <ellipse cx="60"  cy="60" rx="22" ry="14" transform="rotate(35 60 60)"/>
          <ellipse cx="92"  cy="92" rx="22" ry="14" transform="rotate(35 92 92)"/>
          <ellipse cx="124" cy="124" rx="22" ry="14" transform="rotate(35 124 124)"/>
          <ellipse cx="156" cy="156" rx="22" ry="14" transform="rotate(35 156 156)"/>
          <ellipse cx="188" cy="188" rx="22" ry="14" transform="rotate(35 188 188)"/>
        </g>
        <g transform="translate(180 80) rotate(28)" fill="url(#g1)" opacity=".85">
          <ellipse cx="50" cy="50" rx="40" ry="48"/>
          <rect x="44" y="96" width="12" height="120" rx="3"/>
        </g>
      </svg>
    </section>
  );
}

const SERVICES_COPY = {
  dom: { h: <>The <em>Services</em></>, p: 'Boutique offerings. Tiered consent. Itemized invoices for tax purposes.' },
  sub: { h: <>Available <em>Disciplines</em></>, p: 'You will not enjoy these. That is the point. Please confirm pronouns and pain tolerance at checkout.' },
};

function ServicesGrid({ mode, services, onBuy }) {
  const c = SERVICES_COPY[mode] || SERVICES_COPY.dom;
  return (
    <>
      <div className="section-h">
        <h2>{c.h}</h2>
        <p>{c.p}</p>
      </div>
      <div className="svc-grid">
        {services.map(s => {
          const I = Ico[s.icon];
          return (
            <div key={s.id} className="svc">
              {s.tag && <span className="svc-tag">{s.tag}</span>}
              <span className="svc-glyph"><I/></span>
              <h3>{s.name} <em>{s.italic}</em></h3>
              <p>{s.desc}</p>
              <div className="svc-foot">
                <span className="svc-price"><b>{s.price}</b><span>{s.unit}</span></span>
                <button className="svc-buy" onClick={()=>onBuy(s)}>{mode==='sub'?'Submit →':'Book →'}</button>
              </div>
            </div>
          );
        })}
      </div>
    </>
  );
}

function Avatar({ initials, verified }) {
  return (
    <div className={'ava' + (verified?' verified':'')}>
      {initials}
    </div>
  );
}

function Post({ post, onLike, onSub, isLiked, isSubbed }) {
  return (
    <article className="post">
      <Avatar initials={post.initials} verified={post.verified}/>
      <div>
        <div className="post-h">
          <span className="name">{post.author}</span>
          <span className="dot">·</span>
          <span className="pub">{post.pub}</span>
          <span className="dot">·</span>
          <span className="time">{post.time}</span>
          <button className={'sub-btn' + (isSubbed?' subbed':'')} onClick={()=>onSub(post.id)}>
            {isSubbed ? 'Subscribed ✓' : 'Subscribe'}
          </button>
        </div>
        <h3 className="post-title">
          {post.title.pre}<em>{post.title.em}</em>{post.title.post}
        </h3>
        <p className="post-snip">{post.snip}</p>
        {post.imgTag && (
          <div className="post-img">
            <span className="tag">{post.imgTag}</span>
          </div>
        )}
        {post.imgPlaceholder && (
          <div className="post-img placeholder">
            <span>{post.imgPlaceholder}</span>
          </div>
        )}
        <div className="post-actions">
          <button className={isLiked?'liked':''} onClick={()=>onLike(post.id)}>
            {isLiked ? <Ico.heartFill/> : <Ico.heart/>}
            {(post.likes + (isLiked?1:0)).toLocaleString()}
          </button>
          <button><Ico.bubble/>{post.comments}</button>
          <button><Ico.restack/>{post.restacks}</button>
          <span className="spread"/>
          <button><Ico.bookmark/></button>
          <button><Ico.share/></button>
        </div>
      </div>
    </article>
  );
}

const RAIL_COPY = {
  dom: {
    searchPh: 'Search rivals to publicly dominate…',
    cardIcon:'chain', cardKicker:'Take the collar',
    cardHa:'Join the ', cardHem:'obedient', cardHb:'.',
    cardP:"Submit your email and one (1) competitor's URL. We handle the rest. Boundaries enforced. Receipts itemized.",
    cardCta:'Start your Domstack',
    boardKicker:'Top of the rope',
  },
  sub: {
    searchPh: 'Search editors to apologize to…',
    cardIcon:'collar', cardKicker:'Accept your station',
    cardHa:'Become ', cardHem:'usefully bad', cardHb:'.',
    cardP:'Submit your worst draft, a recent rejection letter, and any nicknames your mother used. Our editors will take it from there.',
    cardCta:'Present your drafts',
    boardKicker:'Most apologetic this week',
  },
};

function RightRail({ mode, leaderboard, onStart, onSignin }) {
  const c = RAIL_COPY[mode] || RAIL_COPY.dom;
  const CardIcon = Ico[c.cardIcon];
  return (
    <aside className="rail">
      <div className="search">
        <Ico.search style={{width:16,height:16,color:'var(--ink-muted)'}}/>
        <input placeholder={c.searchPh} />
      </div>

      <div className="card">
        <div className="card-h">
          <span className="ico"><CardIcon/></span>
          <h4>{c.cardKicker}</h4>
        </div>
        <h3>{c.cardHa}<em>{c.cardHem}</em>{c.cardHb}</h3>
        <p>{c.cardP}</p>
        <button className="btn btn-primary" onClick={onStart}>{c.cardCta}</button>
        <button className="btn btn-ghost">Sign in</button>
      </div>

      <div className="card">
        <div className="card-h">
          <span className="ico"><Ico.trend/></span>
          <h4>{c.boardKicker}</h4>
        </div>
        <div className="leaderboard">
          {leaderboard.map(l => (
            <div key={l.rank} className="lb">
              <span className="lb-rank">{String(l.rank).padStart(2,'0')}</span>
              <span className="lb-ava">{l.init}</span>
              <span>
                <div className="lb-name">{l.name}</div>
                <div className="lb-sub">{l.pub} · {l.sub}</div>
              </span>
              <span className={'lb-arr ' + (l.up?'up':'down')}>{l.arr}</span>
            </div>
          ))}
        </div>
      </div>

      <div className="safety">
        <b>A note on conduct.</b> Domstack is a parody of Substack and an entirely fictional service.
        Negotiate scenes before publishing. Use your safeword. The safeword is <span className="mono">unsubscribe</span>.
      </div>
    </aside>
  );
}

// ───────────────────────── Switch → Better Letter takeover ─────────────────────────
function BetterLetterTakeover({ onBack }) {
  return (
    <div className="bl-wrap">
      <style>{`
        .bl-wrap{position:fixed;inset:0;z-index:40;overflow-y:auto;
          background:
            radial-gradient(900px 500px at 20% 0%, #ffe8d6 0%, transparent 60%),
            radial-gradient(700px 500px at 100% 100%, #d6efe4 0%, transparent 55%),
            #fbf6ee;
          color:#1c2018;font-family:'Inter',sans-serif;
          animation:blFade .35s ease}
        @keyframes blFade{from{opacity:0}to{opacity:1}}
        .bl-nav{display:flex;align-items:center;gap:14px;padding:24px 36px;
          max-width:1180px;margin:0 auto}
        .bl-brand{display:flex;align-items:center;gap:10px;font-family:'DM Serif Display',serif;
          font-weight:600;font-size:22px;letter-spacing:-.005em;color:#1c2018}
        .bl-mark{width:32px;height:32px;border-radius:50%;display:grid;place-items:center;
          background:linear-gradient(140deg,#FFAA70,#FF8040);color:#fff;
          box-shadow:0 6px 18px rgba(236,140,91,.35),inset 0 1px 0 rgba(255,255,255,.6);
          font-family:'DM Serif Display',serif;font-style:italic;font-weight:700;font-size:18px}
        .bl-nav a{color:#5a6056;font-size:14px;font-weight:500;cursor:pointer}
        .bl-nav a:hover{color:#1c2018}
        .bl-spacer{flex:1}
        .bl-back{display:inline-flex;align-items:center;gap:6px;font-size:13px;
          color:#5a6056;padding:6px 12px;border-radius:99px;border:1px solid #d8d1c2;
          background:#fff8ec;font-weight:500}
        .bl-back:hover{background:#fff;color:#1c2018}

        .bl-hero{max-width:1180px;margin:0 auto;padding:60px 36px 48px;
          display:grid;grid-template-columns:1.2fr 1fr;gap:60px;align-items:center}
        @media (max-width:880px){.bl-hero{grid-template-columns:1fr;gap:36px;padding:36px}}
        .bl-eyebrow{display:inline-flex;align-items:center;gap:8px;font-size:11px;
          letter-spacing:.16em;text-transform:uppercase;color:#D44E00;font-weight:600;
          padding:6px 14px;border-radius:99px;background:#fff;border:1px solid #f1e0c1;margin-bottom:22px}
        .bl-eyebrow .ddot{width:6px;height:6px;border-radius:99px;background:#FF8040}
        .bl-h1{font-family:'DM Serif Display',serif;font-weight:500;font-size:72px;
          line-height:1;letter-spacing:-.02em;margin:0 0 18px;color:#161914;text-wrap:balance}
        .bl-h1 em{font-style:italic;color:#FF6719;font-weight:600}
        .bl-sub{font-size:18px;line-height:1.55;color:#4a4e44;max-width:520px;margin:0 0 28px}
        .bl-cta-row{display:flex;gap:12px;flex-wrap:wrap;align-items:center}
        .bl-btn{display:inline-flex;align-items:center;gap:8px;padding:13px 22px;
          border-radius:99px;font-weight:600;font-size:15px;letter-spacing:.005em;cursor:pointer;border:0;text-decoration:none}
        .bl-btn-p{background:#1c2018;color:#fff;
          box-shadow:0 10px 30px rgba(28,32,24,.18),inset 0 1px 0 rgba(255,255,255,.10)}
        .bl-btn-p:hover{background:#000}
        .bl-btn-g{background:transparent;color:#1c2018;border:1px solid #cdc6b6}
        .bl-btn-g:hover{background:#fff}
        .bl-micro{font-size:12px;color:#7c8278;margin-left:6px}

        .bl-card{background:#fff;border:1px solid #ece4d1;border-radius:24px;
          padding:28px;box-shadow:0 30px 60px -30px rgba(28,32,24,.20);
          position:relative}
        .bl-card .ribbon{position:absolute;top:-12px;right:24px;
          padding:5px 12px;font-size:10px;letter-spacing:.18em;text-transform:uppercase;
          color:#fff;background:#FF6719;border-radius:99px;font-weight:700;font-family:'Inter',sans-serif}
        .bl-card h3{font-family:'DM Serif Display',serif;font-weight:600;font-size:28px;
          line-height:1.15;letter-spacing:-.01em;margin:6px 0 8px;color:#161914}
        .bl-card .by{font-size:13px;color:#7c8278;margin:0 0 16px}
        .bl-card .by b{color:#1c2018;font-weight:600}
        .bl-card .ex{font-size:14.5px;line-height:1.55;color:#3d4239;margin:0 0 18px}
        .bl-card .ph{height:140px;border-radius:14px;
          background:linear-gradient(135deg,#ffe6c4,#d6efe4);margin-bottom:18px;
          display:flex;align-items:flex-end;padding:14px;font-family:'DM Mono',monospace;
          font-size:11px;color:#CC5510;letter-spacing:.06em}
        .bl-card .stats{display:flex;gap:14px;font-size:12px;color:#7c8278;flex-wrap:nowrap;align-items:center}
        .bl-card .stats b{color:#1c2018;font-weight:600;font-family:'DM Mono',monospace}

        .bl-feat{max-width:1180px;margin:0 auto;padding:32px 36px 80px;
          display:grid;grid-template-columns:repeat(3,1fr);gap:18px}
        @media (max-width:880px){.bl-feat{grid-template-columns:1fr;padding:24px 36px 60px}}
        .bl-feat-card{background:#fff;border:1px solid #ece4d1;border-radius:18px;padding:24px;
          transition:transform .25s,box-shadow .25s}
        .bl-feat-card:hover{transform:translateY(-3px);box-shadow:0 20px 40px -20px rgba(28,32,24,.15)}
        .bl-feat-card .gl{width:42px;height:42px;border-radius:12px;display:grid;place-items:center;
          background:#fff4e2;color:#FF6719;margin-bottom:14px}
        .bl-feat-card .gl svg{width:22px;height:22px}
        .bl-feat-card h4{font-family:'DM Serif Display',serif;font-size:22px;font-weight:600;
          margin:0 0 4px;color:#161914;letter-spacing:-.005em}
        .bl-feat-card p{margin:0;color:#5a6056;font-size:13.5px;line-height:1.55}

        .bl-foot{text-align:center;padding:0 36px 60px;color:#7c8278;font-size:12px;
          max-width:560px;margin:0 auto;line-height:1.6}
        .bl-foot b{color:#1c2018;font-weight:600}

        /* ── Math strip ── */
        .bl-math-wrap{border-top:1px solid #ece4d1;border-bottom:1px solid #ece4d1;
          background:#fff;padding:56px 0}
        .bl-math{max-width:1180px;margin:0 auto;padding:0 36px;
          display:grid;grid-template-columns:1fr auto 1fr;gap:32px;align-items:center}
        @media(max-width:680px){.bl-math{grid-template-columns:1fr;gap:24px}}
        .bl-math-col{text-align:center}
        .bl-math-lbl{font-size:10.5px;font-weight:600;letter-spacing:.12em;text-transform:uppercase;
          color:#9a8a78;margin-bottom:10px}
        .bl-math-num{font-family:'DM Serif Display',serif;font-size:clamp(52px,7vw,76px);
          font-weight:600;letter-spacing:-.025em;line-height:1}
        .bl-math-bad{color:#b33a2a}
        .bl-math-good{color:#FF6719}
        .bl-math-sub{font-size:13px;color:#7c8278;margin-top:8px;line-height:1.5}
        .bl-math-vs{font-family:'DM Serif Display',serif;font-size:30px;
          color:#c8bfb0;text-align:center;font-style:italic}

        /* ── Deed / Certificate ── */
        .bl-deed-wrap{max-width:1180px;margin:0 auto;padding:72px 36px 64px}
        .bl-deed-hd{text-align:center;margin-bottom:44px}
        .bl-sect-ey{font-size:10.5px;font-weight:600;letter-spacing:.14em;text-transform:uppercase;
          color:#FF6719;display:block;margin-bottom:14px}
        .bl-sect-h{font-family:'DM Serif Display',serif;font-size:clamp(34px,4vw,50px);
          font-weight:500;letter-spacing:-.02em;color:#161914;margin:0;line-height:1.1}
        .bl-sect-h em{font-style:italic;color:#FF6719}
        .bl-cert{max-width:560px;margin:0 auto;background:#fff;
          border:1px solid rgba(255,103,25,.18);border-radius:3px;
          padding:38px 44px;position:relative;
          box-shadow:0 2px 8px rgba(0,0,0,.04),0 18px 44px rgba(0,0,0,.07)}
        .bl-cert::before{content:'';position:absolute;inset:0;border-radius:3px;
          pointer-events:none;
          background:repeating-linear-gradient(0deg,transparent,transparent 28px,
            rgba(255,103,25,.025) 28px,rgba(255,103,25,.025) 29px)}
        .bl-cert-ey{font-size:9px;font-weight:600;letter-spacing:.18em;text-transform:uppercase;
          color:#FF6719;margin-bottom:8px;position:relative;z-index:1}
        .bl-cert-tag{font-family:'DM Serif Display',serif;font-style:italic;font-size:15px;
          color:#7c8278;margin-bottom:0;position:relative;z-index:1}
        .bl-cert-rule{height:1px;border:none;
          background:linear-gradient(to right,transparent,rgba(255,103,25,.22),transparent);
          margin:20px 0;position:relative;z-index:1}
        .bl-cert-list{list-style:none;padding:0;margin:0;
          display:flex;flex-direction:column;gap:14px;position:relative;z-index:1}
        .bl-cert-item{display:flex;align-items:baseline}
        .bl-cert-svc{font-size:14px;color:#5a6056;white-space:nowrap}
        .bl-cert-dots{flex:1;margin:0 10px 3px;
          border-bottom:1px dotted rgba(255,103,25,.2)}
        .bl-cert-own{font-size:11px;font-weight:700;color:#FF6719;
          text-transform:uppercase;letter-spacing:.06em;white-space:nowrap}
        .bl-cert-money{color:#CC4400}
        .bl-cert-legal{font-size:10.5px;color:#9a8a78;line-height:1.6;
          position:relative;z-index:1}
        .bl-cert-seal{position:absolute;top:50%;left:50%;
          transform:translate(-50%,-50%);
          width:84px;height:84px;border-radius:50%;
          border:1.5px solid rgba(255,103,25,.14);
          display:flex;align-items:center;justify-content:center;
          font-family:'DM Serif Display',serif;font-style:italic;font-size:26px;
          color:rgba(255,103,25,.18);pointer-events:none;z-index:0}
        .bl-cert-seal::before{content:'';position:absolute;inset:6px;border-radius:50%;
          border:1px dashed rgba(255,103,25,.09)}
        .bl-deed-fn{max-width:52ch;margin:28px auto 0;font-size:13px;color:#9a8a78;
          text-align:center;line-height:1.65}

        /* ── Pricing ── */
        .bl-pricing-wrap{border-top:1px solid #ece4d1;
          max-width:1180px;margin:0 auto;padding:64px 36px 72px}
        .bl-pricing-hd{margin-bottom:40px}
        .bl-pricing-intro{font-size:16px;color:#5a6056;line-height:1.7;
          margin-top:14px;max-width:50ch}
        .bl-pg{display:grid;grid-template-columns:repeat(3,1fr);gap:14px}
        @media(max-width:800px){.bl-pg{grid-template-columns:1fr}}
        .bl-pc{background:#fff;border:1px solid #ece4d1;border-radius:16px;
          padding:26px 26px 30px;transition:border-color .15s}
        .bl-pc:hover{border-color:#c8bfb0}
        .bl-pc-feat{background:#1c2018;border-color:#1c2018;color:#f5ede0}
        .bl-pc-tier{font-size:10.5px;font-weight:600;letter-spacing:.12em;
          text-transform:uppercase;color:#9a8a78;margin-bottom:18px}
        .bl-pc-feat .bl-pc-tier{color:rgba(245,237,224,.45)}
        .bl-pc-amt{font-size:46px;font-weight:700;letter-spacing:-.04em;line-height:1;margin-bottom:4px}
        .bl-pc-per{font-size:13px;color:#9a8a78;margin-bottom:20px}
        .bl-pc-feat .bl-pc-per{color:rgba(245,237,224,.45)}
        .bl-pc-feats{list-style:none;padding:0;margin:0 0 24px;
          display:flex;flex-direction:column;gap:7px}
        .bl-pc-feats li{font-size:13.5px;color:#5a6056;padding-left:16px;
          position:relative;line-height:1.4}
        .bl-pc-feat .bl-pc-feats li{color:rgba(245,237,224,.68)}
        .bl-pc-feats li::before{content:'–';position:absolute;left:0;color:#FF6719}
        .bl-pc-feat .bl-pc-feats li::before{color:rgba(255,103,25,.55)}
        .bl-pc-cta{display:block;text-align:center;padding:11px 18px;border-radius:100px;
          border:1.5px solid #d8d1c2;font-size:14px;font-weight:600;color:#1c2018;
          cursor:pointer;text-decoration:none;
          transition:border-color .12s,background .12s}
        .bl-pc-cta:hover{border-color:#1c2018;background:rgba(255,103,25,.05)}
        .bl-pc-feat .bl-pc-cta{background:#f5ede0;border-color:#f5ede0;color:#1c2018}
        .bl-pc-feat .bl-pc-cta:hover{opacity:.88}
      `}</style>

      <nav className="bl-nav">
        <span className="bl-mark">b</span>
        <span className="bl-brand">Better Letter</span>
        <span className="bl-spacer"/>
        <a href="https://better-letter.pages.dev#layer">Writers</a>
        <a href="https://better-letter.pages.dev#pricing">Pricing</a>
        <a href="https://better-letter.pages.dev#deed">About</a>
        <button className="bl-back" onClick={onBack}>← back to Domstack</button>
      </nav>

      <section className="bl-hero">
        <div>
          <div className="bl-eyebrow"><span className="ddot"></span>You typed "switch." We were hoping you would.</div>
          <h1 className="bl-h1">Have you considered <em>just being nice</em> on the internet?</h1>
          <p className="bl-sub">
            Better Letter is the soft newsletter platform for writers who would prefer not to dominate, submit, or spitroast anyone. Just publish. Drink tea. Reply kindly. Go for a walk.
          </p>
          <div className="bl-cta-row">
            <a href="https://better-letter.pages.dev" className="bl-btn bl-btn-p">Switch in 90 seconds →</a>
            <a href="https://better-letter.pages.dev" className="bl-btn bl-btn-g">Take a tour</a>
            <span className="bl-micro">No safeword required.</span>
          </div>
        </div>

        <div className="bl-card">
          <span className="ribbon">Real story</span>
          <div className="ph">[ rooftop office, the newsletter that paid for it ]</div>
          <h3>I was writing Substack a $2,400 check every month.</h3>
          <p className="by"><b>Sam Holloway</b> · The Compound Brief · 41k subscribers</p>
          <p className="ex">
            The month I crossed $24k in revenue, I finally did the math. Twenty-four hundred dollars, gone, every single month, to a platform where I owned nothing. No domain. No real portability. Just a CSV if I ever wanted out. I switched in eleven minutes. Haven't thought about it since.
          </p>
          <div className="stats">
            <span>$2,400 → $49/mo</span>
            <span>⚡ 11 min</span>
            <span>✓ 41k, yours</span>
          </div>
        </div>
      </section>

      <section className="bl-feat">
        <div className="bl-feat-card">
          <span className="gl"><Ico.chain/></span>
          <h4>Compound commands</h4>
          <p>One instruction. Multiple actions across your whole stack. Schedule, segment, and analyze without touching a settings panel.</p>
        </div>
        <div className="bl-feat-card">
          <span className="gl"><Ico.trend/></span>
          <h4>Churn prediction</h4>
          <p>ML model trained on engagement signals. Know who's drifting before they leave, and exactly what to send them.</p>
        </div>
        <div className="bl-feat-card">
          <span className="gl"><Ico.check/></span>
          <h4>5-minute deploy</h4>
          <p>Automated setup to your own accounts. No DevOps required. You end the session owning every credential.</p>
        </div>
      </section>

      {/* ── Math strip ── */}
      <div className="bl-math-wrap">
        <div className="bl-math">
          <div className="bl-math-col">
            <p className="bl-math-lbl">Substack takes</p>
            <p className="bl-math-num bl-math-bad">10%</p>
            <p className="bl-math-sub">of every dollar your readers pay you</p>
          </div>
          <div className="bl-math-vs">vs</div>
          <div className="bl-math-col">
            <p className="bl-math-lbl">Better Letter charges</p>
            <p className="bl-math-num bl-math-good">$49<span style={{fontSize:'0.45em',letterSpacing:'-.01em'}}>/mo</span></p>
            <p className="bl-math-sub">flat. We have no idea what you earn.</p>
          </div>
        </div>
      </div>

      {/* ── Deed / Certificate ── */}
      <div className="bl-deed-wrap">
        <div className="bl-deed-hd">
          <span className="bl-sect-ey">The ownership model</span>
          <h2 className="bl-sect-h">Every account is yours.<br/><em>We just orchestrate.</em></h2>
        </div>
        <div className="bl-cert">
          <p className="bl-cert-ey">Certificate of Stack Ownership</p>
          <p className="bl-cert-tag">Better Letter deploys to your accounts. Not ours. Not theirs.</p>
          <hr className="bl-cert-rule"/>
          <ul className="bl-cert-list" aria-label="Owned infrastructure">
            {[
              ['Ghost CMS',        'Your account', false],
              ['Cloudflare DNS',   'Your account', false],
              ['Railway (hosting)','Your account', false],
              ['Resend (email)',   'Your account', false],
              ['GitHub (backups)', 'Your account', false],
              ['Payment Gateway',  'Your money',   true],
            ].map(([svc, own, money]) => (
              <li key={svc} className="bl-cert-item">
                <span className="bl-cert-svc">{svc}</span>
                <span className="bl-cert-dots" aria-hidden="true"/>
                <span className={'bl-cert-own' + (money ? ' bl-cert-money' : '')}>{own}</span>
              </li>
            ))}
          </ul>
          <hr className="bl-cert-rule"/>
          <p className="bl-cert-legal">
            Better Letter provides orchestration services only. All revenue flows directly to creator-controlled payment gateways.<br/>
            <em>Cancel anytime. Everything above continues to run.</em>
          </p>
          <div className="bl-cert-seal" aria-hidden="true">BL</div>
        </div>
        <p className="bl-deed-fn">Any webhook-capable payment processor works: Stripe, Adyen, PayPal, Square, Paddle, or your own. Your gateway, your reader relationships.</p>
      </div>

      {/* ── Pricing ── */}
      <div style={{borderTop:'1px solid #ece4d1'}}>
        <div className="bl-pricing-wrap">
          <div className="bl-pricing-hd">
            <span className="bl-sect-ey">Pricing</span>
            <h2 className="bl-sect-h">Flat fee.<br/><em>No percentage.</em></h2>
            <p className="bl-pricing-intro">We have no idea what you earn and we'd like to keep it that way. Pick a tier, we deploy your stack, done.</p>
          </div>
          <div className="bl-pg">
            <div className="bl-pc">
              <p className="bl-pc-tier">Starter</p>
              <p className="bl-pc-amt">$25</p>
              <p className="bl-pc-per">per month</p>
              <ul className="bl-pc-feats">
                <li>Self-owned stack deployment</li>
                <li>Ghost CMS + Cloudflare + Resend</li>
                <li>Up to 5,000 subscribers</li>
                <li>Your own payment gateway</li>
              </ul>
              <a href="https://better-letter.pages.dev" className="bl-pc-cta">Get started</a>
            </div>
            <div className="bl-pc bl-pc-feat">
              <p className="bl-pc-tier">Pro</p>
              <p className="bl-pc-amt">$49</p>
              <p className="bl-pc-per">per month</p>
              <ul className="bl-pc-feats">
                <li>Everything in Starter</li>
                <li>AI MCP agents + compound commands</li>
                <li>Churn prediction model</li>
                <li>Semantic search across archive</li>
                <li>Unlimited subscribers</li>
                <li>GitHub automated backups</li>
              </ul>
              <a href="https://better-letter.pages.dev" className="bl-pc-cta">Get started</a>
            </div>
            <div className="bl-pc">
              <p className="bl-pc-tier">Scale</p>
              <p className="bl-pc-amt">$149</p>
              <p className="bl-pc-per">per month</p>
              <ul className="bl-pc-feats">
                <li>Everything in Pro</li>
                <li>White-label for agencies</li>
                <li>Multi-publication management</li>
                <li>Priority support</li>
                <li>Custom integrations</li>
              </ul>
              <a href="https://better-letter.pages.dev" className="bl-pc-cta">Get started</a>
            </div>
          </div>
        </div>
      </div>

      <div className="bl-foot">
        Not a parody. Every service in the certificate above deploys to accounts you own and control. We charge $49 a month. We have no idea what you earn and we'd like to keep it that way. Cancel anytime. The stack keeps running because it's yours.
      </div>
    </div>
  );
}

// ───────────────────────── App ─────────────────────────
const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "palette": ["#c8163b", "#6b0a1d", "#0a0708"],
  "intensity": "playful",
  "fontPair": "garamond"
}/*EDITMODE-END*/;

function App() {
  const [t, setTweak] = useTweaks(TWEAK_DEFAULTS);
  const [route, setRoute] = useState('home');
  const [mode, setMode] = useState('dom'); // dom | sub | switch
  const [liked, setLiked] = useState({});
  const [subs, setSubs] = useState({});
  const [buy, setBuy] = useState(null);
  const [toast, setToast] = useState(null);

  // Apply palette tweaks
  useEffect(()=>{
    const [c1, c2, bg] = t.palette || ['#c8163b','#6b0a1d','#0a0708'];
    const root = document.documentElement;
    root.style.setProperty('--crimson', c1);
    root.style.setProperty('--crimson-2', c2);
    root.style.setProperty('--bg', bg);
  }, [t.palette]);

  // Apply font tweak
  useEffect(()=>{
    const root = document.documentElement;
    if (t.fontPair === 'playfair') {
      document.querySelectorAll('.serif, h1, h2, h3, h4, .brand-name, .post-title, .hero h1')
        .forEach(el => el.style.fontFamily = "'Playfair Display','DM Serif Display',serif");
    }
  }, [t.fontPair]);

  const showToast = (msg) => {
    setToast(msg);
    setTimeout(()=>setToast(null), 2400);
  };

  const onLike = (id) => {
    setLiked(prev => ({...prev, [id]: !prev[id]}));
  };
  const onSub = (id) => {
    setSubs(prev => ({...prev, [id]: !prev[id]}));
    showToast(subs[id] ? 'Unsubscribed. Their loss.' : 'Subscribed. Welcome to the dungeon.');
  };
  const onBuy = (svc) => setBuy(svc);
  const onConfirmBuy = () => {
    showToast(`Booked: ${buy.name} ${buy.italic}. Invoice sent.`);
    setBuy(null);
  };
  const onStart = () => showToast('Starting your Domstack… please choose a safeword.');

  return (
    <div className="app">
      {mode === 'switch' && <BetterLetterTakeover onBack={()=>{setMode('dom'); showToast('Welcome home. The dungeon missed you.');}} />}
      <Sidebar route={route} setRoute={setRoute} onCreate={onStart}/>

      <main className="main">
        <div className="topbar">
          <div className="switch" role="tablist" aria-label="Mode">
            <button className={mode==='dom'?'on':''} onClick={()=>{setMode('dom'); showToast('Dom mode: you are on top of the feed.')}}>Dom</button>
            <button className={mode==='sub'?'on':''} onClick={()=>{setMode('sub'); showToast('Sub mode: feed will now serve you.')}}>Sub</button>
            <button className={mode==='switch'?'on':''} onClick={()=>{setMode('switch'); showToast('Switch → redirecting to Better Letter…')}}>Switch</button>
          </div>
          <span className="pill"><span className="dot"></span>{mode === 'dom' ? 'Active session' : mode === 'sub' ? 'Awaiting orders' : 'Looking for the exit'}</span>
          <span className="top-spacer"/>
          <a className="top-link">Help</a>
          <a className="top-link">For writers</a>
        </div>

        <Hero mode={mode} onPrimary={onStart}/>

        <div className="tabs">
          <span className="tab active">For you <span className="ct">·  fresh</span></span>
          <span className="tab">Following</span>
          <span className="tab">Confessions <span className="ct">NEW</span></span>
          <span className="tab">Discipline</span>
        </div>

        <ServicesGrid mode={mode} services={mode==='sub' ? SUB_SERVICES : DOM_SERVICES} onBuy={onBuy}/>

        <div className="section-h" style={{marginTop:44}}>
          <h2>{mode==='sub' ? <>From the <em>confessional</em></> : <>From the <em>feed</em></>}</h2>
          <p>{mode==='sub'
            ? 'Writers in active repentance. Drafts shared without dignity.'
            : 'Subscribers we admire. Newsletters we have, technically, ruined.'}</p>
        </div>

        <div className="feed">
          {(mode==='sub' ? SUB_POSTS : DOM_POSTS).map(p => (
            <Post key={p.id} post={p}
                  onLike={onLike} onSub={onSub}
                  isLiked={!!liked[p.id]} isSubbed={!!subs[p.id]}/>
          ))}
        </div>

        <div className="endcap">
          <h4>{mode==='sub' ? 'You may stop reading now.' : 'That is enough for one day.'}</h4>
          <p>{mode==='sub'
            ? 'You did not earn this rest. Take it anyway. Tomorrow we draft.'
            : 'You have been very, very good. Read tomorrow\'s at 06:30 sharp.'}</p>
          <div className="mono">— The Editors, Domstack</div>
        </div>
      </main>

      <RightRail mode={mode}
                 leaderboard={mode==='sub' ? SUB_LEADERBOARD : DOM_LEADERBOARD}
                 onStart={onStart}/>

      {buy && (
        <div className="modal-bg" onClick={(e)=>{ if(e.target===e.currentTarget) setBuy(null); }}>
          <div className="modal">
            <button className="modal-x" onClick={()=>setBuy(null)} aria-label="Close">✕</button>
            <span className="svc-glyph">{React.createElement(Ico[buy.icon])}</span>
            <h3>{buy.name} <em>{buy.italic}</em></h3>
            <p style={{margin:0,color:'var(--ink-dim)',fontSize:14,lineHeight:1.55}}>{buy.desc}</p>
            <div className="price-row">
              <b>{buy.price}</b><span>{buy.unit}</span>
              <span style={{marginLeft:'auto',color:'var(--blush)',fontSize:11,letterSpacing:'.08em',textTransform:'uppercase'}}>Consensual · Reversible</span>
            </div>
            <ul className="modal-includes">
              <li><Ico.check/><span>Pre-session contract & emoji-coded safewords</span></li>
              <li><Ico.check/><span>White-glove competitor outreach (literally; we wear gloves)</span></li>
              <li><Ico.check/><span>Aftercare: a follow-up email and a vegan brownie</span></li>
              <li><Ico.check/><span>30-day no-questions-asked safewording policy</span></li>
            </ul>
            <button className="btn btn-primary" style={{width:'100%',justifyContent:'center',background:'linear-gradient(180deg,var(--crimson),var(--crimson-2))',color:'#fff'}} onClick={onConfirmBuy}>
              Book session — {buy.price}
            </button>
          </div>
        </div>
      )}

      {toast && (
        <div className="toast">
          <span className="dot"></span>{toast}
        </div>
      )}

      <TweaksPanel>
        <TweakSection label="Palette" />
        <TweakColor label="Theme" value={t.palette}
          options={[
            ['#c8163b','#6b0a1d','#0a0708'],
            ['#7a1fa2','#3d0a55','#0c0810'],
            ['#1d4ed8','#0b2a6b','#070b14'],
            ['#b45309','#5a2607','#100905'],
            ['#0f766e','#053e3a','#06100f'],
          ]}
          onChange={(v)=>setTweak('palette', v)} />
        <TweakSection label="Tone" />
        <TweakRadio label="Intensity" value={t.intensity}
          options={['playful','spicy','hardcore']}
          onChange={(v)=>{setTweak('intensity', v); showToast(`Intensity: ${v}`);}} />
        <TweakSection label="Type" />
        <TweakRadio label="Display font" value={t.fontPair}
          options={['garamond','playfair']}
          onChange={(v)=>setTweak('fontPair', v)} />
      </TweaksPanel>
    </div>
  );
}

ReactDOM.createRoot(document.getElementById('root')).render(<App/>);
