// page-legal.jsx — Shared legal document layout
// Used by Terms / Privacy / Refunds / Acceptable Use.
// Renders an OS-style document: clause numbering, in-page TOC, monospace body.

function LegalDocument({ docId, version, effective, sections, summary }) {
  const T = window.useTokens();
  return (
    <>
      <DocMetaStrip docId={docId} version={version} effective={effective} T={T}/>
      <window.PageSection>
        <div style={{
          display: 'grid', gridTemplateColumns: 'minmax(0, 220px) 1fr',
          gap: 56, alignItems: 'start',
        }}>
          {/* Sticky TOC */}
          <nav style={{
            position: 'sticky', top: 100,
            fontFamily: T.mono, fontSize: 11.5, lineHeight: 1.8,
          }}>
            <div style={{ fontSize: 10, letterSpacing: 1.6,
              color: T.amber, marginBottom: 12 }}>
              ─ INDEX ─
            </div>
            {sections.map((s, i) => (
              <a key={i} href={`#s${i + 1}`} style={{
                display: 'block', color: T.textMute, textDecoration: 'none',
                padding: '4px 0', letterSpacing: 0.3,
                borderLeft: `2px solid transparent`, paddingLeft: 12,
                marginLeft: -12,
                transition: 'all 0.15s',
              }}
                onMouseEnter={e => { e.currentTarget.style.color = T.amber;
                  e.currentTarget.style.borderLeftColor = T.amber; }}
                onMouseLeave={e => { e.currentTarget.style.color = T.textMute;
                  e.currentTarget.style.borderLeftColor = 'transparent'; }}>
                <span style={{ color: T.textDim, marginRight: 8 }}>§{i + 1}</span>
                {s.title}
              </a>
            ))}
          </nav>

          {/* Body */}
          <article>
            {summary && (
              <div style={{
                border: `1px solid ${T.amber}`, background: T.bg3,
                padding: '20px 24px', marginBottom: 48,
              }}>
                <div style={{ fontFamily: T.mono, fontSize: 10, letterSpacing: 1.6,
                  color: T.amber, marginBottom: 10 }}>
                  ─ PLAIN-LANGUAGE SUMMARY ─
                </div>
                <p style={{ fontFamily: T.mono, fontSize: 13, color: T.text,
                  margin: 0, lineHeight: 1.65 }}>
                  {summary}
                </p>
              </div>
            )}
            {sections.map((s, i) => (
              <section key={i} id={`s${i + 1}`} style={{
                marginBottom: 48, scrollMarginTop: 100,
              }}>
                <div style={{
                  fontFamily: T.mono, fontSize: 10.5, letterSpacing: 1.8,
                  color: T.amber, marginBottom: 8, fontWeight: 600,
                }}>
                  §{i + 1}
                </div>
                <h2 style={{
                  fontFamily: T.mono, fontSize: 22, fontWeight: 600,
                  letterSpacing: '-0.005em',
                  color: T.text, margin: '0 0 18px',
                }}>
                  {s.title}
                </h2>
                {s.clauses.map((c, ci) => (
                  <div key={ci} style={{
                    display: 'grid', gridTemplateColumns: '60px 1fr',
                    gap: 16, marginBottom: 14,
                  }}>
                    <span style={{
                      fontFamily: T.mono, fontSize: 11, letterSpacing: 1.2,
                      color: T.textDim, paddingTop: 4,
                    }}>
                      {i + 1}.{ci + 1}
                    </span>
                    <p style={{
                      fontFamily: T.mono, fontSize: 13.5, lineHeight: 1.7,
                      color: T.textMute, margin: 0, letterSpacing: 0.1,
                    }}>
                      {c}
                    </p>
                  </div>
                ))}
              </section>
            ))}

            <div style={{
              marginTop: 64, paddingTop: 24,
              borderTop: `1px solid ${T.border}`,
              fontFamily: T.mono, fontSize: 11, color: T.textDim,
              letterSpacing: 0.4, lineHeight: 1.7,
            }}>
              Questions? <a href="Contact.html" style={{ color: T.amber,
                textDecoration: 'none', borderBottom: `1px solid ${T.amber}` }}>
                hello@campsitesniper.com
              </a> · This document supersedes all prior versions.
            </div>
          </article>
        </div>
      </window.PageSection>
    </>
  );
}

