/* Folclore Latin Fund I — case studies.
   Numbers are illustrative placeholders until a source workbook is on file.
   Compare-page answers wait on the question script (FCP_CASES). */

const { useState, useEffect } = React;

const CASE_YEARS = [2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023, 2024, 2025];

const CASE_INDEX_LINES = [
  {
    id: "lvmh",
    name: "LVMH",
    color: "var(--gold-500)",
    v: [37600, 42636, 46826, 53670, 44651, 64215, 79184, 86153, 84683, 80807],
    format: (v) => "€" + (v / 1000).toFixed(1) + "B",
  },
  {
    id: "tods",
    name: "Tod’s Group",
    color: "var(--alabaster)",
    v: [1004, 963, 940, 916, 637, 884, 1007, 1127, 1046, 966],
    format: (v) => "€" + v.toLocaleString("en-US") + "M",
    discFrom: 8,
  },
  {
    id: "ferragamo",
    name: "Ferragamo",
    color: "var(--sapphire-400)",
    v: [1438, 1393, 1347, 1377, 916, 1136, 1252, 1156, 1035, 977],
    format: (v) => "€" + v.toLocaleString("en-US") + "M",
  },
];

const CASE_COMPARE_PAIRS = [
  { a: "lvmh", b: "ferragamo", title: "The group and the single house" },
  { a: "tods", b: "ferragamo", title: "Same scale, two ownership paths" },
  { a: "sdj", b: "osklen", title: "Brazil, built two ways" },
];

