---
import BaseLayout from '../layouts/BaseLayout.astro';
import SectionBand from '../components/SectionBand.astro';
import CheckItem from '../components/CheckItem.astro';

// Copy source of truth: docs/features/pricing/copy.md (root repo). Team and
// Enterprise are v1/v2 - rendered as "coming soon" cards (no live CTA) so the
// launch page sells only Free + Pro while still telling teams what's ahead.
// TODO(launch): Free/Pro CTAs open the coming-soon dialog until signup ships;
// then point them at the real signup/billing flow.

const description =
  'PCBJam pricing - everything public is free, for everyone, forever. Private work is free for three people and seven projects in your personal team, then one seat per person.';

type Price = { amount: string; period?: string };
type Feature = string | { label: string; note: string };

const plans: {
  name: string;
  tagline: string;
  price: { monthly: Price; yearly?: Price };
  features: Feature[];
  cta: { label: string } | null;
  soon: boolean;
  featured: boolean;
}[] = [
  {
    name: 'Free',
    price: { monthly: { amount: '$0' } },
    tagline: 'For open hardware, learning, and side projects.',
    features: [
      'Real-time collaboration',
      'Unlimited public projects',
      '7 private projects',
      '3 people on private work, in your personal team (owner included)',
      '1 GB storage',
      'Unlimited free members on public projects - view, comment & edit',
    ],
    cta: { label: 'Start free' },
    soon: false,
    featured: false,
  },
  {
    name: 'Pro',
    price: {
      monthly: { amount: '$10', period: '/month' },
      yearly: { amount: '$100', period: '/year' },
    },
    tagline: 'For the independent engineer.',
    features: [
      'Everything from the Free plan',
      'Unlimited private projects in your personal team',
      {
        label: 'AI assistant + MCP, credits included monthly - on any project you can edit',
        note: 'coming soon',
      },
      '10 GB storage',
      { label: 'Create teams beyond your default one', note: 'coming soon' },
    ],
    cta: { label: 'Go Pro' },
    soon: false,
    featured: true,
  },
  {
    name: 'Team',
    price: {
      monthly: { amount: '$30', period: '/seat/month' },
      yearly: { amount: '$300', period: '/seat/year' },
    },
    tagline: 'For teams shipping hardware.',
    features: [
      'Every person with private access is a seat - any role, owner included',
      'Every seat is a Pro seat - AI + MCP on this team’s projects only',
      'Unlimited private projects',
      '10 GB + 5 GB per seat storage',
      'Roles, permissions, seat management',
    ],
    cta: null,
    soon: true,
    featured: false,
  },
  {
    name: 'Enterprise',
    price: { monthly: { amount: 'Let’s talk' } },
    tagline: 'For organizations with requirements.',
    features: [
      'Everything from the Team plan',
      'SSO/SAML and audit logs',
      'Pay by invoice',
      'Pooled AI credits with spend limits',
      'Priority support',
    ],
    cta: null,
    soon: true,
    featured: false,
  },
];

