//  File:    page.tsx
//  Path:    apps/web/app/methodology/page.tsx
//  Project: AI Risk Index — airiskindex.io
//  Author:  Simon-Pierre Boucher
//  Contact: contact@spboucher.ai
//  Copyright © 2026 Simon-Pierre Boucher. All rights reserved.
//
//  Description: Human-readable methodology documentation (weights pulled live from the engine).

import Link from "next/link";
import {
  DIMENSIONS,
  EXPOSURE_DIMENSIONS,
  HIGH_EXPOSURE_THRESHOLD,
  INDEX_VERSION,
  INVERTED_DIMENSIONS,
  WEIGHTS,
  type DimensionKey,
} from "@airiskindex/scoring";

export const metadata = {
  title: "Methodology — AI Risk Index",
  description:
    "Full methodology of the AI Risk Index: task-based scoring, multi-model LLM rater panel, confidence intervals, weights, formulas, versioning and limitations.",
};

const DIMENSION_DETAILS: Array<{
  key: DimensionKey;
  name: string;
  question: string;
  anchors: string;
}> = [
  {
    key: "automatability",
    name: "Task automatability",
    question:
      "Could current AI (including tool-using agents) perform this task end-to-end with at least 50% time saving at equal quality?",
    anchors:
      "1 = no meaningful part automatable today · 3 = roughly half automatable with significant setup · 5 = the full task meets the ≥50%-time-saving-at-equal-quality bar with off-the-shelf systems. The 50% threshold follows Eloundou et al. (2024), the citable standard.",
  },
  {
    key: "feasibility",
    name: "Current technical feasibility",
    question:
      "Do deployed products demonstrably perform this task reliably today — not merely in demos or benchmarks?",
    anchors:
      "1 = research-stage only · 3 = products exist with material error rates or narrow scope · 5 = mature products perform this reliably in production at scale. Separating conceivable from deployable is the failure that undid whole-occupation forecasts.",
  },
  {
    key: "cost_ratio",
    name: "Cost of substitution vs. wage",
    question:
      "How does the all-in AI cost per task-equivalent (inference, integration, oversight) compare to the loaded human wage for the same output?",
    anchors:
      "1 = AI is more expensive all-in · 3 = roughly comparable · 5 = AI is at least an order of magnitude cheaper.",
  },
  {
    key: "barriers",
    name: "Adoption barriers",
    question:
      "How strong are the real-world barriers to substitution: licensing and authorization requirements, liability and error-cost asymmetry, regulation of the automation itself, human-contact requirements, organizational friction?",
    anchors:
      "1 = nothing prevents substitution · 3 = some friction (oversight requirements, customer preference for humans) · 5 = hard barriers — a licensed human must legally perform or sign off on the task. This dimension is inverted in the composite: strong barriers lower the substitution score.",
  },
  {
    key: "adoption_velocity",
    name: "Sector adoption velocity",
    question:
      "How fast and deep are the sectors where this task occurs actually adopting AI — agents in production and measured displacement, not vendor announcements?",
    anchors:
      "1 = laggard sectors (small firms, physical work, low digitization) · 3 = pilots common, production rare · 5 = fast, deep adoption (information, finance, professional-services patterns).",
  },
];

function Section({
  id,
  title,
  children,
}: {
  id: string;
  title: string;
  children: React.ReactNode;
}): JSX.Element {
  return (
    <section id={id} className="mt-12 scroll-mt-6">
      <h2 className="text-2xl font-semibold tracking-tight">{title}</h2>
      <div className="mt-4 space-y-4 leading-relaxed text-[var(--ink-2)]">{children}</div>
    </section>
  );
}