const CASE_STUDIES = {
  updated: "28 Oct 2026",
  lead:
    "Five houses, each read on its own page. LVMH sets the benchmark for how luxury compounds. Tod’s and Ferragamo show what craft alone cannot do. Sol de Janeiro and Osklen show how far — and how unevenly — Brazilian provenance travels. This page sets them side by side.",
  italic: "— what the record says about heritage, scale and patience.",
  meta: [
    { label: "Prepared by", value: "The Fund I team" },
    { label: "Updated", value: "28 Oct 2026" },
    { label: "Cases", value: "2 listed · 3 private" },
    { label: "Basis", value: "Filings, disclosures, estimates" },
  ],
  lessons: [
    {
      n: "I",
      title: "Luxury compounds; multiples do not",
      evidence: "LVMH revenue 2.1× in a decade; its shares roughly halved from end-2023.",
      cases: ["lvmh"],
      fund: "Underwrite to cash flow; hold long enough to choose the exit.",
    },
    {
      n: "II",
      title: "Provenance is not a plan",
      evidence: "Tod’s −4% and Ferragamo −32% over the decade, with a century of craft each.",
      cases: ["tods", "ferragamo"],
      fund: "Back a product engine and a creative lead who stays.",
    },
    {
      n: "III",
      title: "Brazil travels, if built to",
      evidence: "Sol de Janeiro: about €885M in sales within ten years, led by US retail.",
      cases: ["sdj"],
      fund: "International wholesale and direct sales from the first year.",
    },
    {
      n: "IV",
      title: "Earn in hard currency",
      evidence: "Osklen: about 3.0× in reais, about 1.1× in dollars, 2012 to 2021.",
      cases: ["osklen"],
      fund: "Plans and returns are set in dollars, at entry.",
    },
    {
      n: "V",
      title: "Groups pay for heritage",
      evidence: "L Catterton and LVMH at an 18% premium for Tod’s; L’Occitane at $450M for Sol de Janeiro.",
      cases: ["tods", "sdj"],
      fund: "Build each house for a named set of strategic buyers.",
    },
  ],
  indexNote: "— LVMH more than doubled; two century-old shoemakers of the same scale did not grow.",
  houses: [
    {
      id: "lvmh",
      n: "01",
      name: "LVMH",
      role: "Benchmark",
      ownership: "listed",
      ownershipLabel: "Listed · Paris",
      ownershipDetail: "Listed · Euronext Paris",
      basisSummary: "Every figure reported in audited filings",
      filterType: "Listed",
      home: "Paris",
      founded: "1987",
      ownershipFact: "Listed; family-controlled",
      periodOrChannel: { label: "Period", value: "2016–2025" },
      italic: "— a house of houses, compounded.",
      lead:
        "The world’s largest luxury group is the reference point for every exit we underwrite. Over ten years its revenue more than doubled, through a pandemic and a slowdown — while its share price showed how far listed luxury multiples can swing around the same business.",
      cardFigure: { value: "2.1×", label: "Revenue, 2016 to 2025", basis: "reported" },
      compare: {
        place: "Benchmark · Paris",
        ownershipLine: "Listed",
        ownershipNote: null,
        revenue: { value: "€80.8B", note: "2025", basis: "reported" },
        period: "2.1× since 2016",
        valueMark: { value: "€321B", note: "Market value, Dec 2025", basis: "reported" },
      },
      headlines: [
        { label: "Revenue · 2025", value: "€80.8B", note: "2.1× 2016, or 8.9% a year", basis: "reported" },
        { label: "Operating margin", value: "22.0%", note: "26.7% at the 2021 peak", basis: "reported" },
        { label: "Leather goods margin", value: "35%", note: "€13.2B on €37.8B, Fashion & Leather Goods, 2025", basis: "reported" },
        { label: "Market value", value: "€321B", note: "31 December 2025", basis: "reported" },
      ],
      figure: {
        type: "bars",
        title: "Figure 1 · LVMH, 2016–2025",
        metrics: {
          rev: {
            name: "Revenue",
            unit: "Revenue, €B",
            v: [37.6, 42.6, 46.8, 53.7, 44.7, 64.2, 79.2, 86.2, 84.7, 80.8],
            format: (v) => v.toFixed(1),
            note: "— revenue 2.1× in nine years, 8.9% a year compounded, through a pandemic and a slowdown.",
          },
          pro: {
            name: "Operating profit",
            unit: "Profit from recurring operations, €B · margin below",
            v: [7.0, 8.3, 10.0, 11.5, 8.3, 17.2, 21.1, 22.8, 19.6, 17.8],
            m: [18.7, 19.5, 21.4, 21.4, 18.6, 26.7, 26.6, 26.5, 23.1, 22.0],
            format: (v) => v.toFixed(1),
            note: "— operating profit 2.5×; the margin peaked at 26.7% in 2021 and held at 22.0% in 2025.",
          },
          px: {
            name: "Share price",
            unit: "Share price at year-end, €",
            v: [181, 245, 258, 414, 511, 727, 680, 734, 636, 645],
            format: (v) => String(v),
            note: "— 3.6× by year-end 2025, then below €400 in October 2026, on revenue only 6% off its peak.",
          },
        },
        defaultMetric: "rev",
      },
      timeline: {
        title: "Houses added in the decade",
        items: [
          { year: "2016", title: "Rimowa", body: "An 80% stake in the Cologne luggage maker, founded 1898." },
          { year: "2017", title: "Christian Dior Couture", body: "The couture house brought fully inside the group." },
          { year: "2019", title: "Belmond", body: "Heritage hotels and trains — luxury as an experience." },
          { year: "2021", title: "Tiffany & Co.", body: "$15.8B — the largest acquisition the sector has seen." },
          { year: "2021", title: "Tod’s, 10%", body: "A minority seat beside the Della Valle family — see case 02." },
        ],
      },
      lens: [
        { title: "Fundamentals compound", body: "Revenue doubled across a decade that included 2020. We underwrite to cash flow, not to a re-rating." },
        { title: "Multiples swing", body: "The shares roughly halved from end-2023 on revenue 6% off its peak. A five-to-seven-year hold lets us choose the moment to sell." },
        { title: "Groups buy houses", body: "Five houses joined LVMH in the decade, from luggage to jewelry. Global groups are the natural buyers of what we build." },
      ],
      basisTitle: "Basis of figures",
      basisHeading: "Where each number comes from.",
      basisRows: [
        { figure: "Revenue and operating profit", value: "2016–2025", basis: "reported", source: "LVMH annual reports and key figures" },
        { figure: "Leather goods margin", value: "35%", basis: "reported", source: "Fashion & Leather Goods segment, 2025 annual report" },
        { figure: "Share price and market value", value: "Year-end", basis: "reported", source: "Euronext year-end closes; October 2026 level from market data" },
        { figure: "Acquisitions", value: "2016–2021", basis: "reported", source: "LVMH announcements at completion" },
      ],
    },
    {
      id: "tods",
      n: "02",
      name: "Tod’s Group",
      role: "Leather goods",
      ownership: "private",
      ownershipLabel: "Private since 2024",
      ownershipDetail: "Private since 2024",
      basisSummary: "Listed filings through 2023; later figures disclosed in the press",
      filterType: "Private",
      home: "Marche",
      founded: "1920",
      ownershipFact: "Private since 2024 · listed 2000–2024",
      periodOrChannel: { label: "Period", value: "2016–2025" },
      italic: "— craft at scale, then taken private.",
      lead:
        "An Italian leather-goods house at roughly €1B of sales. Revenue barely moved over the decade. In 2024 the Della Valle family, with L Catterton and a LVMH minority, took the group private at about €1.4B of equity value.",
      privateCallout: {
        title: "Private since 2024",
        body: "Tod’s Group was listed from 2000 to 2024. Revenue after the take-private is as reported in the press. Earlier years come from its own filings.",
        linkLabel: "Basis of figures ↓",
      },
      cardFigure: { value: "€1.4B", label: "Take-private equity value", basis: "reported" },
      compare: {
        place: "Leather goods · Marche",
        ownershipLine: "Private since 2024",
        ownershipNote: "Listed 2000–2024",
        revenue: { value: "€966M", note: "2025, as reported in the press", basis: "disclosed" },
        period: "−4% since 2016",
        valueMark: { value: "≈ €1.4B", note: "Take-private equity value, 2024", basis: "reported" },
      },
      headlines: [
        { label: "Revenue · 2025", value: "€966M", note: "As reported in the press", basis: "disclosed" },
        { label: "Over the decade", value: "−4%", note: "Revenue, 2016 to 2025", basis: "reported" },
        { label: "Take-private · 2024", value: "≈ €1.4B", note: "Equity value", basis: "reported" },
        { label: "LVMH stake", value: "10%", note: "From 2021 — see case 01", basis: "reported" },
      ],
      figure: {
        type: "indexed",
        title: "Figure 1 · Revenue, indexed to 2016 = 100",
        note: "— revenue almost flat across the decade; the last two years are disclosed, not filed.",
        lineId: "tods",
      },
      timeline: {
        title: "Ownership",
        items: [
          { year: "2000", title: "Listed", body: "Tod’s Group lists in Milan." },
          { year: "2021", title: "LVMH, 10%", body: "A minority seat beside the Della Valle family." },
          { year: "2024", title: "Take-private", body: "Family, L Catterton and LVMH take the group private at about €1.4B equity value." },
        ],
      },
      lens: null,
      basisTitle: "Basis of figures",
      basisHeading: "Where each number comes from.",
      basisRows: [
        { figure: "Revenue, 2016–2023", value: "Filed years", basis: "reported", source: "Tod’s Group annual reports while listed" },
        { figure: "Revenue, 2024–2025", value: "€1,046M · €966M", basis: "disclosed", source: "Press coverage after the take-private" },
        { figure: "Take-private equity value, 2024", value: "≈ €1.4B", basis: "reported", source: "Transaction announcements at completion" },
        { figure: "LVMH stake, 2021", value: "10%", basis: "reported", source: "LVMH and Tod’s announcements" },
      ],
    },
    {
      id: "ferragamo",
      n: "03",
      name: "Ferragamo",
      role: "Leather goods",
      ownership: "listed",
      ownershipLabel: "Listed · Milan",
      ownershipDetail: "Listed · Milan",
      basisSummary: "Every figure reported in audited filings",
      filterType: "Listed",
      home: "Florence",
      founded: "1927",
      ownershipFact: "Listed",
      periodOrChannel: { label: "Period", value: "2016–2025" },
      italic: "— a century of craft, without the compounding.",
      lead:
        "A Florence leather-goods house of the same scale as Tod’s. Over the decade its revenue fell by about a third. Market value is pending verification in this room.",
      cardFigure: { value: "−32%", label: "Revenue, 2016 to 2025", basis: "reported" },
      compare: {
        place: "Leather goods · Florence",
        ownershipLine: "Listed",
        ownershipNote: null,
        revenue: { value: "€977M", note: "2025", basis: "reported" },
        period: "−32% since 2016",
        valueMark: { value: "—", note: "Market value pending verification", basis: "pending" },
      },
      headlines: [
        { label: "Revenue · 2025", value: "€977M", note: "Calendar 2025", basis: "reported" },
        { label: "Over the decade", value: "−32%", note: "Revenue, 2016 to 2025", basis: "reported" },
        { label: "Peak · 2016", value: "€1.44B", note: "Starting point of the series", basis: "reported" },
        { label: "Market value", value: "—", note: "Pending verification", basis: "pending" },
      ],
      figure: {
        type: "bars",
        title: "Figure 1 · Ferragamo revenue, 2016–2025",
        metrics: {
          rev: {
            name: "Revenue",
            unit: "Revenue, €M",
            v: [1438, 1393, 1347, 1377, 916, 1136, 1252, 1156, 1035, 977],
            format: (v) => String(Math.round(v)),
            note: "— revenue −32% from 2016; the house did not grow with the category.",
          },
        },
        defaultMetric: "rev",
      },
      timeline: null,
      lens: null,
      basisTitle: "Basis of figures",
      basisHeading: "Where each number comes from.",
      basisRows: [
        { figure: "Revenue, 2016–2025", value: "€1,438M → €977M", basis: "reported", source: "Ferragamo annual reports" },
        { figure: "Market value", value: "—", basis: "pending", source: "Not yet verified for this room" },
      ],
    },
    {
      id: "sdj",
      n: "04",
      name: "Sol de Janeiro",
      role: "Beauty",
      ownership: "private",
      ownershipLabel: "Private · L’Occitane",
      ownershipDetail: "Private · L’Occitane Group",
      basisSummary: "Disclosed by its parent and buyer",
      filterType: "Private",
      home: "New York",
      founded: "2015",
      ownershipFact: "L’Occitane Group since 2021",
      periodOrChannel: { label: "Channel", value: "Sephora and social; no own stores" },
      italic: "— Brazil, built for the American shelf.",
      lead:
        "Launched with three products, among them the Brazilian Bum Bum Cream, the brand turned the language of Brazilian beach culture into fragrance-led body care, sold through Sephora and social media rather than its own stores. It never released a makeup line. Within ten years it reached the scale of a century-old Italian shoemaker.",
      privateCallout: {
        title: "Private company, inside a private group",
        body: "Sol de Janeiro files no accounts of its own, and L’Occitane Group left the Hong Kong exchange in 2024. Sales are derived from the group’s disclosed brand mix; margins are not disclosed.",
        linkLabel: "Basis of figures ↓",
      },
      cardFigure: { value: "≈ €885M", label: "Net sales, ten years in", basis: "disclosed" },
      compare: {
        place: "Beauty · New York",
        ownershipLine: "Private",
        ownershipNote: "Within L’Occitane Group",
        revenue: { value: "≈ €885M", note: "Year to March 2025", basis: "disclosed" },
        period: "Launched 2015",
        valueMark: { value: "$450M", note: "Acquisition valuation, 2021", basis: "disclosed" },
      },
      headlines: [
        { label: "Revenue · 2019", value: "$39M", note: "Four years after launch", basis: "disclosed" },
        { label: "Acquisition · 2021", value: "$450M", note: "Valuation; 83% to L’Occitane", basis: "disclosed" },
        { label: "Net sales · FY2025", value: "≈ €885M", note: "From €686M a year earlier", basis: "disclosed" },
        { label: "Sephora North America", value: "No. 1", note: "Beauty brand, all categories", basis: "disclosed", accent: true },
      ],
      figure: {
        type: "hbar",
        title: "Figure 1 · Annual sales, latest fiscal year, €M",
        note: "— a ten-year-old body-care brand, at the scale of two century-old shoemakers.",
        caption: "Ferragamo and Tod’s: calendar 2025. Sol de Janeiro: 31.6% of L’Occitane Group’s €2.8B in the year to March 2025.",
        rows: [
          { name: "Ferragamo", sub: "Founded 1927 · Listed", value: 977, label: "977", basis: "reported", highlight: false },
          { name: "Tod’s Group", sub: "Founded 1920 · Private", value: 966, label: "966", basis: "disclosed", highlight: false },
          { name: "Sol de Janeiro", sub: "Founded 2015 · Private", value: 885, label: "≈ 885", basis: "disclosed", highlight: true },
        ],
        max: 1000,
      },
      timeline: {
        title: "Ten years",
        items: [
          { year: "2015", title: null, body: "Launched in New York with three products." },
          { year: "2021", title: null, body: "L’Occitane Group acquires 83% at a $450M valuation; the founders keep a stake." },
          { year: "2024", title: null, body: "L’Occitane Group leaves the Hong Kong exchange in a €6.5B take-private." },
          { year: "FY2025", title: null, body: "31.6% of group sales; the leading fragrance brand at Sephora North America and on Amazon US." },
          { year: "2026", title: null, body: "Co-founder Heela Yang steps down as chief executive in April." },
        ],
      },
      lens: [
        { title: "Brazil travels", body: "The Brazilian idea sells at global scale when it is built for international retail from the first day." },
        { title: "Distribution multiplies", body: "One retail partner and social media carried the brand to scale without a store of its own. We plan distribution before we plan stores." },
        { title: "The steward matters", body: "A beauty group with global reach gave the brand its runway. We build each house for the owner who can take it furthest." },
      ],
      alongside: { id: "osklen", label: "Read alongside · Case 05", body: "Osklen — the same Brazilian idea, built at home and measured in reais." },
      basisTitle: "Basis of figures",
      basisHeading: "Where each number comes from.",
      basisRows: [
        { figure: "Revenue, 2019", value: "$39M", basis: "disclosed", source: "Press coverage of the 2021 acquisition" },
        { figure: "Acquisition valuation, 2021", value: "$450M · 83%", basis: "disclosed", source: "L’Occitane acquisition announcement and press, 2021" },
        { figure: "Net sales, FY2024", value: "€686M", basis: "disclosed", source: "Brand figure in L’Occitane Group results, year to March 2024" },
        { figure: "Net sales, FY2025", value: "≈ €885M", basis: "disclosed", source: "Derived: 31.6% brand share × €2.8B group net sales, year to March 2025" },
        { figure: "Operating margin", value: "Not disclosed", basis: "estimate", source: "Not yet estimated", confidence: "to be set" },
      ],
    },
    {
      id: "osklen",
      n: "05",
      name: "Osklen",
      role: "Fashion & lifestyle",
      ownership: "private",
      ownershipLabel: "Private · Grupo Dass",
      ownershipDetail: "Private · Grupo Dass",
      basisSummary: "Values are Fund I estimates",
      filterType: "Private",
      home: "Rio de Janeiro",
      founded: "1989",
      ownershipFact: "Grupo Dass since 2022",
      periodOrChannel: { label: "Stores", value: "80+ across Brazil" },
      italic: "— Rio’s own, measured in two currencies.",
      lead:
        "Oskar Metsavaht, a physician and snowboarder, made Rio’s beach culture into a lifestyle house and an early advocate of sustainable materials, with more than 80 stores across Brazil. In 2012 Alpargatas, owner of Havaianas, bought 30% after talks that had included LVMH and PPR, and later took 60%. To refocus on Havaianas it sold that stake to the footwear manufacturer Grupo Dass, closing in April 2022.",
      privateCallout: {
        title: "Private company",
        body: "Osklen publishes no accounts. Values here are Fund I estimates built from the stake prices Alpargatas filed; revenue is not yet estimated.",
        linkLabel: "How we estimate ↓",
      },
      cardFigure: { value: "≈ 1.1×", label: "Value in dollars, 2012 to 2021", basis: "estimate" },
      compare: {
        place: "Fashion & lifestyle · Rio",
        ownershipLine: "Private",
        ownershipNote: "Grupo Dass since 2022",
        revenue: { value: "Not disclosed", note: "Estimate in preparation", basis: "estimate" },
        period: "≈ 1.1× in dollars, 2012–21",
        periodBasis: "estimate",
        valueMark: { value: "≈ $121M", note: "Implied by stake price, 2021", basis: "estimate" },
      },
      headlines: [
        { label: "Stake sold · 2012", value: "$33M", note: "For 30%, to Alpargatas", basis: "reported" },
        { label: "Stake sold · 2021", value: "R$400M", note: "For 60%, to Grupo Dass", basis: "reported" },
        { label: "Implied value · 2021", value: "≈ $121M", note: "≈ R$667M for 100%", basis: "estimate" },
        { label: "Revenue", value: "—", note: "Not disclosed; estimate in preparation", basis: "estimate", muted: true },
      ],
      figure: {
        type: "fx",
        title: "Figure 1 · Osklen’s implied value, 2012 and 2021",
        note: "— nearly tripled at home; almost unchanged in the currency our limited partners commit in.",
        panels: [
          {
            title: "In Brazilian reais",
            multiple: "≈ 3.0×",
            accent: false,
            rows: [
              { year: "2012", pct: 33.7, value: "R$225M" },
              { year: "2021", pct: 100, value: "R$667M" },
            ],
          },
          {
            title: "In US dollars",
            multiple: "≈ 1.1×",
            accent: true,
            rows: [
              { year: "2012", pct: 90.9, value: "$110M" },
              { year: "2021", pct: 100, value: "≈ $121M" },
            ],
          },
        ],
      },
      timeline: {
        title: "Ownership",
        items: [
          { year: "1989", title: null, body: "Founded in Rio de Janeiro by Oskar Metsavaht." },
          { year: "2012", title: null, body: "Alpargatas buys 30% for $33M, after talks that had included LVMH and PPR; it later takes 60%." },
          { year: "2021", title: null, body: "Alpargatas agrees to sell its 60% to Grupo Dass for R$400M, to refocus on Havaianas." },
          { year: "2022", title: null, body: "The sale closes in April." },
        ],
      },
      lens: [
        { title: "Earn in dollars", body: "Fund I underwrites in dollars. International sales are a requirement of every plan, not an option within it." },
        { title: "Admired is not exported", body: "A house can lead its home market and still stand still in dollar terms. We underwrite the second market before we close." },
        { title: "The steward matters", body: "Osklen’s owner sold to refocus on its own brand. Fit of owner shapes the outcome." },
      ],
      alongside: { id: "sdj", label: "Read alongside · Case 04", body: "Sol de Janeiro — the same Brazilian idea, built in New York for the American shelf." },
      basisTitle: "Basis of figures · How we estimate",
      basisHeading: "Two filed prices; everything else is inferred.",
      basisRows: [
        { figure: "Stake sale, 2012", value: "30% for $33M (R$67.5M)", basis: "reported", source: "Alpargatas material fact, 2012" },
        { figure: "Stake sale, 2021", value: "60% for R$400M", basis: "reported", source: "Alpargatas material facts, 2021–2022" },
        {
          figure: "Implied value, 2012",
          value: "≈ $110M · R$225M",
          basis: "estimate",
          source: "Stake price divided by stake. A minority price, with no control premium or discount applied.",
          confidence: "Medium",
        },
        {
          figure: "Implied value, 2021",
          value: "≈ R$667M · ≈ $121M",
          basis: "estimate",
          source: "Stake price divided by stake, converted at about R$5.5 to the dollar. Debt and earn-out terms are not reflected.",
          confidence: "Medium",
        },
        { figure: "Revenue", value: "Not disclosed", basis: "estimate", source: "Not yet estimated", confidence: "to be set" },
      ],
    },
  ],
};

