/* ──────────────────────────────────────────────────────────────────
   German External Employment Counsel — Strategic Confidence Assessment
   ====================================================================
   A landing page + audience-assessment tool for the German EXTERNAL
   EMPLOYMENT COUNSEL segment: partner-level Fachanwälte für Arbeitsrecht
   who advise employers. This is the external-counsel sibling of
   deploy/german-hr-assessment/. It is a DIFFERENT audience: where the
   HR tool reads as a co-pilot, this one reads as adversarial leverage —
   "a second pair of eyes that pressure-tests the file the way opposing
   counsel and the judge will" (ARAM_ExternalCounsel_Persona.md).

   Built on the ApprovedLeadMagnet "BAG Reconstruction Snapshot" stack
   (React 18 + Babel Standalone, ARAM "Neue Authority / Verfahrenscheck"
   brand). The architecture is the 15-question shape from
   deploy/Audience Assessment Architecture.md:

     Q1–Q10  Yes / Unknown / No  → the score (route-specific best practice)
     Q11     Current reality
     Q12     Desired reality
     Q13     Obstacles (multi-select)
     Q14     Preferred next step
     Q15     Open text

   Landing copy spine: deploy/Landing Pages/# Landing Page Version 3.md
     hook ("What would opposing counsel see first?") · three benefits
     (Vulnerability Visibility / Evidence Strength / Litigation Readiness)
     · authority with BAG citations · assessment testimonials · CTA.

   The five Q1–Q10 question sets are derived, in counsel's own register,
   from the five defensibility checklists in deploy/ (the A–E folders):
     conduct          ← CONDUCT_DEFENSIBILITY_CHECKLIST.md
     capability       ← PERSONALCAPAB_DEFENSIBILITY_CHECKLIST.md
     restructuring    ← RESTRUCTURING_DEFENSIBILITY_CHECKLIST.md
     extraordinary    ← EXTRAORDINARY_DEFENSIBILITY_CHECKLIST.md
     senior_executive ← SENIOR_EXECUTIVE_DEFENSIBILITY_CHECKLIST.md (DRAFT)

   Audience framing (persona §18, §20): mechanism IS the credibility.
   Speak in defensibility, §102 robustness, Beweislage, Ausschlussfrist,
   Sozialauswahl-Plausibilität, adversarial pressure-test, audit-ready
   file. Clean RDG positioning — supports counsel, is not Rechtsberatung.
─────────────────────────────────────────────────────────────────── */

const { useMemo, useState } = React;

