/* global React */
const { useState, useEffect, useRef, useMemo } = React;

// ---------- Content ----------
const PAGES = [
  { id: "welcome", title: "Welcome to Party Studios", kicker: "Start here" },
  { id: "cannon",  title: "Super Bunny Cannon", kicker: "Games" },
  { id: "tos",     title: "Terms of service", kicker: "Policies" },
];

// Group sidebar
const SIDEBAR = [
  { kicker: "Start here", ids: ["welcome"] },
  { kicker: "Games", ids: ["cannon"] },
  { kicker: "Policies", ids: ["tos"] },
];

// ---------- Tweaks defaults (persisted via host) ----------
const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "density": "cozy",
  "accent": "violet",
  "sidebar": "flat"
}/*EDITMODE-END*/;

const ACCENTS = {
  lime:   { hex: "#c5f14b", ink: "#0a0d02" },
  violet: { hex: "#B99CFF", ink: "#0a0616" },
  orange: { hex: "#ff8a3d", ink: "#160a02" },
  cyan:   { hex: "#6ad8ff", ink: "#021016" },
  white:  { hex: "#f4f4f4", ink: "#000000" },
};

// ---------- Icons ----------
const Icon = {
  search: () => (
    <svg className="s-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.4">
      <circle cx="7" cy="7" r="4.5"/><path d="M10.5 10.5L14 14"/>
    </svg>
  ),
  arrow: ({dir="right"}) => (
    <svg width="12" height="12" viewBox="0 0 12 12" fill="none" stroke="currentColor" strokeWidth="1.4" style={{transform: dir==="left"?"rotate(180deg)":"none"}}>
      <path d="M2 6h8M7 3l3 3-3 3"/>
    </svg>
  ),
  ext: () => (
    <svg width="10" height="10" viewBox="0 0 10 10" fill="none" stroke="currentColor" strokeWidth="1.4">
      <path d="M3 3h4v4"/><path d="M7 3L3 7"/>
    </svg>
  ),
};

// ---------- Small components ----------
function PixelRule() {
  return <div className="pixel-rule">{Array.from({length:48}).map((_,i)=><span key={i}/>)}</div>;
}

function Callout({ kind="note", title, children }) {
  return (
    <div className={`callout ${kind}`}>
      <div>
        <h4>{title}</h4>
        <p>{children}</p>
      </div>
    </div>
  );
}

function KV({ rows }) {
  return (
    <div className="kv">
      {rows.map(([k,v],i)=>(
        <div className="kv-row" key={i}>
          <div className="k">{k}</div>
          <div className="v">{v}</div>
        </div>
      ))}
    </div>
  );
}

function Steps({ items }) {
  return (
    <div className="steps">
      {items.map((s,i)=>(
        <div className="step" key={i}>
          <div className="step-n">{String(i+1).padStart(2,"0")}</div>
          <div className="step-body">
            <h4>{s.title}</h4>
            <p>{s.body}</p>
          </div>
        </div>
      ))}
    </div>
  );
}