function getCaseHouse(id) {
  return CASE_STUDIES.houses.find((h) => h.id === id) || null;
}

function caseHouseIndex(id) {
  return CASE_STUDIES.houses.findIndex((h) => h.id === id);
}

/* ---------- Shared marks ---------- */

function BasisMark({ basis, title }) {
  const b = basis || "reported";
  if (b === "pending") {
    return <span className="cs-basis cs-basis-pending" title={title || "Pending"} aria-hidden="true" />;
  }
  if (b === "disclosed") {
    return <span className="cs-basis cs-basis-disclosed" title={title || "Disclosed"} aria-hidden="true" />;
  }
  if (b === "estimate") {
    return <span className="cs-basis cs-basis-estimate" title={title || "Estimate"} aria-hidden="true" />;
  }
  return <span className="cs-basis cs-basis-reported" title={title || "Reported"} aria-hidden="true" />;
}

function OwnershipBadge({ ownership, label }) {
  const privateish = ownership === "private";
  return (
    <span className={"cs-own-badge" + (privateish ? " private" : "")}>
      <span className="cs-own-dot" />
      {label}
    </span>
  );
}

function ConfidenceBars({ level }) {
  const filled = level === "Medium" ? 2 : level === "High" ? 3 : 0;
  return (
    <span className="cs-confidence">
      <span className="cs-confidence-bars" aria-hidden="true">
        {[0, 1, 2].map((i) => (
          <span key={i} className={i < filled ? "on" : ""} />
        ))}
      </span>
      Confidence · {level || "to be set"}
    </span>
  );
}