/* ── The five route-specific question sets (Q1–Q10) + failure modes ── */
const ROUTES = [
  {
    id: "conduct",
    de: "Verhaltensbedingte Kündigung",
    en: "Conduct-based dismissal (§1 Abs. 2 KSchG / §626 BGB)",
    blurb:
      "A substantively correct conduct dismissal still dies on a missing Abmahnung or a procedural slip. This set pressure-tests whether the file carries what the Arbeitsgericht — and opposing counsel — reach for first.",
    questions: [
      { n: 1, text: "Is the Kündigungsgrund pinned to specific facts, dates, and observers — not a general conclusion an employee-side lawyer can pick apart?" },
      { n: 2, text: "Is a prior written Abmahnung for the same type of conduct on file — carrying genuine Rüge-, Hinweis- and Warnfunktion — or a documented basis for waiving it (grave breach, trust destroyed)?" },
      { n: 3, text: "Is the Interessenabwägung on the record — severity, Betriebszugehörigkeit, prior conduct, and personal circumstances actually weighed?" },
      { n: 4, text: "Were milder means (Versetzung, Ermahnung, a further Abmahnung) considered and rejected on the record, so ultima ratio holds?" },
      { n: 5, text: "Is special-protection status cleared (Schwangerschaft, Elternzeit, Schwerbehinderung, BR-Mandat) before notice?" },
      { n: 6, text: "Where a Betriebsrat exists, is the §102 Anhörung complete, accurate, dated before notice, and congruent with the stated grounds — no Nachschieben?" },
      { n: 7, text: "Is the dismissal in §623 BGB written form with a wet signature and provable Zugang?" },
      { n: 8, text: "Is the decision-maker and their Kündigungsberechtigung identifiable from the file alone?" },
      { n: 9, text: "Is the decision basis free of any AGG indicium — or is a §22-ready rebuttal documented?" },
      { n: 10, text: "Could you defend this dismissal from the file alone — through the Güteverhandlung and into the LAG — without re-interviewing the client's managers?" },
    ],
    patterns: [
      { id: "abmahnung", name: "Warning gap (Abmahnung)", name_de: "Abmahnungs-Lücke", concern: "Without a documented prior warning carrying all three functions — or a clearly justified waiver — a conduct dismissal is the easiest type to void, however serious the conduct.", q: [2] },
      { id: "proportionality", name: "Proportionality / ultima ratio gap", name_de: "Verhältnismäßigkeits-Lücke", concern: "If the Interessenabwägung and the milder means aren't on the record, the file can't show why termination was the necessary step — the first thing the bench probes.", q: [3, 4] },
      { id: "works_council", name: "Works-council hearing gap (§102 BetrVG)", name_de: "Betriebsrat-Anhörungslücke", concern: "A defective §102 Anhörung voids a substantively correct dismissal. Process risk dwarfs substance risk, and it is the kill-shot opposing counsel reaches for first.", q: [6] },
      { id: "protection", name: "Special-protection gap", name_de: "Sonderkündigungsschutz-Lücke", concern: "An unchecked Sonderkündigungsschutz (Schwangerschaft, Schwerbehinderung, BR-Mandat) can bar the dismissal outright.", q: [5] },
      { id: "form", name: "Form & delivery gap", name_de: "Form- und Zugangslücke", concern: "§623 BGB written form and provable Zugang are deterministic — a slip ends the matter before substance is ever reached.", q: [7] },
      { id: "evidence", name: "Reason & decision-owner gap", name_de: "Beweis- und Entscheidungsträger-Lücke", concern: "If the Kündigungsgrund isn't bridged to evidence, or the Kündigungsberechtigter can't be identified from the file, the decision is hard to reconstruct 18 months out.", q: [1, 8] },
      { id: "discrimination", name: "Anti-discrimination gap (AGG)", name_de: "AGG-Lücke", concern: "An AGG indicium shifts the burden under §22; an undocumented rebuttal becomes a §15 Entschädigung opening.", q: [9] },
      { id: "replayability", name: "File replayability gap", name_de: "Akten-Rekonstruierbarkeits-Lücke", concern: "The client may know what happened, but if the file can't prove it once memories fade and disclosure is full, the decision is exposed.", q: [10] },
    ],
  },
  {
    id: "capability",
    de: "Personenbedingte Kündigung — Krankheit / Leistung",
    en: "Capability / illness-based dismissal (§1 Abs. 2 KSchG)",
    blurb:
      "Illness and capability dismissals turn on the BEM and the negative prognosis. A missing BEM is one of the strongest defects an employee-side review can raise — and the burden it shifts lands on your client.",
    questions: [
      { n: 1, text: "Is the Fehlzeiten record documented over a representative period (around 24 months), with dates and a clear pattern?" },
      { n: 2, text: "Was a BEM (§167 Abs. 2 SGB IX) offered in writing and documented before notice — or its omission defensibly explained?" },
      { n: 3, text: "Is a negative Gesundheitsprognose on file, with its basis and its date?" },
      { n: 4, text: "Is the Betriebsbeeinträchtigung documented — disruption, cover costs, or why the absences hurt the business?" },
      { n: 5, text: "Is the Interessenabwägung on the record — tenure, age, cause of the illness, and labour-market position weighed?" },
      { n: 6, text: "Were milder means evaluated (leidensgerechter Arbeitsplatz, Versetzung, Teilzeit, Umschulung) with rejection reasons recorded?" },
      { n: 7, text: "If the employee is schwerbehindert, was Integrationsamt approval obtained before notice (§168 SGB IX)?" },
      { n: 8, text: "Where a Betriebsrat exists, is the §102 Anhörung complete, dated before notice, with the response window respected?" },
      { n: 9, text: "Is the dismissal in §623 BGB written form with a wet signature and provable Zugang?" },
      { n: 10, text: "Could you carry the prognosis-and-impact story from the file alone, under cross-examination, without re-interviewing anyone?" },
    ],
    patterns: [
      { id: "bem", name: "BEM gap", name_de: "BEM-Lücke (§167 SGB IX)", concern: "BAG treats the BEM as an ultima-ratio indicator. Absent a documented offer, your client carries the burden of showing no milder means existed — the strongest substantive defect in this route.", q: [2] },
      { id: "absence_prognosis", name: "Absence record & prognosis gap", name_de: "Fehlzeiten- und Prognose-Lücke", concern: "Without a representative Fehlzeiten record and a dated negative Prognose, the factual basis for the dismissal can't be reconstructed.", q: [1, 3] },
      { id: "operational_impact", name: "Operational-impact gap", name_de: "Betriebsbeeinträchtigungs-Lücke", concern: "If the Betriebsbeeinträchtigung isn't documented, the file shows absences but not why they justified termination.", q: [4] },
      { id: "proportionality", name: "Proportionality & alternatives gap", name_de: "Verhältnismäßigkeits-Lücke", concern: "Tenure, illness cause, and milder means (Teilzeit, Umschulung, Versetzung) must be weighed on the record, or ultima ratio fails.", q: [5, 6] },
      { id: "protection", name: "Integrationsamt approval gap", name_de: "Integrationsamt-Lücke", concern: "For a schwerbehinderter employee, prior Integrationsamt approval is mandatory — notice without it is void.", q: [7] },
      { id: "works_council", name: "Works-council hearing gap (§102 BetrVG)", name_de: "Betriebsrat-Anhörungslücke", concern: "A defective §102 Anhörung voids the dismissal independently of the medical substance.", q: [8] },
      { id: "form", name: "Form & delivery gap", name_de: "Form- und Zugangslücke", concern: "Written form and provable Zugang are deterministic gates — a slip ends the matter early.", q: [9] },
      { id: "replayability", name: "File replayability gap", name_de: "Akten-Rekonstruierbarkeits-Lücke", concern: "If the file can't carry the prognosis-and-impact story on its own, the decision is exposed once memories fade.", q: [10] },
    ],
  },
  {
    id: "restructuring",
    de: "Betriebsbedingte Kündigung",
    en: "Operational / restructuring dismissal (§1 Abs. 2–3 KSchG)",
    blurb:
      "Restructuring is the type most exposed to reconstruction: a Betriebsrat or the employee's lawyer rebuilds the Sozialauswahl after the fact. The pool, the scores, and the §102 hearing are where it most often breaks.",
    questions: [
      { n: 1, text: "Is the unternehmerische Entscheidung documented — which positions are eliminated and the headcount rationale — and dated before notice?" },
      { n: 2, text: "Is the vergleichbare-Arbeitnehmer pool defined via the three-test (hierarchy / Austauschbarkeit / same Betrieb), with inclusion-exclusion reasoning?" },
      { n: 3, text: "Is the Sozialdaten set complete for every pool member — Betriebszugehörigkeit, age, Unterhaltspflichten, Schwerbehinderung?" },
      { n: 4, text: "Is the weighting model for the Sozialauswahl documented and justified?" },
      { n: 5, text: "Is the Punkteschema complete, and does the selected employee actually carry the lowest score?" },
      { n: 6, text: "Were per-employee Weiterbeschäftigung alternatives evaluated (ultima ratio)?" },
      { n: 7, text: "If the §17 KSchG threshold is met, was the Massenentlassungsanzeige filed with the Agentur für Arbeit before issuance?" },
      { n: 8, text: "Where a Betriebsrat exists, does the §102 Anhörung carry the Sozialauswahl data, complete and dated before notice?" },
      { n: 9, text: "Is the dismissal in §623 BGB written form with provable Zugang, and the selection free of any AGG indicium?" },
      { n: 10, text: "Could you defend the selection from the file alone — the pool, the scores, the rationale — against a hostile Sozialauswahl-Plausibilität challenge?" },
    ],
    patterns: [
      { id: "social_selection", name: "Sozialauswahl gap", name_de: "Sozialauswahl-Lücke", concern: "The pool, the Sozialdaten, the weighting, and the Punkteschema are the heart of a restructuring file. A gap in any of them lets the selection be rebuilt — and overturned.", q: [2, 3, 4, 5] },
      { id: "works_council", name: "Works-council hearing gap (§102 BetrVG)", name_de: "Betriebsrat-Anhörungslücke", concern: "A §102 Anhörung that omits the pool and scores voids the dismissal even when the substance is perfect — the top reconstruction kill-shot for restructuring.", q: [8] },
      { id: "business_decision", name: "Business-decision gap", name_de: "Unternehmerentscheidungs-Lücke", concern: "Without a dated, documented unternehmerische Entscheidung, the dringende betriebliche Erfordernisse can't be shown.", q: [1] },
      { id: "redeployment", name: "Redeployment gap", name_de: "Weiterbeschäftigungs-Lücke", concern: "If per-employee alternatives weren't evaluated, ultima ratio fails — termination wasn't shown to be the last resort.", q: [6] },
      { id: "mass_layoff", name: "Mass-layoff notification gap", name_de: "Massenentlassungsanzeige-Lücke", concern: "Where the §17 threshold is met, a late or missing Massenentlassungsanzeige voids the dismissals filed under it — a high-frequency invalidation ground.", q: [7] },
      { id: "form_agg", name: "Form & anti-discrimination gap", name_de: "Form- und AGG-Lücke", concern: "Written form, provable Zugang, and an AGG-clean basis are hard gates sitting underneath the whole selection.", q: [9] },
      { id: "replayability", name: "File replayability gap", name_de: "Akten-Rekonstruierbarkeits-Lücke", concern: "If the selection can't be replayed from the file, the obvious objection — 'the pool was gerrymandered' — has room to land.", q: [10] },
    ],
  },
  {
    id: "extraordinary",
    de: "Außerordentliche (fristlose) Kündigung",
    en: "Extraordinary / summary dismissal (§626 BGB)",
    blurb:
      "Extraordinary dismissals have the shortest fuse: the §626 Abs. 2 two-week Ausschlussfrist and, where a Betriebsrat exists, a compressed three-day hearing window. The clock is the single hardest gate.",
    questions: [
      { n: 1, text: "Is the Kenntnis date of the kündigungsberechtigte Person documented — who knew, when, and how?" },
      { n: 2, text: "Was the dismissal issued AND delivered within the §626 Abs. 2 BGB two-week Ausschlussfrist from that knowledge?" },
      { n: 3, text: "Is the triggering event fully documented — facts, date, time, and witnesses?" },
      { n: 4, text: "Is a wichtiger-Grund analysis on file — why continued employment to the ordinary Frist was unzumutbar?" },
      { n: 5, text: "Was the Interessenabwägung performed — severity vs. tenure vs. prior conduct vs. circumstances?" },
      { n: 6, text: "Were milder means weighed and rejected on the record (ordentliche Kündigung, Suspendierung, Versetzung)?" },
      { n: 7, text: "Is a prior Abmahnung on file, or a justified waiver — and, if this is a Verdachtskündigung, is the mandatory Anhörung documented?" },
      { n: 8, text: "Is the dismissal in §623 BGB written form with a wet signature, an authorized signatory, and provable Zugang?" },
      { n: 9, text: "Where a Betriebsrat exists, is the §102 Anhörung complete and the compressed three-day window honoured before notice?" },
      { n: 10, text: "Are special protections cleared (Schwangerschaft, Schwerbehinderung, BR-Mandat §103) with no AGG concern — and is the matter defensible from the file alone?" },
    ],
    patterns: [
      { id: "deadline", name: "Two-week Ausschlussfrist gap (§626 Abs. 2)", name_de: "Zwei-Wochen-Frist-Lücke", concern: "The Ausschlussfrist is deterministic and runs from the authorized person's reliable Kenntnis. If the file can't prove notice and Zugang inside it, nothing downstream matters.", q: [1, 2] },
      { id: "triggering_reason", name: "Triggering event & important-reason gap", name_de: "Sachverhalts- und wichtiger-Grund-Lücke", concern: "Without a fully documented event and a wichtiger-Grund analysis, the basis for going straight to summary dismissal can't be reconstructed.", q: [3, 4] },
      { id: "proportionality", name: "Proportionality / milder-means gap", name_de: "Verhältnismäßigkeits-Lücke", concern: "The Interessenabwägung and the rejection of milder means (ordentliche Kündigung, Suspendierung) must be on the record, or ultima ratio fails.", q: [5, 6] },
      { id: "abmahnung", name: "Warning / Verdachts-Anhörung gap", name_de: "Abmahnungs- / Anhörungs-Lücke", concern: "Even for extraordinary dismissal a prior warning is often expected; and a Verdachtskündigung without the mandatory Anhörung is independently defective.", q: [7] },
      { id: "works_council", name: "Works-council hearing gap (§102 BetrVG)", name_de: "Betriebsrat-Anhörungslücke", concern: "The three-day window compresses an already tight timeline — a §102 slip here voids the dismissal.", q: [9] },
      { id: "form", name: "Form & delivery gap", name_de: "Form- und Zugangslücke", concern: "Written form and provable Zugang inside the window are deterministic — a defect ends the matter.", q: [8] },
      { id: "protection_agg", name: "Special-protection & AGG gap", name_de: "Sonderschutz- und AGG-Lücke", concern: "Unchecked special protections or an AGG indicium can bar the dismissal regardless of the underlying reason.", q: [10] },
    ],
  },
  {
    id: "senior_executive",
    de: "Leitender Angestellter / Geschäftsführer",
    en: "Senior-executive dismissal (preliminary)",
    draft: true,
    blurb:
      "Senior-executive dismissals turn first on status: is the §5 Abs. 3 BetrVG carve-out genuinely documented? If status fails, the case falls back to the ordinary route — and the §102 hearing the employer skipped becomes the problem.",
    questions: [
      { n: 1, text: "Is §5 Abs. 3 BetrVG executive status documented through Einstellungs-/Entlassungsbefugnis, Prokura, or genuine entrepreneurial tasks — not the title alone?" },
      { n: 2, text: "Is there contemporaneous proof that this authority was actually exercised?" },
      { n: 3, text: "For a Geschäftsführer: are the HRB-Bestellung, Dienstvertrag, and §621 BGB notice regime clear, so the Statusfrage is unambiguous?" },
      { n: 4, text: "Is the Sprecherausschuss hearing letter on file (§31 SprAuG) with core facts, the route, and favourable facts disclosed?" },
      { n: 5, text: "Was the statutory hearing window observed (7 days ordinary / 3 days extraordinary), and notice not issued prematurely?" },
      { n: 6, text: "Is there hearing-to-litigation congruence — no Nachschieben of reasons that weren't in the hearing?" },
      { n: 7, text: "Is the special-protection screen cleared (§15 KSchG, §168 SGB IX, §17 MuSchG, §18 BEEG)?" },
      { n: 8, text: "Is §623 BGB written form with an authorized signatory in place — and, if extraordinary, the §626 Abs. 2 Kenntnis clock documented?" },
      { n: 9, text: "Where KSchG still applies: are Verhältnismäßigkeit / ultima ratio, the Abmahnung (or a documented exception), and the Sozialauswahl (if betriebsbedingt) documented?" },
      { n: 10, text: "Could you defend both the executive status AND the dismissal from the file alone?" },
    ],
    patterns: [
      { id: "status", name: "Executive-status gap (§5 Abs. 3 BetrVG)", name_de: "Status-Lücke", concern: "If status rests on the title alone, the carve-out collapses — the case reverts to the ordinary route and the §102 hearing the employer skipped becomes catastrophic.", q: [1, 2, 3] },
      { id: "hearing", name: "Sprecherausschuss hearing gap (§31 SprAuG)", name_de: "Sprecherausschuss-Lücke", concern: "An incomplete hearing letter, a violated window, or Nachschieben are absolute bars on this route.", q: [4, 5, 6] },
      { id: "protection", name: "Special-protection gap", name_de: "Sonderkündigungsschutz-Lücke", concern: "Special protections persist for executives — an unchecked status can bar the dismissal.", q: [7] },
      { id: "form_deadline", name: "Form & deadline gap", name_de: "Form- und Fristlücke", concern: "Written form and, for summary dismissal, the §626 Abs. 2 clock apply to executives identically.", q: [8] },
      { id: "substantive", name: "Substantive-file gap", name_de: "Materielle Lücke", concern: "Where KSchG still applies, Verhältnismäßigkeit, the Abmahnung, and any Sozialauswahl must still be on the record.", q: [9] },
      { id: "replayability", name: "File replayability gap", name_de: "Akten-Rekonstruierbarkeits-Lücke", concern: "Status and substance both have to be defensible from the file alone — counsel will probe whichever is thinner.", q: [10] },
    ],
  },
];