function DocMetaStrip({ docId, version, effective, T }) {
  return (
    <window.PageSection dense>
      <div style={{
        border: `1px solid ${T.border}`, background: T.bg3,
        padding: '20px 24px',
        display: 'grid',
        gridTemplateColumns: 'repeat(auto-fit, minmax(150px, 1fr))',
        gap: 24,
      }}>
        {[
          ['DOC ID', docId],
          ['VERSION', version],
          ['EFFECTIVE', effective],
          ['LANGUAGE', 'EN-US'],
          ['JURISDICTION', 'OREGON, USA'],
        ].map(([k, v]) => (
          <div key={k}>
            <div style={{ fontFamily: T.mono, fontSize: 9.5, letterSpacing: 1.6,
              color: T.textDim, marginBottom: 4 }}>{k}</div>
            <div style={{ fontFamily: T.mono, fontSize: 13, color: T.text,
              letterSpacing: 0.3, fontWeight: 600 }}>{v}</div>
          </div>
        ))}
      </div>
    </window.PageSection>
  );
}

// ──────────────────────────────────────────────────────────────
// TERMS

const TERMS_SECTIONS = [
  {
    title: 'Service description',
    clauses: [
      'Campsite Sniper monitors campsite and permit reservation systems on your behalf and notifies you when matching availability appears. We do not book reservations for you. You are responsible for completing any booking through the relevant operator (e.g. Recreation.gov).',
      'The service operates on a best-effort basis. We monitor public availability data; we do not have privileged access to any reservation system. Campsite Sniper is not affiliated with, endorsed by, or sponsored by Recreation.gov or any government agency.',
    ],
  },
  {
    title: 'Account & responsibilities',
    clauses: [
      'You must be 13 or older. Accounts for users under 18 should be created or supervised by a parent or guardian. One account per person.',
      'Authentication is passwordless. You sign in with Sign in with Apple, Sign in with Google, or a one-time email link. You are responsible for the security of the email address and identity provider account you use to sign in.',
      'You agree not to use the service to facilitate scalping, automated booking, or commercial resale of reservations. We reserve the right to terminate accounts that do.',
    ],
  },
  {
    title: 'Subscription & payment',
    clauses: [
      'The Pro tier is offered on a monthly or annual auto-renewable subscription through the Apple App Store. Pricing is shown in-app before purchase and may vary by region.',
      'Pro Monthly includes a 7-day free trial for first-time subscribers. The trial converts to a paid subscription at the displayed monthly price unless you cancel at least 24 hours before the trial ends.',
      'Auto-renewal: subscriptions renew automatically at the end of each billing period until you cancel. Cancel anytime by opening your iPhone Settings → Apple ID → Subscriptions → Campsite Sniper. Cancellation takes effect at the end of the current billing period.',
      'Apple handles billing, payment method storage, and the transaction record. We never see or store your credit card or payment instrument.',
    ],
  },
  {
    title: 'Acceptable use',
    clauses: [
      'You may not resell, white-label, or sublicense the service. You may not attempt to extract our probe data, reverse-engineer our queue, or interfere with our infrastructure.',
      'Detailed acceptable-use rules are in our Acceptable Use Policy, which is incorporated by reference.',
    ],
  },
  {
    title: 'Limitation of liability',
    clauses: [
      'The service is provided "as is." We make no guarantee that any specific reservation will become available, that any alert will be delivered within a specific time, or that any provider will continue to expose the data we monitor.',
      'Our total liability under these Terms is limited to the amounts you paid us in the 12 months preceding the claim. This does not limit liabilities that cannot be limited under applicable law.',
    ],
  },
  {
    title: 'Termination',
    clauses: [
      'You may terminate at any time by deleting your account in-app (Account → Delete Account) and cancelling your Apple subscription separately in Settings.',
      'We may terminate or suspend your account if you breach these Terms, with notice except where notice would compound the breach.',
      'On termination, we delete your account data within 30 days, except where retention is required by law (typically billing records, retained as required by tax authorities).',
    ],
  },
  {
    title: 'Changes',
    clauses: [
      'We may update these Terms. Material changes will be announced 30 days before they take effect, in-product and (where you have opted in) by email. Your continued use after the effective date constitutes acceptance.',
    ],
  },
];