/* ---------- Charts ---------- */

function IndexedRevenueChart({ highlightId, note }) {
  const [hy, setHy] = useState(CASE_YEARS.length - 1);
  const idx = CASE_INDEX_LINES.map((l) => l.v.map((v) => (v / l.v[0]) * 100));
  const Y = (i) => ((240 - i) / 190) * 100;
  const pts = (a, from, to) =>
    a
      .map((v, i) => [i, v])
      .filter(([i]) => i >= from && i <= to)
      .map(([i, v]) => (i + 0.5) * 100 + "," + Y(v).toFixed(2))
      .join(" ");

  const readLines = highlightId && highlightId !== "all"
    ? CASE_INDEX_LINES.filter((l) => l.id === highlightId)
    : CASE_INDEX_LINES;

  return (
    <figure className="cs-figure">
      <div className="cs-figure-head">
        <span className="cs-figure-title">Figure 1 · Revenue, indexed to 2016 = 100</span>
        <div className="cs-figure-legend">
          {CASE_INDEX_LINES.map((l) => (
            <span key={l.id} className="cs-legend-item">
              <span className="cs-legend-line" style={{ background: l.color }} />
              {l.name}
            </span>
          ))}
          <span className="cs-legend-item">
            <span className="cs-legend-line dashed" />
            Disclosed, not filed
          </span>
        </div>
      </div>
      <div className="cs-idx-wrap">
        <div className="cs-idx-y">
          <span style={{ top: "21.05%" }}>200</span>
          <span style={{ top: "47.37%" }}>150</span>
          <span style={{ top: "73.68%" }} className="cs-idx-y-base">100</span>
          <span style={{ top: "100%" }}>50</span>
        </div>
        <div className="cs-idx-plot" onMouseLeave={() => setHy(CASE_YEARS.length - 1)}>
          <span className="cs-idx-grid" style={{ top: "21.05%" }} />
          <span className="cs-idx-grid" style={{ top: "47.37%" }} />
          <span className="cs-idx-grid base" style={{ top: "73.68%" }} />
          <span className="cs-idx-grid base" style={{ top: "100%" }} />
          <span className="cs-idx-cross" style={{ left: (hy + 0.5) * 10 + "%" }} />
          <svg viewBox="0 0 1000 100" preserveAspectRatio="none" className="cs-idx-svg">
            {CASE_INDEX_LINES.map((l, k) => {
              const solidTo = l.discFrom != null ? l.discFrom : idx[k].length - 1;
              return (
                <React.Fragment key={l.id}>
                  <polyline
                    points={pts(idx[k], 0, solidTo)}
                    fill="none"
                    stroke={l.color}
                    strokeWidth={l.id === "tods" ? 1.5 : 2}
                    strokeLinejoin="round"
                    vectorEffect="non-scaling-stroke"
                  />
                  {l.discFrom != null && (
                    <polyline
                      points={pts(idx[k], l.discFrom, idx[k].length - 1)}
                      fill="none"
                      stroke={l.color}
                      strokeWidth={1.5}
                      strokeDasharray="5 4"
                      strokeLinejoin="round"
                      vectorEffect="non-scaling-stroke"
                    />
                  )}
                </React.Fragment>
              );
            })}
          </svg>
          {CASE_INDEX_LINES.map((l, k) => (
            <span
              key={l.id}
              className="cs-idx-dot"
              style={{ left: (hy + 0.5) * 10 + "%", top: Y(idx[k][hy]) + "%", background: l.color }}
            />
          ))}
          <div className="cs-idx-hit">
            {CASE_YEARS.map((_, i) => (
              <span key={i} onMouseEnter={() => setHy(i)} onClick={() => setHy(i)} />
            ))}
          </div>
        </div>
      </div>
      <div className="cs-idx-years">
        {CASE_YEARS.map((y, i) => (
          <span key={y} className={i === hy ? "on" : ""}>{y}</span>
        ))}
      </div>
      <div className="cs-idx-read">
        {readLines.map((l) => {
          const k = CASE_INDEX_LINES.indexOf(l);
          const ch = Math.round(idx[k][hy] - 100);
          const disc = l.discFrom != null && hy >= l.discFrom;
          return (
            <div key={l.id}>
              <span className="cs-idx-read-label">
                <span style={{ background: l.color }} />
                {l.name} · {CASE_YEARS[hy]}
              </span>
              <span className="cs-idx-read-v">{l.format(l.v[hy])}</span>
              <span className={"cs-idx-read-ch" + (ch > 0 && hy ? " up" : "")}>
                {hy === 0 ? "Base year · 100" : (ch > 0 ? "+" : ch < 0 ? "−" : "") + Math.abs(ch) + "% on 2016"}
                {disc ? " · disclosed" : ""}
              </span>
            </div>
          );
        })}
      </div>
      {(note || CASE_STUDIES.indexNote) && (
        <span className="cs-figure-note">{note || CASE_STUDIES.indexNote}</span>
      )}
    </figure>
  );
}