const faqs = [
  {
    q: 'Who counts toward billing?',
    a: 'Anyone with access to a team’s private projects - any role, reading included, owner included. Your personal team’s first three people are free - that allowance exists once, not per project and not per team: ten teams don’t make thirty free people. Shared teams bill every person with private access. Public involvement never counts, for anyone.',
  },
  {
    q: 'Do viewers and commenters pay?',
    a: 'Anyone can view public projects. Members can be readers, commenters, or editors: readers see the project and its comments, commenters can also add comments, editors can edit too - membership is free and unlimited on public projects. On private projects, members use one of your three free spots or a seat - the role doesn’t matter, having access to private work is what counts.',
  },
  {
    q: 'We stopped paying - what happens?',
    a: 'Nothing is deleted. A shared team falls back to public-only: its private projects go read-only until seats return. Your personal team falls back to the free tier: 3 people and 7 private projects, extras read-only. Public projects are never touched.',
  },
  {
    q: 'Can I get a refund?',
    a: 'Yes - ask anytime and we refund the unused remainder of your current billing period, pro-rata to what you paid, and downgrade your plan. Nothing is deleted; content over the free limits goes read-only. The EU 14-day cooling-off right applies on top. Refunds are processed by Paddle, our merchant of record.',
  },
  {
    q: 'What happens when I add someone to private work?',
    a: 'On your personal team the first three people are free. Beyond them - and for everyone on a shared team - private access needs a seat: with an unassigned seat they’re in immediately, otherwise the billing admin gets a seat request to confirm. We never charge silently.',
  },
  {
    q: 'Are team seats Pro seats?',
    a: 'Yes - every seat includes Pro features (AI assistant + MCP) on that team’s projects, and only there. A personal Pro subscription is separate: it travels with you to any project you can edit, in any team.',
  },
  {
    q: 'Can I move a team to someone else?',
    a: 'Yes - ownership and billing transfer separately, and the recipient confirms before anything changes. Your projects and history stay put.',
  },
  {
    q: 'How does yearly billing work?',
    a: 'Pay for 10 months, get 12.',
  },
  {
    q: 'What about tax and invoices?',
    a: 'Checkout, VAT/sales tax, and receipts are handled by Paddle, our merchant of record. Businesses can enter a VAT ID at checkout for reverse-charge invoices.',
  },
];

const jsonLd = {
  '@context': 'https://schema.org',
  '@type': 'SoftwareApplication',
  name: 'PCBJam',
  applicationCategory: 'DesignApplication',
  operatingSystem: 'Web browser',
  description,
  offers: [
    { '@type': 'Offer', name: 'Free', price: '0', priceCurrency: 'USD' },
    { '@type': 'Offer', name: 'Pro', price: '10', priceCurrency: 'USD' },
  ],
};
---

<BaseLayout title="Pricing - PCBJam" description={description} wide>
  <script
    slot="head"
    type="application/ld+json"
    is:inline
    set:html={JSON.stringify(jsonLd)}
  />

  <SectionBand labelledby="pricing-h1" grid>
    <div class="section-head">
      <span class="eyebrow">Pricing</span>
      <h1 id="pricing-h1">Design boards together.</h1>
      <p class="lead">
        Everything public is <strong>free</strong> - for everyone, forever. Private work is free
        for up to three people and seven projects in your personal team - beyond that, one seat
        per person.
      </p>
    </div>

    <div class="billing-toggle" role="group" aria-label="Billing period">
      <button type="button" class="bt-opt active" data-period="monthly" aria-pressed="true">
        Monthly
      </button>
      <button type="button" class="bt-opt" data-period="yearly" aria-pressed="false">
        Yearly <span class="bt-hint">2 months free</span>
      </button>
    </div>

    <div class="plans" data-period="monthly">
      {
        plans.map((p) => (
          <div class:list={['card', 'plan', { featured: p.featured, soon: p.soon }]}>
            <div class="plan-head">
              <span class="plan-name">{p.name}</span>
              {p.soon && <span class="soon-chip">coming soon</span>}
            </div>
            <div class="plan-price">
              {p.price.yearly ? (
                <>
                  <span class="price price-m">
                    <span class="amount">{p.price.monthly.amount}</span>
                    {p.price.monthly.period && <span class="period">{p.price.monthly.period}</span>}
                  </span>
                  <span class="price price-y" hidden>
                    <span class="amount">{p.price.yearly.amount}</span>
                    {p.price.yearly.period && <span class="period">{p.price.yearly.period}</span>}
                  </span>
                </>
              ) : (
                <span class="price">
                  <span class="amount">{p.price.monthly.amount}</span>
                  {p.price.monthly.period && <span class="period">{p.price.monthly.period}</span>}
                </span>
              )}
            </div>
            <p class="tagline">{p.tagline}</p>
            <ul class="checklist">
              {p.features.map((f) =>
                typeof f === 'string' ? (
                  <CheckItem label={f} />
                ) : (
                  <CheckItem label={f.label} note={f.note} />
                ),
              )}
            </ul>
            {p.cta && (
              <button
                type="button"
                class:list={['btn', 'plan-cta', p.featured ? 'btn-primary' : 'btn-ghost']}
              >
                {p.cta.label}
              </button>
            )}
          </div>
        ))
      }
    </div>

    <p class="deal muted">
      Public is free for everyone. Private collaboration is what you pay for - your personal
      team’s first three people are on us.
    </p>
  </SectionBand>

  <SectionBand tone="soft" labelledby="faq-h2">
    <div class="section-head">
      <span class="eyebrow">FAQ</span>
      <h2 id="faq-h2">Questions, answered plainly.</h2>
    </div>
    <dl class="faq">
      {
        faqs.map((f) => (
          <div class="faq-item">
            <dt>{f.q}</dt>
            <dd class="muted">{f.a}</dd>
          </div>
        ))
      }
    </dl>
  </SectionBand>

  <dialog id="coming-soon" class="coming-soon" aria-labelledby="coming-soon-h3">
    <div class="dialog-body">
      <h3 id="coming-soon-h3">Coming soon</h3>
      <p class="muted">
        Plans aren't live yet - we're getting signup and checkout ready. Join the waitlist and
        we'll tell you the moment they open.
      </p>
      <div class="dialog-actions">
        <a class="btn btn-primary" href="/#waitlist">Join the waitlist</a>
        <button type="button" class="btn btn-ghost dialog-close">Close</button>
      </div>
    </div>
  </dialog>