/* ── Shared context questions (Q11–Q15), per the architecture ───────── */
const CTX_CURRENT = {
  key: "current",
  n: 11,
  label: "Current reality",
  text: "When a mandate lands on your desk, how complete is the file the client hands you?",
  options: [
    { v: "court_ready", label: "Court-ready — evidence-linked, procedurally complete, challenge-tested" },
    { v: "audit_ready", label: "Mostly there — an independent reviewer could reconstruct the reasoning" },
    { v: "fact_driven", label: "Some documentation, but I rebuild the chain myself" },
    { v: "intuition", label: "Often intuition-driven — 'the employee isn't a fit'" },
  ],
};
const CTX_DESIRED = {
  key: "desired",
  n: 12,
  label: "Desired reality",
  text: "Where do you want to be before your next Kündigungsschutzklage or Güteverhandlung?",
  options: [
    { v: "before_opposing", label: "Every weakness surfaced before opposing counsel finds it" },
    { v: "lag_survivable", label: "Files that survive the LAG, not just the Arbeitsgericht" },
    { v: "faster_triage", label: "A faster way to triage exposure before I commit to the strategy" },
    { v: "exploring", label: "Just exploring for now" },
  ],
};
const CTX_OBSTACLES = {
  key: "obstacles",
  n: 13,
  label: "Obstacles",
  text: "What's getting in the way today? (select all that apply)",
  multi: true,
  options: [
    { v: "time", label: "Time / mandate volume" },
    { v: "incomplete_files", label: "Clients deliver incomplete files" },
    { v: "manager_recall", label: "Manager recollection as the only evidence" },
    { v: "section102_done", label: "§102 hearing already executed before I'm consulted" },
    { v: "ausschlussfrist", label: "Ausschlussfrist pressure (§626 Abs. 2)" },
    { v: "betriebsrat", label: "Betriebsrat-Anhörung friction" },
    { v: "exposure", label: "Unclear exposure (Annahmeverzug, Streitwert, §15 AGG)" },
  ],
};
const CTX_SOLUTION = {
  key: "solution",
  n: 14,
  label: "Preferred next step",
  text: "If you addressed this, what would suit your practice best?",
  options: [
    { v: "review", label: "An adversarial second read on individual files (a VerfahrensCheck pressure-test)" },
    { v: "co_counsel", label: "Co-counsel leverage on the high-stakes mandates" },
    { v: "software", label: "Software my Kanzlei can run on intake itself" },
    { v: "doctrine", label: "Doctrinal extraction / a repeatable defensibility framework" },
    { v: "unsure", label: "Not sure yet" },
  ],
};
const CTX_OPEN = {
  key: "open",
  n: 15,
  label: "Anything else",
  text: "Anything else about the matter we should know? An Ausschlussfrist running, a specific contested file, an upcoming LAG appeal? (optional)",
};

const ANSWER_POINTS = { yes: 0, unknown: 1, no: 2 };

const RISK_BANDS = [
  { min: 80, max: 100, grade: "green", en: "Strongly defensible from the file", de: "Tragfähig aus der Akte verteidigbar" },
  { min: 60, max: 79, grade: "amber", en: "Mostly defensible, with gaps to close", de: "Überwiegend verteidigbar, mit Lücken" },
  { min: 40, max: 59, grade: "amber", en: "Partially defensible — meaningful exposure", de: "Teilweise verteidigbar — spürbares Risiko" },
  { min: 0, max: 39, grade: "red", en: "Weakly defensible — the file may not hold up", de: "Schwach verteidigbar — die Akte trägt womöglich nicht" },
];

function bandFor(score) {
  return RISK_BANDS.find((b) => score >= b.min && score <= b.max) || RISK_BANDS[RISK_BANDS.length - 1];
}

/* ── EmailJS (no backend) ────────────────────────────────────────────
   Client-side delivery via EmailJS. Replace the three placeholders with
   your own IDs from https://dashboard.emailjs.com (Account → API Keys,
   Email Services, Email Templates). The EmailJS SDK is loaded in
   index.html and exposed as window.emailjs.

   While the placeholders are unchanged (or the SDK hasn't loaded) the
   gate runs in PREVIEW mode: it validates the form and reveals the
   report without sending — so the page is usable before it's wired.

   Expected template params (use these {{tags}} in your EmailJS template):
     to_name, to_email, route, score, band, primary, report
─────────────────────────────────────────────────────────────────────── */
const EMAILJS_PUBLIC_KEY = "SM8sj-pNjL_dGZP2f";
const EMAILJS_SERVICE_ID = "service_uyczdqf";
const EMAILJS_TEMPLATE_ID = "template_9q2y3oi";

// Placeholder privacy policy URL — point this at your real policy.
const PRIVACY_URL = "#privacy-policy";

function emailJsConfigured() {
  return (
    typeof window !== "undefined" &&
    window.emailjs &&
    !EMAILJS_PUBLIC_KEY.startsWith("YOUR_") &&
    !EMAILJS_SERVICE_ID.startsWith("YOUR_") &&
    !EMAILJS_TEMPLATE_ID.startsWith("YOUR_")
  );
}

function validEmail(s) {
  return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test((s || "").trim());
}

/* Compose a plain-text report for the email body ({{report}} tag). */
function buildReportText({ name, route, result, ctx, qualLabel, next }) {
  const lines = [];
  lines.push(`Strategic Confidence Diagnostic — ${route.de}`);
  lines.push(`(${route.en})`);
  if (route.draft) lines.push(`NOTE: Preliminary route — criteria still in design; read as indicative.`);
  lines.push("");
  if (name) lines.push(`Prepared for: ${name}`);
  lines.push(`Documented defensibility: ${result.defensibility}% — ${result.band.en} (${result.band.de})`);
  lines.push("");
  if (result.primary) {
    lines.push(`Primary failure mode: ${result.primary.name} (${result.primary.name_de})`);
    lines.push(`  ${result.primary.concern}`);
    lines.push("");
  }
  lines.push(`Summary: ${result.unsupportedCount} gap(s) answered "No", ${result.unknownCount} couldn't confirm ("Unknown"), ${result.activePatterns.length} active gap pattern(s).`);
  lines.push("");
  if (result.activePatterns.length) {
    lines.push("Where the file could break down:");
    result.activePatterns.forEach((p) => {
      lines.push(`  • ${p.name} (${p.name_de})`);
      lines.push(`      ${p.concern}`);
    });
    lines.push("");
  }
  lines.push(`Engagement fit: ${qualLabel}`);
  lines.push(`Recommended next step: ${next.cta}`);
  lines.push(`  ${next.body}`);
  lines.push("");
  lines.push("———");
  lines.push("This is a non-legal, operational read of how well a termination decision is documented, based on generalised patterns drawn from published BAG decisions. It supports counsel; it is not legal advice, does not predict outcomes, and does not constitute Rechtsberatung within the meaning of the RDG. The human remains the decider (no Art. 22 GDPR automated decision-making).");
  return lines.join("\n");
}

/* Escape dynamic values before embedding in the email HTML.
   EmailJS does NOT HTML-escape template variables, and `name` is visitor
   input — so everything interpolated into {{report}} runs through this. */