function CaseBarChart({ figure }) {
  const keys = Object.keys(figure.metrics);
  const [metric, setMetric] = useState(figure.defaultMetric || keys[0]);
  const M = figure.metrics[metric];
  const max = Math.max(...M.v);
  const last = M.v.length - 1;

  return (
    <figure className="cs-figure">
      <div className="cs-figure-head">
        <div>
          <span className="cs-figure-title">{figure.title}</span>
          <span className="cs-figure-unit">{M.unit}</span>
        </div>
        {keys.length > 1 && (
          <div className="cs-tabs">
            {keys.map((k) => (
              <button
                key={k}
                type="button"
                className={"cs-tab" + (metric === k ? " on" : "")}
                onClick={() => setMetric(k)}
              >
                {figure.metrics[k].name}
              </button>
            ))}
          </div>
        )}
      </div>
      <div className="cs-bars">
        {M.v.map((v, i) => (
          <div key={CASE_YEARS[i]} className="cs-bar-col" title={CASE_YEARS[i] + " · " + M.format(v)}>
            <span className={i === last ? "on" : ""}>{M.format(v)}</span>
            <div style={{ height: Math.round((v / max) * 196) + "px" }} className={i === last ? "last" : ""} />
          </div>
        ))}
      </div>
      <div className="cs-bar-years">
        {CASE_YEARS.map((y, i) => (
          <span key={y} className={i === last ? "on" : ""}>{y}</span>
        ))}
      </div>
      {M.m && (
        <div className="cs-bar-subs">
          {M.m.map((m, i) => (
            <span key={i}>{m.toFixed(1)}%</span>
          ))}
        </div>
      )}
      <span className="cs-figure-note">{M.note}</span>
    </figure>
  );
}

function CaseHBarChart({ figure }) {
  return (
    <figure className="cs-figure">
      <div className="cs-figure-head">
        <span className="cs-figure-title">{figure.title}</span>
        <div className="cs-figure-legend">
          <span className="cs-legend-item"><span className="cs-swatch reported" />Reported</span>
          <span className="cs-legend-item"><span className="cs-swatch disclosed" />Disclosed</span>
        </div>
      </div>
      <div className="cs-hbars">
        {figure.rows.map((r) => (
          <div key={r.name} className="cs-hbar-row">
            <div className="cs-hbar-label">
              <span className={r.highlight ? "hi" : ""}>{r.name}</span>
              <span>{r.sub}</span>
            </div>
            <div className="cs-hbar-track">
              <span
                className={"cs-hbar-fill " + r.basis + (r.highlight ? " hi" : "")}
                style={{ width: (r.value / figure.max) * 100 + "%" }}
              />
            </div>
            <span className={"cs-hbar-val" + (r.highlight ? " hi" : "")}>{r.label}</span>
          </div>
        ))}
      </div>
      <span className="cs-figure-note">{figure.note}</span>
      {figure.caption && <span className="cs-figure-caption">{figure.caption}</span>}
    </figure>
  );
}

function CaseFxChart({ figure }) {
  return (
    <figure className="cs-figure">
      <div className="cs-figure-head">
        <span className="cs-figure-title">{figure.title}</span>
        <span className="cs-legend-item"><span className="cs-swatch estimate" />Fund I estimate</span>
      </div>
      <div className="cs-fx-grid">
        {figure.panels.map((p) => (
          <div key={p.title} className={"cs-fx-panel" + (p.accent ? " accent" : "")}>
            <div className="cs-fx-head">
              <span>{p.title}</span>
              <span className="cs-fx-mult">{p.multiple}</span>
            </div>
            {p.rows.map((r) => (
              <div key={r.year} className="cs-fx-row">
                <span>{r.year}</span>
                <div className="cs-fx-track">
                  <span style={{ width: r.pct + "%" }} className={p.accent ? "accent" : ""} />
                </div>
                <span>{r.value}</span>
              </div>
            ))}
          </div>
        ))}
      </div>
      <span className="cs-figure-note">{figure.note}</span>
    </figure>
  );
}

function CaseFigure({ figure }) {
  if (!figure) return null;
  if (figure.type === "bars") return <CaseBarChart figure={figure} />;
  if (figure.type === "hbar") return <CaseHBarChart figure={figure} />;
  if (figure.type === "fx") return <CaseFxChart figure={figure} />;
  if (figure.type === "indexed") {
    return <IndexedRevenueChart highlightId={figure.lineId} note={figure.note} />;
  }
  return null;
}

/* ---------- Overview ---------- */