</BaseLayout>

<script>
  // Monthly/yearly price toggle. Re-inits after ClientRouter page swaps; the
  // swap replaces the DOM, so the bound-flag resets with it.
  function initBillingToggle() {
    const wrap = document.querySelector<HTMLElement>('.billing-toggle');
    const plans = document.querySelector<HTMLElement>('.plans');
    if (!wrap || !plans || wrap.dataset.bound === '1') return;
    wrap.dataset.bound = '1';
    wrap.addEventListener('click', (e) => {
      const btn = (e.target as HTMLElement).closest<HTMLButtonElement>('button[data-period]');
      if (!btn) return;
      const yearly = btn.dataset.period === 'yearly';
      plans.dataset.period = yearly ? 'yearly' : 'monthly';
      // Visibility via the [hidden] attribute (global reset: display:none
      // !important) - independent of scoped-CSS delivery.
      plans.querySelectorAll<HTMLElement>('.price-m').forEach((el) => (el.hidden = yearly));
      plans.querySelectorAll<HTMLElement>('.price-y').forEach((el) => (el.hidden = !yearly));
      wrap.querySelectorAll('button').forEach((b) => {
        b.classList.toggle('active', b === btn);
        b.setAttribute('aria-pressed', String(b === btn));
      });
    });
  }
  document.addEventListener('astro:page-load', initBillingToggle);

  // "Coming soon" dialog for the Free/Pro CTAs (no signup flow yet). Native
  // <dialog>: Esc closes for free; backdrop clicks are detected because the
  // padded .dialog-body swallows inside clicks, so e.target === dialog only
  // on the backdrop.
  function initComingSoon() {
    const dialog = document.querySelector<HTMLDialogElement>('#coming-soon');
    if (!dialog) return;
    // A ClientRouter swap can re-insert the dialog with a stray `open`
    // attribute (renders as an unstyled non-modal box) — always arrive closed.
    if (dialog.open) dialog.close();
    dialog.removeAttribute('open');
    if (dialog.dataset.bound === '1') return;
    dialog.dataset.bound = '1';
    document
      .querySelectorAll<HTMLButtonElement>('.plan-cta')
      .forEach((b) => b.addEventListener('click', () => dialog.showModal()));
    dialog.addEventListener('click', (e) => {
      if (e.target === dialog) dialog.close();
    });
    dialog.querySelector('.dialog-close')?.addEventListener('click', () => dialog.close());
  }
  document.addEventListener('astro:page-load', initComingSoon);
</script>