export default function MethodologyPage(): JSX.Element {
  return (
    <main className="mx-auto max-w-3xl px-4 py-10 sm:px-6 sm:py-14">
      <p className="text-xs font-semibold uppercase tracking-[0.14em] text-[var(--muted)]">
        Methodology · version {INDEX_VERSION}
      </p>
      <h1 className="mt-3 text-4xl font-bold tracking-tight">
        How the AI Risk Index is computed
      </h1>
      <p className="mt-4 text-lg leading-relaxed text-[var(--ink-2)]">
        Every number on this site is reproducible: the weights, formulas, rater prompts and every
        individual model rating are public. This page is the human-readable specification; the
        machine-readable version is at{" "}
        <Link href="/api/v1/methodology" className="underline">
          /api/v1/methodology
        </Link>
        .
      </p>

      <nav className="mt-8 card p-5 text-sm">
        <p className="font-medium text-[var(--ink)]">Contents</p>
        <ol className="mt-2 list-inside list-decimal space-y-1 text-[var(--ink-2)]">
          {[
            ["principles", "Principles"],
            ["three-scores", "The three scores"],
            ["data", "Data sources"],
            ["rating", "Task rating — the multi-model panel"],
            ["dimensions", "The five dimensions and their weights"],
            ["formulas", "Formulas"],
            ["aggregation", "From tasks to occupations"],
            ["uncertainty", "Confidence intervals"],
            ["versioning", "Versioning and immutable runs"],
            ["validation", "Validation and sensitivity"],
            ["limitations", "Known limitations"],
            ["citation", "Citation, license and contact"],
          ].map(([anchor, label]) => (
            <li key={anchor}>
              <a href={`#${anchor}`} className="hover:text-[var(--ink)] hover:underline">
                {label}
              </a>
            </li>
          ))}
        </ol>
      </nav>

      <Section id="principles" title="1. Principles">
        <p>
          <strong className="text-[var(--ink)]">Task-based, not occupation-based.</strong>{" "}
          Occupations are bundles of tasks with very different AI exposure. Scoring whole
          occupations with a single judgment — the approach of the famous 2013 "47% of jobs"
          estimate — has a poor empirical track record. We score each of the ~18,800 O*NET task
          statements individually and derive occupation scores from them, weighted by how
          important each task is to the occupation.
        </p>
        <p>
          <strong className="text-[var(--ink)]">Uncertainty is part of the score.</strong> Large
          language models disagree with each other substantially when rating AI exposure —
          replications show the share of "highly exposed" occupations can vary by an order of
          magnitude depending on which frontier model does the rating. A single-model index is
          therefore an artifact. Every score we publish carries a confidence interval derived from
          disagreement across an independent multi-model panel.
        </p>
        <p>
          <strong className="text-[var(--ink)]">Fully reproducible.</strong> Weights live in one
          versioned source file, prompts are versioned, every rating stores the model, prompt
          version, raw response and parsed score, and every published number traces to a stored,
          immutable computation run.
        </p>
        <p>
          <strong className="text-[var(--ink)]">Adaptation, not doom.</strong> The evidence to
          date shows concentrated, cohort-specific effects — especially on entry-level hiring in
          automation-exposed occupations — rather than economy-wide job loss, while measured AI
          usage remains majority-augmentative. The index is designed to guide adaptation
          decisions, not to produce headlines.
        </p>
      </Section>

      <Section id="three-scores" title="2. The three scores">
        <p>Every occupation gets three sub-scores on a 0–100 scale, never collapsed into one:</p>
        <ul className="list-inside list-disc space-y-2">
          <li>
            <strong className="text-[var(--ink)]">Exposure</strong> — AI is technically capable of
            performing the occupation's tasks. Computed from the capability dimensions only
            (automatability and feasibility).
          </li>
          <li>
            <strong className="text-[var(--ink)]">Substitution</strong> — the headline composite:
            AI actually replaces the human, once cost, adoption barriers and sector adoption
            velocity are accounted for. Exposure without adoption predicts nothing; this score is
            exposure discounted by the real world.
          </li>
          <li>
            <strong className="text-[var(--ink)]">Augmentation</strong> — AI assists the human on
            the task, raising productivity without replacing them. Rated separately per task; it
            is not part of the substitution composite. High augmentation alongside moderate
            substitution reads as a changing job, not a disappearing one.
          </li>
        </ul>
      </Section>

      <Section id="data" title="3. Data sources">
        <div className="overflow-x-auto card">
          <table className="w-full text-left text-sm">
            <thead className="border-b border-[var(--grid)] text-xs uppercase tracking-wide text-[var(--muted)]">
              <tr>
                <th className="px-4 py-3 font-medium">Input</th>
                <th className="px-4 py-3 font-medium">Source</th>
                <th className="px-4 py-3 font-medium">Role</th>
              </tr>
            </thead>
            <tbody>
              {[
                [
                  "Occupations & task statements",
                  "O*NET 30.3 (U.S. Dept. of Labor, CC BY 4.0), O*NET-SOC 2019 taxonomy",
                  "Unit of analysis: 1,016 occupations, ~18,800 tasks",
                ],
                [
                  "Task importance weights",
                  "O*NET Task Ratings, importance scale (IM, 1–5)",
                  "Aggregation weights from tasks to occupations",
                ],
                [
                  "Wages & employment",
                  "BLS Occupational Employment and Wage Statistics, May 2025 national",
                  "Median wages (shown per occupation; cost-ratio context)",
                ],
                [
                  "EU/France crosswalk (planned)",
                  "ESCO v1.2 + official ESCO↔O*NET crosswalk; ROME 4.0",
                  "European occupation coverage in a future release",
                ],
                [
                  "Adoption evidence",
                  "Census BTOS, Anthropic Economic Index, sector deployment studies",
                  "Grounding for the adoption-velocity rubric anchors",
                ],
              ].map(([a, b, c]) => (
                <tr key={a as string} className="border-b border-[var(--grid)] last:border-b-0 align-top">
                  <td className="px-4 py-3 font-medium text-[var(--ink)]">{a}</td>
                  <td className="px-4 py-3">{b}</td>
                  <td className="px-4 py-3">{c}</td>
                </tr>
              ))}
            </tbody>
          </table>
        </div>
        <p>
          Raw source dumps are immutable and never edited in place; all transformations run
          through a pipeline that records manifests (hashes and row counts) for every derived
          artifact.
        </p>
      </Section>

      <Section id="rating" title="4. Task rating — the multi-model panel">
        <p>
          Each task statement is rated on six dimensions (the five composite dimensions plus
          augmentation) on an integer 1–5 scale by{" "}
          <strong className="text-[var(--ink)]">at least two independent frontier language
          models</strong> using an identical, versioned rubric prompt. Ratings are requested with
          schema-constrained JSON output; each rating must include a written rationale grounded in
          the task statement.
        </p>
        <p>
          For every task × dimension, the panel produces a rating band:{" "}
          <code>low</code> = the panel minimum, <code>mid</code> = the panel mean,{" "}
          <code>high</code> = the panel maximum. Bands — not single numbers — flow into all
          downstream computation, which is how rater disagreement becomes a visible confidence
          interval instead of hidden noise.
        </p>
        <p>
          <strong className="text-[var(--ink)]">Audit trail.</strong> Every rating stores the
          model identifier, prompt version, raw API response, parsed score and rationale. On any
          occupation page, expanding a task shows every model's rating and rationale — nothing is
          asserted that cannot be inspected.
        </p>
        <p>
          <strong className="text-[var(--ink)]">Human validation.</strong> A sample of ratings is
          reviewed by humans; disagreements above one point on the 5-point scale route the task to
          an expert review queue whose overrides replace the model band and are flagged in the
          data. Prompt changes bump the prompt version and invalidate cached ratings.
        </p>
      </Section>

      <Section id="dimensions" title="5. The five dimensions and their weights">
        <div className="overflow-x-auto card">
          <table className="w-full text-left text-sm">
            <thead className="border-b border-[var(--grid)] text-xs uppercase tracking-wide text-[var(--muted)]">
              <tr>
                <th className="px-4 py-3 font-medium">Dimension</th>
                <th className="px-4 py-3 font-medium">Weight</th>
                <th className="px-4 py-3 font-medium">Orientation</th>
              </tr>
            </thead>
            <tbody className="tabular-nums">
              {DIMENSIONS.map((key) => (
                <tr key={key} className="border-b border-[var(--grid)] last:border-b-0">
                  <td className="px-4 py-3 font-medium text-[var(--ink)]">
                    {DIMENSION_DETAILS.find((d) => d.key === key)?.name ?? key}
                  </td>
                  <td className="px-4 py-3">{(WEIGHTS[key] * 100).toFixed(0)}%</td>
                  <td className="px-4 py-3">
                    {INVERTED_DIMENSIONS.has(key)
                      ? "inverted — higher rating lowers substitution"
                      : "direct"}
                  </td>
                </tr>
              ))}
            </tbody>
          </table>
        </div>
        <div className="space-y-5">
          {DIMENSION_DETAILS.map((dim) => (
            <div key={dim.key}>
              <h3 className="font-semibold text-[var(--ink)]">
                {dim.name}{" "}
                <span className="text-sm font-normal text-[var(--muted)]">
                  · weight {(WEIGHTS[dim.key] * 100).toFixed(0)}%
                </span>
              </h3>
              <p className="mt-1">{dim.question}</p>
              <p className="mt-1 text-sm text-[var(--muted)]">{dim.anchors}</p>
            </div>
          ))}
        </div>
        <p>
          Weights are defined once, in a versioned source file, and served live at{" "}
          <Link href="/api/v1/methodology" className="underline">
            /api/v1/methodology
          </Link>{" "}
          — any change is a new index version with a changelog entry and regenerated sensitivity
          analyses.
        </p>
      </Section>

      <Section id="formulas" title="6. Formulas">
        <p>
          A rating <em>r</em> ∈ [1, 5] normalizes to substitution pressure <em>p</em> ∈ [0, 1]:
        </p>
        <pre className="overflow-x-auto card p-4 text-sm text-[var(--ink)]">
          {`p = (r − 1) / 4            direct dimensions
p = 1 − (r − 1) / 4        inverted dimensions (barriers)

substitution_task = 100 · Σ_d  w_d · p_d          over all five dimensions
exposure_task     = 100 · (w_auto·p_auto + w_feas·p_feas) / (w_auto + w_feas)
augmentation_task = 100 · p_augmentation`}
        </pre>
        <p>
          The exposure sub-score uses only {EXPOSURE_DIMENSIONS.join(" and ")}, renormalized. The
          scoring engine is a pure, deterministic library with no network, clock or randomness —
          identical inputs always produce identical scores, which is what makes runs auditable.
          Its behavior is pinned by property-based tests and by a published worked example that
          the test suite reproduces to three decimals.
        </p>
      </Section>

      <Section id="aggregation" title="7. From tasks to occupations">
        <p>
          Occupation scores are the importance-weighted mean of task scores, using O*NET task
          importance ratings normalized within each occupation. Tasks without an importance rating
          receive the occupation's mean importance. The same aggregation applies to the low, mid
          and high bounds.
        </p>
        <p>
          We additionally report the <strong className="text-[var(--ink)]">highly exposed task
          share</strong>: the fraction of an occupation's tasks with a substitution score of{" "}
          {HIGH_EXPOSURE_THRESHOLD} or more. "X% of tasks in this occupation are highly exposed"
          is the preferred headline reading — more honest than a single composite.
        </p>
      </Section>

      <Section id="uncertainty" title="8. Confidence intervals">
        <p>
          The low and high bounds are worst/best-case envelopes over the rater panel: the lower
          bound takes each dimension's pressure-minimizing rating bound (for the inverted barriers
          dimension, that is the <em>high</em> rating), the upper bound symmetrically. By
          construction low ≤ score ≤ high always holds. A wide interval means the models disagreed
          — treat that score as an open question, not a verdict. Occupations with the widest
          intervals are exactly where human expert review is prioritized.
        </p>
      </Section>

      <Section id="versioning" title="9. Versioning and immutable runs">
        <p>
          The index version ({INDEX_VERSION}) follows semantic versioning: formula or weight
          changes are major; data-source refreshes are minor; recomputations with refreshed
          adoption inputs are patches. Every computation writes a run record with the index
          version, prompt version and rater models. Runs are immutable — recomputations create new
          runs and old runs remain queryable, so any historically published score can be
          reproduced and checked forever. Every API response carries the index version it was
          computed under.
        </p>
      </Section>

      <Section id="validation" title="10. Validation and sensitivity">
        <p>Published alongside each major or minor release:</p>
        <ul className="list-inside list-disc space-y-2">
          <li>
            <strong className="text-[var(--ink)]">Convergent validity</strong> — rank correlation
            against independent published measures (Felten AIOE, "GPTs are GPTs" task scores, ILO
            exposure gradients).
          </li>
          <li>
            <strong className="text-[var(--ink)]">Rater stability</strong> — the distribution of
            cross-model agreement; the widest-band occupations are flagged in the product.
          </li>
          <li>
            <strong className="text-[var(--ink)]">Outcome tracking</strong> — correlation against
            realized labor-market indicators (entry-level employment in exposed occupations,
            measured usage data). Exposure indices individually explain little of realized
            unemployment risk, so we report outcome tracking honestly rather than claiming
            prediction.
          </li>
          <li>
            <strong className="text-[var(--ink)]">Weight sensitivity</strong> — rank stability of
            the composite under ±25% perturbation of each weight.
          </li>
        </ul>
      </Section>

      <Section id="limitations" title="11. Known limitations">
        <ul className="list-inside list-disc space-y-2">
          <li>
            LLM raters co-evolve with the technology they measure; multi-model panels bound this
            "ruler problem" but do not eliminate it.
          </li>
          <li>
            Sector-level adoption evidence enters through rubric anchors rather than per-sector
            statistical joins in the current version.
          </li>
          <li>
            Scores describe tasks as currently constituted; occupations reorganize, and task mixes
            shift precisely when exposure is high.
          </li>
          <li>
            Wages shown are U.S. national medians; cross-country generalization awaits the
            ESCO/ROME release.
          </li>
          <li>
            This index measures pressure, not destiny. It cannot tell an individual whether their
            job is safe; it can tell them which of their tasks are changing and how confident the
            evidence is.
          </li>
        </ul>
      </Section>

      <Section id="citation" title="12. Citation, license and contact">
        <p>
          Cite as: <em>AI Risk Index, version {INDEX_VERSION}, airiskindex.io, Simon-Pierre
          Boucher (2026)</em>. Scores and derived data are published under{" "}
          <strong className="text-[var(--ink)]">CC BY 4.0</strong> — reuse freely with
          attribution and the index version. Occupation and task data incorporate the O*NET
          database (U.S. Department of Labor, CC BY 4.0); wage data from BLS OEWS.
        </p>
        <p>
          Methodology questions, corrections, expert-panel participation, data licensing or
          anything else:{" "}
          <a href="mailto:contact@spboucher.ai" className="underline">
            contact@spboucher.ai
          </a>{" "}
          — Simon-Pierre Boucher.
        </p>
      </Section>
    </main>
  );
}