// ---------- Pages ----------
const Pages = {
  welcome: {
    crumb: ["Docs", "Welcome"],
    title: "Welcome to Party Studios",
    sub: "Everyone wants to trade. They just don't know it yet.",
    toc: [
      {id:"thesis",  label:"The Thesis"},
      {id:"megaeth", label:"Live on MegaETH"},
      {id:"catalog", label:"Game Catalogue"},
    ],
    body: () => (<>
      <h2 className="section" id="thesis"><span className="num">01</span>The Thesis</h2>
      <p className="lede">
        Party Studios builds onchain games deeply integrated with rulebook clarity,
        outcome finality, and verifiable fairness. We enable you to participate and
        play the greatest games in financial history. Play, instead of being played.
      </p>
      <p>
        We're defining the lines between trading and play. Whatever happens on the
        other side of an action, you know exactly why it happened. Actions, outcomes,
        and inputs feel rewarded. Every single interaction has a purpose and meaning.
      </p>

      <h2 className="section" id="megaeth"><span className="num">02</span>Live on MegaETH</h2>
      <p>
        Party games run on <strong>MegaETH</strong> since our instantaneous gameplay
        requires RNG that moves at the speed of real-time. Each of the world's most
        popular multiplayer games require instant RNG within the gameplay loop to
        satisfy competitive requirements. The same kind of roll that decides the spray
        variance within a shot in Valorant or a crit in LoL needs to be resolved within
        frames. MegaETH is the only chain fast enough to do that provably onchain,
        making every outcome verifiable at the same speed as the game you're playing.
      </p>

      <h2 className="section" id="catalog"><span className="num">03</span>Game Catalogue</h2>
      <p>
        <strong>Pump Cannon</strong> is Party Studios' first title and is live now on
        MegaETH. We're insanely proud to launch this, as a proof of concept of RNG at
        the speed of realtime. MegaETH is the only chain where this is possible, and
        you can experience it first-hand. Edges, odds, and payout curves are all
        documented and verifiable on-chain. Pump Cannon sets our current standard for
        verifiable instantaneous RNG: fast, legible, and provably fair.
      </p>
      <p>
        <strong>&lt;Redacted&gt;</strong> is our next release. It's a skill-based
        spatial wagering game built on the durability principle that makes poker work.
        Human competitiveness adds infinite variability to gameplay, where RNG
        introduces a profound excitement where anything can happen. Just like in trad
        and on-chain markets, winners and losers walk away believing they played well
        — and both are right. Where poker relies on hand memorization and years of
        pattern recognition, &lt;Redacted&gt; replaces that with spatial intuition, so
        a new player can understand the entire game within a single round. More
        details as we get closer to launch.
      </p>
    </>),
  },

  cannon: {
    crumb: ["Docs", "Games", "Super Bunny Cannon"],
    title: "🐰 Super Bunny Cannon",
    sub: "A fast-paced onchain launch game. Pick your entry, pick your shot count, fire — each bunny lands in a random payout zone, instantly and verifiably.",
    toc: [
      {id:"overview", label:"Overview"},
      {id:"how", label:"How to play"},
      {id:"zones", label:"Reward bar outcomes"},
      {id:"example", label:"Worked example"},
      {id:"mechanics", label:"Core mechanics"},
      {id:"rtp", label:"RTP & risk"},
      {id:"fairness", label:"Verifiable fairness"},
      {id:"lock", label:"How the game is locked"},
      {id:"verify", label:"How to verify a round"},
      {id:"code", label:"Verification code"},
      {id:"open", label:"Open data sources"},
      {id:"summary", label:"Summary"},
    ],
    body: () => (<>
      <p className="lede">
        Super Bunny Cannon is a fast-paced onchain launch game where players choose
        their bet size and fire 1, 3, or 10 bunny shots at once. Every shot is
        instant, independent, and verifiably fair.
      </p>

      <h2 className="section" id="overview"><span className="num">01</span>Overview</h2>
      <p>
        The loop is deliberately simple: choose your size, choose your shot count,
        fire, and see where your bunnies land. Each shot resolves independently using
        the same fairness model, so a batch of 10 is just ten single shots settled in
        parallel. Results are shown instantly after launch.
      </p>

      <h2 className="section" id="how"><span className="num">02</span>How to play</h2>
      <Steps items={[
        {title:"Choose your bet size", body:"Set the entry amount per shot."},
        {title:"Choose your shot count", body:"Fire 1, 3, or 10 bunnies at once."},
        {title:"Press and hold to launch", body:"Your shots are submitted as a single onchain round."},
        {title:"Settle", body:"Each bunny lands in a random payout zone. Your payout is determined by where each bunny lands, totalled across the batch."},
      ]}/>

      <h2 className="section" id="zones"><span className="num">03</span>Reward bar outcomes</h2>
      <p>
        Each shot resolves independently into one of the payout zones on the reward
        bar:
      </p>
      <KV rows={[
        ["Dead",  "lose that shot"],
        ["1×",    "receive 1× that shot's entry"],
        ["2×",    "receive 2× that shot's entry"],
        ["3×",    "receive 3× that shot's entry"],
        ["5×",    "receive 5× that shot's entry"],
        ["10×",   "receive 10× that shot's entry"],
        ["25×",   "receive 25× that shot's entry"],
      ]}/>

      <h2 className="section" id="example"><span className="num">04</span>Worked example</h2>
      <p>
        Say you choose a <code>0.001 ETH</code> entry and fire 3 shots. Each bunny
        gets its own independent result. If the three shots land on{" "}
        <strong>Dead</strong>, <strong>2×</strong>, and <strong>5×</strong>:
      </p>
      <div className="formula">
        <span className="cmt"># per-shot entry: 0.001 ETH, 3 shots</span>{"\n"}
        <span className="lbl">total wagered</span> = 3 × 0.001 = <span className="hl">0.003 ETH</span>{"\n"}
        <span className="lbl">returns</span>        = 0× + 2× + 5× = <span className="hl">7× of a single shot entry</span>{"\n"}
        <span className="lbl">total return</span>   = 7 × 0.001 = <span className="hl">0.007 ETH</span>
      </div>

      <h2 className="section" id="mechanics"><span className="num">05</span>Core mechanics</h2>
      <ul>
        <li>Players choose <strong>bet size</strong> and <strong>shot count</strong>.</li>
        <li>Shots can be fired in batches of <strong>1, 3, or 10</strong>.</li>
        <li>Each bunny lands in a <strong>random payout zone</strong>.</li>
        <li>Each shot is resolved <strong>independently</strong>.</li>
        <li>Results are shown instantly after launch.</li>
      </ul>

      <h2 className="section" id="rtp"><span className="num">06</span>RTP & risk</h2>
      <p>
        Super Bunny Cannon is designed so that every shot has a defined payout
        distribution. Most shots will land in lower-return or losing zones; rare shots
        land in high-multiplier zones. Firing more shots at once increases total
        exposure, but each shot still resolves independently under the same rules.
        The game's RTP is determined by the full weighted distribution of payout
        zones across all possible outcomes.
      </p>
      <Callout kind="warn" title="High variance is not a strategy">
        You cannot outrun a negative-EV game by firing bigger or firing more.
        Bankroll management and knowing when to stop matter more than batch size.
      </Callout>

      <PixelRule/>

      <h2 className="section" id="fairness"><span className="num">07</span>Verifiable fairness</h2>
      <p>
        Super Bunny Cannon is mathematically provable and verifiably fair. We use a
        signed commit-reveal system secured by a smart contract: the outcome is
        locked by the server before you play, and the final result is only determined
        once your launch is submitted with your own entropy input. This prevents the
        house from changing the outcome after the fact or pre-selecting results
        unfairly.
      </p>

      <h2 className="section" id="lock"><span className="num">08</span>How the game is locked</h2>
      <Steps items={[
        {title:"Server commitment", body:"Before launch, the server generates a secret seed and computes its hash. It signs this hash and sends it to your browser."},
        {title:"Your launch input", body:"When you launch, your client generates a unique user salt. The signed server commitment and your salt are submitted to the smart contract through the createGame function."},
        {title:"Onchain lock", body:"The contract stores the gameSeedHash onchain. Because the server already signed this hash, it is bound to that seed and cannot swap it later."},
        {title:"Reveal and resolution", body:"After the launch resolves, the server reveals the raw secret seed when calling the settlement function. The contract hashes the revealed seed and verifies that it matches the original committed hash. If it does not match, the transaction fails."},
      ]}/>
      <Callout title="What this guarantees">
        The server cannot alter the committed randomness after you play.
      </Callout>

      <h2 className="section" id="verify"><span className="num">09</span>How to verify a round</h2>
      <p>
        Each shot outcome is selected deterministically from the combined entropy of
        the server's secret seed and your user salt. You can independently verify any
        round by checking the onchain commitment and reproducing the outcome
        selection logic.
      </p>
      <Steps items={[
        {title:"Find the hash", body:"Locate your createGame transaction on the block explorer and copy the gameSeedHash from the emitted event logs."},
        {title:"Find the revealed seed and salt", body:"Locate the settlement transaction and copy the revealed gameSeed and the salt."},
        {title:"Verify integrity", body:"Hash the revealed gameSeed with Keccak-256. It must exactly match the original gameSeedHash."},
        {title:"Reproduce the result", body:"Run the selection logic using the revealed gameSeed and salt to reproduce the shot outcomes."},
      ]}/>

      <h2 className="section" id="code"><span className="num">10</span>Verification code</h2>
      <p>
        The following logic shows how a game result can be derived from the committed
        seed and user salt. If multiple shots are fired at once, each shot is derived
        using the same fairness model, with each result resolved independently.
      </p>
      <div className="formula">
        <span className="cmt">{"// TypeScript · viem"}</span>{"\n"}
        import {"{"} encodeAbiParameters, Hex, keccak256, parseAbiParameters {"}"} from "viem";{"\n\n"}
        const payoutResults: any[] = []; <span className="cmt">{"// payout table source"}</span>{"\n\n"}
        function <span className="hl">runGame</span>(gameSeed: Hex, salt: Hex): any {"{"}{"\n"}
        {"  "}const combinedSeed = keccak256({"\n"}
        {"    "}encodeAbiParameters({"\n"}
        {"      "}parseAbiParameters("bytes32, bytes32"),{"\n"}
        {"      "}[gameSeed, salt]{"\n"}
        {"    "}){"\n"}
        {"  "});{"\n\n"}
        {"  "}const seedNumber  = parseInt(combinedSeed.slice(2, 10), 16);{"\n"}
        {"  "}const randomIndex = seedNumber % payoutResults.length;{"\n\n"}
        {"  "}return payoutResults[randomIndex];{"\n"}
        {"}"}
      </div>

      <h2 className="section" id="open"><span className="num">11</span>Open data sources</h2>
      <p>
        Transparency is at the core of Super Bunny Cannon. Players should be able to
        inspect the game logic, review the payout structure, and verify how outcomes
        are generated.
      </p>
      <KV rows={[
        ["Game build", <a href="#" style={{color:"var(--accent)"}}>View on IPFS <Icon.ext/></a>],
        ["Outcome data / payout table", <a href="#" style={{color:"var(--accent)"}}>View on IPFS <Icon.ext/></a>],
        ["Smart contract", <a href="https://megaeth.blockscout.com/address/0x6CA22286D318250c823e38F741da26878e96fC4D" target="_blank" rel="noreferrer" style={{color:"var(--accent)"}}>View contract <Icon.ext/></a>],
      ]}/>

      <PixelRule/>

      <h2 className="section" id="summary"><span className="num">12</span>Summary</h2>
      <p>
        Super Bunny Cannon is a simple, high-speed multiplier game: choose your
        entry, choose 1, 3, or 10 shots, fire, watch each bunny land in a payout
        zone, and settle instantly with verifiable fairness.
      </p>
    </>),
  },

  tos: {
    crumb: ["Docs", "Policies", "Terms of service"],
    title: "Terms of service",
    sub: "These Terms govern your access to and use of Pump Party's websites, applications, smart contracts, and related services. By accessing or using Pump Party, you agree to be bound by them. If you do not agree, please discontinue use immediately.",
    toc: [
      {id:"definitions", label:"1. Definitions"},
      {id:"eligibility", label:"2. Eligibility"},
      {id:"jurisdictions", label:"3. Restricted jurisdictions"},
      {id:"gameplay", label:"4. Deposits, wagers & payouts"},
      {id:"withdrawals", label:"5. Withdrawals & refunds"},
      {id:"responsible", label:"6. Responsible play"},
      {id:"integrity", label:"7. Game integrity"},
      {id:"prohibited", label:"8. Prohibited conduct"},
      {id:"ip", label:"9. Intellectual property"},
      {id:"warranties", label:"10. Disclaimer of warranties"},
      {id:"liability", label:"11. Limitation of liability"},
      {id:"disputes", label:"12. Dispute resolution"},
      {id:"termination", label:"13. Termination"},
      {id:"taxes", label:"14. Taxes & reporting"},
      {id:"third-party", label:"15. Third-party services"},
      {id:"privacy", label:"16. Privacy"},
      {id:"changes", label:"17. Changes to terms"},
      {id:"contact", label:"18. Contact"},
    ],
    body: () => (<>
      <p className="lede">
        Welcome to <strong>Pump Party</strong>, a platform where players can participate
        in blockchain-based games, competitions, and related interactive experiences (the
        "<strong>Games</strong>").
      </p>

      <h2 className="section" id="definitions"><span className="num">01</span>Definitions</h2>
      <KV rows={[
        [<>"User," "you," "your"</>, "any person using the Platform."],
        [<>"Pump Party," "we," "us"</>, "the operator of the Platform and its affiliates."],
        ["\"Account\"", "your registered profile, connected wallet, or other credentials used to access the Platform."],
        ["\"Virtual Currency\"", "in-platform tokens, credits, points, or balances used for gameplay or participation."],
        ["\"Real Money\"", "fiat or cryptocurrency funds deposited, staked, or otherwise used in connection with the Platform."],
        [<>"Wager" / "Bet"</>, "any amount staked, risked, or committed on a Game outcome or competition result."],
        [<>"Prize" / "Payout"</>, "any reward, winnings, or return resulting from successful participation."],
        ["\"Game\"", "any game, competition, or gameplay experience made available through Pump Party."],
      ]}/>

      <h2 className="section" id="eligibility"><span className="num">02</span>Eligibility</h2>
      <ul>
        <li>You must be of legal age to participate in wagering, gaming, or similar activities in your jurisdiction, and at least 18 years old.</li>
        <li>You may not use the Platform from any location where participation in such activities is restricted or illegal.</li>
        <li>Pump Party reserves the right to restrict or block access based on region, IP address, wallet activity, sanctions screening, or compliance policy.</li>
        <li>You agree to provide accurate information where requested and to maintain the security of your account or wallet.</li>
        <li>Pump Party may require identity verification, KYC/AML checks, or other compliance measures before allowing deposits, withdrawals, payouts, or continued use.</li>
      </ul>

      <h2 className="section" id="jurisdictions"><span className="num">03</span>Restricted jurisdictions</h2>
      <p>
        Access to the Platform may be limited in certain countries, territories, or
        regions due to applicable laws, regulations, or internal compliance policies.
      </p>
      <Callout kind="warn" title="Your responsibility">
        It is your responsibility to determine whether accessing or using Pump Party is
        lawful where you reside or access the Platform before registering, depositing,
        wagering, or playing. Pump Party makes no representation that its services are
        appropriate or lawful in any specific jurisdiction.
      </Callout>
      <p>We reserve the right to:</p>
      <ul>
        <li>Block or restrict access from any country, region, territory, or IP address we consider prohibited or high-risk;</li>
        <li>Refuse registrations, wagers, gameplay, deposits, withdrawals, or payouts from restricted jurisdictions;</li>
        <li>Update our list of restricted jurisdictions at any time without notice.</li>
      </ul>

      <h2 className="section" id="gameplay"><span className="num">04</span>Deposits, wagers, gameplay & payouts</h2>
      <ul>
        <li>Users may fund their participation using approved wallets, payment methods, or supported digital assets.</li>
        <li>All wagers, bets, or equivalent gameplay commitments are final once accepted or confirmed on the Platform.</li>
        <li>Certain Games may involve skill, strategy, timing, chance, or a combination of these factors.</li>
        <li>Pump Party may describe individual Games, including whether outcomes are primarily influenced by skill, chance, or mixed gameplay elements.</li>
        <li>Payouts or rewards may be credited only after verification and may be subject to blockchain network fees, payment processor fees, taxes, or other deductions.</li>
        <li>Pump Party may impose gameplay limits, deposit limits, wager caps, payout restrictions, or other risk controls at any time.</li>
      </ul>

      <h2 className="section" id="withdrawals"><span className="num">05</span>Withdrawals & refunds</h2>
      <ul>
        <li>Withdrawals are subject to verification, compliance review, processing times, and applicable network or transaction fees.</li>
        <li>Once gameplay, wagering, or a Game round has started, refunds will generally not be provided except in cases of verified malfunction, technical error, fraud, or where required by law.</li>
        <li>Pump Party reserves the right to delay, deny, suspend, or reverse withdrawals where necessary for security, compliance, fraud prevention, or operational reasons.</li>
      </ul>

      <h2 className="section" id="responsible"><span className="num">06</span>Responsible play</h2>
      <h3 className="subsection">6.1 Commitment to player well-being</h3>
      <p>
        Pump Party encourages responsible participation. The Platform is intended for
        entertainment purposes and should not be treated as a guaranteed source of income.
      </p>
      <h3 className="subsection">6.2 Understanding the risks</h3>
      <p>Participation may involve real financial risk. Only use funds you can afford to lose.</p>
      <h3 className="subsection">6.3 Staying in control</h3>
      <ul>
        <li>Set limits on time and spending;</li>
        <li>Avoid chasing losses;</li>
        <li>Take breaks regularly;</li>
        <li>Treat winnings as discretionary, not expected.</li>
      </ul>
      <h3 className="subsection">6.4 Self-exclusion & limits</h3>
      <p>
        Users who wish to reduce or stop their participation should use any available
        controls or discontinue use of the Platform.
      </p>
      <h3 className="subsection">6.5 Independent support</h3>
      <p>
        If you or someone you know may be experiencing gambling-related harm, consider
        seeking support from organizations such as:
      </p>
      <ul>
        <li>Gamblers Anonymous</li>
        <li>BeGambleAware</li>
        <li>GamCare</li>
      </ul>
      <h3 className="subsection">6.6 Underage access</h3>
      <p>Users under the legal participation age are strictly prohibited from using the Platform.</p>
      <h3 className="subsection">6.7 Monitoring & fair play</h3>
      <p>
        Pump Party may monitor gameplay and account activity to identify fraud, abuse,
        collusion, manipulation, or problematic behavior and may take preventive or
        enforcement action.
      </p>
      <h3 className="subsection">6.8 User responsibility</h3>
      <p>You are solely responsible for your decisions, wagers, gameplay activity, and use of the Platform.</p>

      <h2 className="section" id="integrity"><span className="num">07</span>Game integrity & fairness</h2>
      <ul>
        <li>Pump Party may use randomization systems, smart contract logic, or other mechanisms intended to support game integrity and fairness.</li>
        <li>Where applicable, certain Games may use provably fair, auditable, or blockchain-verifiable mechanics.</li>
        <li>Malfunctions, exploits, errors, or unintended behavior may void affected gameplay, wagers, or payouts.</li>
        <li>Pump Party may modify, suspend, rebalance, pause, or discontinue any Game or feature at any time.</li>
      </ul>

      <h2 className="section" id="prohibited"><span className="num">08</span>Prohibited conduct</h2>
      <p>You agree not to:</p>
      <ul>
        <li>Use bots, scripts, automation, or unauthorized software;</li>
        <li>Collude or coordinate unfairly with other users;</li>
        <li>Commit fraud, money laundering, market manipulation, or abuse of the Platform;</li>
        <li>Create multiple accounts or wallets for abusive, deceptive, or prohibited purposes;</li>
        <li>Exploit bugs, vulnerabilities, or unintended mechanics;</li>
        <li>Interfere with the operation, security, or integrity of the Platform.</li>
      </ul>
      <p>
        Violations may result in suspension, termination, confiscation of winnings where
        permitted, cancellation of gameplay, or permanent bans.
      </p>

      <h2 className="section" id="ip"><span className="num">09</span>Intellectual property</h2>
      <p>
        All content, software, branding, artwork, code, game design, and materials on the
        Platform are owned by Pump Party or its licensors and are protected by applicable
        intellectual property laws. Unauthorized use, reproduction, or distribution is
        prohibited.
      </p>

      <h2 className="section" id="warranties"><span className="num">10</span>Disclaimer of warranties</h2>
      <p>
        The Platform and all Games are provided <strong>"as is" and "as available."</strong> Pump
        Party makes no guarantees regarding uptime, availability, uninterrupted access,
        error-free operation, or the outcome of any Game or wager.
      </p>

      <h2 className="section" id="liability"><span className="num">11</span>Limitation of liability</h2>
      <p>
        To the fullest extent permitted by law, Pump Party shall not be liable for any
        indirect, incidental, special, consequential, or punitive damages. Pump Party's
        maximum aggregate liability to you shall not exceed the greater of (a) the total
        amount you wagered or paid through the Platform in the prior 30 days, or (b) USD 100.
      </p>
      <p>
        You agree to indemnify and hold harmless Pump Party and its affiliates from any
        claims, liabilities, damages, losses, or expenses arising out of your breach of
        these Terms or misuse of the Platform.
      </p>

      <h2 className="section" id="disputes"><span className="num">12</span>Dispute resolution & governing law</h2>
      <p>
        These Terms shall be governed by the laws of the jurisdiction selected by Pump
        Party, without regard to conflict of laws principles. Any disputes arising out of
        or relating to these Terms or the Platform shall be resolved by binding
        arbitration or in the competent courts of that jurisdiction, unless otherwise
        required by applicable law.
      </p>

      <h2 className="section" id="termination"><span className="num">13</span>Termination</h2>
      <p>
        Pump Party may suspend, restrict, or terminate your access to the Platform at any
        time for any reason, including breach of these Terms, legal or compliance
        concerns, fraud prevention, or security risk. Outstanding gameplay, wagers, or
        balances may be canceled, withheld, or reviewed at our discretion, subject to
        applicable law.
      </p>

      <h2 className="section" id="taxes"><span className="num">14</span>Taxes & reporting</h2>
      <p>
        You are solely responsible for determining and paying any taxes applicable to
        your use of the Platform, including taxes related to winnings, rewards, payouts,
        or digital asset transactions. Pump Party may withhold, report, or disclose
        transaction information where required by law.
      </p>

      <h2 className="section" id="third-party"><span className="num">15</span>Third-party services</h2>
      <p>
        The Platform may integrate with or rely on third-party wallets, blockchain
        networks, payment providers, smart contracts, analytics providers, or other
        services. These third parties operate independently, and Pump Party is not
        responsible for their availability, conduct, security, or data handling practices.
      </p>

      <h2 className="section" id="privacy"><span className="num">16</span>Privacy</h2>
      <p>
        Your use of the Platform is also governed by the <strong>Pump Party Privacy
        Policy</strong>, which explains how we collect, use, and store information.
      </p>

      <h2 className="section" id="changes"><span className="num">17</span>Changes to terms</h2>
      <p>
        Pump Party may revise these Terms at any time. Updated versions will be posted on
        the Platform or associated documentation pages. Your continued use of the Platform
        after updated Terms are posted constitutes acceptance of those revised Terms.
      </p>

      <h2 className="section" id="contact"><span className="num">18</span>Contact</h2>
      <p>
        For questions regarding these Terms or the Platform, please contact Pump Party
        through the official website or support channels designated by the Platform.
      </p>

      <PixelRule/>
      <p className="mono" style={{color:"var(--ink-3)", fontSize:13, textAlign:"center", letterSpacing:"0.04em"}}>
        Play smart. Play responsibly.
      </p>
      <p className="mono" style={{color:"var(--ink-4)", fontSize:12, marginTop:20}}>
        terms.v1 · last updated 2026-04-23
      </p>
    </>),
  },

};