function TermsPage() {
  return (
    <window.PageShell
      active="Terms"
      eyebrow="010 / TERMS"
      title="Terms of service."
      subtitle="The rules of the road. Written in plain language and short sentences. Seven sections, twenty minutes if you read every word."
    >
      <LegalDocument
        docId="CS-TOS-001"
        version="4.0"
        effective="2026-05-27"
        summary="You watch campsites. We monitor and notify. You book yourself on Recreation.gov. Pro is a monthly or annual subscription through the Apple App Store with a 7-day trial. Cancel anytime in Settings. Don't use this for scalping."
        sections={TERMS_SECTIONS}/>
    </window.PageShell>
  );
}

// ──────────────────────────────────────────────────────────────
// PRIVACY

const PRIVACY_SECTIONS = [
  {
    title: 'What we collect',
    clauses: [
      'Account data: email address you use to sign in, plus the identity-provider subject ID returned by Sign in with Apple or Sign in with Google (a stable identifier that is not your Apple ID or Google account password). Sign-in is passwordless — we never see or store a password. Display name is optional; if you provide one or your sign-in provider supplies it, we store it to personalize alerts.',
      'Subscription data: a subscription state record provided by RevenueCat after Apple processes your in-app purchase — Apple transaction ID, plan tier, renewal status, trial status. We do not see your credit card or payment instrument; Apple handles billing.',
      'Watch data: the campground IDs, date ranges, and notification preferences you configure for each watch.',
      'Device data: APNs push notification tokens (anonymous, supplied by Apple, scoped to your account on this device), app version, iOS version, device model. Used to deliver push alerts and to debug crashes.',
      'Operational data: IP address (used for rate-limiting and abuse detection), request timestamps. Server logs are retained 30 days, then deleted.',
      'Crash and error telemetry via Sentry: stack traces, breadcrumbs, device model, app version. Email addresses, auth tokens, push tokens, and other PII are scrubbed before transmission.',
      'Marketing-site analytics (this website only, not the app): Google Analytics 4 (G-PTSB3XWKW9) for aggregate traffic and Microsoft Clarity (wl1b8o0dtb) for anonymized session replay on landing pages. These do not run inside the iOS app.',
      'What we do not collect: contacts, photos, microphone, IDFA / advertising identifiers, browsing history, location beyond what is necessary to show campgrounds on the map (and only while the app is in use, per iOS permission).',
    ],
  },
  {
    title: 'Why we collect it',
    clauses: [
      'To deliver alerts. Without your watches and a push token, we cannot tell you about availability.',
      'To deliver the service you paid for. RevenueCat tells us which tier you have so we can gate features. Apple handles charging your payment method; we never see it.',
      'To diagnose problems. Server logs and Sentry crash reports help us find bugs, detect abuse, and respond to incidents.',
      'To prevent abuse. We rate-limit requests by IP, block disposable email domains at signup, and cap watch creation per hour.',
    ],
  },
  {
    title: 'What we do not do',
    clauses: [
      'We do not sell your data. We do not share it with advertisers or data brokers.',
      'We do not run advertising in the app. There is no IDFA tracking, no Facebook Pixel, no ad-network SDK in the iOS binary.',
      'We do not run cross-app or cross-site behavioural tracking. The marketing-site analytics named in §1 are first-party aggregate measurement only; they do not link to your in-app identity.',
      'We do not store passwords or credit card numbers. We never see them.',
    ],
  },
  {
    title: 'Who we share with',
    clauses: [
      'Apple — handles in-app purchase billing, payment method, and subscription state. Apple\'s privacy policy governs the data Apple holds on your behalf.',
      'RevenueCat — receives your Apple subscription transactions and exposes plan state to our backend. Their privacy policy: revenuecat.com/privacy.',
      'Supabase — our backend database and authentication provider. Hosts the data described in §1.',
      'Sentry — crash and error telemetry. PII scrubbed at send time.',
      'Mapbox — renders the campsite map. We disable Mapbox telemetry; the SDK does not phone home with your activity.',
      'Resend — sends your magic-link sign-in emails and the optional email digest. They process the email address to deliver the message.',
      'Apple Push Notification service — delivers push alerts using the anonymous device token Apple issued for our app.',
      'Recreation.gov and other reservation operators — when our probes check availability for sites you have asked us to watch, the requests come from our server infrastructure, identified by our user agent. Operators do not receive your email or account details.',
      'Law enforcement, only with valid legal process. We do not voluntarily disclose user data.',
    ],
  },
  {
    title: 'Your rights',
    clauses: [
      'Access: email hello@campsitesniper.com from your account email to request a copy of all data we hold on you. We respond within 14 days.',
      'Deletion: from inside the app, Account → Delete Account → confirm. We delete your data within 30 days, except billing records, which we retain as required by tax authorities.',
      'Subscription cancellation: cancel the Apple App Store subscription in your iPhone Settings → Apple ID → Subscriptions. Deleting your account in our app does not cancel your Apple subscription — you must do that separately or you may continue to be billed by Apple.',
      'Portability: email us to request your watches and account data in machine-readable form.',
      'Correction: edit any profile field directly in the app via Account → Profile.',
    ],
  },
  {
    title: 'Security',
    clauses: [
      'Data in transit: TLS 1.3 minimum, HTTPS-only (no cleartext). Apple Transport Security is enforced and arbitrary loads are disabled in the iOS binary.',
      'Data at rest: AES-256 (managed by Supabase) for the application database. Auth tokens are managed by Supabase Auth and Apple/Google identity providers — we never see plaintext credentials.',
      'In the event of a breach affecting your data, we will notify you within 72 hours of confirmed scope.',
    ],
  },
  {
    title: 'Children',
    clauses: [
      'The service is not directed to children under 13. We do not knowingly collect data from children under 13. If you believe a child under 13 has created an account, email hello@campsitesniper.com and we will delete it.',
    ],
  },
];

