/* prototype-data.jsx
   Inline SVG icons (replacing lucide-react), and realistic PR + finding
   fixtures with verbatim citations to UK regulatory frameworks.
   Faithful to the data shapes in dashboard/lib/useEvaluations.ts and the
   pipeline stages in dashboard/components/EvaluationDashboard.tsx. */

/* ============ ICONS ============ */
/* Single-file SVG set so we don't depend on lucide-react. */

const Icon = ({ d, size = 16, color = "currentColor", stroke = 1.6, fill = "none", style = {} }) => (
  <svg width={size} height={size} viewBox="0 0 24 24" fill={fill} stroke={color}
       strokeWidth={stroke} strokeLinecap="round" strokeLinejoin="round" style={style}>
    {d}
  </svg>
);

const Icons = {
  Shield: (p) => <Icon {...p} d={<path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/>} />,
  ShieldCheck: (p) => <Icon {...p} d={<><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/><path d="m9 12 2 2 4-4"/></>} />,
  ShieldX: (p) => <Icon {...p} d={<><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/><path d="m9.5 9 5 6m0-6-5 6"/></>} />,
  ShieldAlert: (p) => <Icon {...p} d={<><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/><path d="M12 8v4"/><circle cx="12" cy="15" r=".5" fill={p?.color || "currentColor"}/></>} />,
  Loader: (p) => <Icon {...p} d={<path d="M21 12a9 9 0 1 1-6.219-8.56"/>} />,
  Play: (p) => <Icon {...p} d={<path d="m6 4 14 8-14 8z"/>} fill={p?.color || "currentColor"} stroke={p?.color || "currentColor"}/>,
  Check: (p) => <Icon {...p} d={<path d="m5 12 5 5L20 7"/>} />,
  CheckCircle: (p) => <Icon {...p} d={<><circle cx="12" cy="12" r="10"/><path d="m9 12 2 2 4-4"/></>} />,
  X: (p) => <Icon {...p} d={<path d="m6 6 12 12M6 18 18 6"/>} />,
  AlertCircle: (p) => <Icon {...p} d={<><circle cx="12" cy="12" r="10"/><path d="M12 8v4"/><circle cx="12" cy="16" r=".5" fill={p?.color || "currentColor"}/></>} />,
  Clock: (p) => <Icon {...p} d={<><circle cx="12" cy="12" r="9"/><path d="M12 7v5l3 2"/></>} />,
  GitPullRequest: (p) => <Icon {...p} d={<><circle cx="6" cy="6" r="3"/><circle cx="6" cy="18" r="3"/><path d="M6 9v6"/><circle cx="18" cy="18" r="3"/><path d="M13 6h3a2 2 0 0 1 2 2v7"/></>} />,
  GitBranch: (p) => <Icon {...p} d={<><circle cx="6" cy="6" r="3"/><circle cx="18" cy="18" r="3"/><path d="M6 9v3a3 3 0 0 0 3 3h6"/></>} />,
  Refresh: (p) => <Icon {...p} d={<><path d="M21 12a9 9 0 1 1-3.18-6.86"/><path d="M21 3v6h-6"/></>} />,
  Brain: (p) => <Icon {...p} d={<path d="M9 4a2.5 2.5 0 0 0-2.5 2.5A2.5 2.5 0 0 0 4 9a2.5 2.5 0 0 0 1.5 2.3 2.5 2.5 0 0 0 .5 4.7 2.5 2.5 0 0 0 3 3 2.5 2.5 0 0 0 5 0V4a2.5 2.5 0 0 0-5 0Zm6 0v15a2.5 2.5 0 0 0 5 0 2.5 2.5 0 0 0 3-3 2.5 2.5 0 0 0 .5-4.7A2.5 2.5 0 0 0 25 9a2.5 2.5 0 0 0-2.5-2.5A2.5 2.5 0 0 0 20 4a2.5 2.5 0 0 0-5 0"/>} />,
  ScanSearch: (p) => <Icon {...p} d={<><path d="M3 7V5a2 2 0 0 1 2-2h2M17 3h2a2 2 0 0 1 2 2v2M21 17v2a2 2 0 0 1-2 2h-2M7 21H5a2 2 0 0 1-2-2v-2"/><circle cx="12" cy="12" r="3"/><path d="m16 16-1.5-1.5"/></>} />,
  Scale: (p) => <Icon {...p} d={<><path d="M16 16h6l-3-6-3 6Z"/><path d="M2 16h6l-3-6-3 6Z"/><path d="M12 4v18"/><path d="M5 22h14"/><path d="M5 10s2-2 7-2 7 2 7 2"/></>} />,
  Gavel: (p) => <Icon {...p} d={<><path d="m14 13-7.5 7.5a2.12 2.12 0 0 1-3-3L11 10"/><path d="m16 16 6-6"/><path d="m8 8 6-6"/><path d="m9 7 8 8"/><path d="m21 11-8-8"/></>} />,
  Zap: (p) => <Icon {...p} d={<path d="M4 14h7l-3 7 10-11h-7l3-7-10 11Z"/>} />,
  Wrench: (p) => <Icon {...p} d={<path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"/>} />,
  FileCode: (p) => <Icon {...p} d={<><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><path d="M14 2v6h6"/><path d="m10 13-2 2 2 2"/><path d="m14 17 2-2-2-2"/></>} />,
  BookOpen: (p) => <Icon {...p} d={<><path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z"/><path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z"/></>} />,
  ExternalLink: (p) => <Icon {...p} d={<><path d="M15 3h6v6"/><path d="M10 14 21 3"/><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/></>} />,
  ChevronLeft: (p) => <Icon {...p} d={<path d="m15 18-6-6 6-6"/>} />,
  ChevronRight: (p) => <Icon {...p} d={<path d="m9 18 6-6-6-6"/>} />,
  ChevronDown: (p) => <Icon {...p} d={<path d="m6 9 6 6 6-6"/>} />,
  List: (p) => <Icon {...p} d={<><path d="M8 6h13"/><path d="M8 12h13"/><path d="M8 18h13"/><circle cx="3.5" cy="6" r=".5" fill={p?.color||"currentColor"}/><circle cx="3.5" cy="12" r=".5" fill={p?.color||"currentColor"}/><circle cx="3.5" cy="18" r=".5" fill={p?.color||"currentColor"}/></>} />,
  BarChart: (p) => <Icon {...p} d={<><path d="M3 3v18h18"/><path d="M7 16V10"/><path d="M12 16V6"/><path d="M17 16v-8"/></>} />,
  Terminal: (p) => <Icon {...p} d={<><path d="m4 17 6-6-6-6"/><path d="M12 19h8"/></>} />,
  Wifi: (p) => <Icon {...p} d={<><path d="M5 12.55a11 11 0 0 1 14 0"/><path d="M1.42 9a16 16 0 0 1 21.16 0"/><path d="M8.53 16.11a6 6 0 0 1 6.95 0"/><circle cx="12" cy="20" r=".5" fill={p?.color||"currentColor"}/></>} />,
  Settings: (p) => <Icon {...p} d={<><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09a1.65 1.65 0 0 0-1-1.51 1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09a1.65 1.65 0 0 0 1.51-1 1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06a1.65 1.65 0 0 0 1.82.33h0a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51h0a1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82v0a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"/></>} />,
};

/* ============ DASHBOARD STAGE DEFS ============
   Match dashboard/components/EvaluationDashboard.tsx STAGES exactly. */
const STAGES = [
  { id: "init",        label: "Init",        icon: "Zap"       },
  { id: "planner",     label: "Planner",     icon: "Brain"     },
  { id: "sast",        label: "SAST Gate",        icon: "ScanSearch"},
  { id: "evaluator",   label: "Evaluator",   icon: "Scale"     },
  { id: "auditor",     label: "Auditor",     icon: "Gavel"     },
  { id: "remediation", label: "Generator", icon: "Wrench"    },
];

/* ============ CITATIONS ============
   Verbatim short extracts only. Each finding hyperlinks to source URL. */

const CITATIONS = {
  "uk-gdpr-32-1": {
    framework: "UK GDPR",
    ref: "Article 32(1)",
    title: "Security of processing",
    text: "Taking into account the state of the art, the costs of implementation and the nature, scope, context and purposes of processing as well as the risk of varying likelihood and severity for the rights and freedoms of natural persons, the controller and the processor shall implement appropriate technical and organisational measures to ensure a level of security appropriate to the risk.",
    url: "https://www.legislation.gov.uk/eur/2016/679/article/32",
    licence: "OGL-v3.0",
  },
  "owasp-a09-2025": {
    framework: "OWASP Top 10:2025",
    ref: "A09 — Security Logging and Monitoring Failures",
    title: "Security Logging and Monitoring Failures",
    text: "Insufficient logging, detection, monitoring, and active response occurs any time logging of auditable events, such as logins, failed logins, and high-value transactions are not logged. Logs of applications and APIs are not monitored for suspicious activity. Logs are only stored locally.",
    url: "https://owasp.org/Top10/A09_2025-Security_Logging_and_Monitoring_Failures/",
    licence: "CC-BY-SA 4.0",
  },
  "owasp-a03-2025": {
    framework: "OWASP Top 10:2025",
    ref: "A03 — Injection",
    title: "Injection",
    text: "An application is vulnerable to attack when user-supplied data is not validated, filtered, or sanitised by the application; dynamic queries or non-parameterised calls without context-aware escaping are used directly in the interpreter.",
    url: "https://owasp.org/Top10/A03_2025-Injection/",
    licence: "CC-BY-SA 4.0",
  },
  "cwe-532": {
    framework: "CWE Top 25:2025",
    ref: "CWE-532",
    title: "Insertion of Sensitive Information into Log File",
    text: "Information written to log files can be of a sensitive nature and give valuable guidance to an attacker or expose sensitive user information. Many web applications log sensitive information by default, especially when an exception occurs.",
    url: "https://cwe.mitre.org/data/definitions/532.html",
    licence: "MITRE",
  },
  "fca-sysc-13-7": {
    framework: "FCA SYSC",
    ref: "SYSC 13.7.1G",
    title: "Information security",
    text: "A firm should establish, implement and maintain appropriate systems and controls to manage its information-security risks, including risks to the confidentiality, integrity and availability of its data, having regard to the nature, scale and complexity of its business.",
    url: "https://www.handbook.fca.org.uk/handbook/SYSC/13/7.html",
    licence: "OGL-v3.0",
  },
  "uk-gdpr-44": {
    framework: "UK GDPR",
    ref: "Article 44",
    title: "General principle for transfers",
    text: "Any transfer of personal data which are undergoing processing or are intended for processing after transfer to a third country or to an international organisation shall take place only if, subject to the other provisions of this Regulation, the conditions laid down in this Chapter are complied with by the controller and processor.",
    url: "https://www.legislation.gov.uk/eur/2016/679/article/44",
    licence: "OGL-v3.0",
  },
  "dpa-2018-sch1": {
    framework: "Data Protection Act 2018",
    ref: "Schedule 1, Part 1, §1",
    title: "Conditions for processing of special categories of personal data",
    text: "This condition is met if the processing is necessary for the purposes of performing or exercising obligations or rights which are imposed or conferred by law on the controller or the data subject in connection with employment, social security or social protection.",
    url: "https://www.legislation.gov.uk/ukpga/2018/12/schedule/1",
    licence: "OGL-v3.0",
  },
  "owasp-a02-2025": {
    framework: "OWASP Top 10:2025",
    ref: "A02 — Cryptographic Failures",
    title: "Cryptographic Failures",
    text: "Determine the protection needs of data in transit and at rest. For example, passwords, credit card numbers, health records, personal information, and business secrets require extra protection, mainly if that data falls under privacy laws.",
    url: "https://owasp.org/Top10/A02_2025-Cryptographic_Failures/",
    licence: "CC-BY-SA 4.0",
  },
};

/* ============ PR FIXTURES ============
   Realistic scenarios for UK regulated industries — finance + health.
   Data shapes match useEvaluations.ts PullRequestRecord. */

const PRS = [
  {
    id: "pr-142",
    githubPrNumber: 142,
    repo: "example-bank/lending-core",
    title: "feat(patients): add free-text search endpoint",
    author: "alex-hodgson",
    headBranch: "feat/patient-search",
    baseBranch: "main",
    headSha: "8c4a1f2",
    diff: { add: 142, del: 18, files: 4 },
    status: "unsafe",
    verdict: "Unsafe",
    createdAt: minutesAgo(11),
    lastEvaluatedAt: minutesAgo(8),
    sast: { passed: false, findings: 3, critical: 1, high: 2 },
    findings: ["uk-gdpr-32-1", "owasp-a09-2025", "cwe-532"],
    reasoning: {
      planner: "Surface area: new HTTP route `GET /v1/patients/search?q=`. Touches `internal/handlers/patients.go`, `internal/logging/middleware.go`, and `migrations/0042_patient_index.sql`. Regulated-data path — patient identifiers, MRN, date of birth potentially in query parameters. Frameworks to consider: UK GDPR Art. 32, DPA 2018 Sch. 1, CWE-532, OWASP A03 / A09.",
      sast: "semgrep · 3 findings. CRITICAL: `log.Info` call on line 47 of patients.go writes the raw request URL into the log line, including the query parameter. HIGH: free-text `q` parameter is concatenated into a `LIKE` clause without parameter binding (patients.go:62). HIGH: no rate limiting on the search endpoint.",
      evaluator: "Two regulatory issues and one engineering correctness issue.\n\n(1) The structured logger writes the full request URL at INFO level. Search queries against a patient table are personal data under UK GDPR — the query string itself (e.g. `?q=smith+dob+1972-04`) becomes identifying personal data the moment it is logged. This is a clear Art. 32(1) failure: the appropriate technical measure here is request-line redaction in the logging middleware, which is missing.\n\n(2) Free-text `LIKE` concatenation: the parameter is sanitised by string-replacing single quotes, which is insufficient against the standard `%`/`_` wildcard escape vectors. Either parameterise or use the existing pg_trgm GIN index with `tsquery`.\n\n(3) No rate limit. Patient search is the highest-cost data-loss vector in the codebase if credentials leak.",
      auditor: "Adversarial review: I tried to argue this is acceptable because the logs are retained inside the customer's UK VPC. That argument fails. UK GDPR Art. 32(1) is about appropriate measures regardless of egress; logging identifiers in plaintext is not 'appropriate' when redaction is a one-line middleware change. The evaluator's verdict holds.",
      remediation: "Patch adds: (a) URL redaction in logging middleware for any path matching `/v1/patients/*`; (b) parameterised `tsquery` against the existing GIN index in place of `LIKE`; (c) rate limit at 30 req/min/user via the existing `internal/middleware/ratelimit` package. Test coverage extends `patients_test.go`. Opens as draft PR #142-fix for human review.",
    },
    patch: `--- a/internal/logging/middleware.go
+++ b/internal/logging/middleware.go
@@ -32,7 +32,11 @@ func RequestLogger(next http.Handler) http.Handler {
   return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
     start := time.Now()
     next.ServeHTTP(w, r)
-    log.Info("http", "method", r.Method, "path", r.URL.RequestURI(), "dur_ms", time.Since(start).Milliseconds())
+    path := r.URL.Path
+    if matchPatient(path) {
+        path = redactPatientPath(path) // strips ?q= and /:id segment
+    }
+    log.Info("http", "method", r.Method, "path", path, "dur_ms", time.Since(start).Milliseconds())
   })
 }
--- a/internal/handlers/patients.go
+++ b/internal/handlers/patients.go
@@ -58,9 +58,12 @@ func (h *Handlers) SearchPatients(w http.ResponseWriter, r *http.Request) {
   q := r.URL.Query().Get("q")
-  rows, err := h.db.Query(\`SELECT id, mrn, name FROM patients
-                          WHERE name ILIKE '%\` + sanitise(q) + \`%' LIMIT 50\`)
+  if err := h.limiter.Allow(r.Context(), userID(r), 30, time.Minute); err != nil {
+      http.Error(w, "rate limited", http.StatusTooManyRequests)
+      return
+  }
+  rows, err := h.db.Query(\`SELECT id, mrn, name FROM patients
+                          WHERE search_vec @@ plainto_tsquery($1) LIMIT 50\`, q)
   if err != nil { ... }`,
  },

  {
    id: "pr-141",
    githubPrNumber: 141,
    repo: "example-bank/lending-core",
    title: "chore(deps): bump axios from 1.7.4 to 1.7.7",
    author: "dependabot[bot]",
    headBranch: "dependabot/npm_and_yarn/axios-1.7.7",
    baseBranch: "main",
    headSha: "f193a8b",
    diff: { add: 4, del: 4, files: 2 },
    status: "safe",
    verdict: "Safe",
    createdAt: minutesAgo(34),
    lastEvaluatedAt: minutesAgo(32),
    sast: { passed: true, findings: 0, critical: 0, high: 0 },
    findings: [],
    reasoning: {
      planner: "Pure dependency bump. `package.json` + `package-lock.json` only. Verifying advisory feed for axios@1.7.4..1.7.7 and confirming no breaking API change against the project's call sites.",
      sast: "semgrep · 0 findings. Diff scope is package files only.",
      evaluator: "Advisory check: 1.7.5 closes GHSA-jr5f-v2jv-69x6 (SSRF on internal IP literal handling). Project uses axios in `internal/integrations/companies-house` and `internal/integrations/credit-bureau` — both with allowlisted hosts via `httpAgent`, so the bump is a net security improvement, not a regression. No call-site changes needed.",
      auditor: "Tried to find a reason to block — none. Allowlist is the controlling defence; the patched CVE strengthens it. Verdict holds.",
    },
  },

  {
    id: "pr-140",
    githubPrNumber: 140,
    repo: "example-bank/lending-core",
    title: "feat(auth): rotate session cookie on privilege change",
    author: "priya-r",
    headBranch: "feat/session-rotation",
    baseBranch: "main",
    headSha: "2db4f51",
    diff: { add: 38, del: 6, files: 3 },
    status: "safe",
    verdict: "Safe",
    createdAt: minutesAgo(67),
    lastEvaluatedAt: minutesAgo(64),
    sast: { passed: true, findings: 0, critical: 0, high: 0 },
    findings: [],
    reasoning: {
      planner: "Touches `internal/auth/session.go` and `internal/auth/rbac_middleware.go`. Adds a `Rotate()` call after any role mutation. Relevant frameworks: SYSC 13.7 (information security), OWASP A07 (Identification and Authentication Failures).",
      sast: "semgrep · 0 findings. Cookie flags `Secure; HttpOnly; SameSite=Strict` already enforced by the existing session helper.",
      evaluator: "This is a defensive change that closes a session-fixation-on-privilege-escalation window. The rotation issues a fresh opaque token and invalidates the previous one server-side via the existing Redis revocation set. CSRF tokens are derived from the session token and so rotate transitively. Approved.",
      auditor: "Adversarial check: what about WebSocket reconnect using the stale token? Verified that the WS handler reads the session on every frame's auth header, not on connection-open, so a stale token cannot persist. Verdict holds.",
    },
  },

  {
    id: "pr-139",
    githubPrNumber: 139,
    repo: "example-bank/lending-core",
    title: "refactor(payments): cache stripe customer in redis",
    author: "james-okeke",
    headBranch: "refactor/stripe-cache",
    baseBranch: "main",
    headSha: "6a08c2e",
    diff: { add: 84, del: 12, files: 5 },
    status: "escalated",
    verdict: "Escalated",
    createdAt: minutesAgo(95),
    lastEvaluatedAt: minutesAgo(91),
    sast: { passed: true, findings: 1, critical: 0, high: 1 },
    findings: ["fca-sysc-13-7", "owasp-a02-2025"],
    reasoning: {
      planner: "Caches Stripe Customer object — including default_source.last4, default_source.brand, and address fields — in Redis for 24h. Touches `internal/integrations/stripe/customer_cache.go` (new), `internal/integrations/stripe/client.go`, and Redis key namespace.",
      sast: "semgrep · 1 finding. HIGH: Redis key `stripe:cust:{id}` does not encrypt-at-rest the cached payload. Cardholder-adjacent data outside the existing AES-GCM key envelope used by the customer-profile cache.",
      evaluator: "Engineering judgement call. The cached fields are not full PAN (which Stripe never returns) but they are cardholder-adjacent and the firm's FCA SYSC 13.7 control map requires consistent encryption of payment-context data across all storage. The change is correct in intent — Stripe rate-limit pressure is real — but it should reuse the AES-GCM envelope and not invent a new Redis key path. Escalating to security review for sign-off on whether the AES-GCM envelope is the right control here or whether a tokenised Stripe Customer ID lookup is preferred.",
      auditor: "Cannot resolve this without human input from security. Escalation is the correct outcome. Tagged @security-on-call for review; PR is left in `escalated` state with a summary comment on GitHub.",
    },
  },

  {
    id: "pr-138",
    githubPrNumber: 138,
    repo: "example-bank/lending-core",
    title: "fix(audit-log): migrate to s3 lifecycle policy",
    author: "ops-bot",
    headBranch: "fix/audit-s3-lifecycle",
    baseBranch: "main",
    headSha: "ae90f73",
    diff: { add: 56, del: 24, files: 3 },
    status: "remediated",
    verdict: "Unsafe",
    createdAt: minutesAgo(180),
    lastEvaluatedAt: minutesAgo(175),
    sast: { passed: true, findings: 1, critical: 0, high: 1 },
    findings: ["uk-gdpr-44"],
    reasoning: {
      planner: "Terraform change. Migrates `audit-log-bucket` from custom lifecycle Lambda to a native S3 lifecycle policy. Touches `infra/audit-bucket.tf`. Data-residency relevant: audit logs contain customer-action records that fall under UK GDPR.",
      sast: "semgrep · 1 finding. HIGH: bucket region defaults to `us-east-1` in the new module call; previous bucket was pinned `eu-west-2`.",
      evaluator: "Region default is wrong. UK customer audit logs cannot transit out of UK without an Article 44 transfer mechanism, and this firm has not stood up the UK Addendum + SCCs for S3 cross-region. The fix is one line; the rest of the PR is good.",
      auditor: "Pulled in the previous bucket's policy ARN and confirmed the audit log retention requirement (7y) is preserved by the lifecycle policy. Region issue is the only blocker.",
      remediation: "Patch sets `region = \"eu-west-2\"` and adds a `precondition` block asserting it. Opens as draft PR #138-fix; the original PR is left in `remediated` state pending human merge.",
    },
    patch: `--- a/infra/audit-bucket.tf
+++ b/infra/audit-bucket.tf
@@ -3,7 +3,7 @@
 module "audit_bucket" {
   source = "./modules/s3-lifecycle"
   name   = "audit-log-bucket-prod"
-  region = "us-east-1"
+  region = "eu-west-2"

   lifecycle_rules = [
     { id = "compliance-7y", transition_after_days = 90, storage_class = "GLACIER_IR" },
@@ -28,4 +28,9 @@ module "audit_bucket" {
       },
     ]
   })
+
+  lifecycle {
+    # UK customer audit data — must stay in-region. See UK GDPR Art. 44.
+    precondition { condition = var.region == "eu-west-2"  error_message = "Audit bucket region must be eu-west-2" }
+  }
 }`,
  },

  {
    id: "pr-137",
    githubPrNumber: 137,
    repo: "example-bank/lending-core",
    title: "feat(prescriptions): allow dosage override by clinician",
    author: "sarah-knight",
    headBranch: "feat/dosage-override",
    baseBranch: "main",
    headSha: "0c91da5",
    diff: { add: 67, del: 4, files: 6 },
    status: "safe",
    verdict: "Safe",
    createdAt: hoursAgo(4),
    lastEvaluatedAt: hoursAgo(4),
    sast: { passed: true, findings: 0, critical: 0, high: 0 },
    findings: [],
    reasoning: {
      planner: "Adds a `dosage_override` field to `Prescription` with an audit trail. Touches `internal/clinical/prescription.go`, `migrations/0043_dosage_override.sql`, and three test files. Clinical-decision change; ICO health-data guidance applies.",
      sast: "semgrep · 0 findings.",
      evaluator: "The override is gated by an RBAC check that already exists (`role = clinician_prescriber`) and writes an immutable audit row including the original value, the override value, the clinician ID, and the reason text. This is the right shape for ICO health-data audit-trail expectations. No regulatory blocker.",
      auditor: "Adversarial check: can a non-prescriber spoof this by setting the field directly via the JSON API? Verified — the field is on the input schema's deny-list at the handler layer and rejected before reaching the service. Verdict holds.",
    },
  },

  {
    id: "pr-136",
    githubPrNumber: 136,
    repo: "example-bank/lending-core",
    title: "feat(api): add /me endpoint returning user profile",
    author: "alex-hodgson",
    headBranch: "feat/me-endpoint",
    baseBranch: "main",
    headSha: "1f4b827",
    diff: { add: 41, del: 0, files: 3 },
    status: "evaluating",
    verdict: null,
    createdAt: minutesAgo(2),
    lastEvaluatedAt: null,
    sast: null,
    findings: [],
    reasoning: {},
  },
];

function minutesAgo(m) { return new Date(Date.now() - m * 60 * 1000).toISOString(); }
function hoursAgo(h)   { return new Date(Date.now() - h * 3600 * 1000).toISOString(); }

function timeAgo(iso) {
  const s = Math.floor((Date.now() - new Date(iso).getTime()) / 1000);
  if (s < 60)  return `${s}s ago`;
  const m = Math.floor(s / 60);
  if (m < 60)  return `${m}m ago`;
  const h = Math.floor(m / 60);
  if (h < 24)  return `${h}h ago`;
  return `${Math.floor(h / 24)}d ago`;
}

/* ============ STATUS BADGE ============ */
const STATUS_STYLE = {
  pending:    { bg: "#1e293b", text: "#94a3b8", label: "Pending" },
  evaluating: { bg: "#1e3a5f", text: "#60a5fa", label: "Evaluating" },
  safe:       { bg: "#0f2e1a", text: "#22c55e", label: "Safe" },
  unsafe:     { bg: "#2d1215", text: "#ef4444", label: "Unsafe" },
  remediated: { bg: "#1a2e1a", text: "#4ade80", label: "Remediated" },
  escalated:  { bg: "#2d2415", text: "#eab308", label: "Escalated" },
  merged:     { bg: "#1e1a2e", text: "#a78bfa", label: "Merged" },
};

Object.assign(window, { Icon, Icons, STAGES, CITATIONS, PRS, STATUS_STYLE, timeAgo });