function esc(s) {
  return String(s == null ? "" : s)
    .replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;")
    .replace(/"/g, "&quot;").replace(/'/g, "&#39;");
}

// Self-contained, inline-styled HTML email body. The shared EmailJS template's
// whole Content is the single {{report}} tag, so this is the entire email.
function buildReportHtml({ name, routeLabelEn, routeLabelDe, defensibility, bandEn, bandDe, grade,
                           primary /* {name, name_de, concern} | null */,
                           rows /* [{name, name_de, concern}] gap patterns OR insights */,
                           qualLabel, nextCta, nextBody }) {
  const C = { ink:"#111111", slate:"#3D4452", muted:"#6B7280", rust:"#E5631F",
              rule:"#DDD9D3", fog:"#F3F2EE", green:"#2E7D32", amber:"#E65100", red:"#C62828" };
  const bandColor = grade === "green" ? C.green : grade === "red" ? C.red : C.amber;
  const serif = "Georgia,'Times New Roman',serif";
  const rowHtml = (rows && rows.length)
    ? rows.map(p => `
        <tr>
          <td style="padding:10px 12px 10px 0;vertical-align:top;border-top:1px solid ${C.rule};width:42%;">
            <strong style="color:${C.ink};">${esc(p.name)}</strong><br>
            <span style="font-family:'Courier New',monospace;font-size:12px;color:${C.muted};">${esc(p.name_de || "")}</span>
          </td>
          <td style="padding:10px 0;vertical-align:top;border-top:1px solid ${C.rule};color:${C.slate};font-size:14px;">${esc(p.concern)}</td>
        </tr>`).join("")
    : `<tr><td style="padding:10px 0;color:${C.slate};font-size:14px;">No active gap patterns — the file scored cleanly on the questions answered.</td></tr>`;
  return `
<div style="font-family:${serif};color:${C.ink};line-height:1.6;max-width:640px;margin:0 auto;padding:8px 4px;">
  <p style="font-family:'Courier New',monospace;font-size:11px;letter-spacing:0.14em;text-transform:uppercase;color:${C.rust};margin:0 0 6px;">Strategic Confidence Diagnostic · Deutsches Arbeitsrecht</p>
  <p style="margin:0 0 4px;">Hello ${esc(name)},</p>
  <p style="margin:0 0 18px;color:${C.slate};">Here is your documented-defensibility report for <strong style="color:${C.ink};">${esc(routeLabelDe)}</strong> <span style="color:${C.muted};">(${esc(routeLabelEn)})</span>.</p>
  <div style="border:1px solid ${C.rule};border-top:3px solid ${C.rust};border-radius:6px;padding:18px 20px;margin:0 0 18px;">
    <div style="font-family:'Courier New',monospace;font-size:11px;letter-spacing:0.1em;text-transform:uppercase;color:${C.muted};">Documented defensibility</div>
    <div style="font-size:40px;font-weight:700;color:${bandColor};line-height:1.1;margin:2px 0;">${esc(String(defensibility))}</div>
    <div style="font-size:16px;color:${C.ink};">${esc(bandEn)}</div>
    <div style="font-family:'Courier New',monospace;font-size:12px;color:${C.muted};">${esc(bandDe)}</div>
    ${primary ? `
    <div style="margin-top:14px;padding-top:14px;border-top:1px solid ${C.rule};">
      <div style="font-family:'Courier New',monospace;font-size:11px;letter-spacing:0.1em;text-transform:uppercase;color:${C.muted};">Primary failure mode</div>
      <div style="font-size:16px;color:${C.ink};margin:2px 0 4px;">${esc(primary.name)} <span style="font-family:'Courier New',monospace;font-size:12px;color:${C.muted};">${esc(primary.name_de || "")}</span></div>
      <div style="font-size:14px;color:${C.slate};">${esc(primary.concern)}</div>
    </div>` : ""}
  </div>
  <p style="font-family:'Courier New',monospace;font-size:11px;letter-spacing:0.1em;text-transform:uppercase;color:${C.rust};margin:18px 0 6px;">Where the file could break down</p>
  <table style="border-collapse:collapse;width:100%;">${rowHtml}</table>
  ${nextCta ? `
  <div style="background:${C.fog};border-radius:6px;padding:16px 18px;margin:18px 0;">
    <div style="font-family:'Courier New',monospace;font-size:11px;letter-spacing:0.08em;text-transform:uppercase;color:${C.muted};">${esc(qualLabel || "")}</div>
    <div style="font-size:16px;color:${C.ink};margin:4px 0;">${esc(nextCta)}</div>
    <div style="font-size:14px;color:${C.slate};">${esc(nextBody || "")}</div>
  </div>` : ""}
  <p style="font-family:'Courier New',monospace;font-size:11px;color:${C.muted};line-height:1.7;border-top:1px solid ${C.rule};padding-top:14px;margin-top:22px;">
    This is a non-legal, operational snapshot of how well a termination decision is documented, based on generalised patterns drawn from published BAG decisions. It supports counsel; it is not legal advice, does not predict outcomes, and does not constitute Rechtsberatung within the meaning of the RDG. The human remains the decider (no Art. 22 GDPR automated decision-making).
  </p>
</div>`.trim();
}

const STYLES = `
.brs-root{
  --paper:#F5F4F0;--surface:#FFFFFF;--fog:#F3F2EE;--warm:#E8E6E0;
  --ink:#111111;--slate:#3D4452;--muted:#6B7280;
  --rust:#E5631F;--accent-text:#B04714;
  --rule:#DDD9D3;--rule-light:#EDEAE5;--panel:#1D1D1D;
  --red:#C62828;--red-bg:#FFEBEE;--amber:#E65100;--amber-bg:#FFF3E0;--green:#2E7D32;--green-bg:#E8F5E9;
  --radius:6px;
  --display:"Bebas Neue","Impact","Arial Narrow",sans-serif;
  --body:"Source Serif 4","Source Serif Pro",Georgia,"Times New Roman",serif;
  --mono:"Roboto Mono",SFMono-Regular,"SF Mono",Menlo,Consolas,monospace;
  --shadow-sm:0 2px 8px rgba(0,0,0,0.08);--shadow-md:0 6px 24px rgba(0,0,0,0.12);
  font-family:var(--body);color:var(--ink);background:transparent;
  padding:3.5rem 1.5rem 5rem;line-height:1.65;-webkit-font-smoothing:antialiased;
}
.brs-wrap{max-width:780px;margin:0 auto;}

.brs-card{background:var(--surface);border:1px solid var(--rule);border-radius:var(--radius);padding:2rem;margin-bottom:1.25rem;box-shadow:var(--shadow-sm);}
.brs-card--accent{border-top:3px solid var(--rust);}

.brs-label{font-family:var(--mono);font-size:0.7rem;font-weight:500;letter-spacing:0.16em;text-transform:uppercase;color:var(--accent-text);margin:0 0 1rem;}
.brs-label--muted{color:var(--muted);}

.brs-h1{font-family:var(--display);font-size:clamp(2.25rem,5.5vw,3.5rem);font-weight:400;line-height:1.0;letter-spacing:0.02em;margin:0 0 0.85rem;color:var(--ink);}
.brs-sub{font-family:var(--body);font-size:1.18rem;line-height:1.5;color:var(--ink);margin:0 0 0.95rem;font-weight:600;}
.brs-help{font-size:1rem;color:var(--slate);margin:0 0 0.5rem;line-height:1.72;}
.brs-help strong{color:var(--ink);font-weight:600;}

/* Benefit / discover blocks */
.brs-benefits{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:1rem;}
.brs-benefit{border:1px solid var(--rule);border-radius:var(--radius);padding:1.1rem 1.15rem;background:var(--fog);}
.brs-benefit h3{font-family:var(--display);font-weight:400;letter-spacing:0.03em;font-size:1.15rem;margin:0 0 0.4rem;color:var(--ink);}
.brs-benefit p{margin:0;font-size:0.9rem;line-height:1.6;color:var(--slate);}
.brs-list{margin:0.4rem 0 0;padding-left:0;list-style:none;}
.brs-list li{font-size:0.95rem;line-height:1.7;color:var(--slate);padding-left:1.35rem;position:relative;}
.brs-list li::before{content:"—";position:absolute;left:0;color:var(--rust);}
.brs-quote{font-style:italic;color:var(--slate);font-size:0.97rem;line-height:1.65;margin:0 0 0.7rem;padding-left:0.95rem;border-left:2px solid var(--rule);}

/* ── BAG authority cards ─────────────────────────────── */
.bag-cases{margin-top:1.4rem;}
.bag-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:1rem;margin-top:1.1rem;}
.bag-card{position:relative;background:var(--surface);border:1px solid var(--rule);border-top:3px solid var(--rust);border-radius:var(--radius);padding:1.3rem 1.4rem 1.4rem;box-shadow:var(--shadow-sm);display:flex;flex-direction:column;transition:transform .18s,box-shadow .18s;}
.bag-card:hover{transform:translateY(-3px);box-shadow:var(--shadow-md);}
.bag-top{display:flex;align-items:flex-start;justify-content:space-between;gap:.6rem;margin-bottom:.8rem;}
.bag-badge{font-family:var(--mono);font-weight:600;font-size:0.98rem;letter-spacing:.01em;color:var(--ink);line-height:1.15;}
.bag-seal{flex:none;text-align:right;font-family:var(--mono);font-size:.52rem;letter-spacing:.1em;text-transform:uppercase;color:var(--muted);line-height:1.5;}
.bag-seal .scale{display:block;font-size:1rem;margin-bottom:.15rem;}
.bag-topic{font-family:var(--mono);font-weight:600;font-size:.64rem;letter-spacing:.12em;text-transform:uppercase;color:var(--accent-text);margin:0 0 .65rem;line-height:1.4;}
.bag-lesson{font-size:.95rem;line-height:1.55;color:var(--ink);margin:0 0 1rem;}
.bag-rule{border:0;border-top:1px solid var(--rule-light);margin:0 0 .85rem;}
.bag-check-lbl{font-family:var(--mono);font-size:.58rem;letter-spacing:.14em;text-transform:uppercase;color:var(--rust);margin:0 0 .55rem;}
.bag-checks{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:.5rem;}
.bag-checks li{position:relative;padding-left:1.4rem;font-size:.88rem;line-height:1.45;color:var(--slate);}
.bag-checks li::before{content:"";position:absolute;left:0;top:.18em;width:.82rem;height:.82rem;border:1.5px solid var(--rust);border-radius:3px;}
.bag-checks li::after{content:"";position:absolute;left:.27rem;top:.32em;width:.27rem;height:.48rem;border:solid var(--rust);border-width:0 1.5px 1.5px 0;transform:rotate(40deg);}
.bag-map{margin-top:.9rem;font-family:var(--mono);font-size:.6rem;letter-spacing:.05em;color:var(--muted);}
.bag-map b{color:var(--accent-text);}
.bag-also{margin-top:1.3rem;font-family:var(--mono);font-size:.72rem;line-height:1.7;color:var(--muted);border-left:2px solid var(--rule);padding-left:1rem;}
.bag-also strong{color:var(--slate);}
@media (prefers-reduced-motion: reduce){ .bag-card{transition:none;} .bag-card:hover{transform:none;} }
.brs-quote:last-child{margin-bottom:0;}

/* Route picker */
.brs-types{display:flex;flex-direction:column;gap:0.55rem;}
.brs-type{position:relative;display:flex;flex-direction:column;align-items:flex-start;text-align:left;padding:0.9rem 1rem;border:1px solid var(--rule);border-radius:var(--radius);background:var(--surface);cursor:pointer;font:inherit;color:inherit;transition:border-color .15s,background .15s,box-shadow .15s;width:100%;}
.brs-type:hover{border-color:var(--slate);}
.brs-type[aria-pressed="true"]{border-color:var(--ink);background:var(--fog);box-shadow:inset 3px 0 0 var(--rust);}
.brs-type-de{font-family:var(--body);font-weight:600;font-size:1rem;color:var(--ink);}
.brs-type-en{font-family:var(--mono);font-size:0.72rem;color:var(--muted);margin-top:0.25rem;letter-spacing:0.02em;}
.brs-draft-tag{display:inline-block;font-family:var(--mono);font-size:0.58rem;letter-spacing:0.1em;text-transform:uppercase;color:var(--amber);background:var(--amber-bg);border:1px solid var(--amber);border-radius:var(--radius);padding:0.1rem 0.4rem;margin-top:0.4rem;}

/* Chips row */
.brs-chips{display:flex;flex-wrap:wrap;gap:0.5rem;margin:0 0 0.25rem;}
.brs-chip{font-family:var(--mono);font-size:0.68rem;letter-spacing:0.06em;text-transform:uppercase;color:var(--slate);background:var(--fog);border:1px solid var(--rule);padding:0.3rem 0.6rem;border-radius:var(--radius);}
.brs-chip span{color:var(--rust);margin-right:0.3rem;}

/* Questions */
.brs-progress{font-family:var(--mono);font-size:0.72rem;text-transform:uppercase;letter-spacing:0.1em;color:var(--muted);margin:0 0 1.25rem;}
.brs-q{padding:1.1rem 0;border-top:1px solid var(--rule-light);}
.brs-q:first-of-type{border-top:none;padding-top:0;}
.brs-qtext{font-size:0.99rem;line-height:1.6;margin:0 0 0.7rem;color:var(--ink);}
.brs-qnum{font-family:var(--mono);color:var(--accent-text);font-weight:600;font-size:0.82rem;margin-right:0.45rem;}
.brs-qsub{font-family:var(--mono);font-size:0.64rem;letter-spacing:0.1em;text-transform:uppercase;color:var(--muted);margin:0 0 0.35rem;}

.brs-seg{display:inline-flex;border:1px solid var(--rule);border-radius:var(--radius);overflow:hidden;}
.brs-seg button{appearance:none;border:0;background:var(--surface);padding:0.5rem 1.1rem;font-family:var(--mono);font-size:0.74rem;letter-spacing:0.06em;text-transform:uppercase;cursor:pointer;color:var(--slate);border-right:1px solid var(--rule);transition:background .12s,color .12s;}
.brs-seg button:last-child{border-right:0;}
.brs-seg button:hover{background:var(--fog);color:var(--ink);}
.brs-seg button[aria-pressed="true"]{background:var(--ink);color:#fff;}

/* Choice list (single + multi) */
.brs-choices{display:flex;flex-direction:column;gap:0.45rem;}
.brs-choice{display:flex;align-items:flex-start;gap:0.6rem;text-align:left;padding:0.65rem 0.85rem;border:1px solid var(--rule);border-radius:var(--radius);background:var(--surface);cursor:pointer;font:inherit;color:var(--ink);font-size:0.93rem;line-height:1.5;transition:border-color .15s,background .15s;}
.brs-choice:hover{border-color:var(--slate);}
.brs-choice[aria-pressed="true"]{border-color:var(--ink);background:var(--fog);box-shadow:inset 3px 0 0 var(--rust);}
.brs-choice-box{flex:0 0 auto;width:1rem;height:1rem;margin-top:0.15rem;border:1.5px solid var(--slate);border-radius:3px;}
.brs-choice[aria-pressed="true"] .brs-choice-box{background:var(--rust);border-color:var(--rust);}
.brs-textarea{width:100%;box-sizing:border-box;min-height:90px;border:1px solid var(--rule);border-radius:var(--radius);background:var(--surface);color:var(--ink);font-family:var(--body);font-size:0.95rem;line-height:1.6;padding:0.75rem 0.85rem;resize:vertical;}
.brs-textarea:focus{outline:none;border-color:var(--ink);}

/* Lead-capture gate */
.brs-field{margin:0 0 1rem;}
.brs-field:last-of-type{margin-bottom:0;}
.brs-flabel{display:block;font-family:var(--mono);font-size:0.66rem;letter-spacing:0.1em;text-transform:uppercase;color:var(--muted);margin:0 0 0.4rem;}
.brs-flabel span{color:var(--rust);}
.brs-input{width:100%;box-sizing:border-box;border:1px solid var(--rule);border-radius:var(--radius);background:var(--surface);color:var(--ink);font-family:var(--body);font-size:0.97rem;line-height:1.5;padding:0.7rem 0.85rem;}
.brs-input:focus{outline:none;border-color:var(--ink);}
.brs-consent{display:flex;align-items:flex-start;gap:0.65rem;text-align:left;padding:0.85rem 0.95rem;border:1px solid var(--rule);border-radius:var(--radius);background:var(--fog);cursor:pointer;font:inherit;color:var(--slate);font-size:0.88rem;line-height:1.6;width:100%;}
.brs-consent:hover{border-color:var(--slate);}
.brs-consent[aria-pressed="true"]{border-color:var(--ink);}
.brs-consent .brs-choice-box{margin-top:0.2rem;}
.brs-consent[aria-pressed="true"] .brs-choice-box{background:var(--rust);border-color:var(--rust);}
.brs-consent a{color:var(--accent-text);text-decoration:underline;}
.brs-gdpr{font-family:var(--mono);font-size:0.66rem;color:var(--muted);line-height:1.7;margin:1rem 0 0;letter-spacing:0.01em;}
.brs-gdpr a{color:var(--accent-text);text-decoration:underline;}
.brs-sending{font-family:var(--mono);font-size:0.72rem;letter-spacing:0.08em;text-transform:uppercase;color:var(--muted);}
.brs-emailed{font-family:var(--body);font-size:0.9rem;color:var(--green);background:var(--green-bg);border:1px solid var(--green);border-radius:var(--radius);padding:0.7rem 0.85rem;margin:0 0 1.25rem;line-height:1.5;}

/* Buttons */
.brs-actions{display:flex;gap:0.85rem;flex-wrap:wrap;margin-top:0.5rem;align-items:center;}
.brs-btn{appearance:none;border:1px solid var(--ink);background:var(--ink);color:#fff;padding:0.8rem 1.6rem;border-radius:var(--radius);font-family:var(--body);font-size:0.92rem;font-weight:600;cursor:pointer;transition:transform .2s cubic-bezier(.16,1,.3,1),box-shadow .2s,background .18s,border-color .18s,color .18s;box-shadow:0 4px 14px rgba(17,17,17,0.22);}
.brs-btn:hover{background:#222;transform:translateY(-2px);box-shadow:0 10px 26px rgba(17,17,17,0.32);}
.brs-btn.rust{background:var(--rust);border-color:var(--rust);box-shadow:0 4px 16px rgba(229,99,31,0.32);}
.brs-btn.rust:hover{background:var(--accent-text);border-color:var(--accent-text);box-shadow:0 12px 28px rgba(229,99,31,0.48);}
.brs-btn.ghost{background:transparent;color:var(--slate);border-color:var(--rule);box-shadow:none;}
.brs-btn.ghost:hover{background:rgba(17,17,17,0.035);border-color:var(--ink);color:var(--ink);box-shadow:0 6px 18px rgba(17,17,17,0.12);}
.brs-btn:disabled{opacity:0.4;cursor:not-allowed;transform:none;box-shadow:none;}

.brs-validation{font-family:var(--body);font-size:0.88rem;color:var(--red);background:var(--red-bg);border:1px solid var(--red);border-radius:var(--radius);padding:0.7rem 0.85rem;margin-top:0.85rem;}
.brs-notice{font-family:var(--body);font-size:0.9rem;color:var(--slate);background:var(--amber-bg);border:1px solid var(--amber);border-radius:var(--radius);padding:0.8rem 0.95rem;margin:0 0 1.25rem;line-height:1.6;}

/* Score */
.brs-score{font-family:var(--display);font-size:clamp(3rem,9vw,4.75rem);font-weight:400;letter-spacing:0.01em;line-height:0.9;margin:0.25rem 0 0;color:var(--ink);}
.brs-score--green{color:var(--green);}.brs-score--amber{color:var(--amber);}.brs-score--red{color:var(--red);}
.brs-score-unit{font-family:var(--mono);font-size:0.7rem;letter-spacing:0.1em;text-transform:uppercase;color:var(--muted);display:block;margin-bottom:0.35rem;}
.brs-band{font-family:var(--display);font-size:1.45rem;font-weight:400;letter-spacing:0.03em;margin:0.6rem 0 0.1rem;color:var(--ink);}
.brs-band-de{font-family:var(--mono);font-size:0.74rem;letter-spacing:0.04em;color:var(--muted);}
.brs-light{display:inline-block;width:0.7rem;height:0.7rem;border-radius:50%;margin-right:0.5rem;vertical-align:middle;}
.brs-light--green{background:var(--green);}.brs-light--amber{background:var(--amber);}.brs-light--red{background:var(--red);}

.brs-primary{margin-top:1.5rem;padding-top:1.5rem;border-top:1px solid var(--rule);}
.brs-primary-name{font-family:var(--display);font-size:1.35rem;font-weight:400;letter-spacing:0.02em;margin:0 0 0.5rem;color:var(--ink);}
.brs-primary-concern{font-size:0.95rem;line-height:1.7;color:var(--slate);margin:0;}

/* Insights */
.brs-insight{padding:0.95rem 0;border-top:1px solid var(--rule-light);font-size:0.97rem;line-height:1.7;color:var(--slate);}
.brs-insight:first-of-type{border-top:none;padding-top:0.2rem;}
.brs-insight strong{color:var(--ink);font-weight:600;}

/* Qualification badge */
.brs-qual{display:inline-flex;align-items:center;gap:0.5rem;font-family:var(--mono);font-size:0.74rem;letter-spacing:0.08em;text-transform:uppercase;border:1px solid var(--rule);border-radius:var(--radius);padding:0.4rem 0.75rem;background:var(--fog);color:var(--ink);margin-bottom:0.85rem;}

/* Summary stat grid */
.brs-summary{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:0.85rem;}
.brs-sum{border:1px solid var(--rule);border-radius:var(--radius);padding:1rem;background:var(--fog);}
.brs-sum-val{font-family:var(--mono);font-size:2rem;font-weight:700;line-height:1;margin:0;color:var(--ink);}
.brs-sum-label{font-family:var(--mono);font-size:0.62rem;text-transform:uppercase;letter-spacing:0.1em;color:var(--muted);margin:0.55rem 0 0;line-height:1.4;}

/* Gap table */
.brs-table{width:100%;border-collapse:collapse;font-size:0.9rem;}
.brs-table th,.brs-table td{text-align:left;vertical-align:top;padding:0.85rem 0.65rem;border-bottom:1px solid var(--rule-light);line-height:1.55;}
.brs-table th{font-family:var(--mono);font-size:0.62rem;text-transform:uppercase;letter-spacing:0.1em;color:var(--muted);font-weight:500;}
.brs-gap-name{font-family:var(--body);font-weight:600;color:var(--ink);}
.brs-gap-de{font-family:var(--mono);color:var(--muted);font-size:0.72rem;margin-top:0.15rem;}
.brs-table td{color:var(--slate);}

/* Killer line + CTA */
.brs-killer{font-family:var(--body);font-size:1.2rem;font-style:italic;font-weight:400;line-height:1.55;margin:0;color:var(--ink);border-left:2px solid var(--rust);padding-left:1rem;}
.brs-cta-title{font-family:var(--display);font-size:1.65rem;font-weight:400;letter-spacing:0.02em;margin:0 0 0.6rem;color:var(--ink);}
.brs-cta-title span{color:var(--rust);}
.brs-cta-body{font-size:0.97rem;line-height:1.72;color:var(--slate);margin:0 0 1.25rem;}
.brs-disclaimer{font-family:var(--mono);font-size:0.68rem;color:var(--muted);line-height:1.7;margin:0;letter-spacing:0.01em;}

[data-theme="dark"] .brs-root{
  --paper:#0E1013;--surface:#16191F;--fog:#1C2027;--warm:#232830;
  --ink:#F2EFE8;--slate:#C7C3BA;--muted:#8A8F98;
  --rust:#E5631F;--accent-text:#F0834A;
  --rule:#2A2F38;--rule-light:#20242B;--panel:#0A0C10;
  --red:#E07A55;--red-bg:rgba(224,122,85,0.10);--amber:#D9B36A;--amber-bg:rgba(217,179,106,0.10);--green:#7FB994;--green-bg:rgba(127,185,148,0.10);
  --shadow-sm:0 1px 2px rgba(0,0,0,0.45);--shadow-md:0 4px 16px rgba(0,0,0,0.5);
}
[data-theme="dark"] .brs-seg button[aria-pressed="true"]{background:var(--ink);color:var(--paper);}
[data-theme="dark"] .brs-btn{background:var(--ink);color:var(--paper);border-color:var(--ink);box-shadow:var(--shadow-md);}
[data-theme="dark"] .brs-btn:hover{background:#fff;}
[data-theme="dark"] .brs-btn.rust{background:var(--rust);border-color:var(--rust);color:#fff;}
[data-theme="dark"] .brs-btn.ghost{background:transparent;color:var(--slate);border-color:var(--rule);}
[data-theme="dark"] .brs-btn.ghost:hover{color:var(--ink);border-color:var(--ink);}

@media (max-width:640px){
  .brs-root{padding:32px 14px;}
  .brs-card{padding:1.4rem;}
  .bag-grid{grid-template-columns:1fr;}
  .brs-seg{display:flex;width:100%;}
  .brs-seg button{flex:1;padding:0.6rem 0.4rem;}
  .brs-table,.brs-table thead,.brs-table tbody,.brs-table tr,.brs-table th,.brs-table td{display:block;}
  .brs-table thead{display:none;}
  .brs-table td{border-bottom:none;padding:0.3rem 0;}
  .brs-table tr{border-bottom:1px solid var(--rule);padding:0.85rem 0;}
}
`;

function ExternalCounselAssessment() {
  const [stage, setStage] = useState("intro"); // intro | questions | gate | results
  const [routeId, setRouteId] = useState(null);
  const [answers, setAnswers] = useState({});  // Q1–Q10 → yes|no|unknown
  const [ctx, setCtx] = useState({ current: null, desired: null, obstacles: [], solution: null, open: "" });
  const [showValidation, setShowValidation] = useState(false);
  const [lead, setLead] = useState({ name: "", email: "", consent: false });
  const [sending, setSending] = useState(false);
  const [sendError, setSendError] = useState(null);
  const [emailedTo, setEmailedTo] = useState(null); // email address once delivered (null = preview/not sent)

  const route = ROUTES.find((r) => r.id === routeId) || null;

  const scoredAnswered = route ? route.questions.filter((q) => answers[q.n]).length : 0;
  const contextReady = ctx.current && ctx.desired && ctx.solution;
  const allAnswered = route && scoredAnswered === 10 && contextReady;

  const result = useMemo(() => {
    if (!route || scoredAnswered !== 10) return null;
    const points = {};
    let gapScore = 0;
    let unknownCount = 0;
    let unsupportedCount = 0;
    for (const q of route.questions) {
      const p = ANSWER_POINTS[answers[q.n]];
      points[q.n] = p;
      gapScore += p;
      if (answers[q.n] === "unknown") unknownCount += 1;
      if (answers[q.n] === "no") unsupportedCount += 1;
    }
    const defensibility = Math.round(((20 - gapScore) / 20) * 100);
    const band = bandFor(defensibility);
    const patterns = route.patterns
      .map((p) => ({ ...p, gap: p.q.reduce((s, qn) => s + (points[qn] || 0), 0) }))
      .filter((p) => p.gap > 0)
      .sort((a, b) => b.gap - a.gap);
    return {
      gapScore,
      defensibility,
      band,
      unknownCount,
      unsupportedCount,
      activePatterns: patterns,
      primary: patterns[0] || null,
    };
  }, [route, answers, scoredAnswered]);

  // Engagement-fit, transparent and score-led, modulated by intent.
  const qualification = useMemo(() => {
    if (!result) return null;
    let fit;
    if (result.defensibility < 60) fit = "high";
    else if (result.defensibility < 80) fit = "medium";
    else fit = "low";
    if (ctx.desired === "exploring" && fit === "high") fit = "medium";
    if (ctx.solution === "doctrine" || ctx.solution === "software") {
      if (fit === "high") fit = "medium";
      else if (fit === "medium") fit = "low";
    }
    return fit;
  }, [result, ctx]);

  function setAnswer(qn, val) {
    setAnswers((prev) => ({ ...prev, [qn]: val }));
    setShowValidation(false);
  }
  function setSingle(key, val) {
    setCtx((prev) => ({ ...prev, [key]: val }));
    setShowValidation(false);
  }
  function toggleMulti(key, val) {
    setCtx((prev) => {
      const has = prev[key].includes(val);
      return { ...prev, [key]: has ? prev[key].filter((x) => x !== val) : [...prev[key], val] };
    });
  }

  function startRoute() {
    if (!routeId) {
      setShowValidation(true);
      return;
    }
    setShowValidation(false);
    setStage("questions");
    scrollTop();
  }
  function generate() {
    if (!allAnswered) {
      setShowValidation(true);
      return;
    }
    setShowValidation(false);
    setStage("gate"); // full report is gated behind name + email + consent
    scrollTop();
  }
  // Submit the lead-capture gate: send the report by email (EmailJS), then
  // reveal the full report. Report is gated BEFORE it is shown.
  async function submitLead() {
    const name = lead.name.trim();
    if (!name || !validEmail(lead.email) || !lead.consent) {
      setShowValidation(true);
      return;
    }
    setShowValidation(false);
    setSendError(null);
    const email = lead.email.trim();
    const next = nextStep();
    const report = buildReportHtml({
      name,
      routeLabelEn: route.en,
      routeLabelDe: route.de,
      defensibility: result.defensibility,
      bandEn: result.band.en,
      bandDe: result.band.de,
      grade: result.band.grade,
      primary: result.primary || null,
      rows: result.activePatterns,
      qualLabel: QUAL_LABEL[qualification],
      nextCta: next.cta,
      nextBody: next.body,
    });

    // Preview mode: not yet wired to EmailJS — reveal without sending.
    if (!emailJsConfigured()) {
      setEmailedTo(null);
      setStage("results");
      scrollTop();
      return;
    }

    setSending(true);
    try {
      await window.emailjs.send(
        EMAILJS_SERVICE_ID,
        EMAILJS_TEMPLATE_ID,
        {
          // Recipient aliases — the shared template's "To Email" reads a
          // variable; send the address under several names so it always
          // resolves (avoids "The recipients address is empty").
          to_email: email, email: email, reply_to: email,
          to_name: name, name: name,
          report,                                          // the whole email body
          // optional structured fields the template could also use:
          route: `${route.de} (${route.en})`,
          score: String(result.defensibility),
          band: result.band.en,
          primary: result.primary ? result.primary.name : "—",
        },
        { publicKey: EMAILJS_PUBLIC_KEY }
      );
      setEmailedTo(email);
      setStage("results");
      scrollTop();
    } catch (err) {
      setSendError(
        (err && (err.text || err.message)) ||
          "We couldn't send the report just now. Please check the address and try again."
      );
    } finally {
      setSending(false);
    }
  }
  function reset() {
    setStage("intro");
    setRouteId(null);
    setAnswers({});
    setCtx({ current: null, desired: null, obstacles: [], solution: null, open: "" });
    setShowValidation(false);
    setLead({ name: "", email: "", consent: false });
    setSending(false);
    setSendError(null);
    setEmailedTo(null);
    scrollTop();
  }
  function scrollTop() {
    if (typeof window !== "undefined") window.scrollTo({ top: 0, behavior: "smooth" });
  }

  /* ── Insights (2–3 plain findings, counsel's register) ── */
  function buildInsights() {
    const out = [];
    out.push(
      <p className="brs-insight" key="band">
        This file scores <strong>{result.defensibility}%</strong> on documented defensibility — <strong>{result.band.en.toLowerCase()}</strong>. The read is what the record proves today, not whether the decision was right on the merits.
      </p>
    );
    if (result.primary) {
      out.push(
        <p className="brs-insight" key="primary">
          The largest gap is in <strong>{result.primary.name}</strong>. {result.primary.concern}
        </p>
      );
    }
    if (result.unknownCount >= 3 || ctx.obstacles.includes("manager_recall")) {
      out.push(
        <p className="brs-insight" key="unknown">
          {result.unknownCount > 0 ? (
            <>You marked <strong>{result.unknownCount}</strong> answer{result.unknownCount === 1 ? "" : "s"} as "Unknown". </>
          ) : null}
          On a contested file, what you can't confirm from the record is exactly what opposing counsel probes first — and manager recollection alone rarely survives cross-examination 18 months out.
        </p>
      );
    } else if (result.defensibility >= 80) {
      out.push(
        <p className="brs-insight" key="strong">
          This file looks well-supported. At this level the leverage is durability — a file that holds at the LAG, not just the Arbeitsgericht, even under full disclosure.
        </p>
      );
    }
    return out;
  }

  function nextStep() {
    if (qualification === "high") {
      return {
        title: <>Surface it before <span>opposing counsel</span> does.</>,
        body: "Your answers point to meaningful exposure on this file. A VerfahrensCheck is a structured adversarial read: it walks the decision path, the evidence tiers, the §102 integrity, and the Ausschlussfrist chain for one anonymised matter — so the weakness is caught while you can still rewrite the hearing, recover the Frist, or recalibrate settle-or-fight before the Güteverhandlung.",
        cta: "Request an adversarial pressure-test",
      };
    }
    if (qualification === "medium") {
      return {
        title: <>Start with your <span>highest-exposure</span> mandate.</>,
        body: "The file is partly there. The fastest return is a focused adversarial read on the matter carrying the most exposure — Annahmeverzug clock, Streitwert, §15 AGG add-ons — so you walk into the Güteverhandlung already knowing where the other side will push, and reserve your own hours for the genuinely hard questions.",
        cta: "Request a focused pressure-test",
      };
    }
    return {
      title: <>Make every intake <span>look like this one</span>.</>,
      body: "This file holds up well. The opportunity is repeatability across the mandates clients bring you. Use the route as a doctrinal intake framework, and keep a VerfahrensCheck in reserve as co-counsel leverage for the files that don't score this cleanly.",
      cta: "See the defensibility framework",
    };
  }

  const QUAL_LABEL = { high: "Ready for a direct review", medium: "A focused pressure-test fits", low: "Framework first, leverage in reserve" };

  return (
    <div className="brs-root">
      <style>{STYLES}</style>
      <div className="brs-wrap">

        {/* ─────────────── INTRO / LANDING ─────────────── */}
        {stage === "intro" && (
          <>
            <section className="brs-card brs-card--accent">
              <p className="brs-label">Strategic Confidence Diagnostic · Externes Arbeitsrecht</p>
              <h1 className="brs-h1">What would opposing counsel see first?</h1>
              <p className="brs-sub">Answer 15 questions to find out.</p>
              <p className="brs-help">
                You don't ask whether the client <em>can</em> terminate. You ask whether you can still defend it 18 months from now, in front of an Arbeitsgericht, with hostile facts, incomplete memories, and full disclosure. This is an adversarial read of how well the file is documented — a second pair of eyes that pressure-tests it the way the other side will, <strong>while you can still fix it</strong>.
              </p>
              <p className="brs-help">
                It is a structural, non-legal read — not Rechtsberatung, and it does not predict outcomes. Mechanism, not marketing: it thinks in process chains, evidence tiers, §102 integrity, and the Ausschlussfrist clock.
              </p>
            </section>

            <section className="brs-card">
              <p className="brs-label">This assessment measures and improves</p>
              <div className="brs-benefits">
                <div className="brs-benefit">
                  <h3>Vulnerability Visibility</h3>
                  <p>Where are the likely points of challenge — the openings opposing counsel reaches for first?</p>
                </div>
                <div className="brs-benefit">
                  <h3>Evidence Strength</h3>
                  <p>Can the key assertions be substantiated from the record, or do they rest on recollection?</p>
                </div>
                <div className="brs-benefit">
                  <h3>Litigation Readiness</h3>
                  <p>How prepared is the file for adversarial review — through the Güteverhandlung and into the LAG?</p>
                </div>
              </div>
            </section>

            <section className="brs-card">
              <p className="brs-label">Why employment lawyers use this assessment</p>
              <p className="brs-help">
                Experienced employment lawyers know weaknesses are most expensive when discovered late. This assessment is informed by recurring review themes in published BAG decisions:
              </p>
              <ul className="brs-list" style={{ marginTop: "0.6rem" }}>
                <li>Evidence substantiation</li>
                <li>Decision justification</li>
                <li>Procedural execution</li>
                <li>Reasoning support</li>
                <li>Documentation consistency</li>
              </ul>
              <div className="bag-cases">
                <div className="bag-grid">

                  <article className="bag-card">
                    <div className="bag-top">
                      <span className="bag-badge">BAG 2 AZR 296/22</span>
                      <span className="bag-seal"><span className="scale">⚖️</span>Bundes-<br/>arbeitsgericht<br/>Published</span>
                    </div>
                    <p className="bag-topic">§286 ZPO · Surveillance evidence</p>
                    <p className="bag-lesson">Open, marked surveillance of intentional misconduct is not caught by an evidence use-ban &mdash; but only a file that <em>proves the camera was open and the purpose documented</em> survives. &ldquo;Datenschutz ist kein Tatenschutz.&rdquo;</p>
                    <hr className="bag-rule"/>
                    <p className="bag-check-lbl">What the assessment checks</p>
                    <ul className="bag-checks">
                      <li>Can you show, from the file alone, that the camera was open and marked at the time of recording?</li>
                      <li>Can you point to the documented collection purpose that covers using the footage to prove misconduct?</li>
                    </ul>
                    <p className="bag-map">Maps to · <b>evidence substantiation</b></p>
                  </article>

                  <article className="bag-card">
                    <div className="bag-top">
                      <span className="bag-badge">BAG 2 AZR 68/24</span>
                      <span className="bag-seal"><span className="scale">⚖️</span>Bundes-<br/>arbeitsgericht<br/>Published</span>
                    </div>
                    <p className="bag-topic">§130 BGB · Zugang / delivery proof</p>
                    <p className="bag-lesson">A sent letter <em>cannot prove its own arrival</em>. Without a dated delivery-confirmation slip, <em>Zugang</em> is not proven &mdash; and the §4 KSchG clock never starts.</p>
                    <hr className="bag-rule"/>
                    <p className="bag-check-lbl">What the assessment checks</p>
                    <ul className="bag-checks">
                      <li>Does the file evidence receipt on a specific date, not just attempted delivery?</li>
                      <li>Is there a second corroborating artifact &mdash; a signed slip, counter-signed receipt, or named witness?</li>
                    </ul>
                    <p className="bag-map">Maps to · <b>documentation consistency</b></p>
                  </article>

                  <article className="bag-card">
                    <div className="bag-top">
                      <span className="bag-badge">BAG 6 AZR 157/22</span>
                      <span className="bag-seal"><span className="scale">⚖️</span>Bundes-<br/>arbeitsgericht<br/>Published</span>
                    </div>
                    <p className="bag-topic">§17 KSchG · Filing sequence</p>
                    <p className="bag-lesson">A termination issued before the mass-dismissal notice is on file <em>cannot take effect</em> &mdash; the file breaks at a step before the merits are ever reached.</p>
                    <hr className="bag-rule"/>
                    <p className="bag-check-lbl">What the assessment checks</p>
                    <ul className="bag-checks">
                      <li>Can the date the filing duty was triggered be proven from the file alone?</li>
                      <li>Can the filing date be placed before the termination-letter date, on the record?</li>
                    </ul>
                    <p className="bag-map">Maps to · <b>procedural execution</b></p>
                  </article>

                  <article className="bag-card">
                    <div className="bag-top">
                      <span className="bag-badge">BAG 2 AZR 17/23</span>
                      <span className="bag-seal"><span className="scale">⚖️</span>Bundes-<br/>arbeitsgericht<br/>Published</span>
                    </div>
                    <p className="bag-topic">Private-chat evidence · confidentiality gate</p>
                    <p className="bag-lesson">A private-chat dismissal turns on the confidentiality gate: unless the file shows the chat group&rsquo;s composition and that the employer did not direct its disclosure, the misconduct never reaches the merits.</p>
                    <hr className="bag-rule"/>
                    <p className="bag-check-lbl">What the assessment checks</p>
                    <ul className="bag-checks">
                      <li>Can you describe the chat group&rsquo;s exact composition at the time of the messages?</li>
                      <li>Can you defeat the worker&rsquo;s confidentiality defence from the file alone, before litigation begins?</li>
                    </ul>
                    <p className="bag-map">Maps to · <b>evidence strength</b></p>
                  </article>

                </div>
                <p className="bag-also">
                  <strong>Also anchored in</strong> &mdash; BAG 2 AZR 736/13 (consultation judged on the employer's decision basis), BAG 2 AZR 15/15 (§102 &mdash; favorable facts withheld), BAG 2 AZR 297/22 (surveillance evidence &mdash; burden of proof).
                </p>
              </div>
            </section>

            <section className="brs-card">
              <p className="brs-label">What employment lawyers often discover</p>
              <p className="brs-quote">"The assessment immediately highlighted areas likely to attract challenge."</p>
              <p className="brs-quote">"It exposed weak evidence chains before litigation."</p>
              <p className="brs-quote">"It surfaced issues that would otherwise emerge much later."</p>
            </section>

            <section className="brs-card">
              <p className="brs-label">Choose the dismissal type</p>
              <div className="brs-types" role="radiogroup" aria-label="Dismissal type">
                {ROUTES.map((r) => (
                  <button
                    key={r.id}
                    type="button"
                    className="brs-type"
                    aria-pressed={routeId === r.id}
                    onClick={() => { setRouteId(r.id); setShowValidation(false); }}
                  >
                    <span className="brs-type-de">{r.de}</span>
                    <span className="brs-type-en">{r.en}</span>
                    {r.draft && <span className="brs-draft-tag">Preliminary — results indicative</span>}
                  </button>
                ))}
              </div>

              <div className="brs-chips" style={{ marginTop: "1.5rem" }}>
                <span className="brs-chip"><span>✓</span>Free</span>
                <span className="brs-chip"><span>✓</span>15 questions</span>
                <span className="brs-chip"><span>✓</span>Under 3 minutes</span>
                <span className="brs-chip"><span>✓</span>Immediate results</span>
                <span className="brs-chip"><span>✓</span>Immediate recommendations</span>
              </div>

              <div className="brs-actions" style={{ marginTop: "1.25rem" }}>
                <button className="brs-btn rust" type="button" onClick={startRoute}>Start the diagnostic — 3 min</button>
              </div>
              <p className="brs-help" style={{ marginTop: "0.85rem", fontSize: "0.92rem" }}>
                Discover what opposing counsel would likely see first.
              </p>
              {showValidation && (
                <div className="brs-validation">Please choose a dismissal type to begin.</div>
              )}
            </section>
          </>
        )}

        {/* ─────────────── QUESTIONS ─────────────── */}
        {stage === "questions" && route && (
          <>
            <section className="brs-card brs-card--accent">
              <p className="brs-label">{route.de}</p>
              <p className="brs-sub">{route.en}</p>
              <p className="brs-help">{route.blurb}</p>
              {route.draft && (
                <div className="brs-notice" style={{ marginTop: "1rem", marginBottom: 0 }}>
                  This route is preliminary: the senior-executive criteria are still in design and not yet part of the active review process. Treat the result as indicative.
                </div>
              )}
            </section>

            <section className="brs-card">
              <p className="brs-label">Best-practice questions (1–10)</p>
              <p className="brs-progress">{scoredAnswered} of 10 answered · these produce your score</p>
              {route.questions.map((q) => (
                <div className="brs-q" key={q.n}>
                  <p className="brs-qtext"><span className="brs-qnum">Q{q.n}.</span>{q.text}</p>
                  <div className="brs-seg" role="radiogroup" aria-label={`Question ${q.n}`}>
                    {["yes", "no", "unknown"].map((v) => (
                      <button key={v} type="button" aria-pressed={answers[q.n] === v} onClick={() => setAnswer(q.n, v)}>
                        {v === "yes" ? "Yes" : v === "no" ? "No" : "Unknown"}
                      </button>
                    ))}
                  </div>
                </div>
              ))}
            </section>

            <section className="brs-card">
              <p className="brs-label">A little context (11–15)</p>

              {[CTX_CURRENT, CTX_DESIRED].map((cq) => (
                <div className="brs-q" key={cq.key}>
                  <p className="brs-qsub">Q{cq.n} · {cq.label}</p>
                  <p className="brs-qtext">{cq.text}</p>
                  <div className="brs-choices">
                    {cq.options.map((o) => (
                      <button key={o.v} type="button" className="brs-choice" aria-pressed={ctx[cq.key] === o.v} onClick={() => setSingle(cq.key, o.v)}>
                        <span className="brs-choice-box" aria-hidden="true" />{o.label}
                      </button>
                    ))}
                  </div>
                </div>
              ))}

              <div className="brs-q">
                <p className="brs-qsub">Q{CTX_OBSTACLES.n} · {CTX_OBSTACLES.label}</p>
                <p className="brs-qtext">{CTX_OBSTACLES.text}</p>
                <div className="brs-choices">
                  {CTX_OBSTACLES.options.map((o) => (
                    <button key={o.v} type="button" className="brs-choice" aria-pressed={ctx.obstacles.includes(o.v)} onClick={() => toggleMulti("obstacles", o.v)}>
                      <span className="brs-choice-box" aria-hidden="true" />{o.label}
                    </button>
                  ))}
                </div>
              </div>

              <div className="brs-q">
                <p className="brs-qsub">Q{CTX_SOLUTION.n} · {CTX_SOLUTION.label}</p>
                <p className="brs-qtext">{CTX_SOLUTION.text}</p>
                <div className="brs-choices">
                  {CTX_SOLUTION.options.map((o) => (
                    <button key={o.v} type="button" className="brs-choice" aria-pressed={ctx.solution === o.v} onClick={() => setSingle("solution", o.v)}>
                      <span className="brs-choice-box" aria-hidden="true" />{o.label}
                    </button>
                  ))}
                </div>
              </div>

              <div className="brs-q">
                <p className="brs-qsub">Q{CTX_OPEN.n} · {CTX_OPEN.label}</p>
                <p className="brs-qtext">{CTX_OPEN.text}</p>
                <textarea
                  className="brs-textarea"
                  value={ctx.open}
                  onChange={(e) => setCtx((prev) => ({ ...prev, open: e.target.value }))}
                  placeholder="Optional — anything that would help us read your result in context."
                />
              </div>

              <div className="brs-actions" style={{ marginTop: 16 }}>
                <button className="brs-btn" type="button" onClick={generate}>Get my report</button>
                <button className="brs-btn ghost" type="button" onClick={reset}>Start over</button>
              </div>
              {showValidation && (
                <div className="brs-validation">
                  Please answer all ten best-practice questions and the three context questions (11, 12, 14) before continuing. Questions 13 and 15 are optional.
                </div>
              )}
            </section>
          </>
        )}

        {/* ─────────────── GATE (name + email + GDPR consent) ─────────────── */}
        {stage === "gate" && route && (
          <>
            <section className="brs-card brs-card--accent">
              <p className="brs-label">Your report is ready</p>
              <h1 className="brs-h1" style={{ fontSize: "clamp(1.9rem,4.5vw,2.6rem)" }}>Where should we send it?</h1>
              <p className="brs-help">
                Your {route.de} assessment is complete. Enter your name and email and we'll send the full report — your documented-defensibility score, the primary failure mode, every active gap pattern, and the recommended next step — and show it on screen straight away.
              </p>
            </section>

            <section className="brs-card">
              <p className="brs-label">Send me the report</p>

              <div className="brs-field">
                <label className="brs-flabel" htmlFor="lead-name">Name <span>*</span></label>
                <input
                  id="lead-name"
                  className="brs-input"
                  type="text"
                  autoComplete="name"
                  value={lead.name}
                  onChange={(e) => { setLead((p) => ({ ...p, name: e.target.value })); setShowValidation(false); }}
                  placeholder="Dr. Maria Schmidt"
                />
              </div>

              <div className="brs-field">
                <label className="brs-flabel" htmlFor="lead-email">Email <span>*</span></label>
                <input
                  id="lead-email"
                  className="brs-input"
                  type="email"
                  autoComplete="email"
                  value={lead.email}
                  onChange={(e) => { setLead((p) => ({ ...p, email: e.target.value })); setShowValidation(false); }}
                  placeholder="kanzlei@example.de"
                />
              </div>

              <button
                type="button"
                className="brs-consent"
                aria-pressed={lead.consent}
                onClick={() => { setLead((p) => ({ ...p, consent: !p.consent })); setShowValidation(false); }}
              >
                <span className="brs-choice-box" aria-hidden="true" />
                <span>
                  I consent to ARAM Algorithm processing my name and email address for the sole purpose of sending me this assessment report and a follow-up about it. I can withdraw consent at any time. See the{" "}
                  <a href={PRIVACY_URL} target="_blank" rel="noopener noreferrer" onClick={(e) => e.stopPropagation()}>privacy notice</a>.
                </span>
              </button>

              <div className="brs-actions" style={{ marginTop: "1.25rem" }}>
                <button className="brs-btn rust" type="button" onClick={submitLead} disabled={sending}>
                  {sending ? "Sending…" : "Email me the report"}
                </button>
                <button className="brs-btn ghost" type="button" onClick={() => { setStage("questions"); scrollTop(); }} disabled={sending}>Back to questions</button>
                {sending && <span className="brs-sending">Contacting EmailJS…</span>}
              </div>

              {showValidation && (
                <div className="brs-validation">
                  Please enter your name, a valid email address, and tick the consent box so we can send your report.
                </div>
              )}
              {sendError && (
                <div className="brs-validation" style={{ marginTop: "0.85rem" }}>{sendError}</div>
              )}

              <p className="brs-gdpr">
                <strong>Data protection (GDPR / DSGVO):</strong> Your name and email are processed by ARAM Algorithm
                solely to deliver this report and a single follow-up, on the legal basis of your consent
                (Art. 6 (1)(a) GDPR). They are transmitted to our email-delivery processor, EmailJS, to send the
                message; they are not sold, and not used for automated decision-making (Art. 22 GDPR). You may
                withdraw consent or request deletion at any time. Your assessment answers stay in your browser and
                are not stored on a server. Full details: <a href={PRIVACY_URL} target="_blank" rel="noopener noreferrer">privacy notice</a>.
              </p>
            </section>
          </>
        )}

        {/* ─────────────── RESULTS ─────────────── */}
        {stage === "results" && result && route && (
          <>
            {emailedTo && (
              <div className="brs-emailed">✓ A copy of this report has been emailed to <strong>{emailedTo}</strong>.</div>
            )}
            <section className="brs-card brs-card--accent">
              <p className="brs-label">Your result — {route.de}</p>
              <span className="brs-score-unit">Documented defensibility</span>
              <p className={`brs-score brs-score--${result.band.grade}`}>{result.defensibility}%</p>
              <p className="brs-band">
                <span className={`brs-light brs-light--${result.band.grade}`} aria-hidden="true" />
                {result.band.en}
              </p>
              <p className="brs-band-de">{result.band.de}</p>
              {route.draft && (
                <div className="brs-notice" style={{ marginTop: "1.1rem", marginBottom: 0 }}>
                  Preliminary route — the senior-executive criteria are still in design. Read this score as indicative, not definitive.
                </div>
              )}
              {result.primary && (
                <div className="brs-primary">
                  <p className="brs-label brs-label--muted">Primary failure mode</p>
                  <p className="brs-primary-name">{result.primary.name}</p>
                  <p className="brs-primary-concern">{result.primary.concern}</p>
                </div>
              )}
            </section>

            <section className="brs-card">
              <p className="brs-label">What your answers say</p>
              {buildInsights()}
            </section>

            <section className="brs-card">
              <p className="brs-label">Summary</p>
              <div className="brs-summary">
                <div className="brs-sum"><p className="brs-sum-val">{result.unsupportedCount}</p><p className="brs-sum-label">Gaps (answered "No")</p></div>
                <div className="brs-sum"><p className="brs-sum-val">{result.unknownCount}</p><p className="brs-sum-label">Couldn't confirm ("Unknown")</p></div>
                <div className="brs-sum"><p className="brs-sum-val">{result.activePatterns.length}</p><p className="brs-sum-label">Active gap patterns</p></div>
              </div>
            </section>

            {result.activePatterns.length > 0 && (
              <section className="brs-card">
                <p className="brs-label">Where the file could break down</p>
                <table className="brs-table">
                  <thead>
                    <tr><th style={{ width: "38%" }}>Gap pattern</th><th style={{ width: "62%" }}>Why it matters</th></tr>
                  </thead>
                  <tbody>
                    {result.activePatterns.map((p) => (
                      <tr key={p.id}>
                        <td><div className="brs-gap-name">{p.name}</div><div className="brs-gap-de">{p.name_de}</div></td>
                        <td>{p.concern}</td>
                      </tr>
                    ))}
                  </tbody>
                </table>
              </section>
            )}

            <section className="brs-card">
              <p className="brs-killer">
                The question is not whether you know what happened. It's whether the file still proves it once memories fade and disclosure is full — and whether you find the weakness before opposing counsel does.
              </p>
            </section>

            <section className="brs-card">
              <span className="brs-qual">{QUAL_LABEL[qualification]}</span>
              <p className="brs-cta-title">{nextStep().title}</p>
              <p className="brs-cta-body">{nextStep().body}</p>
              <div className="brs-actions">
                <button className="brs-btn rust" type="button" onClick={() => alert("Connect this to your booking / contact flow.")}>{nextStep().cta}</button>
                <button className="brs-btn ghost" type="button" onClick={() => { setStage("questions"); scrollTop(); }}>Back to questions</button>
                <button className="brs-btn ghost" type="button" onClick={reset}>Start over</button>
              </div>
            </section>

            <section className="brs-card">
              <p className="brs-disclaimer">
                This tool provides a non-legal, operational snapshot of how well a termination decision is documented, based on generalised review patterns drawn from published BAG decisions. It supports counsel; it does not provide legal advice, does not predict litigation outcomes, does not monitor or score individual managers, and is not automated decision-making within the meaning of Art. 22 GDPR — the human remains the decider. It does not constitute Rechtsberatung within the meaning of the RDG.
              </p>
            </section>
          </>
        )}
      </div>
    </div>
  );
}