function PrivacyPage() {
  return (
    <window.PageShell
      active="Privacy"
      eyebrow="011 / PRIVACY"
      title="Privacy policy."
      subtitle="What we collect, why we collect it, and what we refuse to do with it. We do not sell data. We do not run ad-network trackers in the app. We answer access requests in 14 days."
    >
      <LegalDocument
        docId="CS-PRV-001"
        version="3.0"
        effective="2026-05-27"
        summary="Email + optional display name, your watches, subscription state from Apple. No passwords (passwordless sign-in). No credit cards (Apple handles billing). No advertising. Aggregate analytics on the marketing website only. Delete your account in-app at any time."
        sections={PRIVACY_SECTIONS}/>
    </window.PageShell>
  );
}

// ──────────────────────────────────────────────────────────────
// REFUNDS

const REFUND_SECTIONS = [
  {
    title: 'Apple handles refunds, not us',
    clauses: [
      'Campsite Sniper Pro is sold as an auto-renewable subscription through the Apple App Store. Apple — not Campsite Sniper — processes all payments and issues all refunds for iOS in-app purchases.',
      'We cannot directly issue a refund or a partial refund for an App Store subscription. Anyone telling you otherwise is mistaken.',
      'What we can do: confirm your subscription history, pull our probe logs to verify a missed-alert claim, and provide whatever evidence you need to support a refund request to Apple.',
    ],
  },
  {
    title: 'How to request a refund from Apple',
    clauses: [
      'Open Apple\'s Report a Problem page at https://reportaproblem.apple.com on any device signed in to your Apple ID.',
      'Sign in with the Apple ID you used to subscribe, find the Campsite Sniper charge in your purchase history, and select Request a refund. Choose a reason from the list. Apple usually decides within 24–48 hours and emails you the result.',
      'Apple grants refunds at its discretion. We have no influence over Apple\'s decision and cannot escalate on your behalf.',
    ],
  },
  {
    title: 'How to cancel future renewals',
    clauses: [
      'Cancelling is separate from refunding. Cancellation stops your subscription from renewing; it does not refund what you have already paid for the current period.',
      'On your iPhone, open Settings → tap your Apple ID at the top → Subscriptions → Campsite Sniper → Cancel Subscription. Or from inside the app, Account → Manage Subscription will deep-link you to the same screen.',
      'After cancellation, your Pro features remain active until the end of the current billing period.',
    ],
  },
  {
    title: 'The 7-day Pro trial',
    clauses: [
      'New subscribers get a 7-day free Pro trial. To avoid being charged, cancel the subscription at least 24 hours before the trial ends. Apple emails a reminder before the first paid charge.',
      'If you forget and Apple charges you, request a refund as described in §2 — Apple frequently grants refunds for immediately-cancelled accidental renewals.',
    ],
  },
  {
    title: 'Failed alerts',
    clauses: [
      'If you believe we missed an alert that should have fired, email hello@campsitesniper.com with the watch ID and the date or campsite you were watching.',
      'We will pull our probe logs and tell you exactly what we observed for that resource and when. If our logs confirm a missed alert, we will provide a written summary you can attach to a refund request to Apple under §2.',
    ],
  },
  {
    title: 'When you may not get a refund',
    clauses: [
      'Apple makes its own refund decisions and may decline. We have no override.',
      'Accounts terminated for breach of our Terms (scalping, multi-account abuse, automated booking) are not eligible for support on a refund request.',
    ],
  },
];