function CasesOverview({ navigate }) {
  const [filter, setFilter] = useState("All");
  const houses = CASE_STUDIES.houses;
  const counts = { All: houses.length, Listed: 2, Private: 3 };
  const visible = houses.filter((h) => filter === "All" || h.filterType === filter);

  return (
    <div className="cs-wrap">
      <section className="cs-hero">
        <span className="cs-kicker">Fund I · Benchmarks</span>
        <div className="cs-hero-titles">
          <h1 className="cs-h1">Case studies</h1>
          <span className="cs-italic">{CASE_STUDIES.italic}</span>
        </div>
        <p className="cs-lead">{CASE_STUDIES.lead}</p>
        <div className="cs-meta">
          {CASE_STUDIES.meta.map((m) => (
            <div key={m.label}>
              <span>{m.label}</span>
              <span>{m.value}</span>
            </div>
          ))}
        </div>
        <div className="cs-hero-actions">
          <button type="button" className="cs-btn-ghost" onClick={() => navigate("cases", { caseSlug: "compare", a: "lvmh", b: "ferragamo" })}>
            Compare two houses
          </button>
        </div>
      </section>

      <section className="cs-legend-panel">
        <span className="cs-panel-kicker">How to read the figures</span>
        <div className="cs-legend-cols">
          <div>
            <span className="cs-legend-h">Ownership</span>
            <div className="cs-legend-row">
              <OwnershipBadge ownership="listed" label="Listed" />
              <span>Publishes audited accounts. Figures come from its own filings.</span>
            </div>
            <div className="cs-legend-row">
              <OwnershipBadge ownership="private" label="Private" />
              <span>Accounts partial or absent. Figures are disclosed by others or estimated by Fund I; each case page shows the method.</span>
            </div>
          </div>
          <div>
            <span className="cs-legend-h">Basis of each figure</span>
            <div className="cs-basis-legend">
              <BasisMark basis="reported" /><span>Reported</span><span className="cs-swatch reported" /><span>Audited accounts or regulatory filings</span>
              <BasisMark basis="disclosed" /><span>Disclosed</span><span className="cs-swatch disclosed" /><span>Published by a parent, a buyer or the press</span>
              <BasisMark basis="estimate" /><span>Estimate</span><span className="cs-swatch estimate" /><span>Fund I estimate, with method and confidence</span>
            </div>
          </div>
        </div>
      </section>

      <section className="cs-section">
        <div className="cs-section-head">
          <div>
            <span className="cs-kicker">The five cases</span>
            <h2 className="cs-h2">Choose a house to read in full.</h2>
          </div>
          <div className="cs-tabs">
            {["All", "Listed", "Private"].map((k) => (
              <button
                key={k}
                type="button"
                className={"cs-tab" + (filter === k ? " on" : "")}
                onClick={() => setFilter(k)}
              >
                {k}
                <span className="cs-tab-count">{counts[k]}</span>
              </button>
            ))}
          </div>
        </div>
        <div className="cs-cards">
          {visible.map((h) => (
            <button
              key={h.id}
              type="button"
              className={"cs-card" + (h.id === "lvmh" ? " featured" : "")}
              onClick={() => navigate("cases", { caseSlug: h.id })}
            >
              <span className="cs-card-role">{h.n} · {h.role}</span>
              <span className="cs-card-name">{h.name}</span>
              <OwnershipBadge ownership={h.ownership} label={h.ownershipLabel} />
              <span className="cs-card-spacer" />
              <span className="cs-card-fig">
                <span>{h.cardFigure.value}</span>
                <BasisMark basis={h.cardFigure.basis} />
              </span>
              <span className="cs-card-fig-label">{h.cardFigure.label}</span>
              <span className="cs-card-cta">Read the case<span>→</span></span>
            </button>
          ))}
        </div>
      </section>

      <section className="cs-section">
        <div className="cs-section-head stack">
          <span className="cs-kicker">Side by side</span>
          <h2 className="cs-h2">Five houses, on the same lines.</h2>
        </div>
        <div className="cs-compare-pairs">
          <span className="cs-muted-label">Read together</span>
          {CASE_COMPARE_PAIRS.map((p) => (
            <button
              key={p.a + p.b}
              type="button"
              className="cs-pair-btn"
              onClick={() => navigate("cases", { caseSlug: "compare", a: p.a, b: p.b })}
            >
              <span>{p.title}</span>
              <span>{getCaseHouse(p.a).name} · {getCaseHouse(p.b).name}</span>
            </button>
          ))}
        </div>
        <div className="cs-table-wrap">
          <div className="cs-table">
            <div className="cs-table-head">
              <span>House</span><span>Ownership</span><span>Latest revenue</span><span>Over the period</span><span>Latest value mark</span>
            </div>
            {visible.map((h) => {
              const c = h.compare;
              return (
                <button
                  key={h.id}
                  type="button"
                  className="cs-table-row"
                  onClick={() => navigate("cases", { caseSlug: h.id })}
                >
                  <span className="cs-table-house">
                    <span>{h.name}</span>
                    <span>{c.place}</span>
                  </span>
                  <span className="cs-table-own">
                    <span className={h.ownership === "private" ? "priv" : ""}>{c.ownershipLine}</span>
                    {c.ownershipNote && <span>{c.ownershipNote}</span>}
                  </span>
                  <span className="cs-table-cell">
                    <span><BasisMark basis={c.revenue.basis} />{c.revenue.value}</span>
                    <span>{c.revenue.note}</span>
                  </span>
                  <span className="cs-table-period">
                    {c.periodBasis && <BasisMark basis={c.periodBasis} />}
                    {c.period}
                  </span>
                  <span className="cs-table-cell">
                    <span>
                      {c.valueMark.basis !== "pending" && <BasisMark basis={c.valueMark.basis} />}
                      {c.valueMark.value}
                    </span>
                    <span>{c.valueMark.note}</span>
                  </span>
                </button>
              );
            })}
          </div>
        </div>
        <IndexedRevenueChart highlightId="all" />
      </section>

      <section className="cs-section">
        <div className="cs-section-head stack">
          <span className="cs-kicker">What the cases teach</span>
          <h2 className="cs-h2">Five lessons, and how Fund I applies them.</h2>
        </div>
        <div className="cs-lessons-head">
          <span /><span>Lesson</span><span>The evidence</span><span>In Fund I</span>
        </div>
        {CASE_STUDIES.lessons.map((L) => (
          <div key={L.n} className="cs-lesson">
            <span className="cs-lesson-n">{L.n}</span>
            <span className="cs-lesson-title">{L.title}</span>
            <span className="cs-lesson-ev">
              <span>{L.evidence}</span>
              <span className="cs-lesson-links">
                {L.cases.map((id) => {
                  const h = getCaseHouse(id);
                  return (
                    <button key={id} type="button" className="cs-text-link" onClick={() => navigate("cases", { caseSlug: id })}>
                      Case {h.n} →
                    </button>
                  );
                })}
              </span>
            </span>
            <span className="cs-lesson-fund">{L.fund}</span>
          </div>
        ))}
        <blockquote className="cs-quote">
          Heritage is the raw material. Capital, distribution and patience are what turn it into a return.
        </blockquote>
      </section>

      <section className="cs-section">
        <div className="cs-section-head stack">
          <span className="cs-kicker">Sources</span>
          <h2 className="cs-h2">Every figure, traced.</h2>
        </div>
        <div className="cs-sources-list">
          <div className="cs-source-row">
            <span className="cs-basis cs-basis-reported" />
            <span>
              <span>Case book — five houses</span>
              <span>PDF · Not yet on file · Updated {CASE_STUDIES.updated}</span>
            </span>
            <span className="cs-muted-label">Pending</span>
          </div>
          <div className="cs-source-row">
            <span className="cs-basis cs-basis-reported" />
            <span>
              <span>Source workbook — every figure, with its basis</span>
              <span>Spreadsheet · Not yet on file · Estimates on a separate tab</span>
            </span>
            <span className="cs-muted-label">Pending</span>
          </div>
        </div>
        <p className="cs-sources-note">
          Each case page lists its own sources and the basis of every figure. Estimates for private companies are the Fund I team’s and are revised each reporting season. The performance of these companies does not indicate the returns of Fund I. Figures shown here are illustrative until the source workbook is on file.
        </p>
      </section>

      <footer className="cs-footer-nav">
        <button type="button" className="cs-nav-card" onClick={() => navigate("verticals")}>
          <span>← Previous</span>
          <span>Sector theses</span>
        </button>
        <button type="button" className="cs-nav-card end" onClick={() => navigate("cases", { caseSlug: "lvmh" })}>
          <span>Begin → Case 01</span>
          <span>LVMH</span>
        </button>
      </footer>
    </div>
  );
}

/* ---------- Case page ---------- */