// ---------- App ----------
function App() {
  const [pageId, setPageId] = useState(() => {
    const hash = location.hash.replace("#","");
    return PAGES.find(p=>p.id===hash) ? hash : "welcome";
  });
  const [tweaksOpen, setTweaksOpen] = useState(false);
  const [tw, setTw] = useState(TWEAK_DEFAULTS);
  const [activeToc, setActiveToc] = useState(null);
  const [navOpen, setNavOpen] = useState(false);
  const mainRef = useRef(null);

  const page = Pages[pageId];
  const currentIdx = PAGES.findIndex(p=>p.id===pageId);

  // Apply tweaks to root
  useEffect(()=>{
    const root = document.documentElement;
    root.dataset.density = tw.density;
    root.dataset.sidebar = tw.sidebar;
    const a = ACCENTS[tw.accent] || ACCENTS.lime;
    root.style.setProperty("--accent", a.hex);
    root.style.setProperty("--accent-ink", a.ink);
    // persist via host
    window.parent?.postMessage({type:"__edit_mode_set_keys", edits: tw}, "*");
  }, [tw]);

  // Edit-mode wiring
  useEffect(()=>{
    const onMsg = (e) => {
      if (!e.data) return;
      if (e.data.type === "__activate_edit_mode") setTweaksOpen(true);
      if (e.data.type === "__deactivate_edit_mode") setTweaksOpen(false);
    };
    window.addEventListener("message", onMsg);
    window.parent?.postMessage({type:"__edit_mode_available"}, "*");
    return ()=>window.removeEventListener("message", onMsg);
  }, []);

  // Nav on hash change + intercept in-page section anchors vs page nav
  useEffect(()=>{
    const onHash = () => {
      const h = location.hash.replace("#","");
      if (PAGES.find(p=>p.id===h)) {
        setPageId(h);
        window.scrollTo(0,0);
      }
    };
    window.addEventListener("hashchange", onHash);
    return ()=>window.removeEventListener("hashchange", onHash);
  }, []);

  // Intercept data-nav anchor clicks for cross-page jumps
  useEffect(()=>{
    const onClick = (e) => {
      const a = e.target.closest("a[data-nav]");
      if (!a) return;
      const id = a.getAttribute("href").replace("#","");
      if (PAGES.find(p=>p.id===id)) {
        e.preventDefault();
        location.hash = id;
      }
    };
    document.addEventListener("click", onClick);
    return ()=>document.removeEventListener("click", onClick);
  }, []);

  // Active TOC via scroll spy
  useEffect(()=>{
    const headings = Array.from(document.querySelectorAll("main h2.section, main h3.subsection"));
    if (!headings.length) return;
    const obs = new IntersectionObserver((entries)=>{
      const visible = entries.filter(e=>e.isIntersecting).sort((a,b)=>a.target.offsetTop - b.target.offsetTop);
      if (visible[0]) setActiveToc(visible[0].target.id);
    }, { rootMargin: "-80px 0px -65% 0px", threshold: 0 });
    headings.forEach(h=>obs.observe(h));
    return ()=>obs.disconnect();
  }, [pageId]);

  const prev = currentIdx > 0 ? PAGES[currentIdx-1] : null;
  const next = currentIdx < PAGES.length-1 ? PAGES[currentIdx+1] : null;

  // Close mobile nav on page change
  useEffect(()=>{ setNavOpen(false); }, [pageId]);

  // Cursor-reactive grid parallax — entire grid shifts opposite the cursor
  useEffect(() => {
    if (matchMedia("(prefers-reduced-motion: reduce)").matches) return;
    const root = document.documentElement.style;
    const MAX = 5; // grid shift range (px)
    let tx = 0, ty = 0;
    let cx = 0, cy = 0;
    let rafId = 0;
    const smoothing = 0.18;

    const tick = () => {
      cx += (tx - cx) * smoothing;
      cy += (ty - cy) * smoothing;
      root.setProperty("--bg-x", cx.toFixed(2) + "px");
      root.setProperty("--bg-y", cy.toFixed(2) + "px");
      if (Math.abs(tx - cx) > 0.1 || Math.abs(ty - cy) > 0.1) {
        rafId = requestAnimationFrame(tick);
      } else {
        rafId = 0;
      }
    };

    const onMove = (e) => {
      const nx = e.clientX / innerWidth;
      const ny = e.clientY / innerHeight;
      tx = (0.5 - nx) * MAX * 2;
      ty = (0.5 - ny) * MAX * 2;
      if (!rafId) rafId = requestAnimationFrame(tick);
    };

    window.addEventListener("pointermove", onMove);
    return () => {
      window.removeEventListener("pointermove", onMove);
      if (rafId) cancelAnimationFrame(rafId);
    };
  }, []);

  return (
    <div className="app">
      {/* Topbar */}
      <div className="topbar">
        <div className="brand">
          <button className="nav-toggle" aria-label="Open navigation" onClick={()=>setNavOpen(v=>!v)}>
            <span/><span/><span/>
          </button>
          <span className="brand-mark" aria-hidden/>
          <span className="brand-name">party<span className="dim">/docs</span></span>
        </div>
        <div className="topbar-center"/>
        <div className="topbar-right">
          <a className="tb-cta" href="https://pumpparty.com" target="_blank" rel="noreferrer">Play <Icon.arrow/></a>
        </div>
      </div>

      {navOpen && <div className="nav-scrim" onClick={()=>setNavOpen(false)}/>}

      {/* Sidebar */}
      <aside className={"sidebar" + (navOpen ? " open" : "")}>
        {SIDEBAR.map((grp,gi)=>(
          <div className="sidebar-group" key={gi}>
            <div className="sidebar-kicker">{grp.kicker}</div>
            {grp.ids.map((id, i)=>{
              const p = PAGES.find(x=>x.id===id);
              const idx = PAGES.findIndex(x=>x.id===id) + 1;
              return (
                <a
                  key={id}
                  className={"nav-item" + (pageId===id?" active":"")}
                  href={"#"+id}
                >
                  <span className="n-idx">{String(idx).padStart(2,"0")}</span>
                  <span>{p.title}</span>
                </a>
              );
            })}
          </div>
        ))}
      </aside>

      {/* Main */}
      <main className="content" ref={mainRef}>
        <div className="breadcrumb">
          {page.crumb.map((c,i)=>(
            <React.Fragment key={i}>
              {i>0 && <span className="sep">/</span>}
              <span className={i===page.crumb.length-1?"current":""}>{c}</span>
            </React.Fragment>
          ))}
        </div>
        <h1 className="page-title">{page.title}</h1>
        <p className="page-sub">{page.sub}</p>
        {page.body()}

        <div className="page-nav">
          {prev ? (
            <a className="n" href={"#"+prev.id}>
              <div className="lbl">← Previous</div>
              <div className="ttl">{prev.title}</div>
            </a>
          ) : <div/>}
          {next ? (
            <a className="n next" href={"#"+next.id}>
              <div className="lbl">Next →</div>
              <div className="ttl">{next.title}</div>
            </a>
          ) : <div/>}
        </div>

        <div className="page-foot">
          <div className="meta">Last edited · 2026-04-22 · v0.4</div>
        </div>
      </main>

      {/* TOC */}
      <aside className="toc">
        <div className="toc-kicker">On this page</div>
        {page.toc.map(t => (
          <a
            key={t.id}
            href={"#"+t.id}
            className={activeToc===t.id?"active":""}
            onClick={(e)=>{
              e.preventDefault();
              const el = document.getElementById(t.id);
              if (el) window.scrollTo({top: el.offsetTop - 70, behavior: "smooth"});
            }}
          >
            {t.label}
          </a>
        ))}
      </aside>

      {/* Tweaks */}
      <div className={"tweaks" + (tweaksOpen?" open":"")}>
        <div className="tweaks-head">
          <span><span className="dot"/>Tweaks</span>
          <span style={{cursor:"pointer"}} onClick={()=>setTweaksOpen(false)}>×</span>
        </div>
        <div className="tweaks-body">
          <div className="tw-row">
            <label>Density</label>
            <div className="tw-seg">
              {["cozy","compact"].map(v=>(
                <button key={v} className={tw.density===v?"sel":""} onClick={()=>setTw({...tw, density:v})}>{v}</button>
              ))}
            </div>
          </div>
          <div className="tw-row">
            <label>Accent</label>
            <div className="tw-colors">
              {Object.entries(ACCENTS).map(([k,v])=>(
                <button
                  key={k}
                  className={tw.accent===k?"sel":""}
                  style={{"--c": v.hex}}
                  onClick={()=>setTw({...tw, accent:k})}
                  title={k}
                />
              ))}
            </div>
          </div>
          <div className="tw-row">
            <label>Sidebar style</label>
            <div className="tw-seg">
              {["flat","boxed","bracket"].map(v=>(
                <button key={v} className={tw.sidebar===v?"sel":""} onClick={()=>setTw({...tw, sidebar:v})}>{v}</button>
              ))}
            </div>
          </div>
        </div>
      </div>
    </div>
  );
}

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