function RefundsPage() {
  return (
    <window.PageShell
      active="Refunds"
      eyebrow="012 / REFUNDS"
      title="Refund policy."
      subtitle="Apple processes all refunds for App Store subscriptions, including ours. Use reportaproblem.apple.com. We'll back you up with logs."
    >
      <LegalDocument
        docId="CS-RFD-001"
        version="2.0"
        effective="2026-05-27"
        summary="Apple handles refunds for App Store subscriptions; request at reportaproblem.apple.com. Cancel future renewals in iPhone Settings → Subscriptions. We'll supply probe logs to support a missed-alert refund request."
        sections={REFUND_SECTIONS}/>
    </window.PageShell>
  );
}

// ──────────────────────────────────────────────────────────────
// ACCEPTABLE USE

const AUP_SECTIONS = [
  {
    title: 'What we expect',
    clauses: [
      'Use the service to find campsites and permits for yourself and the people you camp with. That is the entire purpose. Anything that fits inside that is fine.',
      'Be honest with us if something goes wrong. We respond better to "I think I broke this" than to evasion.',
    ],
  },
  {
    title: 'Tier limits',
    clauses: [
      'Free tier: up to 3 active watches.',
      'Pro tier: up to 50 active watches.',
      'Rate limit: at most 10 new watches created per hour, per account, regardless of tier. This is an abuse guardrail and applies uniformly.',
    ],
  },
  {
    title: 'What gets you terminated',
    clauses: [
      'Scalping. Reselling reservations you find through our service for profit. We monitor for patterns (high watch volume, atypical date spreads, repeated cancellation-and-rebook) and will terminate without notice or appeal.',
      'Multi-account abuse. Creating accounts to bypass tier limits or evade prior termination. We block disposable-email domains at signup and identity-provider subject IDs are stable; we will catch this.',
      'Automated booking. Pointing other tools at the URLs we surface to auto-book on your behalf, on someone else\'s behalf, or at scale.',
      'Reverse-engineering our probe schedule, queue, or alert dispatch in order to build a competing service. This is in §4 of our Terms; we re-state it here.',
    ],
  },
  {
    title: 'What gets you suspended (then warned)',
    clauses: [
      'Sustained creation of watches at the rate-limit ceiling combined with patterns suggestive of resale. We will throttle and reach out before suspending.',
      'Patterns that look like resale but might be a large group trip. We ask first.',
    ],
  },
  {
    title: 'How we handle reports',
    clauses: [
      'If you suspect another user is violating this policy, email hello@campsitesniper.com. We investigate within 48h and respond to the reporter where appropriate.',
      'False reports made in bad faith are themselves a policy violation.',
    ],
  },
  {
    title: 'Provider relations',
    clauses: [
      'We respect Recreation.gov and every other operator we monitor. We rate-limit our probes. We identify our agent honestly. We back off when asked.',
      'If a provider asks us to stop monitoring a specific resource, we stop. We tell you why if we can. We will never argue with an operator on your behalf.',
    ],
  },
];

function AcceptableUsePage() {
  return (
    <window.PageShell
      active="Acceptable use"
      eyebrow="013 / ACCEPTABLE USE"
      title="Acceptable use policy."
      subtitle="Tier limits, the short list of things that get you terminated, the medium list of things that get you suspended, and what we promise the providers we monitor."
    >
      <LegalDocument
        docId="CS-AUP-001"
        version="3.0"
        effective="2026-05-27"
        summary="Free 3 watches, Pro 50 watches, max 10 new watches per hour. Don't scalp. Don't auto-book. Don't run a clone on top of our queue. Treat the providers we monitor with respect — we do."
        sections={AUP_SECTIONS}/>
    </window.PageShell>
  );
}

window.TermsPage = TermsPage;
window.PrivacyPage = PrivacyPage;
window.RefundsPage = RefundsPage;
window.AcceptableUsePage = AcceptableUsePage;