function CasePage({ caseSlug, navigate }) {
  const house = getCaseHouse(caseSlug);
  if (!house) {
    return (
      <div className="cs-wrap">
        <p className="cs-lead">That case is not on file.</p>
        <button type="button" className="dr-back-link" onClick={() => navigate("cases")}>All cases</button>
      </div>
    );
  }
  const idx = caseHouseIndex(house.id);
  const prev = idx > 0 ? CASE_STUDIES.houses[idx - 1] : null;
  const next = idx < CASE_STUDIES.houses.length - 1 ? CASE_STUDIES.houses[idx + 1] : null;

  return (
    <article className="cs-wrap cs-case">
      <section className="cs-case-hero">
        <div className="cs-crumb">
          <button type="button" onClick={() => navigate("cases")}>Case studies</button>
          <span>/</span>
          <span>Case {house.n} · {house.role}</span>
        </div>
        <div className="cs-hero-titles">
          <h1 className="cs-h1">{house.name}</h1>
          <span className="cs-italic">{house.italic}</span>
        </div>
        <div className="cs-case-badges">
          <OwnershipBadge ownership={house.ownership} label={house.ownershipDetail} />
          <span className="cs-basis-summary">
            <BasisMark basis={house.ownership === "listed" ? "reported" : house.id === "osklen" ? "estimate" : "disclosed"} />
            {house.basisSummary}
          </span>
        </div>
        <div className="cs-meta">
          <div><span>Home</span><span>{house.home}</span></div>
          <div><span>{house.founded.length === 4 && Number(house.founded) < 1980 ? "Founded" : house.id === "lvmh" ? "Formed" : "Founded"}</span><span>{house.founded}</span></div>
          <div><span>Ownership</span><span>{house.ownershipFact}</span></div>
          <div><span>{house.periodOrChannel.label}</span><span>{house.periodOrChannel.value}</span></div>
        </div>
      </section>

      {house.privateCallout && (
        <div className="cs-private-callout">
          <div>
            <span>{house.privateCallout.title}</span>
            <span>{house.privateCallout.body}</span>
          </div>
          <a href="#cs-basis">{house.privateCallout.linkLabel}</a>
        </div>
      )}

      <p className="cs-lead">{house.lead}</p>

      <div className="cs-headlines">
        {house.headlines.map((f) => (
          <div key={f.label} className={f.accent ? "accent" : f.muted ? "muted" : ""}>
            <span>
              <BasisMark basis={f.basis} />
              {f.label}
            </span>
            <span className="cs-headline-v">{f.value}</span>
            <span>{f.note}</span>
          </div>
        ))}
      </div>

      <CaseFigure figure={house.figure} />

      {house.timeline && (
        <div className="cs-timeline">
          <span className="cs-timeline-title">{house.timeline.title}</span>
          {house.timeline.items.map((it, i) => (
            <div key={i} className={"cs-timeline-row" + (it.title ? " titled" : "")}>
              <span>{it.year}</span>
              {it.title ? (
                <>
                  <span className="cs-timeline-name">{it.title}</span>
                  <span>{it.body}</span>
                </>
              ) : (
                <span className="cs-timeline-body">{it.body}</span>
              )}
            </div>
          ))}
        </div>
      )}

      {house.lens && (
        <div className="cs-lens">
          <span className="cs-panel-kicker">The Fund I lens</span>
          {house.lens.map((L) => (
            <div key={L.title}>
              <span>{L.title}</span>
              <p>{L.body}</p>
            </div>
          ))}
        </div>
      )}

      {house.alongside && (
        <button
          type="button"
          className="cs-alongside"
          onClick={() => navigate("cases", { caseSlug: house.alongside.id })}
        >
          <span>
            <span>{house.alongside.label}</span>
            <span>{house.alongside.body}</span>
          </span>
          <span>→</span>
        </button>
      )}

      <section id="cs-basis" className="cs-basis-section">
        <div className="cs-section-head stack">
          <span className="cs-kicker">{house.basisTitle}</span>
          <h2 className="cs-h2">{house.basisHeading}</h2>
        </div>
        <div className="cs-basis-table">
          <div className="cs-basis-head">
            <span>Figure</span><span>Value</span><span>Basis</span><span>Source and method</span>
          </div>
          {house.basisRows.map((r) => (
            <div key={r.figure} className="cs-basis-row">
              <span>{r.figure}</span>
              <span>{r.value}</span>
              <span className="cs-basis-cell"><BasisMark basis={r.basis} />{r.basis === "reported" ? "Reported" : r.basis === "disclosed" ? "Disclosed" : r.basis === "pending" ? "Pending" : "Estimate"}</span>
              <span>
                <span>{r.source}</span>
                {r.confidence && <ConfidenceBars level={r.confidence} />}
              </span>
            </div>
          ))}
        </div>
      </section>

      <footer className="cs-footer-nav">
        {prev ? (
          <button type="button" className="cs-nav-card" onClick={() => navigate("cases", { caseSlug: prev.id })}>
            <span>← Previous · Case {prev.n}</span>
            <span>{prev.name}</span>
          </button>
        ) : (
          <button type="button" className="cs-nav-card" onClick={() => navigate("cases")}>
            <span>← All cases</span>
            <span>Case studies overview</span>
          </button>
        )}
        {next ? (
          <button type="button" className="cs-nav-card end" onClick={() => navigate("cases", { caseSlug: next.id })}>
            <span>Next → Case {next.n}</span>
            <span>{next.name}</span>
          </button>
        ) : (
          <button type="button" className="cs-nav-card end" onClick={() => navigate("cases")}>
            <span>All cases →</span>
            <span>Case studies overview</span>
          </button>
        )}
      </footer>
      <p className="cs-disclaimer">
        Prepared by the Fund I team · Updated {CASE_STUDIES.updated}. The performance of these companies does not indicate the returns of Fund I. Figures are illustrative.
      </p>
    </article>
  );
}

/* ---------- Compare two (shell; questions await FCP_CASES) ---------- */