<style>
  .billing-toggle {
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
    padding: 0.3rem;
    border: 1px solid var(--border);
    border-radius: 999px;
    background: var(--bg);
    box-shadow: 0 1px 2px rgb(16 23 41 / 0.05);
    margin-bottom: 2rem;
  }
  .bt-opt {
    display: inline-flex;
    align-items: center;
    gap: 0.45rem;
    border: none;
    background: transparent;
    padding: 0.45rem 1.1rem;
    border-radius: 999px;
    font: inherit;
    font-size: 0.95rem;
    font-weight: 600;
    color: var(--fg-muted);
    cursor: pointer;
    transition:
      background-color var(--dur) var(--ease),
      color var(--dur) var(--ease);
  }
  .bt-opt:hover {
    color: var(--fg);
  }
  .bt-opt.active {
    background: var(--brand-600);
    color: #fff;
  }
  .bt-opt.active:hover {
    color: #fff;
  }
  .bt-hint {
    font-size: 0.7rem;
    font-weight: 700;
    letter-spacing: 0.01em;
    padding: 0.1rem 0.5rem;
    border-radius: 999px;
    background: color-mix(in srgb, var(--signal-600) 12%, transparent);
    color: var(--signal-600);
    white-space: nowrap;
  }
  .bt-opt.active .bt-hint {
    background: rgb(255 255 255 / 0.2);
    color: #fff;
  }

  .plans {
    display: grid;
    gap: 1rem;
    grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
    align-items: stretch;
  }
  .plan {
    display: flex;
    flex-direction: column;
    background: var(--bg);
  }
  .plan.featured {
    border-color: var(--accent);
    box-shadow: 0 0 0 1px var(--accent);
  }
  .plan.soon {
    background: var(--bg-soft);
  }
  .plan-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
  }
  .plan-name {
    font-size: 0.85rem;
    font-weight: 700;
    letter-spacing: 0.06em;
    color: var(--fg-muted);
    text-transform: uppercase;
  }
  .soon-chip {
    font-family: var(--font-mono);
    font-size: 0.7rem;
    color: var(--fg-muted);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    padding: 0.1rem 0.4rem;
    white-space: nowrap;
  }
  .plan-price {
    margin-top: 0.5rem;
  }
  .price {
    display: flex;
    align-items: baseline;
    gap: 0.25rem;
  }
  .price .amount {
    font-size: 1.9rem;
    font-weight: 700;
    line-height: 1.2;
  }
  .price .period {
    color: var(--fg-muted);
    font-size: 0.9rem;
  }
  .tagline {
    margin: 0.75rem 0 0;
    font-weight: 600;
  }
  .checklist {
    list-style: none;
    padding: 0;
    margin: 0.75rem 0 1.25rem;
    font-size: 0.95rem;
    flex: 1;
  }
  .plan .btn {
    width: 100%;
  }
  .deal {
    margin-top: 2rem;
    max-width: 44rem;
  }
  .faq {
    margin: 0;
    display: grid;
    gap: 1.5rem;
    max-width: 48rem;
  }
  .faq-item dt {
    font-weight: 600;
    margin-bottom: 0.25rem;
  }
  .faq-item dd {
    margin: 0;
  }


</style>

<style is:global>
  /* Dialog styles are global, keyed by the unique #coming-soon id: the modal
     must render correctly even when a ClientRouter swap re-inserts it without
     Astro's scoped-style attribute (observed in dev SPA navigation). */
  #coming-soon {
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    padding: 0;
    max-width: 24rem;
    background: var(--bg);
    color: var(--fg);
    box-shadow: 0 20px 50px rgb(16 23 41 / 0.25);
  }
  #coming-soon::backdrop {
    background: rgb(16 23 41 / 0.45);
    backdrop-filter: blur(2px);
  }
  #coming-soon .dialog-body {
    padding: 1.75rem;
  }
  #coming-soon .dialog-body h3 {
    margin-bottom: 0.5rem;
  }
  #coming-soon .dialog-body p {
    margin: 0 0 1.25rem;
  }
  #coming-soon .dialog-actions {
    display: flex;
    gap: 0.6rem;
    flex-wrap: wrap;
  }
</style>
