> ## Documentation Index
> Fetch the complete documentation index at: https://blank.build/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Fee Calculator

> Estimate creator payouts, staking rewards, buybacks, liquidity compounding, and token position value from a speculative market cap and volume assumption.

# Fee Calculator

<Info>
  Market cap does not create fees by itself. Fees come from trading volume, so
  this calculator pairs a target market cap with a 30-day volume assumption.
</Info>

<iframe
  id="fee-calc-iframe"
  title="Fee Projection Calculator"
  onLoad={(e) => {
const iframe = e.currentTarget;
const sync = () => {
  try {
    const h = iframe.contentDocument && iframe.contentDocument.documentElement
      ? iframe.contentDocument.documentElement.scrollHeight
      : 0;
    if (h) iframe.style.height = h + "px";
  } catch (_) {}
};
sync();
if (!iframe.dataset.bound) {
  iframe.dataset.bound = "1";
  window.addEventListener("message", (ev) => {
    if (ev.data && ev.data.source === "fee-calc" && ev.source === iframe.contentWindow) {
      sync();
    }
  });
}
}}
  style={{ width: "100%", height: "1400px", border: 0, borderRadius: "12px", display: "block" }}
  srcDoc={`<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<style>
*, *::before, *::after { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
body {
  font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  color: #f4f4f5;
  background: #090b07;
  font-size: 14px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}
.app {
  border: 1px solid #2a2f20;
  border-radius: 14px;
  background: #0c1008;
  overflow: hidden;
}
.header {
  padding: 16px 18px;
  border-bottom: 1px solid #2a2f20;
}
.header h2 {
  margin: 0;
  font-size: 17px;
  font-weight: 700;
  color: #ffffff;
  letter-spacing: -0.01em;
}
.header p {
  margin: 4px 0 0;
  color: #a1a1aa;
  font-size: 13px;
  line-height: 1.5;
}
.header p strong { color: #c8ff00; font-weight: 700; }
.scenarios {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 6px;
  padding: 12px 18px;
  background: #0a0d06;
  border-bottom: 1px solid #2a2f20;
}
.scenario-btn {
  display: flex;
  flex-direction: column;
  gap: 2px;
  align-items: flex-start;
  padding: 10px 12px;
  border: 1px solid #2a2f20;
  border-radius: 9px;
  background: #060802;
  color: #d4d4d8;
  font: inherit;
  cursor: pointer;
  text-align: left;
  transition: border-color 0.15s, color 0.15s, background 0.15s;
}
.scenario-btn:hover { border-color: #4d6500; }
.scenario-btn.active {
  border-color: #c8ff00;
  background: #1d2b05;
  color: #c8ff00;
}
.scenario-btn strong { font-size: 13px; font-weight: 700; }
.scenario-btn span { font-size: 11px; color: #71717a; font-weight: 500; }
.scenario-btn.active span { color: #a3d600; }
.layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
}
.controls {
  padding: 16px 18px;
  border-right: 1px solid #2a2f20;
  display: flex;
  flex-direction: column;
  gap: 18px;
}
.results {
  padding: 16px 18px;
  background: #0a0d06;
  display: flex;
  flex-direction: column;
  gap: 14px;
}
.section { display: flex; flex-direction: column; gap: 10px; }
.section-head { display: flex; align-items: center; gap: 8px; }
.section-num {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: #1d2b05;
  color: #c8ff00;
  font-size: 10px;
  font-weight: 800;
}
.section-title {
  color: #f4f4f5;
  font-size: 12px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
}
.section-title-aside {
  color: #71717a;
  font-size: 11px;
  font-weight: 500;
  text-transform: none;
  letter-spacing: 0;
  margin-left: auto;
}
.fields-row {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 10px;
}
.field { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.field-label {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 8px;
}
.field-label-text { color: #e4e4e7; font-size: 13px; font-weight: 600; }
.field-label-aside {
  color: #71717a;
  font-size: 12px;
  font-weight: 500;
  font-variant-numeric: tabular-nums;
}
.input-group {
  display: flex;
  align-items: stretch;
  border: 1px solid #333a29;
  border-radius: 8px;
  background: #060802;
  overflow: hidden;
  transition: border-color 0.15s;
}
.input-group:focus-within { border-color: #4d6500; }
.input-group-affix {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0 12px;
  color: #71717a;
  background: #0c1008;
  font-size: 12px;
  font-weight: 700;
  white-space: nowrap;
}
.input-group-affix.prefix { border-right: 1px solid #2a2f20; }
.input-group-affix.suffix { border-left: 1px solid #2a2f20; }
.input-group input[type="number"] {
  flex: 1;
  min-width: 0;
  border: 0;
  background: transparent;
  color: #ffffff;
  padding: 10px 12px;
  font: inherit;
  font-size: 14px;
  font-weight: 600;
  outline: none;
}
input[type="number"]::-webkit-outer-spin-button,
input[type="number"]::-webkit-inner-spin-button {
  -webkit-appearance: none;
  margin: 0;
}
input[type="number"] { -moz-appearance: textfield; }
.slider { display: flex; flex-direction: column; gap: 6px; }
.slider-head { display: flex; align-items: center; gap: 8px; }
.slider-dot {
  width: 9px;
  height: 9px;
  border-radius: 3px;
  flex-shrink: 0;
}
.slider-name {
  flex: 1;
  color: #e4e4e7;
  font-size: 13px;
  font-weight: 600;
}
.slider-value {
  color: #ffffff;
  font-size: 13px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  min-width: 38px;
  text-align: right;
}
input[type="range"] {
  width: 100%;
  accent-color: #c8ff00;
  cursor: pointer;
}
.routing-track {
  position: relative;
  height: 16px;
  border-radius: 8px;
  background: #060802;
  border: 1px solid #2a2f20;
  margin: 14px 0 6px;
}
.routing-clip {
  position: absolute;
  inset: 0;
  border-radius: inherit;
  overflow: hidden;
}
.routing-fill {
  position: absolute;
  top: 0;
  bottom: 0;
  transition: left 0.18s ease-out, width 0.18s ease-out;
}
.routing-handle {
  position: absolute;
  top: -5px;
  width: 14px;
  height: 26px;
  margin-left: -7px;
  background: #ffffff;
  border: 2px solid #0a0d06;
  border-radius: 4px;
  cursor: ew-resize;
  z-index: 10;
  touch-action: none;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.35);
  transition: background 0.12s, transform 0.08s;
}
.routing-handle:hover { background: #c8ff00; }
.routing-handle.dragging {
  background: #c8ff00;
  transform: scale(1.06);
}
.routing-handle::before {
  content: '';
  position: absolute;
  top: 5px;
  bottom: 5px;
  left: 50%;
  width: 1px;
  background: #71717a;
  box-shadow: 2px 0 0 #71717a, -2px 0 0 #71717a;
  transform: translateX(-50%);
}
.feature-toggles {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 8px;
}
.feature-toggle {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 9px 11px;
  border: 1px solid #2a2f20;
  border-radius: 8px;
  background: #060802;
  cursor: pointer;
  transition: border-color 0.15s, background 0.15s;
  user-select: none;
}
.feature-toggle:hover { border-color: #4d6500; }
.feature-toggle.active { border-color: #c8ff00; background: rgba(200, 255, 0, 0.06); }
.feature-toggle input { position: absolute; opacity: 0; pointer-events: none; }
.feature-toggle-label {
  display: flex;
  align-items: center;
  gap: 7px;
  color: #d4d4d8;
  font-size: 12px;
  font-weight: 600;
  min-width: 0;
}
.feature-toggle.active .feature-toggle-label { color: #c8ff00; }
.feature-toggle-dot {
  width: 8px;
  height: 8px;
  border-radius: 2px;
  flex-shrink: 0;
  opacity: 0.4;
  transition: opacity 0.15s;
}
.feature-toggle.active .feature-toggle-dot { opacity: 1; }
.feature-toggle-check {
  position: relative;
  width: 16px;
  height: 16px;
  border: 1px solid #333a29;
  border-radius: 3px;
  background: #0c1008;
  flex-shrink: 0;
  transition: background 0.15s, border-color 0.15s;
}
.feature-toggle.active .feature-toggle-check { background: #c8ff00; border-color: #c8ff00; }
.feature-toggle-check::after {
  content: "";
  position: absolute;
  top: 2px;
  left: 5px;
  width: 3px;
  height: 7px;
  border: solid #0a0d06;
  border-width: 0 2px 2px 0;
  transform: rotate(45deg);
  opacity: 0;
  transition: opacity 0.15s;
}
.feature-toggle.active .feature-toggle-check::after { opacity: 1; }
.alloc-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(110px, 1fr));
  gap: 8px;
}
.alloc-card {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 9px 10px;
  border: 1px solid #2a2f20;
  border-radius: 8px;
  background: #060802;
  min-width: 0;
}
.alloc-card.you-keep {
  border-style: dashed;
  background: rgba(60, 80, 0, 0.05);
}
.alloc-card-head {
  display: flex;
  align-items: center;
  gap: 6px;
  min-width: 0;
}
.alloc-dot {
  width: 9px;
  height: 9px;
  border-radius: 3px;
  flex-shrink: 0;
}
.alloc-name {
  color: #e4e4e7;
  font-size: 12px;
  font-weight: 600;
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.alloc-input {
  display: flex;
  align-items: stretch;
  border: 1px solid #333a29;
  border-radius: 6px;
  overflow: hidden;
  transition: border-color 0.12s;
}
.alloc-input:focus-within { border-color: #4d6500; }
.alloc-input input {
  flex: 1;
  min-width: 0;
  width: 100%;
  border: 0;
  background: transparent;
  color: #ffffff;
  padding: 7px 8px;
  text-align: right;
  font: inherit;
  font-size: 14px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  outline: none;
}
.alloc-input span {
  display: flex;
  align-items: center;
  padding: 0 9px;
  background: #0c1008;
  color: #71717a;
  font-size: 11px;
  font-weight: 700;
  border-left: 1px solid #2a2f20;
}
.alloc-display {
  padding: 7px 10px;
  color: #c8ff00;
  font-size: 14px;
  font-weight: 800;
  font-variant-numeric: tabular-nums;
  text-align: right;
  background: #0c1008;
  border: 1px solid #333a29;
  border-radius: 6px;
}
.routing-hint {
  margin: 0;
  color: #71717a;
  font-size: 11px;
  line-height: 1.5;
}
.hero {
  border: 1px solid #4d6500;
  border-radius: 12px;
  background: linear-gradient(160deg, #1d2b05 0%, #0f1602 100%);
  padding: 16px 18px;
  box-shadow: 0 0 0 1px rgba(200, 255, 0, 0.04);
}
.hero-label {
  color: #c8ff00;
  font-size: 11px;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.08em;
}
.hero-value {
  margin-top: 6px;
  color: #ffffff;
  font-size: 32px;
  font-weight: 800;
  line-height: 1.05;
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
}
.hero-sub {
  margin-top: 5px;
  color: #a1a1aa;
  font-size: 12px;
}
.hero-sub strong { color: #d4d4d8; font-weight: 600; }
.breakdown { display: flex; flex-direction: column; gap: 8px; }
.breakdown-bar {
  display: flex;
  height: 22px;
  border-radius: 8px;
  overflow: hidden;
  background: #060802;
  border: 1px solid #2a2f20;
}
.breakdown-bar-seg { transition: width 0.2s ease-out; }
.breakdown-list { display: flex; flex-direction: column; gap: 5px; }
.breakdown-row {
  display: grid;
  grid-template-columns: 12px minmax(0, 1fr) auto auto;
  gap: 10px;
  align-items: center;
  padding: 9px 11px;
  border-radius: 8px;
  background: #060802;
  border: 1px solid #2a2f20;
}
.breakdown-row.muted { opacity: 0.45; }
.breakdown-dot {
  width: 10px;
  height: 10px;
  border-radius: 3px;
}
.breakdown-name {
  color: #f4f4f5;
  font-size: 13px;
  font-weight: 600;
}
.breakdown-pct {
  color: #a1a1aa;
  font-size: 12px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  min-width: 40px;
  text-align: right;
}
.breakdown-amount {
  color: #ffffff;
  font-size: 13px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  min-width: 70px;
  text-align: right;
}
.c-creator   { background: #c8ff00; }
.c-staking   { background: #60a5fa; }
.c-buyback   { background: #fb923c; }
.c-liquidity { background: #a78bfa; }
.personal {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 12px 14px;
  border: 1px dashed #333a29;
  border-radius: 10px;
  background: rgba(60, 80, 0, 0.04);
}
.personal-head {
  color: #a1a1aa;
  font-size: 10px;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.07em;
}
.personal-row {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 12px;
}
.personal-row + .personal-row {
  padding-top: 8px;
  border-top: 1px solid #1f240e;
}
.personal-key {
  color: #e4e4e7;
  font-size: 13px;
  font-weight: 600;
}
.personal-key small {
  display: block;
  color: #71717a;
  font-size: 11px;
  font-weight: 400;
  margin-top: 2px;
}
.personal-val {
  color: #ffffff;
  font-size: 16px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  text-align: right;
  white-space: nowrap;
}
.notice {
  margin: 0;
  padding: 12px 18px;
  border-top: 1px solid #2a2f20;
  background: #060802;
  color: #a1a1aa;
  font-size: 11px;
  line-height: 1.5;
}
.notice strong { color: #fbbf24; font-weight: 700; }
@media (max-width: 760px) {
  .layout { grid-template-columns: 1fr; }
  .controls { border-right: 0; border-bottom: 1px solid #2a2f20; }
  .scenarios { grid-template-columns: repeat(2, 1fr); }
  .alloc-grid { grid-template-columns: repeat(2, 1fr); }
  .hero-value { font-size: 26px; }
}
@media (max-width: 460px) {
  .fields-row { grid-template-columns: 1fr; }
}
</style>
</head>
<body>
<div class="app">
<div class="header">
  <h2>Fee Projection Calculator</h2>
  <p>Estimate 30-day creator-side earnings from a target market cap, volume assumption, and routing settings.</p>
</div>

<div class="scenarios" role="group" aria-label="Scenario presets">
  <button type="button" class="scenario-btn" data-mc="200000" data-vol="1">
    <strong>Quiet</strong><span>$200K cap, 1x monthly volume</span>
  </button>
  <button type="button" class="scenario-btn active" data-mc="1000000" data-vol="5">
    <strong>Active</strong><span>$1M cap, 5x monthly volume</span>
  </button>
  <button type="button" class="scenario-btn" data-mc="5000000" data-vol="15">
    <strong>Trending</strong><span>$5M cap, 15x monthly volume</span>
  </button>
  <button type="button" class="scenario-btn" data-mc="25000000" data-vol="50">
    <strong>Viral</strong><span>$25M cap, 50x monthly volume</span>
  </button>
</div>

<div class="layout">
  <div class="controls">

    <div class="section">
      <div class="section-head">
        <span class="section-num">1</span>
        <span class="section-title">Scenario</span>
      </div>

      <div class="fields-row">
        <div class="field">
          <div class="field-label">
            <span class="field-label-text">Target market cap</span>
          </div>
          <div class="input-group">
            <div class="input-group-affix prefix">$</div>
            <input id="marketCap" type="number" min="0" step="10000" value="1000000" />
          </div>
        </div>

        <div class="field">
          <div class="field-label">
            <span class="field-label-text">30-day volume</span>
            <span class="field-label-aside">x mcap</span>
          </div>
          <div class="input-group">
            <div class="input-group-affix prefix">x</div>
            <input id="volumeMultiple" type="number" min="0" max="100" step="0.5" value="5" />
            <div class="input-group-affix suffix" id="volumeHint">&asymp; $5.0M</div>
          </div>
        </div>

        <div class="field">
          <div class="field-label">
            <span class="field-label-text">SOL price</span>
          </div>
          <div class="input-group">
            <div class="input-group-affix prefix">$</div>
            <input id="solPrice" type="number" min="1" step="0.01" placeholder="Enter price" />
          </div>
        </div>
      </div>
    </div>

    <div class="section">
      <div class="section-head">
        <span class="section-num">2</span>
        <span class="section-title">Fee routing</span>
        <span class="section-title-aside">Splits the pool &mdash; doesn't change its size</span>
      </div>

      <div class="feature-toggles" role="group" aria-label="Enable fee routing features">
        <label class="feature-toggle">
          <input type="checkbox" id="enableStaking" data-allocation="staking" checked />
          <span class="feature-toggle-label">
            <span class="feature-toggle-dot c-staking"></span>
            <span>Staking</span>
          </span>
          <span class="feature-toggle-check"></span>
        </label>
        <label class="feature-toggle">
          <input type="checkbox" id="enableBuyback" data-allocation="buyback" checked />
          <span class="feature-toggle-label">
            <span class="feature-toggle-dot c-buyback"></span>
            <span>Buyback</span>
          </span>
          <span class="feature-toggle-check"></span>
        </label>
        <label class="feature-toggle">
          <input type="checkbox" id="enableLiquidity" data-allocation="liquidity" checked />
          <span class="feature-toggle-label">
            <span class="feature-toggle-dot c-liquidity"></span>
            <span>Liquidity</span>
          </span>
          <span class="feature-toggle-check"></span>
        </label>
      </div>

      <div class="routing-track" id="routingTrack">
        <div class="routing-clip">
          <div class="routing-fill c-staking" id="fill-staking"></div>
          <div class="routing-fill c-buyback" id="fill-buyback"></div>
          <div class="routing-fill c-liquidity" id="fill-liquidity"></div>
        </div>
        <div class="routing-handle" data-feature="staking" id="handle-staking" role="slider" aria-label="Staking allocation"></div>
        <div class="routing-handle" data-feature="buyback" id="handle-buyback" role="slider" aria-label="Buyback allocation"></div>
        <div class="routing-handle" data-feature="liquidity" id="handle-liquidity" role="slider" aria-label="Liquidity allocation"></div>
      </div>

      <div class="alloc-grid">
        <div class="alloc-card" data-alloc-card="staking">
          <div class="alloc-card-head">
            <span class="alloc-dot c-staking"></span>
            <span class="alloc-name">Staking</span>
          </div>
          <div class="alloc-input">
            <input id="stakingShare" data-allocation="staking" type="number" min="0" max="100" step="1" value="20" inputmode="numeric" />
            <span>%</span>
          </div>
        </div>
        <div class="alloc-card" data-alloc-card="buyback">
          <div class="alloc-card-head">
            <span class="alloc-dot c-buyback"></span>
            <span class="alloc-name">Buyback</span>
          </div>
          <div class="alloc-input">
            <input id="buybackShare" data-allocation="buyback" type="number" min="0" max="100" step="1" value="10" inputmode="numeric" />
            <span>%</span>
          </div>
        </div>
        <div class="alloc-card" data-alloc-card="liquidity">
          <div class="alloc-card-head">
            <span class="alloc-dot c-liquidity"></span>
            <span class="alloc-name">Liquidity</span>
          </div>
          <div class="alloc-input">
            <input id="liquidityShare" data-allocation="liquidity" type="number" min="0" max="100" step="1" value="5" inputmode="numeric" />
            <span>%</span>
          </div>
        </div>
        <div class="alloc-card you-keep">
          <div class="alloc-card-head">
            <span class="alloc-dot c-creator"></span>
            <span class="alloc-name">You keep</span>
          </div>
          <div class="alloc-display" id="creatorKeepDisplay">65%</div>
        </div>
      </div>

      <p class="routing-hint" id="routingMainHint">Drag a handle on the bar or type a value to rebalance. "You keep" is whatever is left.</p>
      <p class="routing-hint" id="stakerNote"><strong>Assumes at least one active staker.</strong> If nobody stakes during the period, the staking slice stays in creator payout.</p>
    </div>

    <div class="section">
      <div class="section-head">
        <span class="section-num">3</span>
        <span class="section-title">Your position</span>
        <span class="section-title-aside">Optional</span>
      </div>

      <div class="slider" id="yourStakeSliderWrap">
        <div class="slider-head">
          <span class="slider-name">Tokens you've staked, as % of all staked</span>
          <span class="slider-value" id="yourStakePct">1%</span>
        </div>
        <input id="yourStakeShare" type="range" min="0" max="100" step="1" value="1" />
      </div>

      <div class="slider">
        <div class="slider-head">
          <span class="slider-name">Tokens you hold, as % of supply</span>
          <span class="slider-value" id="creatorTokenPct">10%</span>
        </div>
        <input id="creatorPosition" type="range" min="0" max="100" step="1" value="10" />
      </div>
    </div>

  </div>

  <div class="results" aria-live="polite">
    <div class="hero">
      <div class="hero-label">Creator-side fee pool &middot; 30 days</div>
      <div class="hero-value" id="feePoolUsd">$60,000</div>
      <div class="hero-sub"><strong id="feePoolSol">— SOL</strong> from <span id="volumeUsd">$5.0M</span> in volume</div>
    </div>

    <div class="breakdown">
      <div class="breakdown-bar" aria-hidden="true">
        <div class="breakdown-bar-seg c-creator" id="bbar-creator"></div>
        <div class="breakdown-bar-seg c-staking" id="bbar-staking"></div>
        <div class="breakdown-bar-seg c-buyback" id="bbar-buyback"></div>
        <div class="breakdown-bar-seg c-liquidity" id="bbar-liquidity"></div>
      </div>

      <div class="breakdown-list">
        <div class="breakdown-row" id="creatorRow">
          <span class="breakdown-dot c-creator"></span>
          <span class="breakdown-name">Creator payout</span>
          <span class="breakdown-pct" id="creatorRowPct">65%</span>
          <span class="breakdown-amount" id="creatorPayout">$39,000</span>
        </div>
        <div class="breakdown-row" id="stakingRow">
          <span class="breakdown-dot c-staking"></span>
          <span class="breakdown-name">Staking rewards</span>
          <span class="breakdown-pct" id="stakingRowPct">20%</span>
          <span class="breakdown-amount" id="stakingRewards">$12,000</span>
        </div>
        <div class="breakdown-row">
          <span class="breakdown-dot c-buyback"></span>
          <span class="breakdown-name">Buyback and burn</span>
          <span class="breakdown-pct" id="buybackRowPct">10%</span>
          <span class="breakdown-amount" id="buybackBudget">$6,000</span>
        </div>
        <div class="breakdown-row">
          <span class="breakdown-dot c-liquidity"></span>
          <span class="breakdown-name">Liquidity compounding</span>
          <span class="breakdown-pct" id="liquidityRowPct">5%</span>
          <span class="breakdown-amount" id="liquidityBudget">$3,000</span>
        </div>
      </div>
    </div>

    <div class="personal">
      <div class="personal-head">For you</div>
      <div class="personal-row">
        <div class="personal-key">
          Your creator fees
          <small><span id="creatorFeesSummaryPct">65%</span> of fee pool</small>
        </div>
        <div class="personal-val" id="yourCreatorFees">$39,000</div>
      </div>
      <div class="personal-row" id="personalStakingRow">
        <div class="personal-key">
          Your staking reward
          <small><span id="yourStakeSummaryPct">1%</span> of staking pool</small>
        </div>
        <div class="personal-val" id="yourStakeAmount">$120</div>
      </div>
      <div class="personal-row">
        <div class="personal-key">
          Your token position
          <small><span id="creatorTokenSummaryPct">10%</span> of supply</small>
        </div>
        <div class="personal-val" id="creatorTokenValue">$100,000</div>
      </div>
    </div>
  </div>
</div>

<p class="notice">
  <strong>Projection only.</strong> Does not model price impact, launch-protection fees, taxes, SOL price movement, migration costs, or volume outside Blank pools.
</p>

</div>

<script>
var rate = 0.012;
var fmtUsd = new Intl.NumberFormat("en-US", { style: "currency", currency: "USD", maximumFractionDigits: 0 });
var fmtUsd2 = new Intl.NumberFormat("en-US", { style: "currency", currency: "USD", maximumFractionDigits: 2 });
var fmtCompact = new Intl.NumberFormat("en-US", { notation: "compact", maximumFractionDigits: 2 });
var fmtNum = new Intl.NumberFormat("en-US", { maximumFractionDigits: 2 });
var byId = function (id) { return document.getElementById(id); };
var read = function (id) { return Number(byId(id).value) || 0; };
var money = function (n) { return fmtUsd.format(n); };
var moneyCompact = function (n) {
  if (n < 1000) return fmtUsd2.format(n);
  return "$" + fmtCompact.format(n);
};
var pct = function (n) { return fmtNum.format(n) + "%"; };
var sol = function (usd, price) {
  if (price <= 0) return "— SOL";
  return fmtNum.format(usd / price) + " SOL";
};

function isFeatureEnabled(feature) {
  var cb = byId("enable" + feature.charAt(0).toUpperCase() + feature.slice(1));
  return cb ? cb.checked : true;
}

function limitAllocation(changed) {
  var controls = {
    staking: byId("stakingShare"),
    buyback: byId("buybackShare"),
    liquidity: byId("liquidityShare")
  };
  var enabled = {
    staking: isFeatureEnabled("staking"),
    buyback: isFeatureEnabled("buyback"),
    liquidity: isFeatureEnabled("liquidity")
  };
  var values = {
    staking: enabled.staking ? Number(controls.staking.value) : 0,
    buyback: enabled.buyback ? Number(controls.buyback.value) : 0,
    liquidity: enabled.liquidity ? Number(controls.liquidity.value) : 0
  };
  var max = {
    staking: 100 - values.buyback - values.liquidity,
    buyback: 100 - values.staking - values.liquidity,
    liquidity: 100 - values.staking - values.buyback
  };
  if (changed && enabled[changed] && values[changed] > max[changed]) {
    values[changed] = Math.max(0, max[changed]);
    controls[changed].value = values[changed];
  }
  controls.staking.max = Math.max(0, 100 - values.buyback - values.liquidity);
  controls.buyback.max = Math.max(0, 100 - values.staking - values.liquidity);
  controls.liquidity.max = Math.max(0, 100 - values.staking - values.buyback);
  return values;
}

function syncToggleUI() {
  ["staking", "buyback", "liquidity"].forEach(function (feature) {
    var enabled = isFeatureEnabled(feature);
    var checkbox = byId("enable" + feature.charAt(0).toUpperCase() + feature.slice(1));
    var label = checkbox ? checkbox.closest(".feature-toggle") : null;
    if (label) label.classList.toggle("active", enabled);
    var card = document.querySelector('[data-alloc-card="' + feature + '"]');
    if (card) card.style.display = enabled ? "" : "none";
    var handle = byId("handle-" + feature);
    if (handle) handle.style.display = enabled ? "" : "none";
  });
  var anyEnabled = isFeatureEnabled("staking") || isFeatureEnabled("buyback") || isFeatureEnabled("liquidity");
  var mainHint = byId("routingMainHint");
  if (mainHint) {
    mainHint.textContent = anyEnabled
      ? 'Drag a handle on the bar or type a value to rebalance. "You keep" is whatever is left.'
      : "Enable a feature above to allocate part of your creator fees.";
  }
  var stakingEnabled = isFeatureEnabled("staking");
  var stakerNote = byId("stakerNote");
  if (stakerNote) stakerNote.style.display = stakingEnabled ? "" : "none";
  var stakeSlider = byId("yourStakeSliderWrap");
  if (stakeSlider) stakeSlider.style.display = stakingEnabled ? "" : "none";
  var stakingPersonal = byId("personalStakingRow");
  if (stakingPersonal) stakingPersonal.style.display = stakingEnabled ? "" : "none";
}

function setWidth(id, value) {
  byId(id).style.width = (value || 0) + "%";
}

function isValidSolPrice(value) {
  return Number.isFinite(value) && value > 0 && value < 2000;
}

function readBlankSolPrice(data) {
  return typeof data.usd === "number" ? data.usd : 0;
}

function readCoinGeckoSolPrice(data) {
  return data && data.solana && typeof data.solana.usd === "number" ? data.solana.usd : 0;
}

async function fetchSolPriceJson(source) {
  var response = await Promise.race([
    fetch(source.url, { cache: "no-store" }),
    new Promise(function (_, reject) {
      window.setTimeout(function () {
        reject(new Error("SOL price request timed out"));
      }, 1800);
    })
  ]);
  if (!response.ok) throw new Error("SOL price request failed");
  var data = await response.json();
  var price = source.read(data);
  if (!isValidSolPrice(price)) throw new Error("SOL price response was invalid");
  return price;
}

async function loadLiveSolPrice() {
  var input = byId("solPrice");
  var sources = [
    { url: "https://api.coingecko.com/api/v3/simple/price?ids=solana&vs_currencies=usd", read: readCoinGeckoSolPrice },
    { url: "/api/sol-price", read: readBlankSolPrice }
  ];

  for (var i = 0; i < sources.length; i++) {
    try {
      var price = await fetchSolPriceJson(sources[i]);
      if (input.dataset.userEdited !== "true") {
        input.value = String(Math.round(price * 100) / 100);
        recalc();
      }
      return;
    } catch (error) {
      // Keep the calculator usable when the docs preview or price API is unavailable.
    }
  }
}

function updateActiveScenario(mc, vol) {
  var btns = document.querySelectorAll(".scenario-btn");
  for (var i = 0; i < btns.length; i++) {
    var btn = btns[i];
    var bmc = Number(btn.dataset.mc);
    var bvol = Number(btn.dataset.vol);
    if (bmc === mc && bvol === vol) {
      btn.classList.add("active");
    } else {
      btn.classList.remove("active");
    }
  }
}

function recalc(changed) {
  syncToggleUI();
  var values = limitAllocation(changed);
  var marketCap = read("marketCap");
  var volumeMultiple = read("volumeMultiple");
  var solPrice = read("solPrice");
  var creatorPosition = read("creatorPosition");
  var yourStake = read("yourStakeShare");

  var staking = values.staking;
  var buyback = values.buyback;
  var liquidity = values.liquidity;

  var volume = marketCap * volumeMultiple;
  var pool = volume * rate;
  var stakingRewards = pool * staking / 100;
  var buybackBudget = pool * buyback / 100;
  var liquidityBudget = pool * liquidity / 100;
  var creatorPayout = pool - stakingRewards - buybackBudget - liquidityBudget;
  var creatorActualPct = 100 - staking - buyback - liquidity;
  var creatorTokenValue = marketCap * creatorPosition / 100;
  var yourStakingRewards = stakingRewards * yourStake / 100;

  byId("yourStakePct").textContent = pct(yourStake);
  byId("creatorTokenPct").textContent = pct(creatorPosition);

  // Routing track: position fills and handles based on configured split
  var fillStaking = byId("fill-staking");
  var fillBuyback = byId("fill-buyback");
  var fillLiquidity = byId("fill-liquidity");
  fillStaking.style.left = "0%";
  fillStaking.style.width = staking + "%";
  fillBuyback.style.left = staking + "%";
  fillBuyback.style.width = buyback + "%";
  fillLiquidity.style.left = (staking + buyback) + "%";
  fillLiquidity.style.width = liquidity + "%";
  byId("handle-staking").style.left = staking + "%";
  byId("handle-buyback").style.left = (staking + buyback) + "%";
  byId("handle-liquidity").style.left = (staking + buyback + liquidity) + "%";
  byId("creatorKeepDisplay").textContent = pct(Math.max(0, 100 - staking - buyback - liquidity));

  byId("feePoolUsd").textContent = money(pool);
  byId("feePoolSol").textContent = sol(pool, solPrice);
  byId("volumeUsd").textContent = moneyCompact(volume);
  byId("volumeHint").textContent = "≈ " + moneyCompact(volume);

  setWidth("bbar-creator", Math.max(0, creatorActualPct));
  setWidth("bbar-staking", staking);
  setWidth("bbar-buyback", buyback);
  setWidth("bbar-liquidity", liquidity);

  byId("creatorRowPct").textContent = pct(creatorActualPct);
  byId("stakingRowPct").textContent = pct(staking);
  byId("buybackRowPct").textContent = pct(buyback);
  byId("liquidityRowPct").textContent = pct(liquidity);

  byId("creatorPayout").textContent = money(creatorPayout);
  byId("stakingRewards").textContent = money(stakingRewards);
  byId("buybackBudget").textContent = money(buybackBudget);
  byId("liquidityBudget").textContent = money(liquidityBudget);

  byId("yourCreatorFees").textContent = money(creatorPayout);
  byId("creatorFeesSummaryPct").textContent = pct(Math.max(0, creatorActualPct));
  byId("yourStakeAmount").textContent = staking > 0 ? money(yourStakingRewards) : "—";
  byId("yourStakeSummaryPct").textContent = pct(yourStake);
  byId("creatorTokenValue").textContent = money(creatorTokenValue);
  byId("creatorTokenSummaryPct").textContent = pct(creatorPosition);

  updateActiveScenario(marketCap, volumeMultiple);
}

document.querySelectorAll(".scenario-btn").forEach(function (btn) {
  btn.addEventListener("click", function () {
    byId("marketCap").value = btn.dataset.mc;
    byId("volumeMultiple").value = btn.dataset.vol;
    recalc();
  });
});

document.querySelectorAll("input").forEach(function (input) {
  input.addEventListener("input", function () {
    if (input.id === "solPrice") {
      input.dataset.userEdited = "true";
    }
    recalc(input.dataset.allocation);
  });
  input.addEventListener("change", function () { recalc(input.dataset.allocation); });
});

// Drag handlers for routing handles
(function () {
  var track = byId("routingTrack");
  var dragging = null;
  function applyPointer(feature, clientX) {
    var rect = track.getBoundingClientRect();
    if (!rect.width) return;
    var fraction = Math.min(1, Math.max(0, (clientX - rect.left) / rect.width));
    var pointerPct = fraction * 100;
    var staking = isFeatureEnabled("staking") ? read("stakingShare") : 0;
    var buyback = isFeatureEnabled("buyback") ? read("buybackShare") : 0;
    var liquidity = isFeatureEnabled("liquidity") ? read("liquidityShare") : 0;
    var preceding = 0;
    if (feature === "buyback") preceding = staking;
    else if (feature === "liquidity") preceding = staking + buyback;
    var others;
    if (feature === "staking") others = buyback + liquidity;
    else if (feature === "buyback") others = liquidity;
    else others = 0;
    var desired = pointerPct - preceding;
    var maxVal = 100 - preceding - others;
    var clamped = Math.max(0, Math.min(maxVal, Math.round(desired)));
    byId(feature + "Share").value = clamped;
    recalc(feature);
  }
  document.querySelectorAll(".routing-handle").forEach(function (handle) {
    handle.addEventListener("pointerdown", function (e) {
      dragging = handle.dataset.feature;
      handle.setPointerCapture(e.pointerId);
      handle.classList.add("dragging");
      e.preventDefault();
    });
    handle.addEventListener("pointermove", function (e) {
      if (dragging === handle.dataset.feature) {
        applyPointer(dragging, e.clientX);
      }
    });
    function release(e) {
      if (handle.hasPointerCapture(e.pointerId)) {
        handle.releasePointerCapture(e.pointerId);
      }
      handle.classList.remove("dragging");
      dragging = null;
    }
    handle.addEventListener("pointerup", release);
    handle.addEventListener("pointercancel", release);
  });
})();

recalc();
loadLiveSolPrice();

// Report content height to parent so the iframe can autosize and avoid scroll
(function () {
  if (window.parent === window) return;
  var lastHeight = 0;
  function postHeight() {
    var h = Math.ceil(document.documentElement.scrollHeight);
    if (h === lastHeight) return;
    lastHeight = h;
    window.parent.postMessage({ source: "fee-calc", height: h }, "*");
  }
  window.addEventListener("load", postHeight);
  window.addEventListener("resize", postHeight);
  if (window.ResizeObserver) {
    new ResizeObserver(postHeight).observe(document.body);
  }
  postHeight();
})();
</script>

</body>
</html>`}
/>