function CasesCompare({ navigate, pairA, pairB }) {
  const order = CASE_STUDIES.houses.map((h) => h.id);
  const defaultA = "lvmh";
  const defaultB = "ferragamo";
  const [a, setA] = useState(() => (order.includes(pairA) ? pairA : defaultA));
  const [b, setB] = useState(() => {
    const start = order.includes(pairB) && pairB !== (order.includes(pairA) ? pairA : defaultA)
      ? pairB
      : defaultB;
    return start === (order.includes(pairA) ? pairA : defaultA)
      ? order.find((k) => k !== (order.includes(pairA) ? pairA : defaultA))
      : start;
  });

  useEffect(() => {
    if (order.includes(pairA) && pairA !== a) setA(pairA);
    if (order.includes(pairB) && pairB !== b && pairB !== a) setB(pairB);
  }, [pairA, pairB]);

  const setPair = (na, nb) => {
    let nextA = order.includes(na) ? na : defaultA;
    let nextB = order.includes(nb) && nb !== nextA ? nb : order.find((k) => k !== nextA);
    setA(nextA);
    setB(nextB);
    navigate("cases", { caseSlug: "compare", a: nextA, b: nextB });
  };

  const pick = (side, k) => {
    if (side === "a") {
      if (k === b) setPair(k, a);
      else setPair(k, b);
    } else {
      if (k === a) setPair(b, k);
      else setPair(a, k);
    }
  };

  const hA = getCaseHouse(a);
  const hB = getCaseHouse(b);
  const external = typeof window !== "undefined" && window.FCP_CASES;
  const questions = external && typeof external.compare === "function"
    ? external.compare(a, b, true)
    : null;

  return (
    <div className="cs-wrap cs-compare-page">
      <section className="cs-hero">
        <div className="cs-crumb">
          <button type="button" onClick={() => navigate("cases")}>Case studies</button>
          <span>/</span>
          <span>Compare two</span>
        </div>
        <div className="cs-hero-titles">
          <h1 className="cs-h1">Two houses, side by side</h1>
          <span className="cs-italic">— nine questions, on the same lines.</span>
        </div>
        <p className="cs-lead">
          Choose any two of the five cases. Each is read against the same nine questions, and every figure keeps its mark: reported, disclosed, estimated or still pending.
        </p>
      </section>

      <section className="cs-choose">
        <div>
          <span className="cs-panel-kicker">House A</span>
          <div className="cs-tabs wrap">
            {CASE_STUDIES.houses.map((h) => (
              <button
                key={h.id}
                type="button"
                className={"cs-tab" + (a === h.id ? " on" : "") + (b === h.id ? " taken" : "")}
                title={b === h.id ? "Already on the other side — click to swap" : ""}
                onClick={() => pick("a", h.id)}
              >
                <span className="cs-tab-n">{h.n}</span>{h.name}
              </button>
            ))}
          </div>
        </div>
        <div>
          <span className="cs-panel-kicker">House B</span>
          <div className="cs-tabs wrap">
            {CASE_STUDIES.houses.map((h) => (
              <button
                key={h.id}
                type="button"
                className={"cs-tab" + (b === h.id ? " on" : "") + (a === h.id ? " taken" : "")}
                title={a === h.id ? "Already on the other side — click to swap" : ""}
                onClick={() => pick("b", h.id)}
              >
                <span className="cs-tab-n">{h.n}</span>{h.name}
              </button>
            ))}
          </div>
        </div>
        <div className="cs-choose-foot">
          <span className="cs-muted-label">Read together</span>
          {CASE_COMPARE_PAIRS.map((p) => {
            const on = (a === p.a && b === p.b) || (a === p.b && b === p.a);
            return (
              <button
                key={p.title}
                type="button"
                className={"cs-pair-btn" + (on ? " on" : "")}
                onClick={() => setPair(p.a, p.b)}
              >
                <span>{p.title}</span>
                <span>{getCaseHouse(p.a).name} · {getCaseHouse(p.b).name}</span>
              </button>
            );
          })}
          <span className="cs-choose-spacer" />
          <button type="button" className="cs-tab" onClick={() => setPair(b, a)}>
            Swap A and B
          </button>
        </div>
      </section>

      <div className="cs-compare-sticky">
        <span className="cs-compare-sticky-label">Nine questions</span>
        <button type="button" className="cs-compare-sticky-house" onClick={() => navigate("cases", { caseSlug: a })}>
          <span>A · {hA.n}</span>
          <span>{hA.name}</span>
          <OwnershipBadge ownership={hA.ownership} label={hA.ownershipLabel} />
        </button>
        <button type="button" className="cs-compare-sticky-house" onClick={() => navigate("cases", { caseSlug: b })}>
          <span>B · {hB.n}</span>
          <span>{hB.name}</span>
          <OwnershipBadge ownership={hB.ownership} label={hB.ownershipLabel} />
        </button>
      </div>

      {questions && questions.length ? (
        questions.map((q, i) => (
          <div key={q.id || i} className="cs-q-block">
            <div className="cs-q-prompt">
              <span>{q.n || (i === 0 ? "" : String(i))}</span>
              <span>{q.q || (i === 0 ? "At a glance" : "")}</span>
              {q.sub && <span>{q.sub}</span>}
            </div>
            {q.ha && <p className="cs-q-ans">{q.ha}</p>}
            {q.hb && <p className="cs-q-ans">{q.hb}</p>}
            {(q.rows || []).map((r, ri) => (
              <React.Fragment key={ri}>
                {r.l && <span className="cs-q-row-label">{r.l}</span>}
                <div className="cs-q-cell">
                  {r.a && r.a.v && (
                    <>
                      <span><BasisMark basis={r.a.basis || "reported"} />{r.a.v}</span>
                      {r.a.note && <span className="cs-q-note">{r.a.note}</span>}
                    </>
                  )}
                </div>
                <div className="cs-q-cell">
                  {r.b && r.b.v && (
                    <>
                      <span><BasisMark basis={r.b.basis || "reported"} />{r.b.v}</span>
                      {r.b.note && <span className="cs-q-note">{r.b.note}</span>}
                    </>
                  )}
                </div>
              </React.Fragment>
            ))}
          </div>
        ))
      ) : (
        <div className="cs-compare-pending">
          <span className="cs-panel-kicker">Nine questions</span>
          <p>
            The compare layout is ready. The nine questions and their answers wait on the case-compare script. Until it is on file, choose a pair above and open each house in full.
          </p>
          <div className="cs-compare-pending-actions">
            <button type="button" className="cs-btn-ghost" onClick={() => navigate("cases", { caseSlug: a })}>
              Read {hA.name}
            </button>
            <button type="button" className="cs-btn-ghost" onClick={() => navigate("cases", { caseSlug: b })}>
              Read {hB.name}
            </button>
          </div>
        </div>
      )}

      <section className="cs-legend-panel">
        <div className="cs-legend-cols">
          <div>
            <span className="cs-panel-kicker">Basis of each figure</span>
            <div className="cs-basis-legend simple">
              <BasisMark basis="reported" /><span>Reported</span><span>Audited accounts or filings</span>
              <BasisMark basis="disclosed" /><span>Disclosed</span><span>By a parent, a buyer or the press</span>
              <BasisMark basis="estimate" /><span>Estimate</span><span>Fund I estimate or reading</span>
              <BasisMark basis="pending" /><span>Pending</span><span>Not yet sourced or estimated</span>
            </div>
          </div>
          <div>
            <span className="cs-panel-kicker">The four Fund I steps</span>
            <div className="cs-steps-legend">
              <span className="cs-step-bars"><span className="on" /><span className="on" /></span><span>Direct evidence</span><span>The case shows the step working</span>
              <span className="cs-step-bars"><span className="on" /><span /></span><span>Partial evidence</span><span>Present, but not decisive</span>
              <span className="cs-step-bars"><span /><span /></span><span>Counter-example</span><span>Shows the cost of skipping it</span>
            </div>
            <p className="cs-figure-caption">
              As set out in the{" "}
              <button type="button" className="cs-text-link" onClick={() => navigate("verticals")}>sector theses</button>
              : supply and working capital, international wholesale, direct to consumer, category extension.
            </p>
          </div>
        </div>
      </section>

      <footer className="cs-footer-nav triple">
        <button type="button" className="cs-nav-card" onClick={() => navigate("cases")}>
          <span>← All cases</span>
          <span>Case studies overview</span>
        </button>
        <button type="button" className="cs-nav-card end" onClick={() => navigate("cases", { caseSlug: a })}>
          <span>Read in full → {hA.n}</span>
          <span>{hA.name}</span>
        </button>
        <button type="button" className="cs-nav-card end" onClick={() => navigate("cases", { caseSlug: b })}>
          <span>Read in full → {hB.n}</span>
          <span>{hB.name}</span>
        </button>
      </footer>
      <p className="cs-disclaimer">
        Prepared by the Fund I team · Updated {CASE_STUDIES.updated}. The performance of these companies does not indicate the returns of Fund I.
      </p>
    </div>
  );
}

function CasePlaceholder({ navigate }) {
  return (
    <article className="cs-wrap cs-case">
      <section className="cs-case-hero">
        <div className="cs-crumb">
          <button type="button" onClick={() => navigate("cases")}>Case studies</button>
          <span>/</span>
          <span>Case 06 · USA</span>
        </div>
        <div className="cs-hero-titles">
          <h1 className="cs-h1">Tiffany's</h1>
          <span className="cs-italic">— reserved. Not yet on file.</span>
        </div>
      </section>
      <p className="cs-lead">This page is held for the Tiffany's case. The write-up, figures, and sources will be added when the case is on file.</p>
      <button type="button" className="dr-back-link" onClick={() => navigate("cases")}>All cases</button>
    </article>
  );
}

function CasesView({ navigate, caseSlug, pairA, pairB }) {
  if (caseSlug === "compare") {
    return <CasesCompare navigate={navigate} pairA={pairA} pairB={pairB} />;
  }
  if (caseSlug === "tiffany") {
    return <CasePlaceholder navigate={navigate} />;
  }
  if (caseSlug) {
    return <CasePage caseSlug={caseSlug} navigate={navigate} />;
  }
  return <CasesOverview navigate={navigate} />;
}

Object.assign(window, {
  CASE_STUDIES,
  CASE_COMPARE_PAIRS,
  CASE_INDEX_LINES,
  getCaseHouse,
  CasesView,
  CasesOverview,
  CasePage,
  CasesCompare,
  BasisMark,
  OwnershipBadge,
});
